fill std::array in the member initialization list - c++

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)?

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

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

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.)

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.

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

Related

How do I initialise an std::array of non-trivial objects in a class, where the size of the array is a template parameter of the class?

Say I have a class that looks like the following
template<std::size_t NumThings>
class Bar
{
public:
Bar(const int& v) : m_v(v) {}
std::array<int,NumThings> GetThings() const
{
std::array<int,NumThings> things;
for(std::size_t i = 0; i < NumThings; ++i)
things[i] = m_v;
return things;
}
protected:
const int& m_v;
};
template<std::size_t NumItems, std::size_t NumPieces>
class Foo
{
public:
Foo(const int& initialValue)
:
m_array(/* what do I do to pass initialValue to each item? */)
{}
protected:
std::array<Bar<NumPieces>,NumItems> m_array;
};
I'm unsure how to initialise the array of Bar in the Foo class by passing the parameter. I suppose I can't use {...} sort of syntax because I don't know how many items yet, but I'm sure this there's some sort of meta-template programming trick I can use.
EDIT made default constructor of Bar impossible.
There is no standard function for constructing an array of N instances of T. But you can implement this function yourself. One way of achieving this is to form a parameter pack the same size as the array you want to initialize. Then, you can unpack that parameter pack inside an array object's initializer list to construct the array correctly. Here is an example :
#include <array>
#include <cstddef> // std::size_t
#include <utility> // std::index_sequence
namespace details {
template<class T, std::size_t ... I>
std::array<T, sizeof...(I)> make_array_impl(const T & p_Value, std::index_sequence<I...>)
{
return {
(I, p_Value)...
};
}
} // namespace details
template<std::size_t N, class T>
auto make_array(const T & p_Value)
{
return details::make_array_impl(p_Value, std::make_index_sequence<N>());
}
The make_array function converts the template argument N to a parameter pack using an std::index_sequence. For example, if N is 3, the template argument I... for make_array_impl will be <0, 1, 2>.
Then, in make_array_impl that parameter pack is expanded in (I, p_value).... To expand a parameter pack, you have to use it in some way. But we don't actually care about the values of I, only by how many values it holds. Again, if N was 3, the parameter pack will expand to return { (0, p_Value), (1, p_Value), (2, p_Value) };. Applying the comma operator, the evaluation will be equivalent to return { p_Value, p_Value, p_Value };.
The usage in your case would look like this :
Foo(const int& initialValue) :
m_array(make_array<NumItems>(Bar<NumPieces>{ initialValue }))
{}
You can make use of std::make_index_sequence to get a parameter pack with the right size.
One way of doing it is partially specializing Foo.
template<std::size_t NumItems, std::size_t NumPieces, typename = std::make_index_sequence<NumItems>>
class Foo;
template<std::size_t NumItems, std::size_t NumPieces, std::size_t ... Count>
class Foo<NumItems, NumPieces, std::index_sequence<Count...>>
{
public:
Foo(const int& initialValue)
:
m_array{(Count, initialValue)...}
{}
protected:
std::array<Bar<NumPieces>,NumItems> m_array;
};
This will however generate warnings, gcc says
prog.cc:30:23: warning: left operand of comma operator has no effect [-Wunused-value]
30 | m_array{(Count, initialValue)...}
For C++14 you can use lambda-expression.
If there is an option to add a default constructor & overload the operator= in Bar class:
Foo(const int initialValue)
:
m_array(
[initialValue] () {
std::array<Bar<some_number>, NumItems> res;
for (auto &val : res) {
val = Bar<some_number>(initialValue);
}
return res;
}()
)
{}
Otherwise:
template<class T, std::size_t N, std::size_t ...Ns>
std::array<T, N> make_array_impl(
std::vector<T> t,
std::index_sequence<Ns...>)
{
return std::array<T, N>{ *(t.begin() + Ns) ... };
}
template<class T, std::size_t N>
std::array<T, N> make_array(std::vector<T> t) {
if(N > t.size())
throw std::out_of_range("that's crazy!");
return make_array_impl<T, N>(t, std::make_index_sequence<N>());
}
template<std::size_t NumItems, std::size_t BarsCount>
class Foo
{
public:
Foo(const int initialValue)
:
m_array(
[initialValue]() {
std::vector<Bar<BarsCount>> vec;
for (size_t i = 0; i < NumItems; i++) {
vec.emplace_back(initialValue);
}
return make_array<Bar<BarsCount>, NumItems>(vec);
}()
)
{}
protected:
std::array<Bar<BarsCount>, NumItems> m_array;
};
Reference for original make_array: https://stackoverflow.com/a/38934685/8038186
Edit:
If you can use a pointers' array instead of immidiate initialized objects' array, you can use the first solution, without having a default constructor or overloading the operator=:
Foo(const int initialValue)
:
m_array(
[initialValue] () {
std::array<std::shared_ptr<Bar<some_number>>, NumItems> res;
for (auto &val : res) {
val = std::make_shared<Bar<some_number>>(Bar<some_number>(initialValue));
}
return res;
}()
)
{}
One option is to have a default constructor in Bar.
Bar() : Bar(0) {}
Bar(const int v) : m_v(v) {}
and then set the values of each item in the function body of Foo's constructor.
Foo(const int initialValue)
{
std::fill_n(m_array, NumItems, Bar(initialValue));
}
Foo(const int initialValue)
{
std::fill_n(m_array, NumItems, initialValue);
}

