为什么在头文件中声明“static const”成员会导致链接器错误?

时间:2011-03-03 16:42:19

标签: c++

我有类声明(.h文件),如下所示:

struct MyClass {
    static const uint32_t SIZE = sizeof(MyType);
};

将我的程序链接在一起时,我会收到MyClass::SIZE的链接器错误。 nm确认符号未定义。 http://forums.devshed.com/c-programming-42/linker-errors-undefined-reference-to-static-member-data-193010.html似乎解决了我的问题,表明“类静态对象也必须在普通全局变量之外的任何函数或类之外声明。”

我有两个问题:

  1. 这个解释对我的案子有效吗?如果是这样,你能更详细地解释为什么这是真的吗?
  2. 解决问题的最佳方法是什么?我想将这个成员的初始化完全保留在.h文件中。

5 个答案:

答案 0 :(得分:5)

您的问题可能与获取变量地址的人无关。它可能只是编译器选择不使用变量作为常量表达式,即使它可以。例如:

f(int const&);
struct X { enum { enum_val = 42 }; static int const static_mem = 42; };

f(5);
f(X::enum_val);
f(X::static_mem);

在前两种情况下,编译器需要将输入用作常量表达式,const&可以用这样的方式初始化。然而最后一种情况则不同。即使您的意图可能是使用static_mem作为常量表达式,并且它完全合法,编译器也可以自由地执行,有些实际上会创建对变量本身的引用。这是变量的“使用”,因此您需要在程序中的某个位置定义该变量。

有两种方法可以解决这个问题:

1)为您的程序添加定义。

2)使用枚举代替:     结构X.     {       枚举{static_mem =? };     };

如果您确实想要获取变量的地址,那么第一个解决方案是必要的。有可能你没有,或者你已经创建了那个定义。后面的解决方案强制编译器使用X::static_mem作为常量表达式,因为枚举成员实际上并不作为程序中的对象存在。根据您在问题中的最后陈述,我认为这是您真正想要的解决方案。

答案 1 :(得分:2)

标准要求静态成员积分常量的定义仅在其地址被采用时,否则使用初始化器(您拥有的)的声明就足够了。该链接器错误消息应该提到哪个对象/函数的地址为MyClass::SIZE

答案 2 :(得分:0)

如果你要这样做:

//.h file
struct MyClass 
{
    static const uint32_t SIZE = sizeof(MyType); //this is declaration!

}; 

//.cpp file
const uint32_t MyClass::SIZE; //this is definition - necessary!

答案 3 :(得分:0)

No definition available for static const member with initializer?

引用自己的话

从9.4.2 / 4:

  

如果静态数据成员是const   整数或常数枚举类型,   它在课堂上的宣言   定义可以指定一个   常数初始化器应该是一个   积分常数表达式(5.19)。   在这种情况下,该成员可以出现在   内积分常数表达式   它的范围。 会员仍然是   如果是,则在命名空间范围内定义   在程序中使用和命名空间   范围定义不得包含   初始化程序。

根据这些参考文献,我们可以推断(“......仍然应该在9.4.2 / 4中定义......”)如果没有定义那么程序就没有格式良好。

@DavidRodríguez - dribeas指出你必须在某个地方获取程序中静态成员的地址。如果你可以避免使用地址,那么就不需要在实现文件中定义(或者你没有获取地址并且有一个错误的编译器)。否则你必须有定义。

答案 4 :(得分:-1)

  1. 最有可能,但没有任何错误消息,很难说。

  2. 使用静态const int。您可以在标题中初始化它,而不需要在cpp中声明它。