现在允许重新定义constexpr静态数据成员吗? (但不是内联常量)?

时间:2017-07-10 19:14:48

标签: c++ c++14 c++17

以下无法在c ++ 14中的gcc和clang下编译,但是使用c ++ 1z成功:

struct Cls {
  static constexpr int N = 0;
};
constexpr int Cls::N;
constexpr int Cls::N;

C ++ 14错误是可预测的:redefinition of ‘constexpr const int Cls::N’

有什么改变使这合法化?我找到了:

  

n4659 10.1.5 [dcl.constexpr]

     

使用constexpr声明的函数或静态数据成员   说明符隐式地是内联函数或变量

所以我认为它可能与内联变量有关,但在两个编译器下c ++ 1z的以下失败

struct Cls {
  static inline const int N = 0;
};
inline const int Cls::N; // note, only one definition here

1 个答案:

答案 0 :(得分:13)

在C ++ 17之前,您需要在一个翻译单元中重新声明类外的所有static变量(通常每个翻译单元都是.cpp文件,反之亦然,但这不是不需要)。正如您所指出的,C ++ 17引入了inline类成员变量,static constexpr变量自动符合条件。正如您在第二个示例中所看到的那样, 允许重新声明类外的inline变量,但constexpr之所以例外,因为之前您被允许(并且在这样做的事实,但语法已被弃用。

在[class.static.data] p2中,它允许非内联成员的语法(“在其类定义中声明非内联静态数据成员不是一个定义,可能是 除了cv void之外的不完整类型。未内联定义的静态数据成员的定义 在类定义中,应出现在包含成员类定义的命名空间范围内。“)

在下一段中,该标准允许constexpr类外声明,并要求它们用于非constexpr数据(强调添加):

  

如果非易失性非内联const静态数据成员是整数或枚举类型,则其声明   在类定义中可以指定 brace-or-equal-initializer   每个 initializer-clause 是一个赋值表达式是一个   常数表达式(8.20)。该成员仍应定义为   如果在程序中使用了odr-used(6.2),则命名空间范围   命名空间范围定义不应包含初始值设定项。内联   静态数据成员可以在类定义中定义   并可以指定大括号或等于初始化程序如果会员是   使用constexpr说明符声明,可以重新声明   没有初始值设定项的命名空间范围(不推荐使用此用法;请参阅   D.1)。其他静态数据成员的声明不得指定   撑 - 或等于初始值设定

以下是弃用说明,D.1静态constexpr数据成员的重新声明[depr.static_constexpr]:

  

为了与先前的C ++国际标准兼容,constexpr静态数据成员可能会在以外的地方被冗余重新声明   没有初始化程序的类。不推荐使用此用法。 [示例:

struct A {
  static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
     

- 结束示例]

相关问题