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

Rya*_*ing 14 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;
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

Dan*_*l H 13

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

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

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

如果非易失性非内联const静态数据成员是整数或枚举类型,则其在类定义中的声明可以指定一个大括号或大小为初始化器,其中作为赋值表达式的每个initializer子句都是一个常量表达式(8.20).如果在程序中使用odr-used(6.2),并且命名空间作用域定义不包含初始值设定项,则仍应在命名空间作用域中定义该成员.可以在类定义中定义内联静态数据成员,并且可以指定大括号或等于初始化器.如果使用说明声明成员,则可以在没有初始化程序的命名空间作用域中重新声明该成员(此用法已弃用;请参阅D.1).其他静态数据成员的声明不应指定 大括号或等于初始化器.constexpr

这里是弃用说明,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)
Run Code Online (Sandbox Code Playgroud)

- 结束例子 ]