Constexpr Class 获取 const 引用不编译

Constexpr Class taking const references not compiling

我有以下示例代码

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) 编译我得到

error: 'Operation<int, int>{a, b}' is not a constant expression:
       constexpr Operation op(a, b);

对于 MSVC 2019,它编译得很好,但 IntelliSense 具有讽刺意味的是在 op(a, b) 中用工具提示 "expression must have a constant value".

强调了 a

关于问题是什么以及如何解决有什么建议吗?

是的,就持续评估而言,这条规则是比较复杂的规则之一。

基本上,您不能拥有对没有静态存储持续时间的对象的 constexpr 引用。引用一个对象基本上就是复制它的地址——为了让对象的地址成为一个常量表达式,地址本身需要是常量——所以它必须持久化。即需要static.

因此,如果您将所指的内容更改为具有静态存储持续时间,则一切正常:

static constexpr int a = 3;
static constexpr int b = 4;

constexpr Operation op(a, b); // now ok

您的程序违反的具体规则是[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]):

In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression.

我们不这么说,但它是有道理的,当然有助于解决这种特殊情况,但“初始化的完整表达式”可以解释为纯右值——因为纯右值是一个表达式,其求值初始化对象 ([basic.lval]/1).

现在,[expr.const]/10 读取:

A constant expression is either a glvalue core constant expression [...], or or a prvalue core constant expression whose value satisfies the following constraints:

  • if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression,
  • [...],
  • if the value is an object of class or array type, each subobject satisfies these constraints for the value.

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) 是纯右值,因此我们需要每个引用数据成员引用作为常量表达式的结果而允许的实体。我们的参考数据成员指的是 ab,它们都没有静态存储持续时间,也不是临时函数,也不是非立即函数。因此,整体初始化不是常量表达式,并且格式错误。

使 ab static 使它们静态存储持续时间,这使得它们允许常量表达式的结果,这使得 prvalue 初始化满足所有要求,这使得 [= 的声明18=]有效。


这是一句冗长的说法:在处理常量评估时,所有地方的一切都必须一直保持常量。我们的一些措辞方式非常复杂(比如这个),但它基于这样一个基本思想,即不断评估的模型基本上就像暂停评估代码以转到 运行 一个单独的程序来产生答案.生成 op 需要这些地址是已知的、固定的东西——这只发生在静态存储期间。