如何判断C++模板参数是函数、函数指针还是Lambda表达式?
区分函数、函数指针与Lambda并排除普通类的实现方案
要实现判断模板参数是函数、函数指针或Lambda,同时禁止普通类传入,可以通过模板元编程结合类型特征来完成,核心是利用Lambda闭包类型的标准特性(final类、包含operator())区分普通类与Lambda。
核心思路
- 函数类型判断:直接用
std::is_function_v识别原始函数类型(注意函数作为参数传递时通常会退化为指针,仅显式指定模板参数时才会保留函数类型)。 - 函数指针判断:结合
std::is_pointer_v和std::is_function_v<std::remove_pointer_t<T>>,判断指针指向的是函数类型。 - Lambda判断:Lambda的闭包类型是标准规定的final类,且必然包含
operator()成员函数,因此通过std::is_class_v+std::is_final_v+自定义的has_call_operator_v来识别。 - 普通类排除:通过
static_assert静态断言,仅允许上述三种类型通过编译,普通类会触发编译错误。
代码实现
#include <type_traits> #include <iostream> #include <utility> namespace detail { // 辅助Trait:检查类型是否包含operator() template<typename T, typename = void> struct has_call_operator : std::false_type {}; template<typename T> struct has_call_operator<T, std::void_t<decltype(&T::operator())>> : std::true_type {}; template<typename T> inline constexpr bool has_call_operator_v = has_call_operator<T>::value; } // 判断是否为Lambda表达式 template<typename T> inline constexpr bool is_lambda_v = std::is_class_v<T> && std::is_final_v<T> && detail::has_call_operator_v<T>; // 判断是否为函数指针 template<typename T> inline constexpr bool is_function_pointer_v = std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>; // 判断是否为函数类型 template<typename T> inline constexpr bool is_function_v = std::is_function_v<T>; // 处理可调用对象的模板函数 template<typename Callable> void process_callable(Callable&& c) { // 静态断言:仅允许函数、函数指针、Lambda传入 static_assert( is_function_v<std::decay_t<Callable>> || is_function_pointer_v<std::decay_t<Callable>> || is_lambda_v<std::decay_t<Callable>>, "Callable must be a function, function pointer, or lambda expression" ); // 编译时分支处理不同类型 if constexpr (is_function_v<std::decay_t<Callable>>) { std::cout << "This is a function type\n"; } else if constexpr (is_function_pointer_v<std::decay_t<Callable>>) { std::cout << "This is a function pointer\n"; } else if constexpr (is_lambda_v<std::decay_t<Callable>>) { std::cout << "This is a lambda expression\n"; } } // 测试用例 void foo() {} // 普通用户类(会触发静态断言错误) struct MyClass { void operator()() {} }; int main() { process_callable(foo); // 输出:This is a function pointer(函数退化为指针) process_callable(&foo); // 输出:This is a function pointer process_callable([](){}); // 输出:This is a lambda expression // process_callable(MyClass{}); // 取消注释会触发编译错误:符合需求 return 0; }
关键细节说明
- std::decay_t:用于处理传入参数的引用、cv限定符,确保我们检查的是原始类型。
- std::is_final_v:Lambda的闭包类型是标准规定的final类,普通用户类默认不是final(除非显式声明),这是区分Lambda与普通类的核心依据。
- C++版本兼容:代码使用了C17的
constexpr if,若需兼容C14及以下,可改用模板特化替代编译时分支。
内容的提问来源于stack exchange,提问作者A18
相关产品推荐
相关产品推荐

