11

The following code works but I would like to avoid the warning:

warning: 'fitness::vect_' should be initialized in the member initialization list [-Weffc++]

when it is compiled with the g++ -Weffc++ switch:

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

Should I ignore the warning? Is there a way to fill vect_ in the constructor initialization list (without changing its type)?

manlio
  • 18,345
  • 14
  • 76
  • 126

5 Answers5

4

I believe you can ignore this warning.

It works if you place an empty initialization for the array in the constructor:

#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;
}
user
  • 7,123
  • 7
  • 48
  • 90
2

Try to use

explicit fitness(T v) : vect_{}
{
//...
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
2

The default constructor (read: value initializer) should work fine in this case. As std::array is an aggregate type, its elements will be each be value-initialized, which for numeric types like double means zero-initialization, and then you can use fill.

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

You might be better off using std::vector and its fill constructor if you don't want to do essentially double the initialization, though. Then your class would become:

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

You could, of course, still keep the N as a template parameter, but there's no need to do so as the length doesn't need to be known at compile-time. (On the other hand, if you stick with std::array you might be able to set up the constructor as a constexpr, though that may require some template fiddling or an auxiliary constexpr function that returns an initializer list to work right, I haven't played around enough with C++11 concepts to know.)

JAB
  • 20,783
  • 6
  • 71
  • 80
  • Initially I used a std::vector but std::array proved to give better performance (fitness class is critical for my framework). I'll check if the compiler is able to avoid a double initialization. – manlio Feb 24 '14 at 17:13
  • ...since std::array is an aggregate, default construction won't zero the memory, but will leave it uninitialised (so no double initialization). – manlio Feb 24 '14 at 17:30
  • For vector to be noticeably slower than array is rare, if you use it properly. – Lightness Races in Orbit Feb 24 '14 at 17:32
  • @manlio Going by http://en.cppreference.com/w/cpp/language/value_initialization, http://en.cppreference.com/w/cpp/language/aggregate_initialization, using an empty expression list in the constructor will indeed zero out the (applicable) members of an aggregate in C++11; if you use `vect_()`, it will perform value-initialization on each element of the array, which for `double`s will cause them to be zero-initialized, and if you use `vect_{}`, it will perform aggregate initialization with an empty initializer list, which results in the same per-element value-initialization. – JAB Feb 24 '14 at 17:51
  • Of course, if `T` is a type with any kind of explicit constructor, then the values will be default-initialized instead (which may result in uninitialized members of `T` if `T` does not have a default constructor that provides default initializers for its members). – JAB Feb 24 '14 at 18:03
  • Of course you are right - sorry. Anyway, luckily, with optimizations turned on the compiler (at least a recent g++/clang++) doesn't output useless code. – manlio Feb 24 '14 at 22:47
1

A function that generates a filled_array should have its return value be elided:

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

but that requires passing in at least the size N, if not the type 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 );
}

note that storing the return value of filled_array in an auto is not advised.

Use:

#include <array>

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

I do not know if the above code will generate a warning in the implementation of filled_array_size, but if it does, disable the warning locally.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • You could simplify this quite a bit by taking advantage of the fact that `fill` takes a `const T&`; it doesn't really add anything to do all that forwarding when the element is going to be copied anyway. – Stuart Olsen Feb 24 '14 at 19:36
  • @StuartOlsen true, but `fill` could eventually be improved with an rvalue version that copies the first N and `move`s into the last one! ... ok ok, that is pretty marginal. – Yakk - Adam Nevraumont Feb 24 '14 at 19:37
  • @StuartOlsen I took advantage of the `const&`, but because of type incompatibilities between the `array` and the initializing type (which I cannot know in the `filled_array` case, as we don't have access to the array-type until later), I have to do forwarding gymnastics until I reach `filled_array_sized`. Note that the above relies extensively on RVO and NRVO to be efficient. – Yakk - Adam Nevraumont Feb 24 '14 at 21:03
  • Well for my purpose the simple filled_array_sized is good enough (with RVO it's as efficient as the other solutions and seems to be more in the spirit of Effective C++). Anyway the post is very instructive. – manlio Feb 26 '14 at 10:03
1

Here's another way, cleaner IMHO, using the C++11 non-static data member initializers:

#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;
}
Avidan Borisov
  • 3,235
  • 4
  • 24
  • 27