为什么引用上的 constexpr 函数不是 constexpr?

Why is a constexpr function on a reference not constexpr?

考虑以下函数:

template <size_t S1, size_t S2>
auto concatenate(std::array<uint8_t, S1> &data1, std::array<uint8_t, S2> &data2) {
    std::array<uint8_t, data1.size() + data2.size()> result;

    auto iter = std::copy(data1.begin(), data1.end(), result.begin());
    std::copy(data2.begin(), data2.end(), iter);

    return result;
}

int main()
{
    std::array<uint8_t, 1> data1{ 0x00 };
    std::array<uint8_t, 1> data2{ 0xFF };

    auto result = concatenate(data1, data2);
    return 0;
}

当使用 clang 6.0 编译时,使用 -std=c++17,此函数无法编译,因为数组上的 size 成员函数不是 constexpr,因为它是引用。错误信息是这样的:

error: non-type template argument is not a constant expression

当参数 不是 引用时,代码按预期工作。

我想知道为什么会这样,因为 size() 实际上 returns 是一个模板参数,它几乎不能再是常量了。参数是否是引用应该没有区别。

我知道我当然可以使用 S1 和 S2 模板参数,该函数只是问题的简短说明。

标准里有什么吗?我很惊讶从中得到一个编译错误。

因为你评价了一个参考。来自 [expr.const]/4:

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:

  • ...
  • an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
    • it is usable in constant expressions or
    • its lifetime began within the evaluation of e;
  • ...

您的引用参数没有预先初始化,因此不能在常量表达式中使用。

您可以在这里简单地使用 S1 + S2

关于此问题的 clang 已报告一个错误,标题为:Clang does not allow to use constexpr type conversion in non-type template argument

其中的讨论指出这并不是真正的错误。

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:

  • [...]
  • an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
    • it is initialized with a constant expression or
    • its lifetime began within the evaluation of e;
  • [...]

以上引用来自 n4659 草案的 [expr.const]/2.11,并添加了重点。

不幸的是,标准规定在class成员访问表达式中计算点或箭头之前的后缀表达式;63 [expr.ref]/1。后缀表达式是 a in a.b。该注释真的很有趣,因为这里正是这种情况:

63) If the class member access expression is evaluated, the subexpression evaluation happens even if the result is unnecessary to determine the value of the entire postfix expression, for example if the id-expression denotes a static member.

因此即使没有必要对 data 求值,并且常量表达式规则也适用于它。