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

Posted

技术标签:

【中文标题】类定义中的静态数据成员初始化?【英文标题】:Static data member initialization in the class definition? 【发布时间】:2019-03-19 21:20:54 【问题描述】:

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

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

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

所以,像这样的例子

struct X 
    static int const n = 7; // should be defined outside the class, 
                            // but it's compiles successfully
;

看到这个话题https://***.com/a/16374286/9780989,有给出这样的例子

struct s

    static std::size_t const len = 10;
    int arr[len];
;
std::size_t const s::len; 

用这样的话,-

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

但实际上没有 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;

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

    为什么可以在类定义中定义 const 静态数据成员 (如果它不是 odr-used,那么它就不需要定义 课外)? 为什么没有 const 的静态数据成员可能不是 在类定义中定义(见我对模板的想法 带有静态成员和 Stroustrup 的话的类(他是 欺骗我们?))?

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

【问题讨论】:

如果静态成员变量是odr-used,则需要在类外定义。否则,它不需要。 @RSahu 好的,但是为什么呢?为什么不能只是没有 const 的静态成员? 不是语言律师,我只能猜测,但这可能是一种优化生成代码的实现方式。没有conststatic 成员如果不被使用,可能会从定义中省略。如果使用它们,那么它们只能在运行时初始化。因此,它们必须被定义。 不太清楚你的问题是什么。您在开始时询问非 const 静态成员,然后将一整页文本用于 const 静态成员。 C++17 允许您定义一个静态成员内联而无需 C++98 过去强加的所有麻烦,这是一个微妙的提示。 一开始就不应该有麻烦。为什么会在那里? 因为人并不完美,会做出不完美的决定。现在已经修好了,尽情享受吧。 【参考方案1】:

为什么(为什么?)无法在类中初始化静态变量?

并非不可能在类定义中初始化静态变量,如果它们是 const 的话。

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

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

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

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

此外,ODR 违规无需诊断。对于缺少定义,这在技术上是正确的。优化可能会移除 odr-uses,以免它们显示为错误。

尝试以下方法来使用变量:

const int *ptr = &X::n;
    为什么可以在类定义中定义 const 静态数据成员?

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

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

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

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

【讨论】:

嗨,谢谢回答,所以由于某些编译器优化,类定义中可能没有定义静态数据成员 without const,是吗? 你可以刷新这个页面吗?我编辑了我的主题并试图在最后具体化我的问题? @adziri 所有静态数据成员(const 和 non-const)必须在类外定义(除非声明为内联,C++17),如果它们是 odr 使用的。如果静态对象不使用 odr,则不需要定义它们。无法保证编译器会诊断出无法定义使用 odr 的对象

以上是关于类定义中的静态数据成员初始化?的主要内容,如果未能解决你的问题,请参考以下文章

关于“只有静态常量整型数据成员才可以在类中初始化”

C++中静态成员变量(不支持在类定义中初始化不是常量的静态数据成员)

mfc 类静态成员

C++类中数据成员初始化

java类中的初始化顺序

C++中的派生类,可以不定义对象直接调用基类的成员和调用自己的成员函数嘛???