C++模板参数包忽略顺序生成类型组合唯一ID实现方案问询
C++ 编译期无顺序依赖类型组合ID实现方案
核心解决思路
通过编译期对类型参数包按固定规则排序,消除输入顺序的影响,排序后的类型组合只要元素相同,最终生成的类型标识完全一致,天然兼容类型别名(别名与原类型属于同一类型,排序规则下会被识别为同一元素)。
完整实现代码
基础运行时版本(C++17及以上兼容)
#include <type_traits> #include <typeindex> #include <string_view> // 编译期获取类型唯一标识字符串(适配多编译器) template <typename T> constexpr std::string_view type_name() { #ifdef _MSC_VER return __FUNCSIG__; #else return __PRETTY_FUNCTION__; #endif } // 类型比较规则:按类型标识字符串的字典序排序 template <typename A, typename B> constexpr bool type_less = type_name<A>() < type_name<B>(); // 类型列表容器 template <typename... Ts> struct type_list {}; // 编译期插入单个类型到已排序的类型列表 template <typename T, typename SortedList> struct insert_type; template <typename T, typename... SortedTs> struct insert_type<T, type_list<SortedTs...>> { template <typename... Accum> struct impl; // 找到比当前类型大的元素,插入到该元素前 template <typename... Accum, typename Next, typename... Rest> requires (type_less<T, Next>) struct impl<type_list<Accum...>, Next, Rest...> { using type = type_list<Accum..., T, Next, Rest...>; }; // 当前元素更小,继续向后遍历 template <typename... Accum, typename Next, typename... Rest> struct impl<type_list<Accum...>, Next, Rest...> { using type = typename impl<type_list<Accum..., Next>, Rest...>::type; }; // 所有元素都更小,插入到列表末尾 template <typename... Accum> struct impl<type_list<Accum...>> { using type = type_list<Accum..., T>; }; using type = typename impl<type_list<>, SortedTs...>::type; }; // 编译期对类型列表排序 template <typename List> struct sort_type_list; template <> struct sort_type_list<type_list<>> { using type = type_list<>; }; template <typename T, typename... Ts> struct sort_type_list<type_list<T, Ts...>> { using sorted_rest = typename sort_type_list<type_list<Ts...>>::type; using type = typename insert_type<T, sorted_rest>::type; }; // 目标函数实现 template <typename... Ts> auto foo() { // 对输入类型参数包排序,相同类型组合排序结果完全一致 using sorted_types = typename sort_type_list<type_list<Ts...>>::type; return std::type_index(typeid(sorted_types)); }
验证效果
foo<int, char, bool>() == foo<char, int, bool>()结果为truefoo<int>() == foo<char>()结果为false- 存在类型别名时:
using my_type = char; foo<bool, int, my_type>() == foo<bool, char, int>()结果为true
零运行时开销优化版本(C++20及以上)
如果需要编译期常量ID,可以直接对排序后的类型标识字符串做哈希,完全消除运行时开销:
// 编译期FNV-1a哈希算法 constexpr size_t fnv1a_hash(std::string_view s) { size_t hash = 14695981039346656037ULL; for (char c : s) { hash ^= static_cast<size_t>(c); hash *= 1099511628211ULL; } return hash; } template <typename... Ts> constexpr size_t foo() { using sorted_types = typename sort_type_list<type_list<Ts...>>::type; return fnv1a_hash(type_name<sorted_types>()); }
该版本返回值为编译期常量,运行时无任何计算开销。
内容的提问来源于stack exchange,提问作者Lauren S
相关产品推荐
相关产品推荐

