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
相关产品推荐
相关产品推荐

