Заполнить std:: array в списке инициализации члена

Следующий код работает, но я бы хотел избежать предупреждения:

предупреждение: 'fitness:: vect_' должно быть инициализировано в список инициализации членов [-WeffС++]

когда он скомпилирован с помощью переключателя g++ -Weffc++:

#include <array>

template<class T, unsigned N>
class fitness
{
public:
  explicit fitness(T v)
  {
    static_assert(N, "fitness zero length");

    vect_.fill(v);
  }

private:
  std::array<T, N> vect_;
};

int main()
{
  fitness<double, 4> f(-1000.0);

  return 0;
}

Следует ли игнорировать предупреждение? Есть ли способ заполнить vect_ в списке инициализации конструктора (без изменения его типа)?

Ответ 1

Функция, которая генерирует символ filled_array, должна иметь свое возвращаемое значение:

template<unsigned N, typename T>
std::array<T, N> filled_array_sized( T const& t ) {
  std::array<T, N> retval;
  retval.fill( t );
  return retval;
}

но для этого требуется передать хотя бы размер N, если не тип T.

template<typename T>
struct array_filler {
  T && t;
  template<typename U, unsigned N>
  operator std::array<U, N>()&& {
    return filled_array_sized<N, U>( std::forward<T>(t) );
  }
  array_filler( T&& in ):t(std::forward<T>(in)) {}
};
template<typename T>
array_filler< T >
filled_array( T&& t ) {
  return array_filler<T>( t );
}

обратите внимание, что сохранение возвращаемого значения filled_array в auto не рекомендуется.

Использование:

#include <array>

template<class T, unsigned N>
class fitness
{
public:
  explicit fitness(T v): vect_( filled_array( std::move(v) ) ) {
    //...
  }
//...

Я не знаю, будет ли приведенный выше код генерировать предупреждение в реализации filled_array_size, но если это так, отключите предупреждение локально.

Ответ 2

Я считаю, что вы можете игнорировать это предупреждение.

Он работает, если вы поместите пустую инициализацию для массива в конструкторе:

#include <array>

template<class T, unsigned N>
class fitness
{
public:
  explicit fitness(T v):
  vect_{}
  {
    static_assert(N, "fitness zero length");

    vect_.fill(v);
  }

private:
  std::array<T, N> vect_;
};

int main()
{
  fitness<double, 4> f(-1000.0);

  return 0;
}

Ответ 3

Попробуйте использовать

explicit fitness(T v) : vect_{}
{
//...
}

Ответ 4

Конструктор по умолчанию (read: value initializer) должен работать нормально в этом случае. Поскольку std::array является агрегированным типом, каждый элемент будет инициализироваться значением, который для числовых типов, таких как double, означает нулевую инициализацию, а затем вы можете использовать fill.

explicit fitness(T v) : vect_() // or vect_{}
{
    vect_.fill(v);
}

Возможно, вам лучше использовать std::vector и его конструктор заполнения, если вы не хотите делать по существу двойную инициализацию. Тогда ваш класс станет следующим:

template<class T>
class fitness
{
public:
  explicit fitness(T v, unsigned n) : vect_(n, v)

private:
  std::vector<T> vect_;
};

int main()
{
   fitness<double> f(-1000.0, 4);

   return 0;
}

Вы могли бы, конечно, сохранить N в качестве параметра шаблона, но нет необходимости делать это, так как длина не должна быть известна во время компиляции. (С другой стороны, если вы придерживаетесь std::array, вы можете настроить конструктор как constexpr, хотя для этого может потребоваться некоторое использование шаблона или вспомогательная функция constexpr, которая возвращает список инициализаторов для правильной работы, Я не играл достаточно с концепциями С++ 11, чтобы знать.)