类定义中的静态数据成员初始化?

adz*_*iri 5 c++

我在这里有一个问题,那么,为什么(为了什么?)不可能在类中初始化静态变量?我管理的

http://eel.is/c++draft/class.static.data#3

如果非易失性非内联 const 静态数据成员是整数或枚举类型, 如果它在程序中被 odr 使用,...仍应在命名空间范围中定义,并且命名空间范围定义不应包含初始化程序。

所以,像这样的例子

struct X {
    static int const n = 7; // should be defined outside the class, 
                            // but it's compiles successfully
};
Run Code Online (Sandbox Code Playgroud)

我看到这个话题/sf/answers/1146200051/,给出了这样一个例子

struct s
{
    static std::size_t const len = 10;
    int arr[len];
};
std::size_t const s::len; 
Run Code Online (Sandbox Code Playgroud)

用这样的话——

“如果 len 没有在类定义中初始化,编译器就不能轻易知道它在下一行中的值来定义 arr 的长度。”

但实际上没有std::size_t const s::len - 这行它也编译成功,所以在什么情况下它不应该工作? https://gcc.godbolt.org/z/OMKzEO

所以我们更进一步,为什么我们不能在类内部初始化静态成员,const 限定符允许这样做,为什么没有我们做不到呢?const 做什么来允许在类内部初始化?

你可能会说我们不能在类内部初始化静态成员,因为 ODR 和 Stroustrup 所说的:

类通常在头文件中声明,而头文件通常包含在许多翻译单元中。但是,为了避免复杂的链接器规则,C++ 要求每个对象都有唯一的定义。如果 C++ 允许需要作为对象存储在内存中的实体的类内定义,那么这条规则就会被打破。

但事实并非如此,为什么编译器会解决模板类的静态成员在标头中(在翻译单元之外)初始化的事实?

// templates are completely pushed into headers
template<typename T>
struct X {
    static int val;
};  

// here the static member of the template class is initialized in the header  
template<typename T>
int X<T>::val = 0; 

int main() {
    X<int> x;
}
Run Code Online (Sandbox Code Playgroud)

好的,我会尝试具体化我的问题:

  1. 为什么可以在类定义中定义 const 静态数据成员(如果它未被 odr 使用,则不需要在类外定义)?
  2. 为什么没有 const 的静态数据成员可能不会在类定义中定义(请参阅我对带有静态成员的模板类的想法和 Stroustrup 的话(他是否在欺骗我们?))?

是的,我看到在 C++17 中我们允许使用内联,但我对这种情况不太感兴趣。

eer*_*ika 3

为什么(为什么?)不可能在类内部初始化静态变量?

如果静态变量是 const,则在类定义中初始化它们也不是不可能。

但实际上没有 std::size_t const s::len - 这行代码也编译成功,那么在什么情况下它不应该工作?

它之所以有效,是因为该变量未被 odr 使用。参见[basic.def.odr]:

名称显示为潜在求值表达式 e 的变量 x 被 e odr 使用,除非

  • x 是参考...
  • x 是非引用类型的变量,可在常量表达式中使用,并且没有可变子对象,而 e 是非易失性限定非类类型表达式的潜在结果集合中的一个元素,左值-应用到右值转换 ([conv.lval]),或者...

此外,ODR 违规不需要诊断。对于缺失的定义来说,这在技术上是正确的。优化可能会删除 odr-uses,这样它们就不会表现为错误。

请尝试以下操作来使用该变量:

const int *ptr = &X::n;
Run Code Online (Sandbox Code Playgroud)
  1. 为什么const静态数据成员可以在类定义中定义?

否。非内联 const 静态数据成员不是由类定义中的声明定义的。如果变量是 odr 使用的,则需要有一个定义(在类之外)。

  1. 为什么静态数据成员 [...] 不能在类定义中定义(请参阅我对具有静态成员的模板类的想法以及 Stroustrup 对此的看法(他是否欺骗了我们?))?

Stroustrup 关于“复杂的链接器规则”的说法是正确的。当无法在标头中定义静态变量时,可以避免这些情况。

您关于模板的观点是有道理的。无论如何,需要这些复杂的链接器规则才能使模板化静态变量成为可能。甚至能够定义非模板化静态变量也具有优势。这些可能就是委员会选择允许在 C++17 的类定义中定义静态成员的原因。请参阅:inline静态成员定义。

  • @adziri 所有静态数据成员(const 和非 const)如果使用 odr,则必须在类外部定义(除非声明为内联,C++17)。如果静态对象不被 ODR 使用,则不需要定义它们。无法保证编译器能够诊断出未能定义 odr 使用的对象 (2认同)