[英]constexpr initializing static member using static function
I want a constexpr
value (ie a compile-time constant) computed from a constexpr
function.我想要从constexpr
函数计算的constexpr
值(即编译时常量)。 And I want both of these scoped to the namespace of a class, ie a static method and a static member of the class.而且我希望这两个范围都在类的命名空间内,即类的静态方法和静态成员。
I first wrote this the (to me) obvious way:我首先用(对我来说)显而易见的方式写了这个:
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
says to that: 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
complains: g++-4.6.3 -std=gnu++0x
抱怨:
error: field initializer is not constant
OK, I thought, perhaps I have to move things out of the class body.好吧,我想,也许我必须把东西移出类体。 So I tried the following:所以我尝试了以下方法:
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
will compile that without complaints. g++-4.5.3
将毫无怨言地编译它。 Unfortunately, my other code uses some range-based for
loops, so I have to have at least 4.6.不幸的是,我的其他代码使用了一些基于范围的for
循环,所以我必须至少有 4.6。 Now that I look closer at the support list , it appears that constexpr
would require 4.6 as well.现在我仔细查看了支持列表,似乎constexpr
也需要 4.6。 And with g++-4.6.3
I get使用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]
This sounds really strange to me.这对我来说听起来很奇怪。 How do things “differ in constexpr
” here?这里的东西“在constexpr
有何不同”? I don't feel like adding -fpermissive
as I prefer my other code to be rigurously checked.我不想添加-fpermissive
因为我更喜欢严格检查我的其他代码。 Moving the foo
implementation outside the class body had no visible effect.将foo
实现移到类体之外没有明显的效果。
Can someone explain what is going on here?有人可以解释这里发生了什么吗? How can I achieve what I'm attempting to do?我怎样才能实现我正在尝试做的事情? I'm mainly interested in answers of the following kinds:我主要对以下类型的答案感兴趣:
Other useful answers are welcome as well, but perhaps won't be accepted as easily.其他有用的答案也是受欢迎的,但可能不会那么容易被接受。
The Standard requires (section 9.4.2):该标准要求(第 9.4.2 节):
A
static
data member of literal type can be declared in the class definition with theconstexpr
specifier;可以在类定义中使用constexpr
说明符声明文字类型的static
数据成员; 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.如果是这样,它的声明应指定一个大括号或等号初始化器,其中每个作为赋值表达式的初始化器子句都是一个常量表达式。
In your "second attempt" and the code in Ilya's answer, the declaration doesn't have a brace-or-equal-initializer .在您的“第二次尝试”和 Ilya 的答案中的代码中,声明没有大括号或相等的初始化程序。
Your first code is correct.
你的第一个代码是正确的。
It's unfortunate that gcc 4.6 isn't accepting it, and I don't know anywhere to conveniently try 4.7.x (eg ideone.com is still stuck on gcc 4.5).
不幸的是 gcc 4.6 不接受它,而且我不知道在哪里可以方便地尝试 4.7.x(例如 ideone.com 仍然停留在 gcc 4.5 上)。
This isn't possible, because unfortunately the Standard precludes initializing a static constexpr
data member in any context where the class is complete.这是不可能的,因为不幸的是,标准禁止在类完成的任何上下文中初始化静态constexpr
数据成员。 The special rule for brace-or-equal-initializers in 9.2p2 only applies to non-static data members, but this one is static. 9.2p2 中大括号或相等初始化器的特殊规则仅适用于非静态数据成员,但这是静态的。
The most likely reason for this is that constexpr
variables have to be available as compile-time constant expressions from inside the bodies of member functions, so the variable initializers are completely defined before the function bodies -- which means the function is still incomplete (undefined) in the context of the initializer, and then this rule kicks in, making the expression not be a constant expression:最可能的原因是constexpr
变量必须作为编译时常量表达式从成员函数体内部可用,因此变量初始值设定项在函数体之前完全定义——这意味着函数仍然不完整(未定义) ) 在初始值设定项的上下文中,然后此规则生效,使表达式不是常量表达式:
an invocation of an undefined
constexpr
function or an undefinedconstexpr
constructor outside the definition of aconstexpr
function or aconstexpr
constructor;未定义的调用constexpr
功能或未定义constexpr
a的定义之外构造constexpr
函数或constexpr
构造;
Consider:考虑:
class C1
{
constexpr static int foo(int x) { return x + bar; }
constexpr static int bar = foo(sizeof(int));
};
1) Ilya's example should be invalid code based on the fact that the static constexpr data member bar is initialized out-of-line violating the following statement in the standard: 1) Ilya 的示例应该是无效代码,因为静态 constexpr 数据成员 bar被初始化为违反标准中的以下语句:
9.4.2 [class.static.data] p3: ... A static data member of literal type can be declared in the class definition with the constexpr specifier; 9.4.2 [class.static.data] p3: ... 可以在类定义中使用 constexpr 说明符声明文字类型的静态数据成员; 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.如果是这样,它的声明应指定一个大括号或等号初始化器,其中每个作为赋值表达式的初始化器子句都是一个常量表达式。
2) The code in MvG's question: 2)MvG问题中的代码:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
is valid as far as I see and intuitively one would expect it to work because the static member foo(int) is defined by the time processing of bar starts (assuming top-down processing).就我所见和直觉上来说是有效的,因为静态成员 foo(int)是由bar开始的时间处理定义的(假设自上而下处理)。 Some facts:一些事实:
an invocation of an undefined constexpr function or an undefined constexpr constructor outside the definition of a constexpr function or a constexpr constructor;在 constexpr 函数或 constexpr 构造函数的定义之外调用未定义的 constexpr 函数或未定义的 constexpr 构造函数;
class C1 { constexpr static int foo() { return bar; } constexpr static int bar = foo(); };
looks invalid but for different reasons and not simply because foo is called in the initializer of bar .看起来无效,但出于不同的原因,而不仅仅是因为foo在bar的初始值设定项中被调用。 The logic goes as follows:逻辑如下:
but by bullet 9 in (5.19 p2) which bar does not satisfy because it is not yet initialized:但通过在子弹9(5.19 P2),该杆不满足,因为它尚未初始化:
- an lvalue-to-rvalue conversion (4.1) unless it is applied to:左值到右值的转换 (4.1) 除非它应用于:
- a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression.整数或枚举类型的泛左值,它引用具有前面初始化的非易失性 const 对象,用常量表达式初始化。
hence the lvalue-to-rvalue conversion of bar does not yield a constant expression failing the requirement in (9.4.2 p3).因此bar的左值到右值转换不会产生不符合 (9.4.2 p3) 要求的常量表达式。
an invocation of a constexpr function with arguments that, when substituted by function invocation substitution (7.1.5), do not produce a constant expression调用带有参数的 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;
}
Such initialization works well but only on clang这种初始化效果很好,但只适用于 clang
Probably, the problem here is related to the order of declaration/definitions in a class.可能,这里的问题与类中声明/定义的顺序有关。 As you all know, you can use any member even before it is declared/defined in a class.众所周知,您甚至可以在类中声明/定义任何成员之前使用它。
When you define de constexpr value in the class, the compiler does not have the constexpr function available to be used because it is inside the class.当您在类中定义 de constexpr 值时,编译器没有可用的 constexpr 函数,因为它在类内部。
Perhaps, Philip answer, related to this idea, is a good point to understand the question.也许,与此想法相关的Philip回答是理解问题的好点。
Note this code which compiles without problems:请注意此代码编译没有问题:
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.