Constexpr 类采用 const 引用未编译
我有以下示例代码
template<class T1, class T2>
class Operation
{
public:
constexpr Operation(const T1& lhs, const T2& rhs) noexcept
: m_lhs(lhs), m_rhs(rhs) { }
private:
const T1& m_lhs;
const T2& m_rhs;
};
int main()
{
constexpr int a = 3;
constexpr int b = 4;
constexpr Operation op(a, b);
return 0;
}
用 cygwin (gcc 8.2) 编译我得到
Compiling this with cygwin (gcc 8.2) I get
error: 'Operation<int, int>{a, b}' is not a constant expression:
constexpr Operation op(a, b);
在 MSVC 2019 中,它编译得很好,但具有讽刺意味的是,IntelliSense 在 op(a, b)
中的 a
下划线,并带有工具提示表达式必须具有常量值".
With MSVC 2019 it compiles fine, but IntelliSense ironically underlines the a
in op(a, b)
with the tooltip "expression must have a constant value".
关于问题是什么以及如何解决它有什么建议吗?
Any advice as to what the issue is, and how to fix it?
推荐答案
是的,就常量评估而言,此规则是较为复杂的规则之一.
Yeah, this rule is one of the more complex ones as far as constant evaluation is concerned.
基本上,您不能拥有对没有静态存储持续时间的对象的 constexpr 引用.引用一个对象基本上就是复制它的地址――为了使一个对象的地址成为一个常量表达式,地址本身需要是常量――所以它必须保持不变.也就是说,它需要是static
.
Basically, you cannot have a constexpr reference to an object that doesn't have static storage duration. Taking a reference to an object is basically copying its address - and in order for an object's address to be a constant expression, the address itself needs to be constant - so it has to persist. That is, it needs to be static
.
因此,如果您更改您所指的具有静态存储期限的内容,则一切正常:
So if you change the things you're referring to to have static storage duration instead, everything works:
static constexpr int a = 3;
static constexpr int b = 4;
constexpr Operation op(a, b); // now ok
你的程序违反的具体规则是[expr.const]/10, 和 TC帮助我了解它是如何应用的.声明一个 constexpr
变量需要初始化为一个常量表达式 ([dcl.constexpr/10]):
The specific rule your program violates is [expr.const]/10, and T.C. helped me understand how it applies. Declaring a constexpr
variable requires the initialization to be a constant expression ([dcl.constexpr/10]):
在任何 constexpr 变量声明中,初始化的完整表达式应为常量表达式.
In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression.
我们不这么说,但它是有道理的,当然有助于解决这种特殊情况,但是初始化的完整表达"很重要.可以解释为纯右值――因为纯右值是一个表达式,它的求值初始化一个对象 ([basic.lval]/1).
We don't say this, but it makes sense and certainly helps resolve this particular situation, but the "full-expression of the initialization" can be interpreted as a prvalue -- since a prvalue is an expression whose evaluation initializes an object ([basic.lval]/1).
现在,[expr.const]/10 读作:
Now, [expr.const]/10 reads:
常量表达式要么是一个泛左值核心常量表达式[...],要么是一个值满足以下约束的纯右值核心常量表达式:
A constant expression is either a glvalue core constant expression [...], or or a prvalue core constant expression whose value satisfies the following constraints:
- 如果值是类类型的对象,则每个引用类型的非静态数据成员都引用一个实体,该实体是常量表达式的允许结果,
- [...],
- 如果值是类或数组类型的对象,则每个子对象都满足该值的这些约束.
一个实体是一个常量表达式的允许结果,如果它是一个具有静态存储持续时间的对象,该对象要么不是临时对象,要么是一个值满足上述约束的临时对象,或者如果它是一个非直接函数.
An entity is a permitted result of a constant expression if it is an object with static storage duration that either is not a temporary object or is a temporary object whose value satisfies the above constraints, or if it is a non-immediate function.
初始化Operation(a, b)
是一个纯右值,所以我们需要每个引用数据成员引用一个作为常量表达式结果允许的实体.我们的引用数据成员指的是a
和b
,它们都没有静态存储持续时间,也不是临时函数,也不是非立即函数.因此,整体初始化不是一个常量表达式,并且是格式错误的.
The initialization Operation(a, b)
is a prvalue, so we need each reference data member to refer to an entity that is permitted as a result of a constant expression. Our reference data members refer to a
and b
, neither of which has static storage duration nor are temporaries nor are non-immediate functions. Hence, the overall initialization isn't a constant expression, and is ill-formed.
使 a
和 b
成为静态的赋予它们静态的存储持续时间,这使得它们成为常量表达式的允许结果,这使得纯右值初始化满足所有要求,这使得op
的声明有效.
Making a
and b
static gives them static storage duration, which makes them permitted results of constant expressions, which makes the prvalue initialization satisfy all the requirements, which makes the declaration of op
valid.
这是一个冗长的说法:在处理常量评估时,所有地方的一切都必须一直保持不变.我们的一些表述方式非常复杂(就像这个),但它基于一个基本思想,即常量评估模型基本上就像暂停评估代码以运行一个单独的程序来产生答案.生成 op
要求这些地址是已知的、固定的――而且这只发生在静态存储期间.
This is all a long winded way of saying: when dealing with constant evaluation, everything everywhere has to be constant all the way down. Some of our ways of wording this are very complex (like this one), but it's based on the fundamental idea that the model of constant evaluation is basically like pausing evaluating the code to go run a separate program to produce an answer. Producing op
requires these addresses to be known, fixed things - and that only happens for static storage duration.
相关文章