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

如何检测泛型Lambda能否以const C&参数调用并返回可转bool值?

泛型Lambda可调用性检测问题的解决方案与原理

问题原理

你的代码出现两种异常情况的核心原因在于SFINAE语境的范围限制:

  • 无显式返回类型的Lambda:编译器推导返回类型时,必须实例化Lambda函数体内的表达式(x*x)。而std::experimental::is_detected_convertible_v的检测流程中,这个实例化处于非SFINAE语境,因此std::string无operator*的错误会直接终止编译,而非被SFINAE忽略并返回false。
  • 有显式返回类型的Lambda:此时返回类型推导被跳过,is_detected_convertible_v仅检查参数是否能匹配Lambda的调用运算符(const C&可以绑定到auto参数),不会检测函数体内的表达式合法性。因此检测错误地返回true,导致断言失败——函数体的错误只有在实际实例化调用运算符时才会触发,SFINAE阶段无法捕获。

解决方案

C++20 方案(推荐)

利用requires表达式在SFINAE语境中完整检测调用合法性与返回值转换性:

#include <string>
#include <type_traits>

template <typename F, typename C>
constexpr bool can_invoke_pred_v = requires(F f, const C& c) {
    { f(c) } -> std::convertible_to<bool>;
};

int main() {
    auto lambda = [](auto x) { return x * x; };

    constexpr auto can_invoke_int = can_invoke_pred_v<decltype(lambda), int>;
    static_assert(can_invoke_int);

    constexpr auto can_invoke_str = can_invoke_pred_v<decltype(lambda), std::string>;
    static_assert(!can_invoke_str);

    // 带显式返回类型的Lambda同样适用
    auto lambda_bool = [](auto x) -> bool { return x * x; };
    constexpr auto can_invoke_str_bool = can_invoke_pred_v<decltype(lambda_bool), std::string>;
    static_assert(!can_invoke_str_bool);

    return 0;
}

requires表达式会同时验证两个条件:

  1. f(c)是合法的调用表达式(Lambda可接受const C&参数)
  2. 调用结果可转换为bool(通过std::convertible_to约束)

C++17 及更早版本方案

使用表达式SFINAE结合重载决议实现检测:

#include <string>
#include <type_traits>

namespace detail {
    // 当调用合法且返回值可转bool时,匹配此重载
    template <typename F, typename C>
    auto test(int) -> decltype(
        static_cast<bool>(std::declval<F>()(std::declval<const C&>())),
        std::true_type{}
    );

    // 兜底重载,调用非法时匹配
    template <typename F, typename C>
    std::false_type test(...);
}

template <typename F, typename C>
constexpr bool can_invoke_pred_v = decltype(detail::test<F, C>(0))::value;

int main() {
    auto lambda = [](auto x) { return x * x; };

    constexpr auto can_invoke_int = can_invoke_pred_v<decltype(lambda), int>;
    static_assert(can_invoke_int);

    constexpr auto can_invoke_str = can_invoke_pred_v<decltype(lambda), std::string>;
    static_assert(!can_invoke_str);

    auto lambda_bool = [](auto x) -> bool { return x * x; };
    constexpr auto can_invoke_str_bool = can_invoke_pred_v<decltype(lambda_bool), std::string>;
    static_assert(!can_invoke_str_bool);

    return 0;
}

通过重载决议,当std::declval<F>()(std::declval<const C&>())合法且可转换为bool时,会匹配第一个test重载并返回std::true_type;否则匹配兜底重载返回std::false_type。整个检测过程处于SFINAE语境,函数体的错误会被正确捕获。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 05:24:53