可变参数构造函数无法初始化std::array问题求助
问题:使用std::vector初始化HexMatrix中的std::array编译失败
以下是我的代码片段:
template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns> class HexMatrix { private: std::array<Type, NumberOfRows*NumberOfColumns> values; // ... public: template <typename... OtherType> HexMatrix(OtherType...); // ... }; template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns> template <typename... OtherType> HexMatrix<Type, NumberOfRows, NumberOfColumns>::HexMatrix(OtherType... args) : values({ args... }) { static_assert(NumberOfRows != 0u); static_assert(NumberOfColumns != 0u); }
此前这段代码可以直接用std::vector通过上述构造函数初始化std::array,但电脑重装系统后,即便仍使用-std=c++2a编译选项,代码也无法编译。编译报错信息如下:
In file included from foo.cpp:3: LinearAlgebra.hpp: In instantiation of ‘HexMatrix< <template-parameter-1-1>, <anonymous>, <anonymous> >::HexMatrix(OtherType ...) [with OtherType = {std::vector<long double, std::allocator<long double> >}; Type = long double; unsigned int NumberOfRows = 6; unsigned int NumberOfColumns = 6]’: LinearAlgebra.hpp:230:9: required from ‘static HexMatrix<Type, NumberOfRows, NumberOfColumns> HexMatrix< <template-parameter-1-1>, <anonymous> >::Make(OtherType ...) [with OtherType = {std::vector<long double, std::allocator<long double> >}; Type = long double; unsigned int NumberOfRows = 6; unsigned int NumberOfColumns = 6]’: foo.cpp:93:80: required from here LinearAlgebra.hpp:299:98: error: no matching function for call to ‘std::array<long double, 36>::array(<brace-enclosed initializer list>)’ 299 | HexMatrix<Type, NumberOfRows, NumberOfColumns>::HexMatrix(OtherType... args) : values({ args... }) | ^ In file included from /usr/include/c++/9/tuple:39, from /usr/include/c++/9/bits/hashtable_policy.h:34, from /usr/include/c++/9/bits/hashtable.h:35, from /usr/include/c++/9/unordered_map:46, from Using.hpp:5, from LinearAlgebra.hpp:4, from foo.cpp:3: /usr/include/c++/9/array:94:12: note: candidate: ‘std::array<long double, 36>::array()’ 94 | struct array | ^~~~~ /usr/include/c++/9/array:94:12: note: candidate expects 0 arguments, 1 provided /usr/include/c++/9/array:94:12: note: candidate: ‘constexpr std::array<long double, 36>::array(const std::array<long double, 36>&)’ /usr/include/c++/9/array:94:12: note: no known conversion for argument 1 from ‘<brace-enclosed initializer list>’ to ‘const std::array<long double, 36>&’ /usr/include/c++/9/array:94:12: note: candidate: ‘constexpr std::array<long double, 36>::array(std::array<long double, 36>&&)’ /usr/include/c++/9/array:94:12: note: no known conversion for argument 1 from ‘<brace-enclosed initializer list>’ to ‘std::array<long double, 36>&&’ make: *** [Makefile:16: foo.o] Error 1
我尝试过去除括号、给args...添加额外括号,但均无效果。请问问题出在哪里?如何解决?
可复现示例:
int main(void) { const std::vector<long double> foo = { 1.L, 2.L, -6.L, 0.L }; HexMatrix<long double, 2u, 2u> givenMatrix(foo); return 0; }
解决方案
问题根源
std::array本身没有接受std::vector的构造函数,之前能编译大概率是依赖了旧编译器的非标准扩展(比如早期GCC的特殊兼容行为)。重装系统后编译器版本更新,这类非标准扩展被移除或禁用,导致标准不兼容的写法直接报错。你之前的values({ args... })当传入单个std::vector时,本质是试图用{vector}初始化std::array,这完全不符合C++标准规范。
修复方法
需要显式将std::vector的元素复制到std::array中,不能依赖初始化列表的隐式转换。以下是两种可行的修复方案:
方案1:单独添加容器构造函数
直接针对容器类型编写构造函数,显式拷贝元素:
#include <algorithm> #include <stdexcept> template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns> class HexMatrix { private: std::array<Type, NumberOfRows*NumberOfColumns> values; public: // 原可变参数构造函数:用于直接传入单个元素 template <typename... OtherType> HexMatrix(OtherType&&... args) : values({std::forward<OtherType>(args)...}) { static_assert(NumberOfRows != 0u); static_assert(NumberOfColumns != 0u); static_assert(sizeof...(args) == NumberOfRows * NumberOfColumns, "元素数量与矩阵尺寸不匹配"); } // 新增容器构造函数:支持vector、array等带begin/end的容器 template <typename Container> HexMatrix(const Container& cont) { static_assert(NumberOfRows != 0u); static_assert(NumberOfColumns != 0u); const auto total_size = NumberOfRows * NumberOfColumns; if (cont.size() != total_size) { throw std::invalid_argument("容器尺寸与矩阵尺寸不匹配"); } std::copy(cont.begin(), cont.end(), values.begin()); } };
方案2:用SFINAE区分参数类型
如果想保留单个可变参数模板,同时自动区分“单个容器”和“多个元素”的情况,可以用SFINAE做类型判断:
#include <algorithm> #include <stdexcept> #include <type_traits> #include <iterator> // 辅助模板:判断是否为容器类型 template <typename T, typename = void> struct is_container : std::false_type {}; template <typename T> struct is_container<T, std::void_t<decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>> : std::true_type {}; template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns> class HexMatrix { private: std::array<Type, NumberOfRows*NumberOfColumns> values; public: // 匹配多个元素的情况:参数都不是容器 template <typename... OtherType, std::enable_if_t<!is_container<std::decay_t<OtherType>>::value || ..., bool> = true> HexMatrix(OtherType&&... args) : values({std::forward<OtherType>(args)...}) { static_assert(NumberOfRows != 0u); static_assert(NumberOfColumns != 0u); static_assert(sizeof...(args) == NumberOfRows * NumberOfColumns, "元素数量与矩阵尺寸不匹配"); } // 匹配单个容器的情况 template <typename Container, std::enable_if_t<is_container<std::decay_t<Container>>::value, bool> = true> HexMatrix(const Container& cont) { static_assert(NumberOfRows != 0u); static_assert(NumberOfColumns != 0u); const auto total_size = NumberOfRows * NumberOfColumns; if (cont.size() != total_size) { throw std::invalid_argument("容器尺寸与矩阵尺寸不匹配"); } std::copy(cont.begin(), cont.end(), values.begin()); } };
额外注意事项
- 编译时建议使用正式的C++20标准选项
-std=c++20,而非早期预览版的-std=c++2a,新版本编译器对正式标准的支持更稳定。 - 添加尺寸检查可以避免运行时的数组越界问题,提升代码健壮性。
内容的提问来源于stack exchange,提问作者Pippin
相关产品推荐
相关产品推荐

