参数包展开异常排查:为何无法生成指定长度的元素数组?
问题分析:vector初始化未正确展开参数包
问题描述
我想通过以下代码创建一个包含10个A类型元素的vector,每个元素用A{1, 2, 3.0}构造,但实际输出显示vector的size是1。参数包为什么没正确展开?问题出在哪?
原代码如下:
#include <iostream> #include <vector> struct A { int a{0}; int b{0}; double c{0.0}; }; template<typename T, size_t... Ns> auto create_array(std::index_sequence<Ns...>, auto&&... args) { return std::vector<T> { ([&](...){ return std::forward<T>(T{args...}); }(Ns), ...) }; } template<typename T, size_t N> auto create_array(auto&&... args) { return create_array<T>(std::make_index_sequence<N>{}, std::forward<decltype(args)>(args)...); } int main() { std::cout << create_array<A, 10>(1,2,3.0).size() << std::endl; }
我原本期望([&](...){ return std::forward<T>(T{args...}); }(Ns), ...)展开为:
[&](...){ return std::forward<T>(T{args...}); }(0), [&](...){ return std::forward<T>(T{args...}); }(1), [&](...){ return std::forward<T>(T{args...}); }(2), //... 直到第9个
问题根源
问题出在逗号运算符的误用。你用(lambda调用(Ns), ...)这种方式展开时,整个表达式是一个逗号表达式序列。而逗号表达式的规则是:依次执行每个子表达式,但最终只返回最后一个子表达式的结果。也就是说,你的展开最终只向vector的初始化列表传入了最后一次lambda调用的结果,所以vector里只有1个元素,而不是10个。
修正方案
要让每个lambda调用的结果都成为vector的独立元素,只需要去掉外层的括号,直接展开lambda调用即可:
修正后的核心函数:
template<typename T, size_t... Ns> auto create_array(std::index_sequence<Ns...>, auto&&... args) { return std::vector<T> { [&](...){ return T{std::forward<decltype(args)>(args)...}; }(Ns)... }; }
甚至可以更简洁,完全不需要lambda,直接利用参数包展开重复构造元素(同时避免Ns未使用的编译警告):
template<typename T, size_t... Ns> auto create_array(std::index_sequence<Ns...>, auto&&... args) { return std::vector<T> { (static_cast<void>(Ns), T{std::forward<decltype(args)>(args)...})... }; }
这里static_cast<void>(Ns)的作用是“占用”参数包中的每个Ns,触发N次展开,同时不会对构造T的表达式产生影响。展开后会生成10个T{1,2,3.0}对象,全部传入vector的初始化列表,最终得到size为10的vector。
内容的提问来源于stack exchange,提问作者Dmitry
相关产品推荐
相关产品推荐

