【问题标题】:Expression did not evaluate to a constant two level constexpr functions (compiler bug?)表达式未计算为恒定的两级 constexpr 函数(编译器错误?)
【发布时间】:2019-07-04 16:08:10
【问题描述】:

我有以下代码:

#include <iostream>
template<int I>
class A
{
public:
    inline constexpr static int size() { return I; }
};

template<typename T>
inline constexpr auto size(const T& arg) noexcept -> decltype(arg.size())
{
    return arg.size();
}

template<typename T>
inline constexpr void twoLevel(const T& arg) noexcept
{
    static_assert(size(arg) > 0);
}

int main()
{
    A<5> a;
    static_assert(size(a)>0); //this works
    twoLevel(a); // this does not
    return 0;
}

无法在 msvc 上编译并出现错误 expression did not evaluate to a constant,但适用于 gcc。 gcc 是否接受了未定义的行为?或者它是 msvc 的编译器错误? 这是一个演示:godbolt code

【问题讨论】:

    标签: c++ templates c++17 constexpr compile-time-constant


    【解决方案1】:

    来自[expr.const]/4

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

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

    在:

    static_assert(size(arg) > 0);
    

    我们有一个id-expression,它引用了一个引用类型的变量,并且这个引用没有前面的初始化,所以我们没有一个常量表达式。

    我认为是这样的:

    static_assert(size(a) > 0);
    

    由于“预先初始化”而起作用 - 我们通过将引用 arg 直接绑定到变量 a 来进入常量评估,而在另一种情况下,我们将引用绑定到另一个引用。

    如果你按价值计算,两者都应该有效。

    【讨论】:

    • 所以 gcc 也不应该编译这个?但是不会按值传递它 - 有没有办法避免这种情况?
    • 函数参数的初始化确实算作前面的初始化,否则同一段落中示例的最后一行constexpr int y = h(1); 可能无效。问题是 a 不能“在常量表达式中使用”,并且它的生命周期不是在 size(arg) &gt; 0 的评估中开始的。
    • @aschepler 有没有办法缓解这个问题,而不是通过值传递对象?因为我不想复制对象(它可能包含一个大的静态数组)。
    • @aschepler 这对我的用例没有帮助 - 我特别需要一个免费函数,因为我希望能够为结构(如普通数组)定义一个特化,即使它们也能工作不提供 size 静态方法。
    • @lightxbulb 然后定义一个 trait struct,可以显式和部分特化?
    猜你喜欢
    • 2015-03-10
    • 2014-01-08
    • 2021-12-29
    • 2020-05-21
    • 2017-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多