[英]Initializing a static constexpr data member of the base class by using a static constexpr data member of the derived class
[英]constexpr initializing static member using static function
我想要从constexpr
函数计算的constexpr
值(即编译时常量)。 而且我希望这两个范围都在类的命名空间内,即类的静态方法和静态成员。
我首先用(对我来说)显而易见的方式写了这个:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
g++-4.5.3 -std=gnu++0x
说:
error: ‘static int C1::foo(int)’ cannot appear in a constant-expression
error: a function call cannot appear in a constant-expression
g++-4.6.3 -std=gnu++0x
抱怨:
error: field initializer is not constant
好吧,我想,也许我必须把东西移出类体。 所以我尝试了以下方法:
class C2 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar;
};
constexpr int C2::bar = C2::foo(sizeof(int));
g++-4.5.3
将毫无怨言地编译它。 不幸的是,我的其他代码使用了一些基于范围的for
循环,所以我必须至少有 4.6。 现在我仔细查看了支持列表,似乎constexpr
也需要 4.6。 使用g++-4.6.3
我得到
3:24: error: constexpr static data member ‘bar’ must have an initializer
5:19: error: redeclaration ‘C2::bar’ differs in ‘constexpr’
3:24: error: from previous declaration ‘C2::bar’
5:19: error: ‘C2::bar’ declared ‘constexpr’ outside its class
5:19: error: declaration of ‘const int C2::bar’ outside of class is not definition [-fpermissive]
这对我来说听起来很奇怪。 这里的东西“在constexpr
有何不同”? 我不想添加-fpermissive
因为我更喜欢严格检查我的其他代码。 将foo
实现移到类体之外没有明显的效果。
有人可以解释这里发生了什么吗? 我怎样才能实现我正在尝试做的事情? 我主要对以下类型的答案感兴趣:
其他有用的答案也是受欢迎的,但可能不会那么容易被接受。
该标准要求(第 9.4.2 节):
可以在类定义中使用
constexpr
说明符声明文字类型的static
数据成员; 如果是这样,它的声明应指定一个大括号或等号初始化器,其中每个作为赋值表达式的初始化器子句都是一个常量表达式。
在您的“第二次尝试”和 Ilya 的答案中的代码中,声明没有大括号或相等的初始化程序。
你的第一个代码是正确的。
不幸的是 gcc 4.6 不接受它,而且我不知道在哪里可以方便地尝试 4.7.x(例如 ideone.com 仍然停留在 gcc 4.5 上)。
这是不可能的,因为不幸的是,标准禁止在类完成的任何上下文中初始化静态constexpr
数据成员。 9.2p2 中大括号或相等初始化器的特殊规则仅适用于非静态数据成员,但这是静态的。
最可能的原因是constexpr
变量必须作为编译时常量表达式从成员函数体内部可用,因此变量初始值设定项在函数体之前完全定义——这意味着函数仍然不完整(未定义) ) 在初始值设定项的上下文中,然后此规则生效,使表达式不是常量表达式:
未定义的调用
constexpr
功能或未定义constexpr
a的定义之外构造constexpr
函数或constexpr
构造;
考虑:
class C1
{
constexpr static int foo(int x) { return x + bar; }
constexpr static int bar = foo(sizeof(int));
};
1) Ilya 的示例应该是无效代码,因为静态 constexpr 数据成员 bar被初始化为违反标准中的以下语句:
9.4.2 [class.static.data] p3: ... 可以在类定义中使用 constexpr 说明符声明文字类型的静态数据成员; 如果是这样,它的声明应指定一个大括号或等号初始化器,其中每个作为赋值表达式的初始化器子句都是一个常量表达式。
2)MvG问题中的代码:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
就我所见和直觉上来说是有效的,因为静态成员 foo(int)是由bar开始的时间处理定义的(假设自上而下处理)。 一些事实:
在 constexpr 函数或 constexpr 构造函数的定义之外调用未定义的 constexpr 函数或未定义的 constexpr 构造函数;
class C1 { constexpr static int foo() { return bar; } constexpr static int bar = foo(); };
看起来无效,但出于不同的原因,而不仅仅是因为foo在bar的初始值设定项中被调用。 逻辑如下:
但通过在子弹9(5.19 P2),该杆不满足,因为它尚未初始化:
- 左值到右值的转换 (4.1) 除非它应用于:
- 整数或枚举类型的泛左值,它引用具有前面初始化的非易失性 const 对象,用常量表达式初始化。
因此bar的左值到右值转换不会产生不符合 (9.4.2 p3) 要求的常量表达式。
调用带有参数的 constexpr 函数,当被函数调用替换 (7.1.5) 替换时,不会产生常量表达式
#include <iostream>
class C1
{
public:
constexpr static int foo(constexpr int x)
{
return x + 1;
}
static constexpr int bar;
};
constexpr int C1::bar = C1::foo(sizeof(int));
int main()
{
std::cout << C1::bar << std::endl;
return 0;
}
这种初始化效果很好,但只适用于 clang
可能,这里的问题与类中声明/定义的顺序有关。 众所周知,您甚至可以在类中声明/定义任何成员之前使用它。
当您在类中定义 de constexpr 值时,编译器没有可用的 constexpr 函数,因为它在类内部。
也许,与此想法相关的Philip回答是理解问题的好点。
请注意此代码编译没有问题:
constexpr int fooext(int x) { return x + 1; }
struct C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = fooext(5);
};
constexpr static int barext = C1::foo(5);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.