【问题标题】:constexpr static data member without initializer没有初始化器的 constexpr 静态数据成员
【发布时间】:2018-11-11 06:41:18
【问题描述】:
#include <complex>

struct S
{
  static std::complex<double> constexpr c;
};

gcc 生成错误,因为缺少初始化程序。 Clang 和 MSVC 不会产生错误。

据我所知,一个 constexpr 静态数据成员必须有一个初始化程序,即使它是具有可以不带参数调用的构造函数的类类型(如本例所示)。不幸的是,我没有最新的 C++ 标准来支持我的假设。

所以正确的代码应该用构造函数初始化,例如:

struct S
{
  static std::complex<double> constexpr c {};
};

谁能证明哪个编译器是对的,哪个是错的?

【问题讨论】:

    标签: c++ gcc visual-c++ clang language-lawyer


    【解决方案1】:

    GCC 是错误的。

    GCC 对 constexpr 变量使用 C++14 规则,这需要提供初始化程序。这是根据P0386 更改的(粗体文本是新添加的文本):

    在 9.2.3.2p3 中,更改:

    如果一个非易失的n​on-inline​const静态数据成员是整数或枚举类型,它在类定义中的声明可以指定ab​race-or-equal -initializer​ 其中每个 initializer-clause​ 是一个​assignment-expression​ 是一个常量表达式(5.20)。 可以在类定义中用 constexpr 说明符声明一个字面量类型的静态数据成员;如果是这样,它的声明应指定一个大括号或等式初始化器,其中作为赋值表达式的每个初始化器子句都是一个常量表达式。 [ 注意:在这两种情况下,成员都可能出现在常量表达式中。 — end note ]​ 如果该成员在程序中被 odr-used (3.2) 并且命名空间范围定义不应包含初始化器,则该成员仍应在命名空间范围内定义。内联静态数据成员可以在类定义中定义,并且可以指定一个b​​race-or-equal-initializer。如果成员使用 constexpr 说明符声明,它可以在命名空间范围内重新声明没有初始化程序(不推荐使用此用法;请参阅 DX)。其他静态数据成员的声明不应指定 b​race-or-equal-initializer。​

    【讨论】:

    • 我认为你是对的。 Clang 6.0.0 在 C++17 模式下不会报告错误,但在 C++14 模式下会报告错误,并抱怨缺少 constexpr 静态数据成员的初始化程序。所以规则似乎已经从 C++14 更改为 C++17(正如您所解释的那样)。 gcc 是错误的,而 clang 和 MSVC(难以置信)是正确的。谢谢你的解释。
    • 其实要看标准。 OP 没有指定 C++ 的特定版本,因此 gcc 不一定是错误的 - 这取决于语言版本(也许还有编译器 - 也许他们同时修复了它?我没有安装最新的 gcc 版本) .
    【解决方案2】:

    在这种特殊情况下,有两个答案:

    • 对于 C++14,gcc 正确(即 constexpr 静态数据成员必须有一个初始化器)。
    • 对于 C++17 及更高版本,gcc 错误,因为它拒绝编译符合要求的代码。

    以前的情况:在草案 N3797 (C++14),9.4.2.3(静态数据成员)[class.static.数据](强调我的):

    文字类型static数据成员可以在类中声明 使用 constexpr 说明符定义;如果是,其声明应 指定一个 brace-or-equal-initializer,其中每个 initializer-clauseassignment-expression 是一个常量表达式。

    另请参阅:http://en.cppreference.com/w/cpp/language/static#Constant_static_members

    我说“在这个特殊情况下”,因为std::complexhas a specialization for doubleLiteralType。因此上述规则适用。 对于一般(即非文字)类型,请参阅codekaizers answer

    后一种情况:对于 C++17,请参阅xskxzr's answer

    【讨论】:

    • c 还是literal?如果不是,则不适用。
    • 专用于double, std::complex is
    • @codekaizer 段落错误。你想要this
    • @andreee 你是正确的 C++11/14。但在 C++17 中,规则发生了变化(参见 xskxzr 的答案)。在 C++11/14 模式下,clang 也会产生错误。
    • @xy:感谢您的关注,已更新!请注意,您没有指定特定的语言版本,因此有两个不同的答案(正如您所指出的)。
    【解决方案3】:

    来自dcl.constexpr#1

    使用 constexpr 说明符声明的函数或 static 数据成员 隐含是 inline 函数或变量

    constexpr static 数据成员隐含为inline

    同样来自class#static.data-3强调我的:

    inlinestatic 数据成员可能是 在class 定义中定义并且可以指定一个 brace-or-equal-initializer.


    因此,GCC 是错误的。 brace-or-equal-initializer 不是 严格要求

    参考:N4659C++17 草案

    【讨论】:

    • 这有 GCC 错误吗? “可以指定”的任何细节。对我来说,“可以指定”意味着它可能不,在这种情况下,该文字类型的 constexpr no-arg c-tor 完成了它的工作。但这不起作用,GCC 需要一个初始化程序
    猜你喜欢
    • 2016-10-15
    • 2012-07-16
    • 1970-01-01
    • 2015-05-29
    • 1970-01-01
    • 2014-09-01
    • 2021-08-07
    • 2013-10-19
    相关资源
    最近更新 更多