关于c ++:与正在定义的类相同类型的静态constexpr成员

static constexpr member of same type as class being defined

我希望C类有一个静态的C.类型的成员,这是C++ 11中的可能吗?

尝试1:

1
2
3
4
5
struct Foo {
    constexpr Foo() {}
    static constexpr Foo f = Foo();
};
constexpr Foo Foo::f;

G++4.7.0说:"不完整类型的无效使用"指的是Foo()调用。

尝试2:

1
2
3
4
5
struct Foo {
    constexpr Foo() {}
    static constexpr Foo f;
};
constexpr Foo Foo::f = Foo();

现在的问题是类定义中缺少constexpr成员f的初始值设定项。

尝试3:

1
2
3
4
5
struct Foo {
    constexpr Foo() {}
    static const Foo f;
};
constexpr Foo Foo::f = Foo();

现在,G++抱怨Foo::f的重新声明与constexpr不同。


如果我正确地解释了标准,那是不可能的。

(§9.4.2/3) [...] A static data member of literal type can be declared in the
class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [...]

综上所述(除了静态数据成员声明中没有关于非文本类型的单独声明外),我认为constexpr的静态数据成员必须是文本类型(定义见§3.9/10),并且其定义必须包含在声明中。后一个条件可以通过使用以下代码来满足:

1
2
3
4
struct Foo {
  constexpr Foo() {}
  static constexpr Foo f {};
};

这与您的尝试1类似,但没有类外部定义。

但是,由于Foo在声明/定义静态成员时不完整,编译器无法检查它是否是文本类型(如§3.9/10中所定义),因此拒绝代码。

请注意,本C++-11后文件(N3308)讨论了本标准中当前constexpr定义的各种问题,并提出了修改建议。具体而言,"提议的措辞"一节建议对第3.9/10节进行修订,这意味着将不完整类型作为一种文字类型包含在内。如果该修正案被接受为标准的未来版本,您的问题将得到解决。


我认为GCC拒绝你的第3次尝试是错误的。C++ 11标准中没有规则(或其任何已接受的缺陷报告),它表示变量的重新声明必须是EDCOX1(0)。最接近该规则的标准在[dcl.constexpr](7.1.5)/1中:

If any declaration of a function or function template has constexpr speci?er, then all its declarations shall contain the constexpr speci?er.

Clang对constexpr的实现接受了您的尝试3。


Richard Smith的答案的更新,尝试3现在编译在GCC 4.9和5.1以及Clang 3.4上。

1
2
3
4
5
6
7
8
9
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仍然正常工作:

1
2
3
4
5
6
7
8
9
10
11
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

铿锵误差:

1
2
3
error: non-type template argument is not a constant expression
std::array<int, Foo<std::size_t>::f.v> a;
                ^~~~~~~~~~~~~~~~~~~~~