c++ 在未求值的上下文中在lambda中使用std::declval法律的吗?

vhmi4jdf  于 2022-11-19  发布在  其他
关注(0)|答案(1)|浏览(158)

下面的代码或godbolt上的代码用gcc和MSVC编译,但失败了,并发出叮当声。我不知道是否/在标准中禁止它。在我看来,它应该得到支持。
那么谁是正确的,clang还是gcc/MSVC?谢谢!

#include <type_traits>

void foo() {
    static_assert(decltype([_=std::declval<int>()]() consteval noexcept { // clang error: declval() must not be used
        if constexpr (std::is_integral<decltype(_)>::value) {
            return std::bool_constant<true>();
        } else {
            return std::bool_constant<false>();
        }
    }())::value);
}

=====================编辑===========
该示例可扩展为以下3种情况或godbolt
1.作为lambda调用参数:通过clang/gcc/MSVC确认
1.作为λ捕获:gcc/MSVC正常,叮当声错误
1.在lambda主体中:clang/gcc/MSVC出错
因此,似乎很清楚,它在lambda主体中是不法律的,但在外部作为调用方参数是合法不清楚是否允许在捕获列表中使用

#include <type_traits>

auto foo_lambda_argument() {
    return decltype([](auto _) noexcept {
        return std::bool_constant<std::is_integral<decltype(_)>::value>();
    }(std::declval<int>()))::value; // OK with clang/gcc/MSVC
}

auto foo_capture_list() {
    return decltype([_=std::declval<int>()]() noexcept { // OK with gcc/MSVC; clang error: declval() must not be used
        return std::bool_constant<std::is_integral<decltype(_)>::value>();
    }())::value;
}

auto foo_lambda_body() {
    return decltype([]() noexcept {
        auto _=std::declval<int>(); // clang/gcc/MSVC error
        return std::bool_constant<std::is_integral<decltype(_)>::value>();
    }())::value;
}
dxpyg8gm

dxpyg8gm1#

我不完全确定这是如何工作的,但这里是我的尝试在一个解决方案:
根据[intro.execution]/3.3,init-capture 的初始化器是lambda表达式的立即子表达式。然而,所列出的项目中没有一个使表达式在lambda的主体子表达式中。
未赋值的操作数是非潜在求值的表达式。但只有它们的子表达式也是非潜在求值的表达式。(请参阅[basic.def.odr]/2)
如果一个函数是由一个可能计算的表达式命名的,或者在一些与这里无关的特殊情况下,则该函数是odr使用的。
所以std::declval在init-capture初始化器中应该是好的,就像你的第2点一样。
在第1点中也是可以的,因为函数参数是函数调用表达式的子表达式,组成了整个未求值的操作数。
第3点是不行的,因为即使lambda表达式出现在未求值的操作数中,lambda主体中的表达式也可能被求值。
我对上述推理的一个担忧是,[exp.prim.lambda.capture]/6指定了一个init-capture,其行为就像用给定的初始化器声明一个变量,然后捕获它。我不确定这在哪里适合上述推理(声明不能是子表达式),以及它是否算作odr-use。

相关问题