能否实现类似std::invoke的函数,自动以默认构造类型调用参数?
实现自动以默认构造实参调用函数的invoke替代版本
完全可以实现这样的函数,核心思路是通过模板元编程提取可调用对象的参数类型,自动生成对应类型的默认构造值作为实参,再传递给std::invoke完成调用。
完整实现代码
#include <iostream> #include <functional> #include <tuple> #include <utility> // 函数特质模板:提取可调用对象的参数类型 template<typename T> struct function_traits : function_traits<decltype(&T::operator())> {}; // 处理const版本的仿函数operator() template<typename Class, typename Ret, typename... Args> struct function_traits<Ret(Class::*)(Args...) const> { using args = std::tuple<Args...>; }; // 处理非const版本的仿函数operator() template<typename Class, typename Ret, typename... Args> struct function_traits<Ret(Class::*)(Args...)> { using args = std::tuple<Args...>; }; // 处理普通函数指针 template<typename Ret, typename... Args> struct function_traits<Ret(*)(Args...)> { using args = std::tuple<Args...>; }; // 辅助实现函数:利用索引序列展开参数 template<typename F, std::size_t... Is> auto invoke_with_defaults_impl(F&& f, std::index_sequence<Is...>) { using ArgsTuple = typename function_traits<std::decay_t<F>>::args; return std::invoke(std::forward<F>(f), std::tuple_element_t<Is, ArgsTuple>{}...); } // 对外接口函数 template<typename F> auto invoke_with_defaults(F&& f) { using ArgsTuple = typename function_traits<std::decay_t<F>>::args; constexpr auto ArgsCount = std::tuple_size_v<ArgsTuple>; return invoke_with_defaults_impl(std::forward<F>(f), std::make_index_sequence<ArgsCount>{}); } // 测试用仿函数 struct Test{ void operator()(int i) { std::cout << std::to_string(i) << "\n"; } }; // 测试多参数普通函数 void multi_param_func(std::string s, double d) { std::cout << "String: " << s << ", Double: " << d << "\n"; } int main(){ Test test; invoke_with_defaults(test); // 输出0 invoke_with_defaults(multi_param_func); // 输出String: , Double: 0 return 0; }
关键部分说明
function_traits模板:
- 针对不同类型的可调用对象(仿函数、普通函数指针)做特化,提取出调用时需要的参数类型,并打包成
std::tuple类型args。 - 分别处理了const/非const版本的仿函数
operator(),确保覆盖常见的仿函数场景。
- 针对不同类型的可调用对象(仿函数、普通函数指针)做特化,提取出调用时需要的参数类型,并打包成
invoke_with_defaults_impl辅助函数:
- 利用
std::index_sequence生成参数索引,通过std::tuple_element_t逐个取出参数类型,构造默认值({}),最后通过折叠表达式展开所有参数传递给std::invoke。
- 利用
对外接口invoke_with_defaults:
- 推导可调用对象的参数数量,生成对应的索引序列,转发给辅助函数完成实际调用。
注意事项
- 要求所有参数类型必须支持默认构造,如果某个参数类型没有默认构造函数,编译会直接报错,这是符合预期的行为。
- 支持C17及以上版本,因为用到了
std::index_sequence、std::tuple_size_v等C17特性。
内容的提问来源于stack exchange,提问作者kekpirat
相关产品推荐
相关产品推荐

