能否仅靠C++语言特性自动生成函数的重载包装类?
借助C++特性自动生成包装类的方案
完全可以仅用C++17及以后的语言特性自动生成这类包含多重重载的包装类,无需手动编写所有重载逻辑。下面分两种场景给出具体实现思路:
一、所有参数均为std::optional的情况
针对原函数参数全是std::optional<T>的场景,可以通过可变参数模板、折叠表达式、std::apply和索引序列等特性,自动生成支持任意参数子集的包装方法。
实现代码
#include <optional> #include <tuple> #include <type_traits> #include <utility> // 判断是否为std::optional类型的辅助模板 template <typename T> struct is_optional : std::false_type {}; template <typename T> struct is_optional<std::optional<T>> : std::true_type { using value_type = T; }; // 包装类模板,接受原函数指针作为模板参数 template <auto Func> class WrapFunc; // 特化:针对参数全为std::optional的函数 template <typename... Args, void(*Func)(std::optional<Args>...)> class WrapFunc<Func> { private: // 辅助函数:构造原函数所需的std::optional参数列表 template <typename Tuple, std::size_t... Idxs, typename... Ts> static auto make_args_tuple(std::index_sequence<Idxs...>, const Tuple& args_tuple, Ts...) { return std::make_tuple( [&]() -> std::optional<Args> { using ArgType = Args; if constexpr (sizeof...(Ts) == 0) { return std::nullopt; } else { // 判断当前参数类型是否在传入的参数列表中 constexpr bool is_present = (std::is_same_v<ArgType, Ts> || ...); if constexpr (is_present) { // 找到该类型在传入参数中的位置 constexpr auto pos = []() { std::size_t idx = 0; ((std::is_same_v<ArgType, Ts> ? void() : ++idx), ...); return idx; }(); return std::make_optional(std::get<pos>(args_tuple)); } else { return std::nullopt; } } }()... ); } public: // 无参数重载 void wf() { auto func_args = make_args_tuple(std::index_sequence_for<Args...>{}, std::tuple<>(), std::tuple<>()); std::apply(Func, func_args); } // 任意数量参数的重载 template <typename... Ts, std::enable_if_t<(sizeof...(Ts) > 0), int> = 0> void wf(Ts... args) { // 静态断言:传入参数必须匹配原函数可选参数的value_type static_assert(((std::disjunction_v<std::is_same<Ts, Args>...>) && ...), "传入参数类型必须与原函数可选参数的value_type匹配"); // 静态断言:每个参数类型必须唯一对应原函数的一个可选参数 static_assert(([]<typename T>(T) { return std::count_if(std::initializer_list<bool>{std::is_same_v<T, Args>...}) == 1; }(std::declval<Ts>()) && ...), "每个参数类型必须唯一对应原函数的一个可选参数"); auto args_tuple = std::make_tuple(args...); auto func_args = make_args_tuple(std::index_sequence_for<Args...>{}, args_tuple, Ts...); std::apply(Func, func_args); } }; // 测试用原函数 void f(std::optional<int> i, std::optional<double> j) { // 业务逻辑 } // 生成包装类 using WrapF = WrapFunc<&f>; // 使用示例 int main() { WrapF wrap; wrap.wf(); // 对应f(nullopt, nullopt) wrap.wf(123); // 对应f(123, nullopt) wrap.wf(45.67); // 对应f(nullopt, 45.67) wrap.wf(123, 45.67); // 对应f(123, 45.67) return 0; }
二、混合必选参数与可选参数的情况
如果原函数同时包含必选参数(非std::optional类型)和可选参数,只需先通过模板元编程分离两类参数,再让包装方法先接受所有必选参数,后续接受任意子集的可选参数。
实现代码
#include <optional> #include <tuple> #include <type_traits> #include <utility> // 判断是否为std::optional类型的辅助模板 template <typename T> struct is_optional : std::false_type {}; template <typename T> struct is_optional<std::optional<T>> : std::true_type { using value_type = T; }; // 分离必选/可选参数的辅助元函数 template <typename... Params> struct SplitParams { private: // 过滤必选参数(非std::optional) template <typename Tuple> struct FilterMandatory; template <typename... Ts> struct FilterMandatory<std::tuple<Ts...>> { using type = std::tuple<typename std::enable_if<!is_optional<Ts>::value, Ts>::type...>; }; // 提取可选参数的value_type template <typename Tuple> struct ExtractOptional; template <typename... Ts> struct ExtractOptional<std::tuple<Ts...>> { using type = std::tuple<typename std::enable_if<is_optional<Ts>::value, typename is_optional<Ts>::value_type>::type...>; }; public: using Mandatory = typename FilterMandatory<std::tuple<Params...>>::type; using Optional = typename ExtractOptional<std::tuple<Params...>>::type; }; // 包装类模板 template <auto Func> class WrapFunc; // 特化:针对混合必选、可选参数的函数 template <typename... Params, void(*Func)(Params...)> class WrapFunc<Func> { private: // 从SplitParams中获取必选、可选参数类型 using MandatoryTuple = typename SplitParams<Params...>::Mandatory; using OptionalTuple = typename SplitParams<Params...>::Optional; // 从元组中提取类型序列 template <typename Tuple> struct TupleToTypes; template <typename... Ts> struct TupleToTypes<std::tuple<Ts...>> { using types = std::tuple<Ts...>; }; using Mandatory = typename TupleToTypes<MandatoryTuple>::types; using Optional = typename TupleToTypes<OptionalTuple>::types; // 辅助函数:构造原函数所需的可选参数列表 template <typename Tuple, std::size_t... Idxs, typename... Ts> static auto make_optional_args(std::index_sequence<Idxs...>, const Tuple& args_tuple, Ts...) { return std::make_tuple( [&]() -> std::optional<std::tuple_element_t<Idxs, OptionalTuple>> { using OptType = std::tuple_element_t<Idxs, OptionalTuple>; constexpr bool is_present = (std::is_same_v<OptType, Ts> || ...); if constexpr (is_present) { constexpr auto pos = []() { std::size_t idx = 0; ((std::is_same_v<OptType, Ts> ? void() : ++idx), ...); return idx; }(); return std::make_optional(std::get<pos>(args_tuple)); } else { return std::nullopt; } }()... ); } public: // 展开必选参数类型 template <typename... MandatoryArgs, std::enable_if_t<std::is_same_v<std::tuple<MandatoryArgs...>, MandatoryTuple>, int> = 0> void wf(MandatoryArgs... mandatory_args) { // 调用原函数,可选参数全传nullopt std::apply([&](auto... opt_args) { Func(mandatory_args..., opt_args...); }, std::make_tuple(std::nullopt...)); } // 必选参数 + 任意数量可选参数 template <typename... MandatoryArgs, typename... Ts, std::enable_if_t<std::is_same_v<std::tuple<MandatoryArgs...>, MandatoryTuple>, int> = 0> void wf(MandatoryArgs... mandatory_args, Ts... optional_args) { // 静态断言:可选参数类型必须匹配原函数的可选参数value_type static_assert(((std::disjunction_v<std::is_same<Ts, std::tuple_element_t<Idxs, OptionalTuple>> || ...>) && ...), "可选参数类型必须与原函数可选参数的value_type匹配"); // 静态断言:每个可选参数类型唯一对应一个原函数可选参数 static_assert(([]<typename T>(T) { return std::count_if(std::initializer_list<bool>{std::is_same_v<T, std::tuple_element_t<Idxs, OptionalTuple>> || ...}) == 1; }(std::declval<Ts>()) && ...), "每个可选参数类型必须唯一对应原函数的一个可选参数"); auto optional_tuple = std::make_tuple(optional_args...); auto func_optional_args = make_optional_args(std::make_index_sequence<std::tuple_size_v<OptionalTuple>>{}, optional_tuple, Ts...); std::apply([&](auto... opt_args) { Func(mandatory_args..., opt_args...); }, func_optional_args); } }; // 测试用原函数(混合必选int,可选double、int) void f(int a, std::optional<double> b, std::optional<int> c) { // 业务逻辑 } // 生成包装类 using WrapF = WrapFunc<&f>; // 使用示例 int main() { WrapF wrap; wrap.wf(123); // 对应f(123, nullopt, nullopt) wrap.wf(123, 45.67); // 对应f(123, 45.67, nullopt) wrap.wf(123, 789); // 对应f(123, nullopt, 789) wrap.wf(123, 45.67, 789); // 对应f(123, 45.67, 789) return 0; }
注意事项
- 上述实现依赖C17特性(折叠表达式、
std::apply、std::index_sequence等),如果使用更早的C版本,需要做兼容调整。 - 如果原函数存在多个可选参数的
value_type相同的情况(比如两个std::optional<int>),需要额外引入标签类来区分不同位置的参数,否则无法通过类型推导确定参数对应的位置。
内容的提问来源于stack exchange,提问作者perreal
相关产品推荐
相关产品推荐

