我想让一个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
的初始化器。
尝试三:
struct Foo {
constexpr Foo() {}
static const Foo f;
};
constexpr Foo Foo::f = Foo();
现在g++抱怨Foo::f
的重新声明与constexpr
不同。
5条答案
按热度按时间fae0ux8s1#
如果我对《标准》的理解正确的话,这是不可能的。
(§9.4.2/3)[......]文本类型的静态数据成员可以在类定义中用constexpr说明符声明;如果是这样,它的声明应该指定一个花括号或等号初始化式,其中每个赋值表达式的初始化子句都是常量表达式。
从上面(沿着在静态数据成员声明中没有关于非文本类型的单独声明这一事实),我认为可以得出结论:
constexpr
的静态数据成员必须是文本类型(定义见第3.9/10节),和它必须将其定义包含在声明中。使用以下代码可以满足后一个条件:这与您的尝试1类似,但没有类外部定义。
然而,由于
Foo
在声明/定义静态成员时是不完整的,编译器无法检查它是否是一个文本类型(如3.9节所定义的),因此它拒绝了代码。注意,this post-C++-11 document (N3308)讨论了标准中
constexpr
当前定义的各种问题,并提出了修改建议。特别是,“建议的措辞”部分建议修改§3.9/10,暗示将不完整类型作为一种文字类型包括在内。如果该修改被接受到标准的未来版本中,你的问题就解决了。aiazj4mn2#
我认为GCC拒绝您的尝试3是不正确的。在C++11标准(或其任何可接受的缺陷报告)中没有规则规定,如果先前的声明是
constexpr
,则变量的重新声明必须是constexpr
。最接近该规则的标准是**[dcl.constexpr](7.1.5)/1_**:如果函数或函数模板的任何声明具有
constexpr
规范,则其所有声明都应包含constexpr
规范。Clang的
constexpr
实现接受您的尝试3。vdzxcuhz3#
Richard Smith's answer的更新,尝试3现在编译GCC 4.9和5.1,以及clang 3.4。
然而,当Foo是一个类模板时,clang 3.4会失败,但GCC 4.9和5.1仍然可以正常工作:
叮当声错误:
wz3gfoph4#
早些时候,我遇到了同样的问题,遇到了这个十年前的问题。我很高兴地报告说,在中间的几年里,一个解决办法出现了;我们只需要做类似于上面的“尝试3”的事情,但是将
Foo::f
的定义标记为inline
。使用g++ --std=c++17
编译的最小示例:英尺高
foo.cpp
主.cpp
yruzcnhs5#
如果你像我一样,试图创建类似
enum
的类,我所想到的最好的方法是使用CRTP将行为放在基类中,然后派生类是“真实的的”类,它的存在只是为了将类似“枚举器”的值作为static constexpr inline Base
成员。但是它的行为类似于Foo
,并且可以隐式转换为Foo
,因此看起来非常接近。https://godbolt.org/z/rTEdKxE3h唯一的问题是,如果您开始使用
decltype(Foo::yes)
,就好像它是Foo
一样。