You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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编译时,仅会提示变量未使用,无编译错误。

问题咨询

  1. 在C++17标准下,如何通过constexpr构造std::array<std::array<double, N>, N>?
  2. 为何移除打印代码后编译错误会消失?

解答

问题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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 13:25:47