能否在可变参数结构体的成员初始化列表中使用C风格数组?
C++可变参数结构体中使用C风格数组初始化并实现模板实参自动推导
问题描述
基于std::tuple实现了可变参数结构体VariadicStruct,希望通过嵌套花括号初始化列表在tuple中存入C风格数组,同时借助类模板实参推导自动推导类型。编写的最小示例代码如下:
#include <iostream> #include <tuple> template <typename ... Args> struct VariadicStruct { std::tuple<Args...> data{}; VariadicStruct() {} VariadicStruct(Args...args) { data = std::make_tuple(args...); }; // 更多数据与函数 // . . . }; int main() { VariadicStruct vs1{ {1,2,3}, 4.0, 'c' }; // 无法运行 std::cout << std::get<0>(vs1.data)[0] << '\t' << std::get<1>(vs1.data) << '\t' << std::get<2>(vs1.data) << '\n'; VariadicStruct vs2{ 1, 4.0, 'c' }; // 运行正常 std::cout << std::get<0>(vs2.data) << '\t' << std::get<1>(vs2.data) << '\t' << std::get<2>(vs2.data) << '\n'; }
期望tuple最终包含int[3]、double和char类型,但vs1无法正常编译,核心问题是模板实参推导失效,以下是可行的实现方案:
原因分析
编译器无法将初始化列表{1,2,3}直接推导为int[3]类型:
- C风格数组在函数参数传递时会隐式退化为指针,模板推导无法从初始化列表反向推导出数组的具体类型和大小;
- 初始化列表本身是无类型实体,无法直接匹配
Args中的数组类型参数。
解决方法
方法1:使用std::array替代C风格数组(推荐)
std::array是C++标准库提供的容器,兼具数组性能与容器便利性,模板推导可正常识别:
#include <iostream> #include <tuple> #include <array> template <typename ... Args> struct VariadicStruct { std::tuple<Args...> data{}; VariadicStruct() {} VariadicStruct(Args...args) : data(std::make_tuple(args...)) {}; }; int main() { VariadicStruct vs1{ std::array{1,2,3}, 4.0, 'c' }; // C++17及以上支持类模板推导 std::cout << std::get<0>(vs1.data)[0] << '\t' << std::get<1>(vs1.data) << '\t' << std::get<2>(vs1.data) << '\n'; VariadicStruct vs2{ 1, 4.0, 'c' }; std::cout << std::get<0>(vs2.data) << '\t' << std::get<1>(vs2.data) << '\t' << std::get<2>(vs2.data) << '\n'; }
方法2:添加类模板推导指引
自定义推导指引,告诉编译器如何将数组变量映射为模板参数:
#include <iostream> #include <tuple> template <typename ... Args> struct VariadicStruct { std::tuple<Args...> data{}; VariadicStruct() {} VariadicStruct(Args...args) : data(std::make_tuple(args...)) {}; }; // 针对int数组的推导指引,可扩展为其他类型数组 template <std::size_t N, typename... Rest> VariadicStruct(const int (&)[N], Rest...) -> VariadicStruct<int[N], Rest...>; int main() { int arr[] = {1,2,3}; VariadicStruct vs1{ arr, 4.0, 'c' }; std::cout << std::get<0>(vs1.data)[0] << '\t' << std::get<1>(vs1.data) << '\t' << std::get<2>(vs1.data) << '\n'; VariadicStruct vs2{ 1, 4.0, 'c' }; std::cout << std::get<0>(vs2.data) << '\t' << std::get<1>(vs2.data) << '\t' << std::get<2>(vs2.data) << '\n'; }
方法3:用std::to_array包装初始化列表
C++20及以上可用std::to_array将初始化列表转为std::array,贴合原需求写法:
#include <iostream> #include <tuple> #include <array> template <typename ... Args> struct VariadicStruct { std::tuple<Args...> data{}; VariadicStruct() {} VariadicStruct(Args...args) : data(std::make_tuple(args...)) {}; }; int main() { VariadicStruct vs1{ std::to_array({1,2,3}), 4.0, 'c' }; // C++20简化写法 std::cout << std::get<0>(vs1.data)[0] << '\t' << std::get<1>(vs1.data) << '\t' << std::get<2>(vs1.data) << '\n'; VariadicStruct vs2{ 1, 4.0, 'c' }; std::cout << std::get<0>(vs2.data) << '\t' << std::get<1>(vs2.data) << '\t' << std::get<2>(vs2.data) << '\n'; }
内容的提问来源于stack exchange,提问作者A M
相关产品推荐
相关产品推荐

