如何将集合参数包展平为成员初始化的std::array?
实现支持嵌套初始化的自定义group类(保留std::array与constexpr特性)
问题描述
需要实现自定义group类的嵌套初始化能力:
group a{1, 2}; group b{a, 3}; // 期望等价于 group b{1, 2, 3}
现有基础代码仅支持普通参数初始化,尝试嵌套初始化时触发编译错误:array must be initialized with a brace-enclosed initializer,调整初始化语法后仍无法解决,且要求必须保留std::array存储和constexpr特性。
解决方案
基于C++20 Concepts实现通用的集合展平工具,编写make_collection和make_array函数,不仅支持std::array,还能适配模板签名类似std::array的自定义结构(如group类),自动将嵌套的集合参数展平为一维参数包,完成初始化。
核心展平工具代码
#include <type_traits> #include <concepts> #include <cstdint> #include <tuple> #include <array> using std::size_t; struct array_marker {}; template <typename T> concept IsTupleLike = requires { std::tuple_size<typename std::remove_cvref<T>::type>::value; }; template <typename T> concept IsArrayMarker = std::same_as<std::remove_cvref_t<T>, array_marker>; template <typename T> concept IsPlain = !IsArrayMarker<T> && !IsTupleLike<T>; template <typename... Ts> struct tuple_data { static constexpr size_t size = (tuple_data<Ts>::size + ...); using value_type = std::common_type_t<typename tuple_data<Ts>::value_type...>; }; template <typename T> struct tuple_data<T> { static constexpr size_t size = 1; using value_type = std::remove_cvref_t<T>; }; template <IsTupleLike T> struct tuple_data<T> { static constexpr size_t size = std::tuple_size_v<std::remove_cvref_t<T>>; using value_type = std::remove_cvref_t<T>::value_type; }; template <typename Ret, IsPlain Arg, typename... Args> constexpr inline Ret make_collection(Arg &&arg, Args &&... args) { return make_collection<Ret>(std::forward<Args>(args)..., std::forward<Arg>(arg)); } template <typename Ret, IsTupleLike Tup, typename... Args, size_t... Indices> constexpr inline Ret make_collection(std::index_sequence<Indices...> &&, Tup &&arr, Args &&... args) { return make_collection<Ret>(std::forward<Args>(args)..., get<Indices>(arr)...); } // 匹配所有std::array或类似的tuple-like类型 template <typename Ret, IsTupleLike Tup, typename... Args> constexpr inline Ret make_collection(Tup &&arr, Args &&... args) { return make_collection<Ret>(std::make_index_sequence<tuple_data<Tup>::size>{}, std::forward<Tup>(arr), std::forward<Args>(args)...); } template <typename Ret, typename... Args> constexpr inline Ret make_collection(IsArrayMarker auto &&, Args &&... args) { return { args... }; } template <template <typename, size_t> typename ArrayLike = std::array, typename... Args, typename Ret = ArrayLike<typename tuple_data<Args...>::value_type, tuple_data<Args...>::size>> constexpr inline Ret make_array(Args &&... args) { return make_collection<Ret>(std::forward<Args>(args)..., array_marker{}); }
自定义group类实现
#include <array> #include <iostream> template <typename Member, size_t Size> struct group { using value_type = Member; constexpr group() : arr{} {} // 利用make_array自动展平嵌套参数 constexpr group(auto &&... args) : arr(make_array(args...)) {} std::array<Member, Size> arr; }; // 推导指引:自动推导group的元素类型和大小 group(auto &&... args) -> group<typename tuple_data<decltype(args)...>::value_type, tuple_data<decltype(args)...>::size>; // 特化tuple_size,让group满足IsTupleLike概念 template <typename Member, size_t Size> struct std::tuple_size<group<Member, Size>> : std::integral_constant<std::size_t, Size> {}; // 实现get接口,支持tuple-like操作 template <size_t Index, typename T, size_t Size> T &get(group<T, Size> &g) { return g.arr[Index]; } template <size_t Index, typename T, size_t Size> const T &get(const group<T, Size> &g) { return g.arr[Index]; } // 重载输出运算符,方便打印测试 template <size_t Index = 0> static inline std::ostream &operator<<(std::ostream &os, IsTupleLike auto &&tup) { constexpr const size_t size = std::tuple_size_v<std::remove_cvref_t<decltype(tup)>>; if constexpr (Index == 0) { return operator<<<Index + 1>(os << "{", tup) << "}"; } else if constexpr (Index > size) { return os; } else if constexpr (Index == 1) { return operator<<<2>(os << get<0>(tup), tup); } else { return operator<<<Index + 1>(os << ", " << get<Index - 1>(tup), tup); } } // 打印工具函数 static inline void println(auto &&... args) { ((std::cout << args << " "), ...) << "\n"; } int main() { auto g1 = make_array<group>(1, 2, 3); auto g2 = make_array<group>(g1, 4, 5, 6); group g3{g1, 0, g2}; println(g1, g2, g3); }
测试输出
{1, 2, 3} {1, 2, 3, 4, 5, 6} {1, 2, 3, 0, 1, 2, 3, 4, 5, 6}
内容的提问来源于stack exchange,提问作者boat d
相关产品推荐
相关产品推荐

