不同C++文件中的相同类名

Cyk*_*ker 22 c++ class name-clash

如果两个C++文件具有相同名称的类的不同定义,那么当它们被编译和链接时,即使没有警告也会抛出某些内容.例如,

// a.cc
class Student {
public:
    std::string foo() { return "A"; }
};
void foo_a()
{
    Student stu;
    std::cout << stu.foo() << std::endl;
}

// b.cc
class Student {
public:
    std::string foo() { return "B"; }
};
void foo_b()
{
    Student stu;
    std::cout << stu.foo() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

当使用g ++编译和链接在一起时,两者都将输出"A"(如果a.cc在命令行顺序中位于b.cc之前).

这里有类似的话题.我看到命名空间将解决这个问题,但我不知道为什么链接器甚至不会发出警告.如果该类的一个定义具有未在另一个中定义的额外函数,则说b.cc更新为:

// b.cc
class Student {
public:
    std::string foo() { return "B"; }
    std::string bar() { return "K"; }
};
void foo_b()
{
    Student stu;
    std::cout << stu.foo() << stu.bar() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

然后stu.bar()效果很好.感谢任何能告诉我编译器和链接器如何在这种情况下工作的人.

作为一个额外的问题,如果类在头文件中定义,它们是否应始终用未命名的命名空间包装以避免这种情况?有副作用吗?

Mic*_*urr 19

这违反了一个定义规则(C++ 03,3.2/5"一个定义规则"),它说(除其他外):

可以有多个类类型的定义(第9节),......在程序中,每个定义出现在不同的转换单元中,并且定义满足以下要求.鉴于这样一个名为D的实体在多个翻译单元中定义,那么

  • D的每个定义应由相同的令牌序列组成;

如果违反了一个定义规则,则行为未定义(这意味着可能发生奇怪的事情).

链接器看到多个定义Student::foo()- 一个在对象文件中,一个在b中.然而它并没有抱怨这个; 它只选择两个中的一个(碰巧,它遇到的第一个).重复函数的这种"软"处理显然只发生在内联函数中.对于非内联函数,链接器抱怨多个定义,并拒绝生成可执行文件(可能有一些选项可以放宽此限制).GNU ld和MSVC的链接器都以这种方式运行.

这种行为有一定道理; 内联函数需要在它们使用的每个翻译单元中可用.在一般情况下,它们需要具有非内联版本(如果调用未内联或者函数的地址被采用). inline实际上只是围绕单一定义规则的免费传递 - 但要使其工作,所有内联定义都必须相同.

当我查看目标文件的转储时,我没有看到任何明显的东西向我解释链接器如何知道允许一个函数有多个定义而其他函数没有,但我确定有一些标记或记录就是这样.不幸的是,我发现链接器和目标文件细节的工作方式并没有特别详细记录,因此精确的机制对我来说可能仍然是一个谜.

至于你的第二个问题:

作为一个额外的问题,如果类在头文件中定义,它们是否应始终用未命名的命名空间包装以避免这种情况?有副作用吗?

你几乎肯定不想这样做,每个类在每个翻译单元中都是一个独特的类型,因此技术上类的实例不能从一个翻译单元传递到另一个翻译单元(通过指针,引用或复制).此外,您最终会得到任何静态成员的多个实例.这可能不会很好.

将它们放在不同的命名空间中.