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

C++17中如何判定两种不同签名Lambda的返回类型?

问题描述

我编写了一个接收可调用Lambda作为参数的函数,该Lambda允许两种签名:可传入一个int或两个int参数。当前的问题是如何确定该Lambda的返回类型。

问题代码示例

#include <iostream>
#include <type_traits>

template <class Lambda>
auto f(Lambda&& f)
{
    using result_type = std::conditional_t<std::is_invocable_v<Lambda, int>, std::invoke_result_t<Lambda, int>, std::invoke_result_t<Lambda, int, int>>;
    result_type res;
    if constexpr (std::is_invocable_v<Lambda, int>) {
        res = f(3);
    }
    else {
        res =  f(3, 3);
    }
    
    return res;
}

int main()
{
    std::cout << f([](int x) -> int { return x; }) << std::endl;
    std::cout << f([](int x, int y) -> int { return x * y; }) << std::endl;

    return 0;
}

这段代码无法编译,原因是std::conditional_t要求第二个和第三个模板参数必须同时合法,但此处仅有一种签名对传入的Lambda有效,导致无效分支的std::invoke_result_t实例化失败。

我原本打算用模板特化编写一个特性类解决问题,示例代码如下:

template <class F, bool T>
struct helper;

template <class F>
struct helper<F, true> {
    using type = std::invoke_result_t<F, int>;
};

template <class F>
struct helper<F, false> {
    using type = std::invoke_result_t<F, int, int>;
};

但不想重复造轮子,想询问是否有用C++17标准库实现的更简洁紧凑的解决方案?


解决方案

方案1:利用if constexpr直接推导返回类型

这是最简洁的方案,C++17的if constexpr会丢弃未选中的分支,无需显式定义返回类型,编译器会自动推导:

#include <iostream>
#include <type_traits>

template <class Lambda>
auto f(Lambda&& f)
{
    if constexpr (std::is_invocable_v<Lambda, int>) {
        return f(3);
    }
    else {
        return f(3, 3);
    }
}

int main()
{
    std::cout << f([](int x) -> int { return x; }) << std::endl;
    std::cout << f([](int x, int y) -> int { return x * y; }) << std::endl;

    return 0;
}

方案2:结合std::enable_if_t显式定义返回类型

如果需要显式指定返回类型,可以用std::enable_if_t确保无效分支被SFINAE过滤:

#include <iostream>
#include <type_traits>

template <class Lambda>
auto f(Lambda&& f)
{
    using result_type = std::conditional_t<
        std::is_invocable_v<Lambda, int>,
        std::invoke_result_t<Lambda, int>,
        std::enable_if_t<std::is_invocable_v<Lambda, int, int>, std::invoke_result_t<Lambda, int, int>>
    >;
    
    if constexpr (std::is_invocable_v<Lambda, int>) {
        return static_cast<result_type>(f(3));
    }
    else {
        return static_cast<result_type>(f(3, 3));
    }
}

int main()
{
    std::cout << f([](int x) -> int { return x; }) << std::endl;
    std::cout << f([](int x, int y) -> int { return x * y; }) << std::endl;

    return 0;
}

方案3:使用std::common_type_t(限返回类型兼容场景)

若两种签名的返回类型可转换为共同类型,可借助std::common_type_t统一返回类型:

#include <iostream>
#include <type_traits>

template <class Lambda>
auto f(Lambda&& f)
{
    using type1 = std::invoke_result_t<Lambda, int>;
    using type2 = std::invoke_result_t<Lambda, int, int>;
    using result_type = std::common_type_t<type1, type2>;
    
    if constexpr (std::is_invocable_v<Lambda, int>) {
        return static_cast<result_type>(f(3));
    }
    else {
        return static_cast<result_type>(f(3, 3));
    }
}

注意:该方案要求两种返回类型存在共同类型,否则会编译失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 01:52:15