正确使用std::function::target:实现Callable与std::function目标匹配函数
判断std::function是否指向特定Callable对象的实现方案
我来帮你解决这个问题——要实现一个能判断std::function的目标是否是传入的Callable对象的test函数,核心难点在于std::function对不同类型Callable的存储方式不一样,我们需要分情况处理函数指针和自定义仿对象。
完整实现代码
#include <type_traits> #include <iostream> #include <functional> int foo(int) {return 0;} int faz(int) {return 0;} // 给仿对象添加默认的operator==,用于值比较 struct Callable { int operator()(int) {return 0;} bool operator==(const Callable&) const = default; } bar, baz; // 辅助模板:判断类型是否为函数指针 template<typename T> struct is_function_pointer : std::is_pointer<T> {}; template<typename R, typename... Args> struct is_function_pointer<R(*)(Args...)> : std::true_type {}; // 针对函数指针的重载 template<class FuncPtr, class F2> std::enable_if_t<is_function_pointer<std::decay_t<FuncPtr>>::value, bool> test(FuncPtr&& f1, std::function<F2> f2) { using PtrType = std::decay_t<FuncPtr>; // 获取std::function内部存储的函数指针的指针 auto target_ptr = f2.template target<PtrType>(); // 先检查target不为空(类型匹配),再比较指针值 return target_ptr != nullptr && *target_ptr == f1; } // 针对自定义可调用对象的重载(非函数指针) template<class CallableObj, class F2> std::enable_if_t<!is_function_pointer<std::decay_t<CallableObj>>::value, bool> test(CallableObj&& f1, std::function<F2> f2) { using ObjType = std::decay_t<CallableObj>; // 获取std::function内部存储的对象的指针 auto target_ptr = f2.template target<ObjType>(); // 检查类型匹配,再比较对象值(依赖operator==) return target_ptr != nullptr && *target_ptr == f1; } int main() { std::function<int(int)> f{foo}; std::cout << std::boolalpha << test(foo, f) << std::endl; // 输出true std::cout << std::boolalpha << test(faz, f) << std::endl; // 输出false f = bar; std::cout << std::boolalpha << test(bar, f) << std::endl; // 输出true std::cout << std::boolalpha << test(baz, f) << std::endl; // 输出false }
关键细节解释
区分函数指针与仿对象
- 我们用
is_function_pointer辅助模板来识别函数指针类型,这样可以针对两种Callable类型分别实现逻辑:- 函数指针场景:
std::function内部直接存储函数指针,target<PtrType>会返回指向该指针的指针。我们需要先判断这个指针不为空(说明类型匹配),再比较内部指针和传入的f1是否相等。 - 仿对象场景:
std::function存储的是仿对象的副本,target<ObjType>返回指向这个副本的指针。我们需要先确认类型匹配,再通过operator==比较副本和传入对象的值是否相等(所以仿对象需要定义operator==,示例中用了默认生成的版本)。
- 函数指针场景:
- 我们用
为什么你之前的写法失败?
- 你尝试的
target<F1>或target<F2>没有考虑转发引用的类型推导问题:F1&&是转发引用,推导出来的类型可能包含引用或cv限定符,需要用std::decay_t去除这些修饰才能正确匹配target的模板参数。 target<F2>是错误的,因为F2是std::function的签名(比如int(int)),而不是Callable的实际类型,所以会返回nullptr。
- 你尝试的
边界情况说明
- 如果你的
std::function存储的是std::ref(bar)(引用包装),当前实现会返回false,因为target查询的是Callable类型,而内部存储的是std::reference_wrapper<Callable>。如果需要支持这种场景,可以添加额外的重载处理std::reference_wrapper。 - 如果仿对象没有定义
operator==,针对仿对象的重载会编译失败,这时候你要么添加operator==,要么修改逻辑为比较地址(但副本的地址肯定和原对象不同,意义不大)。
内容的提问来源于stack exchange,提问作者Baruch
相关产品推荐
相关产品推荐

