如何在编译时推导类中重载test(T)函数的参数类型列表?
实现思路与方案
要在编译时自动推导类中test(T)重载的参数类型列表,核心挑战在于C++20之前缺乏原生反射能力——编译器不会主动暴露类的成员函数重载信息给模板元编程。不过我们可以根据不同的C++版本和需求,选择以下几种可行方案:
一、C++20 原生反射方案(推荐,若编译器支持)
C++20引入了有限的编译时反射特性,可以直接枚举类的成员函数,筛选出目标重载并提取参数类型。以下是简化的实现思路:
#include <concepts> #include <tuple> #include <boost/mpl/list.hpp> template<typename Class> struct test_types { private: // 编译时筛选符合条件的成员函数:名为test、返回void、单参数 template<typename Func> static constexpr bool is_target_func(Func) { return std::same_as<typename Func::return_type, void> && (Func::parameter_count == 1); } // 提取单参数类型 template<typename Func> using param_type = typename Func::template parameter_type<0>; // 收集所有符合条件的参数类型 template<typename... Funcs> static auto collect_types(std::tuple<Funcs...>) { return boost::mpl::list<param_type<Funcs>...>{}; } public: // 利用反射获取类的所有成员函数,筛选后收集类型 using types = decltype(collect_types( std::reflect<Class>::get_member_functions() )); };
注意:C++20的反射特性目前还处于完善阶段,不同编译器(如GCC、Clang)的支持程度不同,可能需要开启特定编译选项(如-std=c++20 -freflection)。
二、模板元编程折中方案(兼容C++11/14,需预设候选类型)
如果无法使用C++20,我们可以通过预设候选类型列表 + SFINAE筛选的方式,间接获取目标重载的参数类型。虽然需要预先准备可能的类型,但可以覆盖绝大多数常见场景:
1. 第一步:实现has_test检测元函数
先实现你已经熟悉的SFINAE检测,判断类是否存在test(T)重载:
#include <type_traits> #include <boost/mpl/list.hpp> template<typename Class, typename T> struct has_test { private: // 重载1:若test(T)存在,匹配此重载并返回true_type template<typename C> static auto test(int) -> decltype( std::declval<C>().test(std::declval<T>()), std::true_type{} ); // 重载2: fallback,返回false_type template<typename C> static std::false_type test(...); public: static constexpr bool value = decltype(test<Class>(0))::value; };
2. 第二步:筛选候选类型列表
定义一个元函数,从预设的候选类型列表中筛选出符合has_test条件的类型:
template<typename Class, typename TypeList> struct filter_test_types; // 递归终止条件:空列表 template<typename Class> struct filter_test_types<Class, boost::mpl::list<>> { using type = boost::mpl::list<>; }; // 递归处理每个类型 template<typename Class, typename T, typename... Rest> struct filter_test_types<Class, boost::mpl::list<T, Rest...>> { private: // 当前类型符合条件则保留,否则跳过 using current_type = typename std::conditional< has_test<Class, T>::value, boost::mpl::list<T>, boost::mpl::list<> >::type; // 递归处理剩余类型 using rest_types = typename filter_test_types<Class, boost::mpl::list<Rest...>>::type; public: // 合并结果 using type = typename boost::mpl::joint_view<current_type, rest_types>::type; };
3. 使用方式
预设候选类型列表后,即可得到目标类型列表:
struct test_class { void test(int a); void test(float b); }; // 预设候选类型(可根据需求扩展) using candidate_types = boost::mpl::list<int, float, double, short, char, long>; // 获取test_class的test(T)参数类型列表 using result_types = typename filter_test_types<test_class, candidate_types>::type; // result_types 等价于 boost::mpl::list<int, float>
三、外部工具生成代码方案(完全自动化,跨版本)
如果需要完全自动化且无需预设类型,可以使用Clang LibTooling或其他代码分析工具,解析类的头文件,自动生成包含目标参数类型的mpl::list代码。例如:
- 编写一个Clang插件,扫描
test_class的定义,提取所有test(T)重载的参数类型。 - 自动生成如下代码:
template<> struct test_types<test_class> { using types = boost::mpl::list<int, float>; };
- 在项目中引入生成的代码,即可直接使用
test_types<test_class>::types。
这种方案完全满足“无需预先准备类型列表”的要求,但需要额外的构建步骤和工具链支持。
内容的提问来源于stack exchange,提问作者Kornel Kisielewicz
相关产品推荐
相关产品推荐

