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

Mar*_*tto 7 c++ language-lawyer constant-expression

考虑以下功能:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

错误:非类型模板参数不是常量表达式

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

我想知道为什么会这样,因为size()实际上返回一个模板参数,它几乎不再是const.参数是否是参考不应该有所作为.

我知道我当然可以使用S1和S2模板参数,该功能仅仅是问题的简短说明.

标准中有什么吗?我很惊讶地发现了编译错误.

P.W*_*P.W 7

关于 clang 的此问题报告了一个错误,标题为:Clang 不允许在非类型模板参数中使用 constexpr 类型转换

其中的讨论表明这并不是一个真正的错误。

表达式 e 是核心常量表达式,除非对 e 的求值遵循抽象机的规则,将求值以下表达式之一:

  • [...]
  • 引用引用类型的变量或数据成员的id 表达式,除非引用具有前面的初始化并且
    • 它用常量表达式初始化或
    • 它的生命周期从 e 的评估开始;
  • [...]

上面的引用来自草案 n4659 的 [expr.const]/2.11,并添加了强调。


xsk*_*xzr 6

因为你已经评估了一个参考。来自[expr.const]/4

表达式 e 是核心常量表达式,除非按照抽象机器的规则对 e 求值会求值以下表达式之一:

  • ...
  • 引用变量或引用类型数据成员的id 表达式,除非该引用具有前面的初始化并且
    • 它可用于常量表达式或
    • 它的生命周期开始于 e 的计算;
  • ...

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

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


Oli*_*liv -2

不幸的是,标准规定,在类成员访问表达式中,对点或箭头之前的后缀表达式进行求值;63 [expr.ref]/1。后缀表达式a位于a.b. 该注释非常有趣,因为这里的情况正是如此:

63) 如果对类成员访问表达式进行求值,则即使不需要结果来确定整个后缀表达式的值,也会发生子表达式求值,例如,如果 id 表达式表示静态成员。

data即使没有必要,也会对so进行求值,并且常量表达式前的规则也适用于它。