【问题标题】:Constexpr Class taking const references not compilingConsexpr 类采用 const 引用不编译
【发布时间】:2019-08-18 14:30:49
【问题描述】:

我有以下示例代码

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) 中强调了 a

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

【问题讨论】:

    标签: c++ reference c++17 constexpr


    【解决方案1】:

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

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

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

    static constexpr int a = 3;
    static constexpr int b = 4;
    
    constexpr Operation op(a, b); // now ok
    

    您的程序违反的具体规则是[expr.const]/10,而 T.C.帮助我了解它是如何应用的。声明constexpr 变量需要初始化为常量表达式([dcl.constexpr/10]):

    在任何 constexpr 变量声明中,初始化的完整表达式应为常量表达式。

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

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

    一个常量表达式要么是一个glvalue核心常量表达式[...],要么是一个prvalue核心常量表达式,其值满足以下约束:

    • 如果值是类类型的对象,则每个引用类型的非静态数据成员都引用一个实体,该实体是常量表达式的允许结果,
    • [...],
    • 如果该值是类或数组类型的对象,则每个子对象都满足该值的这些约束。

    实体是常量表达式的允许结果,如果它是具有静态存储持续时间的对象,要么不是临时对象,要么是其值满足上述约束的临时对象,或者如果它是一个非立即函数。

    初始化Operation(a, b) 是一个prvalue,因此我们需要每个引用数据成员引用一个作为常量表达式的结果而被允许的实体。我们的参考数据成员引用ab,它们都没有静态存储持续时间,也不是临时函数,也不是非立即函数。因此,整体初始化不是一个常量表达式,而且格式不正确。

    ab 设为static 使它们具有静态存储持续时间,这使它们成为常量表达式的允许结果,从而使prvalue 初始化满足所有要求,从而使op 的声明有效。


    这是一种啰嗦的说法:在处理持续评估时,所有地方的一切都必须一直保持不变。我们的一些措辞方式非常复杂(比如这个),但它基于基本思想,即持续评估模型基本上就像暂停评估代码以运行单独的程序以产生答案。生成 op 需要知道这些地址,并进行修复——而且这只发生在静态存储期间。

    【讨论】:

    • 你不认为这对于目前正试图扩大其吸引力的团队开发的通用编程语言来说是荒谬的和深奥的吗?
    • @LightnessRacesinOrbit 如果对象的地址不是恒定的,您如何期望在不断评估期间获取对象的地址?这也是自 C++11 以来的规则,所以它不像......新的。
    • 我并不是说它没有技术原因,或者它是“新的”。我说这是荒谬的神秘和深奥。人们(不是像你这样的语言律师)不应该对这样的事情感到疑惑。因此,问题中的一个显然合理且简单的程序需要在 Stack Overflow 上发布才能真正发挥作用。至少,诊断是无用的。
    • @LightnessRacesinOrbit 所以你的意思是:更好的诊断将完全解决问题。
    • 没有。我所说的正是我所说的。为什么你一直在回应我没有说的事情?!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-29
    相关资源
    最近更新 更多