Initializer list for an unknown ("templated") amount of classes

If I have a class template which contains an array with another class as type with undefined amount of fields (the amount is a template parameter), how do I run their constructors (if they take parameters)?
Here some example code:
class ArrayClass
{
public:
ArrayClass() = delete;
ArrayClass(int anyParameter) {}
};
template <const int amountOfFields>
class ContainingClass
{
ArrayClass myArray[amountOfFields];
public:
ContainingClass();
};
template <const int amountOfFields>
ContainingClass<amountOfFields>::ContainingClass()
:
myArray(5) // doesn't work of cause
{}
Is it possible to give every ArrayClass, no matter how many there are, the same parameter (or different ones)? (I don't essentially need it but it would make things easier for me)
There’s nothing in the C++ standard libraries for this case
If you’re compiling with GCC, it has a proprietary extension called ranged initialization. With GCC, you can write something like this (untested):
template<size_t amountOfFields>
ContainingClass<amountOfFields>::ContainingClass():
myArray( { [0 ... (amountOfFields-1)] = 5} )
{ }
If you’re using any other compiler, you have following options.
As said by the commenters, replace array with std::vector, it has the constructor you need. However this will change RAM layout, i.e. if you have lots of containers with small number of elements each, arrays (both C arrays, and C++ std::array) will be faster because one less pointer to chase.
Remove “=delete” from the default constructor of your ArrayClass, use std::fill or std::fill_n in the ContainingClass constructor to set initial values after they’re already constructed. However this might bring some small runtime cost.
If you don’t have too many elements, technically you can use some template metaprogramming to implement statically-constructed arrays the way you want. However, IMO that’ll be substantial amount of very hard to debug C++ code (there’s no compile-time debugger).
If you have small number of different template arguments in your code, you can write a function like
template<size_t N>
constexpr std::array<ArrayClass,N> fill_array(int val)
specialize it for different values of amountOfFields temple arguments you have, and call the function in the constructor of ContainingClass.
Other solutions are possible, like external tools, macros, boost, etc… But I think 2 & 4 are the most reasonable workarounds.
This work for me with GCC 8.1 / Clang 6.0 and C++14, though I am definitely not sure whether it is Standard compliant:
class E {
public:
E() = delete;
E(int i) : i_(i) { }
operator int() const { return i_; }
private:
int i_;
};
template <typename T>
T dummy(T val, /* [[maybe_unused]] */ size_t I) { return val; }
template <typename T, size_t... I, typename U>
constexpr auto make_array_impl(U val, std::index_sequence<I...> is) {
return std::array<T, is.size()>{dummy(val, I)...};
}
template <typename T, size_t N, typename U>
constexpr auto make_array(U val) {
return make_array_impl<T>(val, std::make_index_sequence<N>{});
}
template <typename T, size_t N>
class A {
public:
A(T val) : a_{make_array<T, N>(val)} { }
void print() { for (auto e : a_) std::cout << e << std::endl; }
private:
std::array<T, N> a_;
};
int main() {
A<E, 5> a(-1);
a.print();
}
Live demo: https://wandbox.org/permlink/Db9Zpf6gUMvg4MER
Updated more generic solution:
template <typename T, size_t... I, typename... Args>
constexpr auto make_array_impl(std::index_sequence<I...> is, Args&&... args) {
return std::array<T, is.size()>{ (I, T(std::forward<Args>(args)...))... };
}
template <typename T, size_t N, typename... Args>
constexpr auto make_array(Args&&... args) {
return make_array_impl<T>(std::make_index_sequence<N>{}, std::forward<Args>(args)...);
}

initialize std::array with 'this' pointer

I am trying to initialize an array inside a template class and pass the this pointer to all elements in the array.
This is what my classes might look like:
template<int NUM> class outer_class;
template<int N>
class inner_class {
private:
outer_class<N> *cl;
public:
inner_class(outer_class<N> *num) {
cl = num;
}
void print_num() {
cl->print_num();
}
};
template<int NUM> class outer_class {
private:
int number = NUM;
// --> here I basically want NUM times 'this' <--
std::array<inner_class<NUM>, NUM> cl = { this, this, this, this };
public:
void print_num() {
std::cout << number << std::endl;
}
void print() {
cl[NUM - 1].print_num();
}
};
int main() {
outer_class<4> t;
t.print();
return 0;
}
How can I pass the this pointer to all elements of inner_class stored in the array of outer_class (in C++11)?
First, you can't have this like this outside of constructor or any other member function. Here you have to initialize cl within initializer list.
Using delegating constructor together with std::*_sequence stuff:
template<int NUM> class outer_class {
...
template <std::size_t... Integers>
outer_class(std::index_sequence<Integers...>)
: cl{(static_cast<void>(Integers), this)...}
{}
public:
outer_class(/* whatever */) : outer_class(std::make_index_sequence<NUM>{}) {}
};
Side notes:
Your print member functions should be marked const as they don't modify your members.
cl[NUM - 1].print_num(); you might want to use std::array::back().
You could use some helper functions and then initialize the member using these functions, e.g.:
template <std::size_t I, class T>
T copy(T t) { return t; }
template <class T, std::size_t... Is>
constexpr std::array<T, sizeof...(Is)> copy_n(T const& t, std::index_sequence<Is...>) {
return {copy<Is>(t)... };
}
template <class T, std::size_t N>
constexpr std::array<T, N> copy_n(T const& t) {
return copy_n(t, std::make_index_sequence<N>{});
}
Then in your class:
std::array<inner_class<NUM>, NUM> cl;
outer_class() : cl(copy_n<inner_class<NUM>, NUM>(this)) { }
Note:
[to be verified] you cannot use this in default member initializer, so you need to have a custom constructor;
you need to explicitly specify inner_class<NUM> as the first template parameter for copy_n, because otherwize T will be deduced as outer_class<NUM>*, and while there is an implicit conversion from outer_class<NUM>* to inner_class<NUM>, there is no conversion from std::array<outer_class<NUM*>, NUM> to std::array<inner_class<NUM>, NUM>;
if you are using C++11 and not 14, or clang, you might get a warning on the return of copy_n, you can get rid of it by adding an extra pair of brackets {}.

DRY way to construct all elements of an array with the same initializer list?

In C++11, is there a DRY way to construct all elements of an array with some same set of parameters for all elements? (e.g. via a single initializer list?)
For example:
class C {
public:
C() : C(0) {}
C(int x) : m_x{x} {}
int m_x;
};
// This would construct just the first object with a parameter of 1.
// For the second and third object the default ctor will be called.
C ar[3] {1};
// This would work but isn't DRY (in case I know I want all the elements in the array to be initialized with the same value.
C ar2[3] {1, 1, 1};
// This is DRYer but obviously still has repetition.
const int initVal = 1;
C ar3[3] {initVal, initVal, initVal};
I know my goal is easily achievable by using an std::vector. I'm wondering if it's possible with raw arrays as well.
c++14 - a little work will make this work for c++11
#include <iostream>
#include <array>
#include <utility>
class C {
public:
C() : C(0) {}
C(int x) : m_x{x} {}
int m_x;
};
namespace detail {
template<class Type, std::size_t...Is, class...Args>
auto generate_n_with(std::index_sequence<Is...>, const Args&...args)
{
return std::array<Type, sizeof...(Is)> {
{(void(Is), Type { args... })...} // Or replace '{ args... }' with '( args... )'; see in comments below.
};
}
}
template<class Type, std::size_t N, class...Args>
auto generate_n_with(const Args&...args)
{
return detail::generate_n_with<Type>(std::make_index_sequence<N>(), args...);
}
int main()
{
auto a = generate_n_with<C, 3>(1);
for (auto&& c : a)
{
std::cout << c.m_x << std::endl;
}
}
results:
1
1
1
I want to guarantee no copies prior to c++17
The you would need to generate into a vector:
template<class Container, class...Args>
auto emplace_n(Container& c, std::size_t n, Args const&...args)
{
c.reserve(n);
while(n--) {
c.emplace_back(args...);
}
};
used like this:
std::vector<C> v2;
emplace_n(v2, 3, 1);
You can construct a sequence of elements using an std::index_sequence<...> and expand that into the initializers of an array. I don't know of any approach avoiding an auxiliary function, though. Here is an example:
#include <iterator>
#include <algorithm>
#include <iostream>
struct S {
int value;
S(int value): value(value) {}
};
std::ostream& operator<< (std::ostream& out, S const& s) {
return out << s.value;
}
#include <array>
#include <iterator>
#include <algorithm>
#include <iostream>
struct S {
int value;
S(int value): value(value) {}
};
std::ostream& operator<< (std::ostream& out, S const& s) {
return out << s.value;
}
template <typename T, std::size_t... I>
std::array<T, sizeof...(I)> fill_aux(T value, std::index_sequence<I...>)
{
return std::array<T, sizeof...(I)>{ (void(I), value)... };
}
template <std::size_t N, typename T>
std::array<T, N> fill(T value) {
return fill_aux(value, std::make_index_sequence<N>());
}
int main()
{
std::array<S, 10> array = fill<10>(S(17));
std::copy(array.begin(), array.end(), std::ostream_iterator<S>(std::cout, " "));
}
By creating derived class, you can effectively create a new default value. It's a bit hackish, but may be less hackish than other solutions. Here's an example:
class C {
public:
C() : C(0) {}
C(int x) : m_x{x} {}
int m_x;
};
template <int init>
struct CInit : C { CInit() : C(init) {} };
CInit<1> ar2[3];
const int initVal = 1;
CInit<initVal> ar3[3];
Another approach is to wrap your raw array inside a struct with a variadic constructor:
template <size_t n>
struct Array {
C array[n];
template <size_t... seq>
Array(int init,std::index_sequence<seq...>)
: array{(void(seq),init)...}
{
}
Array(int init)
: Array(init,std::make_index_sequence<n>())
{
}
};
const int initVal = 1;
Array<3> ar3_1(initVal);
const C (&ar3)[3] = ar3_1.array;
Building on Richard's answer, it's also possible to define
template<class Type, std::size_t N, class...Args>
auto generate_n_with(const std::array<Type, N>&, const Args&...args)
{
return detail::generate_n_with<Type>(std::make_index_sequence<N>(), args...);
};
Allowing you to enter the array as a parameter to make the code more dry in case you already know the type of the array, e.g.
class D {
public:
D();
std::array<int, 3> m_ar;
};
Allowing
D::D() : m_ar{generate_n_with{m_ar, 5}} {}
Instead of the less DRY
D::D() : m_ar{generate_n_with<int, 3>{5}} {}
P.S. maybe there's an even DRYer way without repeating m_ar twice?

Place templated std::array<std::vector<T>, N> in constructor initializer list

I have a templated class containing a std::array<std::vector<T>, N> data_ member.
Currently, I can construct this with
template<typename T, size_t N>
class A
{
public:
A(some parameters)
{
for (size_t n=0; n<N; n++) {
data_[n].resize(calculated size from parameters);
}
}
private:
std::array<std::vector<T>,N> data_;
};
Is there some formulation in which I can instead place the initialization of the array in the constructor?
For example, if I used only a vector and then an indexing function, I could have
...
A(some parameters):
data_(std::vector<T>(N*previous size)) {}
...
#include <array>
#include <vector>
#include <utility>
template <typename T, std::size_t N>
class A
{
public:
A(int a, int b) : A(a, b, std::make_index_sequence<N>{})
{ }
private:
template <std::size_t... Is>
A(int a, int b, std::index_sequence<Is...>)
: data_{ { std::vector<T>(((void)Is, a + b))... } }
{ }
std::array<std::vector<T>, N> data_;
};
DEMO
Since the primary container that you initialise is std::array, and since it only provides aggregate initialisation and you want to parametrise on the size of the array, I'm afraid that you can't do that the way you want. you need to generate an initialisation sequence. This is as far as I know only possible with variadic templates. I tried to put something together, but using the approach from the other answer is clearly the best choice here.
On the other hand there's always the possibility to create a static member function that returns the array as you need it:
Constructor (bool data) : member(initialiser(data)) {}
static array<vector<T>, N> initialiser(bool data) {
array<vector<T>, N> container;
// your code from above
return container;
}
Though it's debatable and dependent on the context whether this is useful or not.