c++ constexpr初始化std::数组std::数组

66bbxpm5  于 2022-12-05  发布在  其他
关注(0)|答案(1)|浏览(200)

以下代码无法编译

#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;
}

检查一下Live on Coliru
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;
  //  }

两个编译器都不再抱怨了。启用警告后,我只得到一个未使用变量a的警告。我正在用-std=c++17 -Wall -pedantic编译。
有没有办法把constexpr构造成std::arraystd::array?为什么如果我省略了打印,错误就会消失?
我主要对c++17的答案感兴趣。
this question的答案解释了为什么上面的代码在C20中编译,而不是在C17中编译。但是,它没有回答constexpr的具体问题-填充std::array<std::array<T, N>, N>。特别是,answer中给出的初始化res{};没有解决这个问题(出现另一个编译错误)。

2skhul33

2skhul331#

在C17中,constexpr函数不能包含“未执行初始化的变量的定义”。
在C
20中已删除此限制。
在C++17中,你可以这样创建二维数组(我假设它是一个单位矩阵):

constexpr double identity_matrix_initializer(std::size_t x, std::size_t y) {
    return x == y ? 1.0 : 0.0;
}

template<std::size_t IndexY, std::size_t... IndicesX>
constexpr auto make_identity_matrix_row_helper(std::index_sequence<IndicesX...>) 
-> std::array<double, sizeof...(IndicesX)> 
{
    return { identity_matrix_initializer(IndicesX, IndexY)... };
}

template<std::size_t... IndicesX, std::size_t... IndicesY>
constexpr auto make_identity_matrix_helper(std::index_sequence<IndicesX...>, std::index_sequence<IndicesY...>) 
-> std::array<std::array<double, sizeof...(IndicesX)>, sizeof...(IndicesY)> 
{
    return {{ make_identity_matrix_row_helper<IndicesY>(std::index_sequence<IndicesX...>{})... }};
}

template<std::size_t N>
constexpr auto make_identity_matrix() -> std::array<std::array<double, N>, N> 
{
    return make_identity_matrix_helper(std::make_index_sequence<N>{}, std::make_index_sequence<N>{});    
}

Demo
当然,这些函数在A中可以是静态的。

相关问题