C++17中constexpr构造嵌套std::array的编译问题咨询
C++17中constexpr构造二维std::array的问题
无法编译的代码
#include <array> #include <iostream> #include <utility> template <std::size_t N> class A { template <std::size_t... Ints> static constexpr void get_phi_base_impl(std::array<std::array<double, N>, N>& res, std::index_sequence<Ints...>) { ( (std::get<Ints>(res).fill(0), std::get<Ints>(std::get<Ints>(res)) = 1), ...); } public: static constexpr std::array<std::array<double, N>, N> get_phi_base(); static constexpr std::array<std::array<double, N>, N> base = get_phi_base(); }; template <std::size_t N> constexpr std::array<std::array<double, N>, N> A<N>::get_phi_base() { std::array<std::array<double, N>, N> res; get_phi_base_impl(res, std::make_index_sequence<N>{}); return res; } int main() { A<4> a; for (const auto& el : a.base) { for (const auto& x : el) std::cout << x << ' '; std::cout << std::endl; } return 0; }
编译错误信息
g++ 错误提示
main.cpp:13:76: error: 'static constexpr std::array<std::array<double, N>, N> A<N>::get_phi_base() [with long unsigned int N = 4]' called in a constant expression 13 | static constexpr std::array<std::array<double, N>, N> base = get_phi_base(); | ~~~~~~~~~~~~^~ main.cpp:17:48: note: 'static constexpr std::array<std::array<double, N>, N> A<N>::get_phi_base() [with long unsigned int N = 4]' is not usable as a 'constexpr' function because: 17 | constexpr std::array<std::array<double, N>, N> A<N>::get_phi_base() | ^~~~
clang++ 错误提示
test.cpp:13:57: error: constexpr variable 'base' must be initialized by a constant expression static constexpr std::array<std::array<double, N>, N> base = get_phi_base(); ^ ~~~~~~~~~~~~~~ test.cpp:27:27: note: in instantiation of static data member 'A<4>::base' requested here for (const auto& el : a.base) ^ test.cpp:19:40: note: non-constexpr constructor 'array' cannot be used in a constant expression std::array<std::array<double, N>, N> res; ^ test.cpp:13:64: note: in call to 'get_phi_base()' static constexpr std::array<std::array<double, N>, N> base = get_phi_base();
奇怪的现象
如果注释掉main函数中的打印代码:
//for (const auto& el : a.base) // { // for (const auto& x : el) // std::cout << x << ' '; // std::cout << std::endl; // }
使用-std=c++17 -Wall -pedantic编译时,仅会提示变量未使用,无编译错误。
问题咨询
- 在C++17标准下,如何通过constexpr构造
std::array<std::array<double, N>, N>? - 为何移除打印代码后编译错误会消失?
解答
问题1:C++17下constexpr构造二维std::array的方法
C17中std::array的默认构造函数不是constexpr(该特性在C20才被加入),因此直接默认初始化std::array无法在constexpr上下文中使用。解决思路是在constexpr函数中显式初始化所有元素,而非依赖默认构造,以下是两种可行方案:
方案一:循环初始化对角线矩阵
直接在constexpr函数中通过循环完成元素初始化,逻辑直观且易维护:
template <std::size_t N> constexpr std::array<std::array<double, N>, N> A<N>::get_phi_base() { std::array<std::array<double, N>, N> res{}; for (std::size_t i = 0; i < N; ++i) { for (std::size_t j = 0; j < N; ++j) { res[i][j] = (i == j) ? 1.0 : 0.0; } } return res; }
方案二:折叠表达式+lambda构造
利用折叠表达式和lambda,直接构造每一行的数组再组合成二维数组,保持原代码的模板元编程风格:
template <std::size_t N> class A { template <std::size_t... Ints> static constexpr std::array<std::array<double, N>, N> get_phi_base_impl(std::index_sequence<Ints...>) { return { { ( [=]{ std::array<double, N> arr{}; arr.fill(0.0); arr[Ints] = 1.0; return arr; } )()... } }; } public: static constexpr std::array<std::array<double, N>, N> get_phi_base(); static constexpr std::array<std::array<double, N>, N> base = get_phi_base(); }; template <std::size_t N> constexpr std::array<std::array<double, N>, N> A<N>::get_phi_base() { return get_phi_base_impl(std::make_index_sequence<N>{}); }
问题2:移除打印代码后编译错误消失的原因
这是C++模板惰性实例化规则导致的:当模板类的静态成员没有被实际使用时,编译器不会强制实例化它的定义。
原代码中,注释掉打印a.base的代码后,A<4>::base这个静态成员并未被实际访问(仅声明了A<4> a;但未读取base),因此编译器不会实例化A<4>::base的初始化表达式,也就不会检查get_phi_base()是否符合constexpr函数的要求。一旦访问a.base,编译器必须实例化base的初始化逻辑,此时才会触发constexpr合法性检查,暴露错误。
内容的提问来源于stack exchange,提问作者francesco
相关产品推荐
相关产品推荐

