可变参数模板与std::array异常运行行为问题求助
问题根源分析
你的代码问题出在可变参数构造函数的递归调用方式上:
Selector(args...);
这行代码并不是在当前对象上继续递归初始化数组,而是创建了一个临时的Selector对象!这个临时对象会独立初始化自己的integers数组,生命周期结束后就被销毁,完全不会修改你主函数里x对象的数组。
所以主函数中的x对象,只有第一次构造调用时的integers[2] = 5是有效的——剩下的两个元素(索引1和0)根本没被赋值,属于未初始化的内存,打印出来自然是垃圾值。
修复方案
我们需要让递归初始化操作在当前对象上完成,而不是创建临时对象。这里提供两种简洁的修复方式:
方式1:递归辅助函数(兼容C++11及以上)
通过带索引参数的递归辅助函数,逐个给当前对象的数组元素赋值:
#include <iostream> #include <array> #include <type_traits> // 用于enable_if template<int N> class Selector { public: template <typename... Args> Selector(Args... args) noexcept { // 强制参数数量和N匹配,避免错误 static_assert(sizeof...(Args) == N, "Number of arguments must equal N"); initialize<N-1>(args...); } void print_num(int i) const { std::cout << "integers[" << i << "]=" << integers[i] << std::endl; } private: std::array<int, N> integers; // 递归终止条件:处理最后一个元素(索引0) template<int Idx> typename std::enable_if<Idx == 0>::type initialize(int x) noexcept { integers[Idx] = x; std::cout << "integers[" << Idx << "]=" << integers[Idx] << std::endl; } // 递归步骤:先赋值当前索引,再递归处理下一个更小的索引 template<int Idx, typename... Rest> typename std::enable_if<Idx > 0>::type initialize(int x, Rest... rest) noexcept { integers[Idx] = x; std::cout << "integers[" << Idx << "]=" << integers[Idx] << std::endl; initialize<Idx-1>(rest...); } }; int main() { Selector<3> x(5, 10, 15); x.print_num(2); // 输出 integers[2]=5 x.print_num(1); // 输出 integers[1]=10 x.print_num(0); // 输出 integers[0]=15 }
方式2:C++17折叠表达式(更简洁)
利用C++17的折叠表达式,可以不用递归函数,一行完成数组赋值:
#include <iostream> #include <array> template<int N> class Selector { public: template <typename... Args> Selector(Args... args) noexcept : integers{} { static_assert(sizeof...(Args) == N, "Number of arguments must equal N"); int idx = N-1; // 折叠表达式:逐个将参数赋值到数组的对应索引 ((integers[idx--] = args), ...); } void print_num(int i) const { std::cout << "integers[" << i << "]=" << integers[i] << std::endl; } private: std::array<int, N> integers; }; int main() { Selector<3> x(5, 10, 15); x.print_num(2); // 输出 integers[2]=5 x.print_num(1); // 输出 integers[1]=10 x.print_num(0); // 输出 integers[0]=15 }
修复后的效果
两种方案都会正确初始化x对象的整个数组,打印结果和你构造时的输出一致,不会再出现垃圾值。
内容的提问来源于stack exchange,提问作者ocirocir
相关产品推荐
相关产品推荐

