我想我知道匿名名称空间可以用于使符号在当前翻译单元中本地化。但是结构定义呢?我可以假设它们确实引用了相同的类型吗?
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 ?
}
答案 0 :(得分:1)
否,它们没有引用相同的类型。标头MyClass.h
在未命名的命名空间中包含类类型MyClass
的定义。未命名的命名空间基本上使内部的所有内容(是,也有类型)具有内部链接[basic.link]/6。您有两个翻译单元,每个翻译单元(间接)都包含MyClass.h
,每个翻译单元都具有自己的MyClass
[basic.link]/11命名空间。
将未命名的命名空间视为是每个翻译单元都有不同名称的命名空间。因此,翻译单元A中的MyClass
实际上是$somerandomstringA$::MyClass
,而翻译单元B中的MyClass
实际上是$somerandomstringB$::MyClass
…
正如对该答案的注释中所讨论的那样,请注意以下事实:由于定义了类A
,因此您所描述的程序将包含ODR冲突(特别是[basic.def.odr]/12.2)包含类型为MyClass*
的成员,该成员在不同的翻译单元中具有不同的含义。
答案 1 :(得分:0)
该程序具有未定义的行为,因为每个翻译单元都定义了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
的事物以及带有模板参数的声明之类的东西,并说多个TU可能每个都有一个定义,但是所有必须具有相同的标记拼写(经过预处理)和相同的含义;因此我们经常将它们放在头文件中,以便多个TU可以使用相同的定义。)
具体在这里,程序违反了[basic.def.odr]/12.2:
一个类的定义可以有多个,...在程序中,前提是每个定义出现在不同的翻译单元中,并且这些定义满足以下要求。给定这样一个名为
D
的实体,它在多个翻译单元中定义,则
- 在
...;和
D
的每个定义中,对应的名称(根据[basic.lookup]查找)应指代
D
定义内定义的实体,或指代相同的实体,在重载解析之后并且在匹配部分模板专业化([temp.over])之后,除了名称可以引用
具有内部链接或不具有链接的非易失性const对象,如果...,或者
具有内部链接或不具有内部链接的引用,并使用常量表达式初始化,使得...;
和....
...如果
D
的定义不满足这些要求,则行为未定义。
MyClass
是class ::A
定义中的一个名称,但引用两个不同的实体,并且不属于任何明确允许的类别。
这在许多系统中实际上都可行,因为两个翻译单元将在它们自己的MyClass
类型中看到相同的成员名称,类型和偏移量。但是,如果MyClass
最终被用在“名称修改”中,那将会出错。而且无论如何,最安全的方法是避免未定义的行为。