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

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

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

когда он скомпилирован с переключателем 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_ в списке инициализации конструктора (без изменения его типа)?


person manlio    schedule 24.02.2014    source источник
comment
Заполнить его непросто, но можно инициализировать как vect_() или vect_{}.   -  person juanchopanza    schedule 24.02.2014


Ответы (5)


Функция, которая генерирует 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, но если да, отключите предупреждение локально.

person Yakk - Adam Nevraumont    schedule 24.02.2014
comment
Вы можете немного упростить это, воспользовавшись тем фактом, что fill принимает const T&; на самом деле он не добавляет ничего, чтобы делать всю эту пересылку, когда элемент все равно будет скопирован. - person Stuart Olsen; 24.02.2014
comment
@StuartOlsen true, но fill в конечном итоге можно улучшить с помощью версии rvalue, которая копирует первые N и moves в последнюю! ... окей, это довольно незначительно. - person Yakk - Adam Nevraumont; 24.02.2014
comment
@StuartOlsen Я воспользовался const&, но из-за несовместимости типов между array и инициализирующим типом (чего я не могу знать в случае filled_array, поскольку у нас нет доступа к типу массива позже), я должен делаю переадресацию, пока не дойду до filled_array_sized. Обратите внимание, что вышеизложенное в значительной степени зависит от эффективности RVO и NRVO. - person Yakk - Adam Nevraumont; 25.02.2014
comment
Что ж, для моей цели достаточно простого fill_array_sized (с RVO он так же эффективен, как и другие решения, и, кажется, больше в духе Effective C ++). В любом случае пост очень поучительный. - person manlio; 26.02.2014

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

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

#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;
}
person user    schedule 24.02.2014

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

explicit fitness(T v) : vect_{}
{
//...
}
person Vlad from Moscow    schedule 24.02.2014

В этом случае конструктор по умолчанию (читай: инициализатор значения) должен работать нормально. Поскольку 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 функции, которая возвращает список инициализаторов для правильной работы, я не играл достаточно с концепциями C ++ 11, чтобы знать.)

person JAB    schedule 24.02.2014
comment
Первоначально я использовал std :: vector, но оказалось, что std :: array дает лучшую производительность (фитнес-класс имеет решающее значение для моей структуры). Я проверю, сможет ли компилятор избежать двойной инициализации. - person manlio; 24.02.2014
comment
... поскольку std :: array является агрегатом, конструкция по умолчанию не обнулит память, но оставит ее неинициализированной (поэтому нет двойной инициализации). - person manlio; 24.02.2014
comment
При правильном использовании вектор будет заметно медленнее, чем массив. - person Lightness Races in Orbit; 24.02.2014
comment
@manlio Идет по en.cppreference.com/w/cpp/language/value_initialization, en.cppreference.com/w/cpp/language/aggregate_initialization, использование пустого списка выражений в конструкторе действительно обнулит (применимые) члены агрегата в C ++ 11; если вы используете vect_(), он выполнит инициализацию значения для каждого элемента массива, что для doubles приведет к их инициализации нулем, а если вы используете vect_{}, он выполнит совокупную инициализацию с пустым списком инициализаторов, что приведет к та же инициализация значения для каждого элемента. - person JAB; 24.02.2014
comment
Конечно, если T является типом с каким-либо явным конструктором, то вместо этого значения будут инициализированы по умолчанию (что может привести к неинициализированным членам T, если T не имеет конструктора по умолчанию, который предоставляет инициализаторы по умолчанию для своих членов). - person JAB; 24.02.2014
comment
Конечно, вы правы - извините. В любом случае, к счастью, при включенной оптимизации компилятор (по крайней мере, недавний g ++ / clang ++) не выводит бесполезный код. - person manlio; 25.02.2014