我希望C类有一个类型为C的静态constexpr成员。这在C ++ 11中是否可行?
尝试1:
struct Foo {
constexpr Foo() {}
static constexpr Foo f = Foo();
};
constexpr Foo Foo::f;
g ++ 4.7.0说:“无效使用不完整类型”是指Foo()
来电。
尝试2:
struct Foo {
constexpr Foo() {}
static constexpr Foo f;
};
constexpr Foo Foo::f = Foo();
现在问题是在类定义中缺少constexpr
成员f
的初始值设定项。
尝试3:
struct Foo {
constexpr Foo() {}
static const Foo f;
};
constexpr Foo Foo::f = Foo();
现在g ++抱怨Foo::f
的{{1}}重新声明不同。
答案 0 :(得分:32)
如果我正确地解释了标准,那是不可能的。
(§9.4.2/ 3)[...]文字类型的静态数据成员可以在中声明 使用constexpr说明符的类定义;如果是这样,它的声明应指定一个大括号或等于初始化器,其中作为赋值表达式的每个initializer子句都是一个常量表达式。 [...]
从上面(以及静态数据成员声明中没有关于非文字类型的单独声明的事实),我认为constexpr
的静态数据成员必须文字类型(如§3.9/ 10中所定义),和必须在声明中包含定义。使用以下代码可以满足后一条件:
struct Foo {
constexpr Foo() {}
static constexpr Foo f {};
};
类似于您的尝试1,但没有类外部定义。
但是,由于Foo
在声明/定义静态成员时不完整,编译器无法检查它是否是文字类型(如§3.9/ 10中所定义),因此它拒绝代码。
请注意,this post-C++-11 document (N3308)讨论了标准中constexpr
当前定义的各种问题,并提出修改建议。具体而言,“建议的措辞”部分建议对§3.9/ 10进行修订,这意味着将不完整类型作为一种文字类型。如果要将该修正案纳入标准的未来版本,您的问题将得到解决。
答案 1 :(得分:11)
我认为GCC拒绝您的尝试3是不正确的.C ++ 11标准(或其任何已接受的缺陷报告)中没有规则表明变量的重新声明必须为constexpr
iff先前的声明是。最接近该规则的标准是 [dcl.constexpr](7.1.5)/ 1 _ :
如果函数或函数模板的任何声明具有
constexpr
指定符,则其所有声明都应包含constexpr
指定符。
Clang的constexpr
实施接受了你的尝试3。
答案 2 :(得分:7)
Richard Smith's answer上的更新,现在尝试3在GCC 4.9和5.1以及clang 3.4上编译。
struct Foo {
std::size_t v;
constexpr Foo() : v(){}
static const Foo f;
};
constexpr const Foo Foo::f = Foo();
std::array<int, Foo::f.v> a;
然而,当Foo是一个类模板时,clang 3.4失败,但GCC 4.9和5.1仍然正常工作:
template < class T >
struct Foo {
T v;
constexpr Foo() : v(){}
static const Foo f;
};
template < class T >
constexpr const Foo<T> Foo<T>::f = Foo();
std::array<int, Foo<std::size_t>::f.v> a; // gcc ok, clang complains
Clang错误:
error: non-type template argument is not a constant expression
std::array<int, Foo<std::size_t>::f.v> a;
^~~~~~~~~~~~~~~~~~~~~