问题描述
你好,如果我有一个 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_
因为数组大小可以? -
为什么在将
sz
传递给函数bar
时,该函数接受const
的左值引用,但链接器无法链接并抱怨Foo::sz_
的定义? -
函数
bar
接受对const int&
的左值引用,因此可以从 r 值对其进行初始化,因此为什么它与初始化器Foo::sz_
的定义有关?
解决方法
为什么我们需要为静态常量数据成员单独定义?
首先,我们必须记住一个定义规则。它实际上是一组规则,但我们在此上下文中感兴趣的规则的简化版本是:必须每个变量都只有一个定义。
其次,我们应该考虑到类经常用于多个翻译单元。
如果静态成员变量的声明是一个定义,那么每个包含类定义的翻译单元都将包含该变量定义。这将与 ODR 相悖,链接器将不知道该怎么做。
因此,必须有一个单独的变量定义,允许将类定义包含在多个翻译单元中,同时将单独的变量定义保留在一个翻译单元中。
虽然过去的语言实现可能无法处理多个定义(在模板出现在 C++ 之前),但现在已经可以了,并且语言已经扩展(在 C++17 中)以允许内联定义的变量。 a b c id
0 4.20 2.0 0.0 0
1 3.01 3.0 0.0 1
2 2.10 4.0 0.0 1
3 1.20 5.0 0.0 1
4 0.52 6.0 0.0 1
5 0.02 6.0 1.0 1
6 5.30 7.0 0.4 0
7 4.20 8.0 0.0 1
8 3.10 9.0 0.0 1
9 2.40 10.0 0.0 1
10 1.30 11.0 0.0 1
11 0.20 12.0 0.0 1
12 5.98 13.0 0.0 0
13 4.23 14.0 0.3 1
14 3.33 15.0 0.0 1
15 2.11 16.0 0.0 1
16 1.30 17.0 0.0 1
17 0.30 18.0 0.0 1
18 5.50 13.0 0.0 0
关键字对于变量和函数具有相同的含义:它放宽了单一定义规则,允许(并且还要求)在每个 TU(使用 odr 的地方)中定义变量。
主要是由于历史原因。盯着 C++17,你可以有 inline
data members。 constexpr
成员默认为 inline
。
您的非链接示例可以简化为:
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_
的 value,那么编译器不需要变量。它可以有效地将 100
用作文字。但是如果我们想要获取它的地址,那么我们需要一个实际的变量来引用。