如何用可变参数模板从std::tuple自动推导成员类型data_t?
从tuple基类自动推导派生类的data_t类型
问题背景
现有多个包含data_t成员类型的结构体:
struct A { using data_t = int; }; struct B { using data_t = double; }; struct C { using data_t = bool; }; // 其他结构体
编译时会选择其中部分结构体组成复合类型base_t(基于std::tuple),希望派生类能自动推导对应的data_t成员,避免手动编写std::tuple<A::data_t, B::data_t, A::data_t, C::data_t>这类容易出错的代码。
目前通过CMake的configure_file生成base_t和派生类data_t的内容,但想改用C++可变参数模板和折叠表达式实现自动推导。之前的尝试存在问题:
- 手动指定索引的helper模板需要逐个写索引,不够自动化:
#include <utility> template<size_t... idx> using helper = std::tuple< typename std::tuple_element_t<idx, base_t>::data_t ... >; struct Bar : public base_t { using data_t = helper<0, 1, 2, 3>; }; - 尝试用
std::index_sequence自动生成索引时编译报错(Intel C++ 2023.02),因为无法直接将std::index_sequence对象转换为size_t参数包:struct Baz : public base_t { static constexpr auto indices{std::make_index_sequence<std::tuple_size_v<base_t>>}; using data_t = helper<indices>; // 错误:无法将std::index_sequence转换为size_t };
解决方案
方案1:直接基于tuple参数包展开的类型萃取
这是最简洁的实现方式,直接针对std::tuple的参数包展开提取每个元素的data_t:
#include <tuple> // 类型萃取模板:从输入的tuple类型中提取每个元素的data_t,组成新tuple template<typename Tuple> struct tuple_data_t; // 特化处理std::tuple<Ts...>类型 template<typename... Ts> struct tuple_data_t<std::tuple<Ts...>> { using type = std::tuple<typename Ts::data_t...>; }; // 简化使用的别名 template<typename Tuple> using tuple_data_t_t = typename tuple_data_t<Tuple>::type;
使用示例:
using base_t = std::tuple<A, B, A, C>; struct Foo : public base_t { // 直接从base_t自动推导data_t using data_t = tuple_data_t_t<base_t>; };
方案2:基于index_sequence的修正实现
如果一定要用索引序列的方式,需要通过中间模板将std::index_sequence类型展开为size_t...参数包:
#include <tuple> #include <utility> using base_t = std::tuple<A, B, A, C>; // 辅助函数模板,用于推导返回类型 template<size_t... idx> auto make_data_t_impl(std::index_sequence<idx...>) -> std::tuple<typename std::tuple_element_t<idx, base_t>::data_t...>; // 通过decltype获取返回类型作为data_t template<typename Seq> using make_data_t = decltype(make_data_t_impl(Seq{})); struct Bar : public base_t { using data_t = make_data_t<std::make_index_sequence<std::tuple_size_v<base_t>>>; };
或者封装成通用的helper模板:
#include <tuple> #include <utility> template<typename Tuple, size_t... idx> using helper_impl = std::tuple<typename std::tuple_element_t<idx, Tuple>::data_t...>; template<typename Tuple> using helper = helper_impl<Tuple, std::make_index_sequence<std::tuple_size_v<Tuple>>>; using base_t = std::tuple<A, B, A, C>; struct Baz : public base_t { using data_t = helper<base_t>; };
错误原因说明
之前的代码报错是因为std::make_index_sequence<std::tuple_size_v<base_t>>生成的是一个类型(比如std::index_sequence<0,1,2,3>),而helper模板需要的是一组size_t字面量参数(0,1,2,3)。必须通过模板特化、decltype推导等方式,将索引序列类型中的参数包提取出来,才能传递给需要size_t...的模板。
内容的提问来源于stack exchange,提问作者Dominic Kerr
相关产品推荐
相关产品推荐

