我想我理解了匿名命名空间可以用来使符号成为当前翻译单元的本地符号。但是,关于结构定义,我是否可以假设它们是指同一类型的呢?
MyClass.h:
namespace {
class MyClass {};
}A.h:
#include "MyClass.h"
class A {
MyClass* impl;
void op();
}A.cpp翻译单元1:
#include "A.h"
void A::op() {
// Let *this->impl refer to a type X.
}B.cpp翻译单元2:
#include "A.h"
void global_op(const A& a) {
// Can I assume that *a->impl refer to same type X ?
}发布于 2019-03-25 02:44:06
不,他们指的不是同一类型。标头MyClass.h包含未命名命名空间中的类类型MyClass的定义。一个未命名的名称空间基本上使它内的所有东西(是的,类型也一样)都有内部链接[basic.link]/6。您有两个翻译单元,每个翻译单元(间接地)包括MyClass.h,每个翻译单元都使用自己的MyClass [basic.link]/11获取自己的未命名命名空间。
把一个未命名的名称空间看作是一个名称空间,它对每个翻译单元都有一个不同的名称。所以翻译单元A中的MyClass实际上是$somerandomstringA$::MyClass,而翻译单元B中的MyClass实际上是$somerandomstringB$::MyClass…
正如对此答案的注释中所讨论的,请注意,您上面描述的程序将包含一个ODR冲突(特别是[basic.def.odr]/12.2),因为您的类A被定义为包含MyClass*类型的一个成员,该成员在不同的翻译单元中有不同的含义。
发布于 2019-03-25 03:23:08
这个程序有未定义的行为,因为每个翻译单元都定义了class ::A,但是有两个不同的含义。
匿名命名空间具有内部链接([basic.link]/4)。类型MyClass与其命名空间具有相同的链接,内部链接([basic.link]/4.3)也是如此。内部链接意味着类型只能从相同的翻译单元中命名,因此由A.cpp和B.cpp形成的两个翻译单元定义了两种不同类型的MyClass。这还不是个问题。
但是全局命名空间和类A有外部链接。单个类型的::A有两个定义,但是它们给成员impl两种不同的类型。这是一个定义规则的违反。
(虽然我们经常说“ODR",但实际上有两种方式:[basic.def.odr]/10适用于对象和函数,这些对象和函数是命名空间成员,而不是标记为inline,并且说程序只能在一个TU中有一个定义;所以我们通常将这些定义放在源文件中。[basic.def.odr]/12适用于类型、标记的inline和带有模板参数的声明,并表示多个TUs可能每个都有一个定义,但它们都必须具有相同的令牌拼写(预处理后)和相同的含义;因此我们经常将这些内容放在头文件中,以便多个TUs可以使用一个通用定义。
特别是在这里,这个程序违反了[basic.def.odr]/12.2
可以有多个类类型的定义,.在程序中,每个定义都出现在不同的翻译单元中,并提供满足以下要求的定义。如果在多个翻译单元中定义了这样一个名为
D的实体,那么
D的每个定义中,根据basic.lookup查找的相应名称应指在D定义内定义的实体,或在重载解析后和部分模板专门化(temp.over)匹配后引用同一实体,但名称可以引用- a non-volatile const object with internal or no linkage if ..., or- a reference with internal or no linkage initialized with a constant expression such that ...;还有..。
..。如果D的定义不能满足这些要求,那么行为就没有定义。
这里,MyClass是class ::A定义中的一个名称,但它指的是两个不同的实体,并不属于任何特定允许的类别。
这在许多系统中都是可行的,因为这两个翻译单元都会在自己的MyClass类型中看到相同的成员名称、类型和偏移量。但是,如果MyClass最终被用于“名称损坏”,那将是错误的。无论如何,尽可能避免未定义的行为是最安全的。
https://stackoverflow.com/questions/55330459
复制相似问题