You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++17中检查无参Lambda是否可被constexpr求值?

在C++17中检查无参Lambda是否可常量求值的问题

现有方案的缺陷

我曾找到Stack Overflow上的一个实现方案:

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

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

但它存在两个明显问题:

  • 仅支持C20:依赖无捕获Lambda可默认构造的特性,不符合C17的需求
  • 结果歧义:返回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确实可常量求值
    //                 (但不可默认构造)

    auto isCxLambdaCx = is_cx_invocable(cxLambda);
    static_assert(!isCxLambdaCx);  // 此处断言失败
}

我尝试的替代方案

为解决上述问题,我实现了另一个版本:

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

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

这个方案的优势:

  • 不依赖C++20特性
  • 能正确判断带捕获但可常量求值的Lambda,比如下面的示例可以通过断言:
int main() {
    int i{};
    auto cxLambda = [i] { return 0; };
    auto isCxLambdaCx = is_cx_invocable(cxLambda);
    static_assert(isCxLambdaCx); // 现在断言通过
}

但该代码在Clang和MSVC中可正常编译,却无法通过GCC编译。

我有两个疑问:

  1. 我的方案是否属于非法代码(即GCC拒绝它是符合标准的)?
  2. 是否存在符合ISO C标准、可移植的方法,在C17中实现is_cx_invocable检查?

解答

1. 你的方案是否非法?

你的代码符合C++17标准,GCC的拒绝属于编译器实现bug。

在C17中,constexpr函数的参数若为常量表达式对象(比如示例中带常量初始化捕获的Lambda),允许在常量表达式中使用。你的方案通过返回类型推导中的lambda()触发SFINAE:当lambda()无法在常量求值环境中执行时,模板重载会被丢弃,转而匹配...版本的重载。这完全符合C17的SFINAE规则,GCC在此处的行为不符合标准。

2. 可移植的C++17实现方案

我们可以利用C++17的SFINAE特性,通过在constexpr上下文内尝试调用Lambda来实现可移植的检查:

#include <type_traits>

namespace detail {
    // 尝试在constexpr上下文调用Lambda,仅当调用合法时匹配此重载
    template <class Lambda>
    constexpr auto test(Lambda lambda) -> decltype(
        []() constexpr {
            constexpr auto dummy = lambda();
            (void)dummy;
            return std::true_type{};
        }()
    ) {
        return std::true_type{};
    }

    // 匹配所有无法常量调用的情况
    constexpr std::false_type test(...) {
        return std::false_type{};
    }
}

template <class Lambda>
constexpr auto is_cx_invocable(Lambda lambda) {
    return detail::test(std::move(lambda));
}

这个实现通过嵌套的constexpr Lambda强制在常量求值环境中尝试调用传入的Lambda,只有当调用合法时,外层模板重载才会被选中,否则会匹配...版本的重载返回false_type。该代码在Clang、MSVC和GCC中均可正常编译运行,完全符合C++17标准。


内容的提问来源于stack exchange,提问作者paolo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 12:47:25