How to construct std::array object with initializer list?
An std::array<>
has no constructor that takes an std::initializer_list<>
(initializer list constructor) and there is no special language support for what it may mean to pass a std::initializer_list<>
to a class' constructors such that that may work. So that fails.
For it to work, your derived class needs to catch all elements and then forward them, a constructor template:
template<typename ...E>
enum_addressable_array(E&&...e) : base_t{{std::forward<E>(e)...}} {}
Note that you need {{...}}
in this case because brace elision (omitting braces like in your case) does not work at that place. It's only allowed in declarations of the form T t = { ... }
. Because an std::array<>
consists of a struct embedding a raw array, that will need two level of braces. Unfortunately, I believe that the exact aggregate structure of std::array<>
is unspecified, so you will need to hope that it works on most implementations.
How to initialize std::array member in constructor initialization list when the size of the array is a template parameter
std::index_sequence
was provided in order to simplify the metaprogramming task of creating and expanding a pack whose size is not fixed. Use std::make_index_sequence
and delegate the construction to some private constructor that deduces the pack:
A(H h) : A(h, std::make_index_sequence<N>{}) {}
template <std::size_t... i>
A(H h, std::index_sequence<i...>) : hashes{((void)i, h)...} {}
Here, ((void)i, h)
is a comma expression that evaluates to h
, but since we mentioned the pack i
inside it, it's eligible to be expanded using ...
. The result is N
copies of h
(one for each element of i
). This expansion occurs inside a braced-init-list, so the result is a braced-init-list contaning N
copies of h
, which will then initialize the hashes
member.
Initialize an std::array of objects with non-trivial constructor
You might write helper class:
template <std::size_t ... Is, typename T>
std::array<T, sizeof...(Is)> make_array_impl(const T& def, std::index_sequence<Is...>)
{
return {{(Is, void(), def)...}};
}
template <std::size_t N, typename T>
std::array<T, N> make_array(const T& def)
{
return make_array_impl(def, std::make_index_sequence<N>());
}
and then
Cube(int size) : c_edgelets(make_array<12>(Edgelet(size - 2)) {}
Using std::array with initialization lists
std::array
is funny. It is defined basically like this:
template<typename T, int size>
struct std::array
{
T a[size];
};
It is a struct which contains an array. It does not have a constructor that takes an initializer list. But std::array
is an aggregate by the rules of C++11, and therefore it can be created by aggregate initialization. To aggregate initialize the array inside the struct, you need a second set of curly braces:
std::array<std::string, 2> strings = {{ "a", "b" }};
Note that the standard does suggest that the extra braces can be elided in this case. So it likely is a GCC bug.
Initialization list for an array of objects that that take parameters C++
If you have C++11, you can do:
B(int R) : myA{1, 2, 3, 4, 5, 6} { /* do something */ }
Although, if you are using Visual Studio 2013, please note that this syntax is currently NOT supported. There is the following workaround, however (which is arguably better style anyways):
#include <array>
class B {
std::array<A, N> myA;
public:
B(int R) : myA({1, 2, 3, 4, 5, 6}) {}
};
Note, however, that N has to be a compile-time constant, and the number of initializers has to match the number of initializers.
How come std::initializer_list is allowed to not specify size AND be stack allocated at the same time?
The thing is, std::initializer_list
does not hold the objects inside itself. When you instantiate it, compiler injects some additional code to create a temporary array on the stack and stores pointers to that array inside the initializer_list. For what its worth, an initializer_list is nothing but a struct with two pointers (or a pointer and a size):
template <class T>
class initializer_list {
private:
T* begin_;
T* end_;
public:
size_t size() const { return end_ - begin_; }
T const* begin() const { return begin_; }
T const* end() const { return end_; }
// ...
};
When you do:
foo({2, 3, 4, 5, 6});
Conceptually, here is what is happening:
int __tmp_arr[5] {2, 3, 4, 5, 6};
foo(std::initializer_list{arr, arr + 5});
One minor difference being, the life-time of the array does not exceed that of the initializer_list.
How do I initialize a member array with an initializer_list?
You can use a variadic template constructor instead of an initializer list constructor:
struct foo {
int x[2];
template <typename... T>
foo(T... ts) : x{ts...} { // note the use of brace-init-list
}
};
int main() {
foo f1(1,2); // OK
foo f2{1,2}; // Also OK
foo f3(42); // OK; x[1] zero-initialized
foo f4(1,2,3); // Error: too many initializers
foo f5(3.14); // Error: narrowing conversion not allowed
foo f6("foo"); // Error: no conversion from const char* to int
}
EDIT: If you can live without constness, another way would be to skip initialization and fill the array in the function body:
struct foo {
int x[2]; // or std::array<int, 2> x;
foo(std::initializer_list<int> il) {
std::copy(il.begin(), il.end(), x);
// or std::copy(il.begin(), il.end(), x.begin());
// or x.fill(il.begin());
}
}
This way, though, you lose the compile-time bounds checking that the former solution provides.
Can I initialize an array using the std::initializer_list instead of brace-enclosed initializer?
Other answered correctly said this is not possible upfront. But with little helpers, you can get pretty close
template<typename T, std::size_T N, std::size_t ...Ns>
std::array<T, N> make_array_impl(
std::initializer_list<T> t,
std::index_sequence<Ns...>)
{
return std::array<T, N>{ *(t.begin() + Ns) ... };
}
template<typename T, std::size_t N>
std::array<T, N> make_array(std::initializer_list<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>());
}
If you are open to more work arounds, you can put this into a class to catch statically-known length violations for the cases where you pass a braced init list. But be warned that most people who read this code will head-desk
template<typename T, std::size_t N>
struct ArrayInitializer {
template<typename U> struct id { using type = U; };
std::array<T, N> t;
template<typename U = std::initializer_list<T>>
ArrayInitializer(typename id<U>::type z)
:ArrayInitializer(z, std::make_index_sequence<N>())
{
if(N > z.size())
throw std::out_of_range("that's crazy!");
}
template<typename ...U>
ArrayInitializer(U &&... u)
:t{ std::forward<U>(u)... }
{ }
private:
template<std::size_t ...Ns>
ArrayInitializer(std::initializer_list<T>& t,
std::index_sequence<Ns...>)
:t{ *(t.begin() + Ns) ... }
{ }
};
template<typename T, std::size_t N>
std::array<T, N> f(ArrayInitializer<T, N> ai) {
return std::move(ai.t);
}
int main() {
f<int, 5>({1, 2, 3, 4, 5}); // OK
f<int, 5>({1, 2, 3, 4, 5, 6}); // "too many initializers for array<int, 5>"
std::initializer_list<int> il{1, 2, 3, 4, 5};
f<int, 5>(il); // ok
}
Note that both the non-static case at the top of the answer and the "head-desk" case do only check whether you provide too few initializing elements, and errors out then, for the initializer_list
case. If you provide too many for the initializer_list
case, the trailing elements are just ignored.
Related Topics
Unsequenced Value Computations (A.K.A Sequence Points)
Should I Learn C Before Learning C++
How to Set Up Googletest as a Shared Library on Linux
Repeated Multiple Definition Errors from Including Same Header in Multiple Cpps
Std::List<>::Sort()' - Why the Sudden Switch to Top-Down Strategy
Why Is Protected Constructor Raising an Error This This Code
Dereferencing an Invalid Pointer, Then Taking the Address of the Result
Why Do We Need to Use Boost::Asio::Io_Service::Work
Which Standard Wording Tells Us That Ref-To-Const Temporary Lifetime Extension Only "Works Once"
Is Std::String Ref-Counted in Gcc 4.X/C++11
Copy Constructor Is Not Inherited
C++: Converting Hexadecimal to Decimal
Why Can't I Capture This By-Reference ('&This') in Lambda
What Is the C++ Compiler Required to Do with Ill-Formed Programs According to the Standard
Argument of Type "Char *" Is Incompatible with Parameter of Type "Lpwstr"