首页 > 解决方案 > 如何在不硬编码数组大小的情况下在类的初始化器列表中初始化 std::array

问题描述

我有 Foo.h:

#include <array>

class Bar {
public:
  Bar(std::string name) : name(name) {}

  std::string name;

};

class Foo {
public:
  enum {ARRAY_SIZE=10};

  Foo();

  void printElement(int idx);

  std::array<Bar,ARRAY_SIZE> myArray;

};

Foo.cc:

#include "Foo.h"
#include <iostream>

Foo::Foo(): myArray({Bar("9"),Bar("8"),Bar("7"),Bar("6"),Bar("5"),
                     Bar("4"),Bar("3"),Bar("2"),Bar("1"),Bar("0")}) {}


void Foo::printElement(int idx) {
  if (idx < ARRAY_SIZE) {
    std::cout << "Value is " << myArray[idx].name << std::endl;
  } else {
    std::cout << "Index out of bounds" << std::endl;
  }
}

int main() {
  Foo foo;
  foo.printElement(1);
}

问题是 {Bar("9"),Bar("8"),Bar("7"),Bar("6"),Bar("5"),Bar("4"),Bar("3"),Bar("2"),Bar("1"),Bar("0")} 太字面了,我需要能够使用一个表达式,该表达式根据 ARRAY_SIZE 的值(可以存在于外部头文件中)扩展到正确的大小。

请注意,在此示例中,我必须myArray在初始化程序列表中进行初始化,否则我会得到:

(...)/gcc/6.3.0/include/c++/6.3.0/array:90:12: error: no matching function for call to 'Bar::Bar()'

最好的方法是什么?

顺便说一句,我正在使用 g++ 6.3.0。

标签: c++arrays

解决方案


使用辅助函数。添加

template <std::size_t N>
auto make_array()
{
    std::array<Bar, N> arr;
    for (std::size_t i = 0; i < N; ++i)
        arr[i] = std::to_string(N - i - 1);
    return arr;
}

到您的班级,然后您可以在成员初始化程序列表中使用它,例如

Foo::Foo(): myArray(make_array<10>()) {}

这仅适用于Bar默认可构造的情况。

对于您的代码,您可以做的是添加另一个具有 astd::index_sequence值的助手来构造Bar's ,这看起来像

// helpers for reversing an integer sequence
template <std::size_t ... Is>
constexpr auto indexSequenceReverse (std::index_sequence<Is...> const &)
   -> decltype( std::index_sequence<sizeof...(Is)-1U-Is...>{} );

template <std::size_t N>
using makeIndexSequenceReverse
   = decltype(indexSequenceReverse(std::make_index_sequence<N>{}));

class Bar {
public:
  Bar(std::string name) : name(name) {}

  std::string name;

};

class Foo {
public:
  enum {ARRAY_SIZE=10};

  Foo();

  void printElement(int idx);

  std::array<Bar,ARRAY_SIZE> myArray;

  // get sequence and expand out initializes 
  template <std::size_t ... Is>
  auto make_array_impl(std::integer_sequence<size_t, Is...>)
  {
      return std::array{ Bar{std::to_string(Is)}... };
  }

  // convenient forwarder to implementation
  template <std::size_t N>
  auto make_array()
  {
      return make_array_impl(makeIndexSequenceReverse<N>{});
  }

};

Foo::Foo(): myArray(make_array<10>()) {}

void Foo::printElement(int idx) {
  if (idx < ARRAY_SIZE) {
    std::cout << "Value is " << myArray[idx].name << std::endl;
  } else {
    std::cout << "Index out of bounds" << std::endl;
  }
}

int main() {
  Foo foo;
  foo.printElement(1);
}

推荐阅读