lambda 在常量表达式中的使用

Usage of lambda in constant expression

取下面的代码:

template <typename T, typename U>
constexpr bool can_represent(U&& w) noexcept
{
    return [] (auto&& x) {
        try {
            return T(std::forward<U>(x)) == std::forward<U>(x);
        } catch(...) {
            return false;
        }
    } (std::forward<U>(w));
}

我在常量表达式(模板)中使用这个函数。

gcc编译没有问题。 clangMSVC没有,感叹函数没有得到常量表达式。

的确,gcc也没有立即接受;它挂在 try 上,通常在 constexpr 函数中是不允许的。这就是为什么我必须使用立即调用的 lambda 表达式的原因。但是,现在它可以工作了,考虑到它只适用于 gcc 我很困惑。

哪个编译器是正确的? 是否有 lambda 的 属性 允许它在 constexpr 上下文中工作,或者这是某种非标准 gcc 扩展?

[我使用 godbolt 与 clangMSVC 一起编译,因为我的机器上有 gcc 8.1.0]

[gcc] was getting hung up on the try, that normally wouldn't be allowed in a constexpr function.

这对于 C++17 程序是正确的。 (C++20 放宽了这一点,因此 try 块现在可以在 constexpr 函数中使用。但是,只有 try 是允许的;不允许执行命中会抛出异常的东西。)

That's why I had to use an immediately invoked lambda expression.

这里的含义是您的方法使您的代码有效。这是不正确的。使用立即调用的 lambda 并不能解决这个问题;它掩盖了问题。 try 仍然是一个问题,但现在编译器不必告诉你它是一个问题。

使用 lambda 将 constexpr criterion 从直接 “函数体不能包含 try 块” 切换到间接 “至少存在一组参数值,这样函数的调用可以是核心常量表达式的评估子表达式”。 这里棘手的部分是违反后一个标准是“无诊断性” required”,这意味着所有的编译器都是正确的,无论他们是否抱怨这段代码。因此,我将其描述为掩盖问题。

那么为什么...该标准需要重复很长时间...涉及“core constant expressions"? C++17 removed the prohibition against lambdas in core constant expressions, so that much looks good. However, there is still a requirement that all function calls within the constexpr function also be themselves constexpr. Lambdas 可以通过两种方式变为 constexpr 的问题是什么。首先,可以明确标记它们constexpr(但如果你在这里这样做,关于 try 块的抱怨应该回来)。其次,它们可以简单地满足 constexpr 功能要求。但是,你的 lambda 包含一个 try,所以它不是 constexpr(在 C++17 中)。

您的 lambda 不是有效的 constexpr 函数。因此在核心常量表达式中不允许调用它。没有通过 can_represent() 的执行路径可以避免调用您的 lambda。因此,can_represent 不是有效的 constexpr 函数,不需要诊断。