能不能在不同的翻译单元有两个同名同成员函数的类?
Can you have two classes with the same name and the same member function in different translation units?
假设我有两个翻译单元:
//A.cpp
class X
{
};
//B.cpp
class X
{
int i;
};
上面的程序是否合式?
如果没有,没有进一步的问题。如果答案是肯定的,程序是良构的(忽略main的缺失),那么第二个问题。如果其中有同名函数怎么办?
//A.cpp
class X
{
void f(){}
};
//B.cpp
class X
{
int i;
void f(){}
};
这对链接器来说是个问题吗,因为它会在两个目标文件中看到 &X::f ?在这种情况下是否必须使用匿名名称空间?
Is the above program well-formed?
没有。它违反了 One-Definition Rule:
[basic.def.odr]
There can be more than one definition of a
- class type ([class]),
- ...
in a program provided that each definition appears in a different translation unit and the definitions satisfy the following requirements.
Given such an entity D defined in more than one translation unit, for all definitions of D, or, if D is an unnamed enumeration, for all definitions of D that are reachable at any given program point, the following requirements shall be satisfied.
- ...
- Each such definition shall consist of the same sequence of tokens, where the definition of a closure type is ...
- ...
Are anonymous namespaces a must in such a situation?
如果您需要不同的 class 定义,它们必须是不同的类型。唯一命名的命名空间是一种选择,匿名命名空间是获得唯一(对翻译单元)命名空间的保证方式。
简短版
嗯,不...C++ 假设名称空间中的每个名称都是唯一的。如果你打破这个假设,你有 0 个保证它会工作。
例如,如果您在两个翻译单元(*.o
个文件)中有同名的方法。链接器不知道给定的调用使用哪一个,所以它只会 return 一个错误。
长版
...但实际上是的!
实际上在很多情况下,您可以用同名的 classes/methods 逃脱。
实际上不要在您的程序中使用任何这些技巧!如果编译器认为它会优化生成的程序,那么他们可以自由地做几乎任何事情,因此下面的任何假设都可能被打破。
类 是最简单的。让我们以一些只有 non-static 成员且没有函数的 class 为例。这样的事情甚至不会在编译后的程序中留下任何痕迹。 Classes/structs只是程序员组织数据的工具,所以不需要处理内存池和偏移量。
所以基本上如果你有两个 classes 在不同的编译单元中具有相同的名称,它应该可以工作。编译器处理完它们后,它们将仅包含几条指令,说明将指针在内存中移动多少以访问特定字段。
这里几乎没有任何东西会混淆链接器。
函数和变量(包括静态 class 变量)更棘手,因为编译器经常在 *.o
文件中为它们创建符号。如果你幸运的话,如果没有使用这样的 function/variable ,链接器可能会忽略它们,但我什至不会指望它。
但是,有一些方法可以省略为它们创建符号。静态全局元素或匿名名称空间中的元素在其翻译单元之外不可见,因此链接器不应抱怨它们。此外,内联函数不作为单独的实体存在,因此它们也没有符号,这在这里尤其重要,因为在 classes 内定义的函数默认是内联的。
如果没有符号,链接器不会发现冲突,一切都应该编译。
模板也使用了一些肮脏的技巧,因为它们是在使用它们的每个编译单元中按需编译的,但它们最终在最终程序中作为一个副本结束。我认为这与多个 不同 同名事物的情况不同,所以让我们放弃这个话题。
总而言之,如果您的 classes 没有静态成员并且它们不在其主体之外定义函数,则可能有两个同名的 classes只要您不将它们包含在同一个文件中。
但是,这是非常脆弱的。即使它现在可以工作,新版本的编译器也可能有一些 fix/optimization/change 会破坏这样的程序。
更不用说包含在更大的项目中往往交织在一起的事实,所以很有可能在某个时候你需要将两个文件包含在同一个地方。
假设我有两个翻译单元:
//A.cpp
class X
{
};
//B.cpp
class X
{
int i;
};
上面的程序是否合式?
如果没有,没有进一步的问题。如果答案是肯定的,程序是良构的(忽略main的缺失),那么第二个问题。如果其中有同名函数怎么办?
//A.cpp
class X
{
void f(){}
};
//B.cpp
class X
{
int i;
void f(){}
};
这对链接器来说是个问题吗,因为它会在两个目标文件中看到 &X::f ?在这种情况下是否必须使用匿名名称空间?
Is the above program well-formed?
没有。它违反了 One-Definition Rule:
[basic.def.odr]
There can be more than one definition of a
- class type ([class]),
- ...
in a program provided that each definition appears in a different translation unit and the definitions satisfy the following requirements. Given such an entity D defined in more than one translation unit, for all definitions of D, or, if D is an unnamed enumeration, for all definitions of D that are reachable at any given program point, the following requirements shall be satisfied.
- ...
- Each such definition shall consist of the same sequence of tokens, where the definition of a closure type is ...
- ...
Are anonymous namespaces a must in such a situation?
如果您需要不同的 class 定义,它们必须是不同的类型。唯一命名的命名空间是一种选择,匿名命名空间是获得唯一(对翻译单元)命名空间的保证方式。
简短版
嗯,不...C++ 假设名称空间中的每个名称都是唯一的。如果你打破这个假设,你有 0 个保证它会工作。
例如,如果您在两个翻译单元(*.o
个文件)中有同名的方法。链接器不知道给定的调用使用哪一个,所以它只会 return 一个错误。
长版
...但实际上是的!
实际上在很多情况下,您可以用同名的 classes/methods 逃脱。
实际上不要在您的程序中使用任何这些技巧!如果编译器认为它会优化生成的程序,那么他们可以自由地做几乎任何事情,因此下面的任何假设都可能被打破。
类 是最简单的。让我们以一些只有 non-static 成员且没有函数的 class 为例。这样的事情甚至不会在编译后的程序中留下任何痕迹。 Classes/structs只是程序员组织数据的工具,所以不需要处理内存池和偏移量。 所以基本上如果你有两个 classes 在不同的编译单元中具有相同的名称,它应该可以工作。编译器处理完它们后,它们将仅包含几条指令,说明将指针在内存中移动多少以访问特定字段。 这里几乎没有任何东西会混淆链接器。
函数和变量(包括静态 class 变量)更棘手,因为编译器经常在 *.o
文件中为它们创建符号。如果你幸运的话,如果没有使用这样的 function/variable ,链接器可能会忽略它们,但我什至不会指望它。
但是,有一些方法可以省略为它们创建符号。静态全局元素或匿名名称空间中的元素在其翻译单元之外不可见,因此链接器不应抱怨它们。此外,内联函数不作为单独的实体存在,因此它们也没有符号,这在这里尤其重要,因为在 classes 内定义的函数默认是内联的。
如果没有符号,链接器不会发现冲突,一切都应该编译。
模板也使用了一些肮脏的技巧,因为它们是在使用它们的每个编译单元中按需编译的,但它们最终在最终程序中作为一个副本结束。我认为这与多个 不同 同名事物的情况不同,所以让我们放弃这个话题。
总而言之,如果您的 classes 没有静态成员并且它们不在其主体之外定义函数,则可能有两个同名的 classes只要您不将它们包含在同一个文件中。 但是,这是非常脆弱的。即使它现在可以工作,新版本的编译器也可能有一些 fix/optimization/change 会破坏这样的程序。 更不用说包含在更大的项目中往往交织在一起的事实,所以很有可能在某个时候你需要将两个文件包含在同一个地方。