【问题标题】:C++: Linking extern variables from within a namespaceC++:从命名空间内链接外部变量
【发布时间】:2011-05-11 18:47:11
【问题描述】:

我似乎无法使用extern 从命名空间内引用外部定义的变量。它在全局范围内工作,但是一旦将名称空间投入其中,它就无法链接。

我的常量文件如下所示:

StringConstants.cpp

#include "MyString.h"

MyString test1("string1");

MyString test2("string2");

主程序如下所示:

main.cpp

#include <stdio.h>
#include "MyString.h"

extern MyString test1;

namespace {
    extern MyString test2;
}

int main(void) {
    printf("%s\n", test1.Str());
    printf("%s\n", test2.Str());
}

我在 GCC 和 Visual Studio 中都遇到了类似的错误:

gcc    main.o StringConstants.o   -o main
main.o:main.cpp:(.text+0x49): undefined reference to `(anonymous namespace)::test2'
collect2: ld returned 1 exit status

1>Linking...
1>main.obj : error LNK2001: unresolved external symbol "class MyString `anonymous namespace'::test2" (?test2@?A0x0df4aa01@@3VMyString@@A)
1>C:\p4\namespace_repro\namespace_repro2\Debug\namespace_repro2.exe : fatal error LNK1120: 1 unresolved externals

我尝试限定对 test2 (extern MyString ::test2) 的引用,但它只是认为 test2 是 MyString 的静态成员。命名命名空间的行为与匿名命名空间没有区别。出于各种原因,我们不想删除命名空间或将 externs 放在命名空间之外。

为了完整起见,这是其他文件:

MyString.h

class MyString {
public:
   MyString(const char* str): mStr(str) {};
   const char* Str() const { return mStr; }
private:
   const char* mStr; 
};

Makefile

CC=gcc 
CFLAGS=-Wall

main: StringConstants.o main.o

该系统的目标是所有常量都定义在一个文件中,并且它们在链接时被解析,而不是在标题中。上面的代码似乎可以工作,但由于它被两个不同的链接器拒绝,我对 C++ 的理解似乎还不够好。关于如何让它工作的建议,除了将外部放在命名空间之外?

【问题讨论】:

    标签: c++ namespaces linker constants extern


    【解决方案1】:

    什么...

    namespace { 
        extern MyString test2; 
    } 
    

    ...确实是说 test2 应该存在于匿名命名空间中,但它不存在 - 它位于全局命名空间中。您对编译器撒了谎,因此它会生成一个不会链接的对象。您需要在与变量相同的命名空间范围内进行 extern 声明。

    但是,应该有一个 StringConstants.h,main.cpp 应该包含它,这样编译单元就可以知道这些字符串而无需任何进一步的语句。

    【讨论】:

    • +1,匿名命名空间是这里的罪魁祸首。请注意,无论是否在标头中,他仍然需要使用 extern。
    • 拥有 StringConstants.h 的问题在于,当有人修改或添加常量时(这很常见),它基本上需要重新编译程序中的每个文件。有什么方法可以告诉编译器我要引用全局 test2?
    • @breath:如果事情如此不稳定,那么没有共享标头会更糟:当有人意识到应该将某个值从 int 更改为 double 时会发生什么?程序在运行时不断构建和链接并失败。如果共享标头真的太难了,那么考虑将其拆分为单独的标头,以用于较少翻译单元需要的任何易失部分。考虑 pImpl、接口和其他有助于将客户端与实现分离的抽象。
    • 嗯,它们都是字符串常量,所以没有人会改变任何类型。我们可以接受缺少常量导致链接器错误。对我们来说更可行的解决方案是简单地将 extern 声明移到源文件中的命名空间之外,或者简单地删除我们所有的命名空间并像以前一样去突击队,但这似乎都不是一个令人满意的选择。如果它不能以其他方式完成,它就不能完成,但它确实看起来应该是可能的。 :)
    • @breath:考虑&lt;iosfwd&gt; - 它为&lt;iostream&gt; 中的类提供前向声明,但与它一起维护(理想情况下 - 特别是在易失性软件系统中 - 将包含在其中以确保一致性)。你能不能做同样的事情:为每个客户端应用程序/翻译单元在逻辑上“在”“all-strings”标头“下”有额外的 extern-declaration 标头,后者包括它们以检查一致性...?
    【解决方案2】:

    $7.3.1/2 - “首先声明的每个名称 在命名空间中是那个的成员 命名空间。”

    这意味着名称“test2”是匿名命名空间的一部分,如果使用,则应在该命名空间中定义。

    这不仅仅是匿名命名空间的问题,而是任何命名空间的问题。

    【讨论】:

    • sigh 我想我不能不同意这个规范。我多次查看该部分,但没有解析该特定行的含义。感谢您拼写出来。
    猜你喜欢
    • 2015-05-13
    • 2017-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-28
    • 1970-01-01
    • 2012-12-19
    • 1970-01-01
    相关资源
    最近更新 更多