为什么我不能在未命名的命名空间中声明变量后在全局范围内定义变量?

时间:2016-10-24 18:45:49

标签: c++ linkage

根据我的理解:

我希望以下内容声明一个变量,然后定义相同的变量:

// Declare ::<unnamed>::my_obj with internal linkage and add my_obj to the
// enclosing namespace scope (the global namespace scope).
namespace { extern int my_obj; }

// Expected: Define the variable declared above.
// Actual: Declare and define a new object, ::my_obj.
int my_obj(42);

相反,它声明了两个不同的对象,并警告我未使用的extern int my_obj

为什么第二个my_obj没有定义第一个my_obj?不在范围内吗?

4 个答案:

答案 0 :(得分:3)

在未命名的命名空间中声明的语句&#34;名称将被添加到封闭的命名空间范围内&#34; 并不意味着未命名的命名空间的成员成为封闭命名空间的成熟成员。此添加由隐式using-directive 执行。这样的添加使这些名称对名称查找可见,但不会为了其他目的而使它们成为封闭名称空间的文字成员。

您的代码中存在的问题与以下代码段中的问题相同

namespace A
{
  void foo();
}

using namespace A;

void foo() // <- Defines `::foo`, not `A::foo`
{
}

// `A::foo` remains undefined

尽管事实上我们明确地添加了#34;从A到全局命名空间的名称,它仍然不意味着我们可以使用非限定名称A::foo定义 foo作为全局命名空间的成员。

为了在上面的示例中定义A::foo,您仍然必须使用其限定名称A::foo来引用它。对于未命名的命名空间,由于显而易见的原因,这是不可能的。

P.S。编译器通常使用内部编译器生成的名称将未命名的名称空间实现为名称空间。如果你以某种方式想出一个&#34; hack&#34;要发现该名称,从技术上讲,您可能可以通过在定义中使用限定名称来单独定义my_obj。但请记住,隐藏的命名空间名称在不同的翻译单元中是不同的,因此在每个翻译单元中生成唯一的my_obj变量。

答案 1 :(得分:1)

未命名的命名空间不是全局命名空间。它是一个特定的命名空间,只在它出现的翻译单元中可见。

This cppreference page更详细地描述了这一点。

如果您尝试在同一个翻译单元中使用未命名的命名空间中定义的符号 - 只需使用它!它的范围很广。您不需要extern

答案 2 :(得分:0)

来自https://msdn.microsoft.com/en-us/library/2tx32sw2.aspx

使用extern存储类说明符声明的变量是对在程序的任何源文件中在外部级别定义的具有相同名称的变量的引用。内部extern声明用于使块内的外部变量定义可见。 除非在外部声明,否则使用extern关键字声明的变量仅在声明它的块中可见。

答案 3 :(得分:-1)

由于对象是在名称空间中声明的,因此必须在同一名称空间中定义(或者更确切地说,如果名称在一个名称空间中用作声明而在该名称空间外使用定义,则它指的是两个不同的实体)。它在文件范围内可见并不意味着它是该命名空间的成员;我认为断言 -

  

在未命名的命名空间中声明的名称将添加到封闭的名称中   命名空间范围

- 并不代表你的想法。

C ++ 11标准说一个未命名的命名空间定义的行为就好像它被替换为:

[inline] namespace unique { /* empty body */ }
using namespace unique ;
namespace unique { namespace-body }

(格式化稍微解释;初始&#34;内联&#34;是可选的,如果出现在未命名的命名空间定义中,则显示。)

那么,你期待这个:

namespace a_name {
    extern int a;
}
using namespace a_name;
int a = 4;

...第一次提到a(在命名空间内)会声明变量,第二次定义它吗? (如果你愿意的话,至少这是一致的。但如果你不这样做,你需要认识到标准明确规定你应该从未命名的命名空间中获得相同的行为。)

我不相信有任何机制可以在一个命名空间中声明成员并为另一个命名空间中的同一成员提供定义,即使该成员在第二个命名空间中可见。

解决以下评论:

  1. 是的,extern导致声明不是定义。我已经删除了extern没有效果的声明,主要是指链接;由于C ++ 11(包括修订),extern未在未命名的命名空间中授予外部链接的名称。虽然在C ++ 03中它可能在技术上授予了外部链接,但这只是声明/定义的一个属性;该符号在翻译单元外部仍然不可见,并且对链接器没有特殊的可见性。 extern对C ++ 03中未命名命名空间成员的重要性显然可归结为该成员作为常量表达式的可用性,如果它是const - 限定指针。
  2. 评论中的example that was linked,跟随另一条评论,指出链接确定符号对链接器的可见性可能产生后果,可能是为了证明(在C ++ 11中)未命名的命名空间中的符号可以有外部链接,并且即使在声明const时,非外部链接指针也不能用作模板参数(即需要常量表达式的上下文)。它确实无法与GCC编译,但它与Clang和英特尔编译器Icc成功编译。对于三个编译器中的任何一个,在内联命名空间中声明的符号是链接级别的局部符号(例如,可以使用objdump看到) - 链接器级别的符号可见性与之间没有因果关系产生的错误。

    C ++ 11明确地声明了一个声明(3.5) extern - 符合条件的符号如果有,则应具有内部联系 驻留在未命名的命名空间中(具体为:具有的名称 上面没有给出内部链接的命名空间范围与封闭命名空间具有相同的链接,如果它是 - 变量的名称;或者...... )。此外,const - 具有内部链接的声明变量应该可以在需要常量表达的上下文中使用。通过阅读标准,很少有人认为GCC编译器在这种情况下表现不正确。