匿名名称空间结构是否唯一

时间:2019-03-25 02:11:07

标签: c++

我想我知道匿名名称空间可以用于使符号在当前翻译单元中本地化。但是结构定义呢?我可以假设它们确实引用了相同的类型吗?

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 ?
}

2 个答案:

答案 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的定义不满足这些要求,则行为未定义。

MyClassclass ::A定义中的一个名称,但引用两个不同的实体,并且不属于任何明确允许的类别。

这在许多系统中实际上都可行,因为两个翻译单元将在它们自己的MyClass类型中看到相同的成员名称,类型和偏移量。但是,如果MyClass最终被用在“名称修改”中,那将会出错。而且无论如何,最安全的方法是避免未定义的行为。