如何检测泛型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表达式会同时验证两个条件:
f(c)是合法的调用表达式(Lambda可接受const C&参数)- 调用结果可转换为
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
相关产品推荐
相关产品推荐

