【问题标题】:Why we need a separate definition for a static const data member?为什么我们需要为静态 const 数据成员单独定义?
【发布时间】:2021-06-27 23:24:04
【问题描述】:
您好,如果我有一个 static const 数据成员,那么我可以为它提供一个类内初始化程序,我不需要在类主体之外再次定义它。
-
但只有在类范围内使用该常量并且如果在外部使用时,必须在外部提供单独的定义,否则对它的任何引用都会导致链接时错误:“未定义对静态对象的引用:x” .
struct Foo{
static int const sz_ = 100;
std::array<int, sz_> ai_100{};
};
//int const Foo::sz_;
int main(){
float pts[Foo::sz_]{}; // ok
void bar(int const&); // defined later on
bar(Foo::sz_); // undefined reference to Foo::sz_
}
void bar(int const&){
//do_something
}
-
为什么在类范围之外使用static const数据成员sz_作为数组大小是OK的?
-
为什么当将sz 传递给函数bar 时,该函数采用对const 的左值引用,链接器无法链接并抱怨Foo::sz_ 的定义?
-
函数bar 接受一个对const int& 的左值引用,因此它可以从一个右值初始化,那么为什么它对初始化器Foo::sz_ 的定义很重要?
【问题讨论】:
标签:
c++
static
constants
in-class-initialization
【解决方案1】:
为什么我们需要为静态 const 数据成员单独定义?
首先,我们必须记住单一定义规则。它实际上是一组规则,但我们对此上下文感兴趣的规则的简化版本是:每个变量都必须有一个定义。
其次,我们应该考虑到类通常用于多个翻译单元。
如果静态成员变量的声明是一个定义,那么每个包含类定义的翻译单元都将包含该变量定义。这与 ODR 相悖,链接器不知道该怎么做。
因此,必须有一个单独的变量定义,允许将类定义包含在多个翻译单元中,同时将单独的变量定义保留在一个翻译单元中。
虽然过去的语言实现可能无法处理多个定义(早在模板出现在 C++ 之前),但现在它们可以了,并且该语言已被扩展(在 C++17 中)以允许内联定义的变量。 inline 关键字对变量的含义与对函数的含义相同:它放宽了单一定义规则,允许(也要求)在每个 TU(使用 odr 的地方)中定义变量。
【解决方案3】:
您的非链接示例可以简化为:
struct Foo{
static int const sz_ = 100;
};
int main(){
int const *p = &Foo::sz_; // undefined reference to Foo::sz_
}
换句话说,如果我们获取Foo::sz_的地址,或者为其分配一个引用(这是一个非常相似的操作,在底层),那么我们需要在某个地方定义int const Foo::sz_;。
这是为什么?好吧,如果我们只使用Foo::sz_ 的值,那么编译器就不需要变量了。它可以只使用 100 作为有效的文字。但是如果我们想获取它的地址,那么我们需要一个实际的变量来引用。