gcc 检查lambda是否可以用constexpr计算(C++17)

bvjxkvbb  于 2023-03-08  发布在  其他
关注(0)|答案(1)|浏览(151)

我试图找到一种方法来检查一个lambda是否可以在C++17中被求值为常量。让我们假设我要检查的lambda不带参数。
我偶然发现了this问题,它的公认答案如下:

template <class Lambda, int = (Lambda{}(), 0)>
constexpr std::true_type is_cx_invocable(Lambda) {
    return {};
}

constexpr std::false_type is_cx_invocable(...) {
    return {};
}

其具有以下问题:
1.它只能在C20中工作(因为需要无捕获的lambda是默认可构造的),我正在寻找一个C17的解决方案;
1.如果is_cx_invocable返回false,则不清楚问题是Lambda不是默认可构造的还是其调用运算符不是constexpr,例如:

int main() {
    static_assert(is_cx_invocable([] { return 0; }));

    int i{};
    auto cxLambda = [i] { return 0; };
    (std::array<int, cxLambda()>{}, (void)0);
    //               ^ cxLambda is indeed constexpr invocable
    //                 (but not default constructible)

    auto isCxLambdaCx = is_cx_invocable(cxLambda);
    static_assert(!isCxLambdaCx);  // This fails
}

所以,我试着这样做:

template <class Lambda>
constexpr auto is_cx_invocable(Lambda lambda)
    -> std::integral_constant<bool, (lambda(), 1)> { // or
    //-> std::enable_if_t<(lambda(), 1), std::true_type> {
    return {};
}

constexpr std::false_type is_cx_invocable(...) {
    return {};
}

其中:
1.不需要C++20功能;
1.修复了上述问题(2):

int main() {
    int i{};
    auto cxLambda = [i] { return 0; };
    auto isCxLambdaCx = is_cx_invocable(cxLambda);
    static_assert(isCxLambdaCx); // OK now
}

问题是,这个解决方案可以很好地编译Clang和MSVC,而不能编译GCC。Here您是一个 * 编译器资源管理器 * 链接玩。
我的问题是:
1.我的解决方案是不是格式错误(所以GCC拒绝我的代码是正确的)?
1.是否有一个可移植的(即可以用ISO C实现,并且独立于编译器的怪癖)来在C17中实现is_cx_invocable检查?

nnt7mjpx

nnt7mjpx1#

有一条规则规定函数参数不应该被同一个函数的默认参数求值:[dcl.fct.默认值]/9.
然而,GCC似乎太严格了,它似乎强制执行了一个规则,即函数参数在函数体开始之前不可能被求值(即,你不能在尾部返回类型中求值)。
所以我认为Clang和MSVC在这里是正确的。事实上,标准中没有任何规则使这个例子成为病态的。
然而,我认为即使GCC bug被修复了,这个检测习惯用法仍然不起作用。注意当我们把lambda变成一个不能在常量表达式中调用的lambda时会发生什么:https://godbolt.org/z/e9YbEWeWE
这里,在Clang和GCC中,你会得到一个硬错误,而不是SFINAE,这是因为导致lambda调用不是常量表达式的求值发生在被调用函数的函数体内部(即lambda的函数调用操作符),并且该函数体不是SFINAE的直接上下文的一部分。
这与([temp. defcrete.general]/9)有关,它声明lambda表达式不是直接上下文的一部分。从技术上讲,[temp. defcrete.general]/9不直接应用于这种情况,因为lambda表达式本身并不存在于所讨论的模板中;然而,正如Note在该段中提到的,当前不能期望实现将 any 函数体视为直接上下文的一部分,因为这样做意味着它们必须能够对任意语句(不仅仅是表达式和声明)执行SFINAE。
假设地说,我认为实现实际上可以处理这个特定的例子而没有太多的困难,因为函数体中没有任何格式不良的东西;调用未能成为常量表达式的事实可以简单地被“冒泡”到立即上下文中。然而,为了使实现支持这一点,您仍然需要写一篇论文并通过C++委员会。

相关问题