C++传递带范围参数的回调遇模板推导失败,求延迟实例化方案
问题描述
你想要实现如下代码,让mycallback作为模板函数接受C++20的整数范围(包括带过滤器的owning_view),同时f函数能接收该回调并以不同范围类型调用,还要支持lambda,但编译器无法推导f的模板参数,报错无法实例化:
示例代码
void mycallback(auto v) { for (auto x : v) { std::cout << x << '\n'; } } void f(auto cb) { cb(std::vector<int>{44,55,66}); cb(std::vector<int>{123, 234, 345} | std::ranges::views::drop(1)); } void run() { f(mycallback); f([](auto v) { for (auto x : v) { std::cout << x << '\n'; } }); }
编译报错
test.cpp:34:6: note: template argument deduction/substitution failed: test.cpp:41:6: note: couldn’t deduce template parameter ‘auto:25’ 41 | f(mycallback);
核心问题:mycallback是模板函数,传递给f时编译器无法推导cb的类型——模板函数本身不是具体类型,只是一组函数的模板家族,而f的auto cb需要一个确定的类型才能实例化。
解决方案
方法1:用lambda直接包装mycallback(最简洁)
在调用f时,用lambda包裹mycallback,lambda是具体的多态函数对象,编译器能正常推导类型:
void run() { f([](auto v) { mycallback(v); }); f([](auto v) { for (auto x : v) { std::cout << x << '\n'; } }); }
lambda的operator()会自动推导参数类型,在f内部调用时才实例化对应范围的版本,无额外开销。
方法2:将mycallback改为函数对象
如果允许修改mycallback的定义,把它改成带模板operator()的结构体,使其成为具体类型,可直接传递给f:
struct mycallback { template <typename Range> void operator()(Range v) const { for (auto x : v) { std::cout << x << '\n'; } } }; void run() { f(mycallback{}); f([](auto v) { for (auto x : v) { std::cout << x << '\n'; } }); }
方法3:用包装器封装模板函数
如果不想修改mycallback,可以写一个简单的包装结构体,把模板函数转化为多态对象:
struct MyCallbackWrapper { template <typename Range> void operator()(Range v) const { mycallback(v); } }; void run() { f(MyCallbackWrapper{}); f([](auto v) { for (auto x : v) { std::cout << x << '\n'; } }); }
方法4:用std::function延迟类型推导(有轻微性能开销)
C++20起std::function支持模板占位符,可包装任意能接收范围参数的回调:
#include <functional> void f(std::function<void(auto)> cb) { cb(std::vector<int>{44,55,66}); cb(std::vector<int>{123, 234, 345} | std::ranges::views::drop(1)); } // 调用方式不变 void run() { f(mycallback); f([](auto v) { for (auto x : v) { std::cout << x << '\n'; } }); }
这种方式会引入std::function的虚函数调用开销,适合对性能要求不高的场景。
内容的提问来源于stack exchange,提问作者hasan
相关产品推荐
相关产品推荐

