如何结合指定对象与std::array元素生成PinBox可变参数初始化列表?
问题描述
假设有两种引脚类型BoardPin和SpecialPin,以及持有BoardPin的Board类:
// Pin for Board struct BoardPin { }; // another kind of pin struct SpecialPin { }; // a Board that comes with a Pin class Board { // Board's pin BoardPin m_pin; public: // get the Board's pin constexpr BoardPin &get_pin() { return m_pin; } };
同时有一个可容纳任意类型引脚的可变参数容器PinBox:
// a box of pins of any kind template <typename... PinTypes> class PinBox { std::tuple<PinTypes...> m_pins; public: // construct a PinBox with any kinds of pins explicit PinBox(PinTypes &...pins) : m_pins{std::tie(pins...)} {}; };
最后是包含多个Board和一个SpecialPin的BoardRoom类:
// a BoardRoom has several Boards // and a SpecialPin class BoardRoom { // number of boards a room has static constexpr size_t M_NUM_BOARDS = 4; // the BoardRoom's Boards std::array<Board, M_NUM_BOARDS> m_boards; // the BoardRoom's SpecialPin SpecialPin m_special_pin; // get the SpecialPin constexpr SpecialPin& get_special_pin() { return m_special_pin; } public: // do something that require a PinBox of all the pins void do_something(); };
当前需要实现BoardRoom::do_something(),构造包含get_special_pin()返回值和所有m_boards中Board的pin的PinBox,要求无需手动逐个枚举std::array元素,且M_NUM_BOARDS变更时无需修改PinBox的定义。
(注:实际场景中,引脚对应互斥锁,Board对应数据库对象,PinBox对应std::scoped_lock,BoardRoom对应需保证一致性的服务器对象)
解决方案
可以利用C++编译时索引序列(std::index_sequence)自动展开std::array元素,生成所有引脚的参数包,再传递给PinBox构造函数,完全避免手动枚举。
实现方式(C++14及以上)
在BoardRoom类中添加私有辅助函数,结合索引序列展开所有Board的引脚,再与特殊引脚组合:
class BoardRoom { // ... 原有成员保持不变 ... private: // 编译时展开array中所有Board的pin,结合特殊引脚生成元组 template <size_t... Idx> auto make_all_pins(std::index_sequence<Idx...>) { return std::forward_as_tuple(get_special_pin(), m_boards[Idx].get_pin()...); } public: void do_something(); }; // 实现do_something void BoardRoom::do_something() { // 生成对应长度的索引序列,调用辅助函数获取所有引脚引用 auto all_pins = make_all_pins(std::make_index_sequence<M_NUM_BOARDS>{}); // 用std::apply把元组展开为PinBox的构造参数 PinBox box = std::apply([](auto&... pins) { return PinBox(pins...); }, all_pins); // 后续业务逻辑... }
简化版本(C++17及以上)
借助C++17的模板lambda和折叠表达式,可以省去单独的辅助函数,直接在do_something内完成逻辑:
void BoardRoom::do_something() { auto build_pin_box = [this]<size_t... Idx>(std::index_sequence<Idx...>) { return PinBox(get_special_pin(), m_boards[Idx].get_pin()...); }; PinBox box = build_pin_box(std::make_index_sequence<M_NUM_BOARDS>{}); // 后续业务逻辑... }
核心说明
std::make_index_sequence<M_NUM_BOARDS>会在编译时生成0,1,...,M_NUM_BOARDS-1的索引序列,自动匹配std::array的所有元素位置。- 无论
M_NUM_BOARDS修改为任何值,代码都无需手动调整枚举逻辑,完全由编译时自动处理。 - 最终生成的
PinBox包含SpecialPin和所有BoardPin的引用,效果与手动枚举完全一致。
内容的提问来源于stack exchange,提问作者Kingsley Oyelabi
相关产品推荐
相关产品推荐

