1

你好,如果我有一个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函数时无法链接并抱怨 的定义?barconstFoo::sz_

  • 该函数bar接受一个左值引用,const int&因此它可以从一个右值初始化,那么为什么它对初始化器的定义很重要Foo::sz_

4

3 回答 3

3

为什么我们需要为静态 const 数据成员单独定义?

首先,我们必须记住单一定义规则。它实际上是一组规则,但我们对此上下文感兴趣的规则的简化版本是:每个变量都必须有一个定义

其次,我们应该考虑到类经常用于多个翻译单元。

如果静态成员变量的声明是一个定义,那么每个包含类定义的翻译单元都将包含该变量定义。这与 ODR 相违背,链接器不知道该怎么做。

因此,必须有一个单独的变量定义,允许将类定义包含在多个翻译单元中,同时将单独的变量定义保留在一个翻译单元中。


虽然过去的语言实现可能无法处理多个定义(早在模板出现在 C++ 之前),但现在它们可以了,并且该语言已被扩展(在 C++17 中)以允许变量的内联定义。inline关键字对变量的含义与对函数的含义相同:它放宽了单一定义规则,允许(并且还要求)在每个 TU(使用 odr 的地方)中定义变量。

于 2021-06-28T00:09:22.780 回答
2

主要是因为历史原因。盯着 C++17,你可以有inline数据成员。并且constexpr成员是inline默认的。

于 2021-06-27T23:34:24.833 回答
1

您的非链接示例可以简化为:

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有效地用作文字。但是如果我们想获取它的地址,那么我们需要一个实际的变量来引用。

于 2021-06-28T01:24:10.090 回答