为什么 GCC 认为定义一个 constexpr 静态数据成员必须标记为 constexpr?
Why does GCC think that the definition of a constexpr static data member must be marked constexpr?
[C++14: 7.1.5/1]:
The constexpr
specifier shall be applied only to the definition of a variable or variable template, the declaration of a function or function template, or the declaration of a static data member of a literal type (3.9). If any declaration of a function, function template, or variable template has a constexpr
specifier, then all its declarations shall contain the constexpr
specifier. [..]
请注意,第二句没有像第一句那样提到 "a static data member",因此在这段话中没有要求所有声明(这里我正在考虑具体的定义声明) constexpr
static
数据成员具有 constexpr
说明符。
我也找不到其他地方的规则来强制执行此操作。
那么,为什么 GCC 会拒绝以下程序?
#include <chrono>
using namespace std::chrono_literals;
#define DUR 1000ms
struct T
{
static constexpr auto dur_1 = DUR;
};
decltype(T::dur_1) T::dur_1;
// main.cpp:12:23: error: 'constexpr' needed for in-class initialization of static data member 'const std::chrono::duration<long int, std::ratio<1l, 1000l> T::dur_1' of non-integral type [-fpermissive]
// decltype(T::dur_1) T::dur_1;
// ^
必须根据 7.1.5 (9) 在 class 中初始化 constexpr 静态数据成员。这是成员的定义。由于 ODR 不允许其他定义,因此 T::dur_1 只能是声明。但是没有规则允许在 class 主体之外声明 const static 数据成员,因此不允许这种声明。
只有在一致使用 constexpr 的情况下,GCC 才支持将其作为扩展。
或者我错了,这是一个错误 ;)
FWIW:clang 在没有警告的情况下接受此代码。
我之前的回答认为代码的问题是 std::chrono_literals 对象作为 constexp
s 无效,但正如 Lightspeed 指出的那样,情况并非如此。
我做了更多的研究,确定你的代码的问题行是你的struct t
:特别是这一行:
static constexp auto ...
关于这个 here
在 SO 上还有另一个答案
要明确指出为什么这是你的问题(因为评论表明这不是很明显):
任何标记为 constexp
的表达式都将在编译时确定。你已经知道这么多了。当您尝试使用 decltype(T::dur_1) T:dur_1
表达式进行实例化时,在您看来,您是在向 chrono 文字构造函数(即 constexp
)提供正确的凭据。问题是该类型没有明确定义,因为您似乎认为它来自 DUR
替换 1000ms 的预处理器定义。
尝试以下操作:
template <class T>
struct foo {
static constexpr auto dur_1 = DUR;
typedef decltype(DUR) milliseconds;
}
template <class T>
constexp milliseconds foo<T>::milliseconds foo<T>::DUR;
通过显式定义消除 GCC 编译器无法在编译时确定自动类型的问题,应该可以解决您的问题。
这就是原link的原因。 GCC 错误地无法在编译时确定自动键入。
这对我来说似乎不够明确,我没有看到明确的要求,但我们可以从 defect report 699: Must constexpr member functions be defined in the class member-specification? 中看出为什么这是一个问题,尽管处理 constexpr 成员函数时说了以下内容(强调我的):
If the prohibition were relaxed to allow separate declaration and
definition of constexpr member functions, some questions would need to
be answered, such as whether the constexpr specifier must appear on
both declaration and definition (the inline specifier need not). If it
can be omitted in one or the other, there's a usability issue
regarding the fact that constexpr implies const; the const qualifier
would need to be specified explicitly in the declaration in which
constexpr was omitted.
虽然在这种情况下添加 const
并不能解决问题,但在更简单的情况下它似乎确实可以解决问题。我们可以在更简单的情况下看到 clang 和 gcc 都需要 const 或 constexpr:
struct T
{
static constexpr int blah = 1 ;
};
const int T::blah ;
更新
这份 gcc 错误报告: Bogus "error: redeclaration ... differs in ‘constexpr’" 引用了 Richard Smith 的话:
There is no rule requiring successive declarations of variables to
agree in 'constexpr'ness (this rule only applies to functions).
所以这看起来像是一个 gcc 错误,尽管它看起来仍然可以在标准中使用一些清晰度。
[C++14: 7.1.5/1]:
Theconstexpr
specifier shall be applied only to the definition of a variable or variable template, the declaration of a function or function template, or the declaration of a static data member of a literal type (3.9). If any declaration of a function, function template, or variable template has aconstexpr
specifier, then all its declarations shall contain theconstexpr
specifier. [..]
请注意,第二句没有像第一句那样提到 "a static data member",因此在这段话中没有要求所有声明(这里我正在考虑具体的定义声明) constexpr
static
数据成员具有 constexpr
说明符。
我也找不到其他地方的规则来强制执行此操作。
那么,为什么 GCC 会拒绝以下程序?
#include <chrono>
using namespace std::chrono_literals;
#define DUR 1000ms
struct T
{
static constexpr auto dur_1 = DUR;
};
decltype(T::dur_1) T::dur_1;
// main.cpp:12:23: error: 'constexpr' needed for in-class initialization of static data member 'const std::chrono::duration<long int, std::ratio<1l, 1000l> T::dur_1' of non-integral type [-fpermissive]
// decltype(T::dur_1) T::dur_1;
// ^
必须根据 7.1.5 (9) 在 class 中初始化 constexpr 静态数据成员。这是成员的定义。由于 ODR 不允许其他定义,因此 T::dur_1 只能是声明。但是没有规则允许在 class 主体之外声明 const static 数据成员,因此不允许这种声明。
只有在一致使用 constexpr 的情况下,GCC 才支持将其作为扩展。
或者我错了,这是一个错误 ;)
FWIW:clang 在没有警告的情况下接受此代码。
我之前的回答认为代码的问题是 std::chrono_literals 对象作为 constexp
s 无效,但正如 Lightspeed 指出的那样,情况并非如此。
我做了更多的研究,确定你的代码的问题行是你的struct t
:特别是这一行:
static constexp auto ...
关于这个 here
在 SO 上还有另一个答案要明确指出为什么这是你的问题(因为评论表明这不是很明显):
任何标记为 constexp
的表达式都将在编译时确定。你已经知道这么多了。当您尝试使用 decltype(T::dur_1) T:dur_1
表达式进行实例化时,在您看来,您是在向 chrono 文字构造函数(即 constexp
)提供正确的凭据。问题是该类型没有明确定义,因为您似乎认为它来自 DUR
替换 1000ms 的预处理器定义。
尝试以下操作:
template <class T>
struct foo {
static constexpr auto dur_1 = DUR;
typedef decltype(DUR) milliseconds;
}
template <class T>
constexp milliseconds foo<T>::milliseconds foo<T>::DUR;
通过显式定义消除 GCC 编译器无法在编译时确定自动类型的问题,应该可以解决您的问题。
这就是原link的原因。 GCC 错误地无法在编译时确定自动键入。
这对我来说似乎不够明确,我没有看到明确的要求,但我们可以从 defect report 699: Must constexpr member functions be defined in the class member-specification? 中看出为什么这是一个问题,尽管处理 constexpr 成员函数时说了以下内容(强调我的):
If the prohibition were relaxed to allow separate declaration and definition of constexpr member functions, some questions would need to be answered, such as whether the constexpr specifier must appear on both declaration and definition (the inline specifier need not). If it can be omitted in one or the other, there's a usability issue regarding the fact that constexpr implies const; the const qualifier would need to be specified explicitly in the declaration in which constexpr was omitted.
虽然在这种情况下添加 const
并不能解决问题,但在更简单的情况下它似乎确实可以解决问题。我们可以在更简单的情况下看到 clang 和 gcc 都需要 const 或 constexpr:
struct T
{
static constexpr int blah = 1 ;
};
const int T::blah ;
更新
这份 gcc 错误报告: Bogus "error: redeclaration ... differs in ‘constexpr’" 引用了 Richard Smith 的话:
There is no rule requiring successive declarations of variables to agree in 'constexpr'ness (this rule only applies to functions).
所以这看起来像是一个 gcc 错误,尽管它看起来仍然可以在标准中使用一些清晰度。