Brace-Enclosed Initializer List Constructor

Brace-enclosed initializer list constructor

It can only be done for aggregates (arrays and certain classes. Contrary to popular belief, this works for many nonpods too). Writing a constructor that takes them is not possible.

Since you tagged it as "C++0x", then this is possible though. The magic words is "initializer-list constructor". This goes like

Phenotype(std::initializer_list<uint8> c) {
assert(c.size() <= std::size(m_array));
std::copy(c.begin(), c.end(), m_array);
}

// used like
Phenotype p1{1, 2, 3};
Phenotype p2({1, 3, 2}); // works too
Phenotype p3(1, 2, 3); // doesn't work

However, such initialization will default construct the array and then use the assignment operator. If you aim for speed and safety (you get compile time errors for too many initializers!), you can also use an ordinary constructor with a variadic template.

This can be more generic than needed though (often an initializer_list completely suffices, especially for plain integers). It benefits from perfect forwarding, so that an rvalue argument can be move constructed into an array element

template<typename ...T>
Phenotype(T&&...t):m_array{ std::forward<T>(t)... } {

}

// used like
Phenotype p1{1, 2, 3};
Phenotype p2(1, 2, 3); // works too
Phenotype p3({1, 2, 3}); // doesn't work

It's a hard choice!

Edit Correction, the last one works too, as we didn't make the constructor explicit, so it can use the copy constructor of Phenotype, constructing a temporary Phenotype object and copy it over to p3. But that's not what we really would want the calls to be :)

Automatic generation of a brace-enclosed initializer list in C++ using language features (NOT pre-processor directives)

Like this:

#include <array>
#include <memory>
#include <utility>

template <std::size_t ...I>
std::array<std::shared_ptr<int>, sizeof...(I)> foo(std::index_sequence<I...>)
{
return {(void(I), std::make_shared<int>())...};
}

std::array<std::shared_ptr<int>, 4> array_ = foo(std::make_index_sequence<4>());

Guaranteed copy elision from C++17 ensures that the array is constructed in place, and no extra moves happen.

The return statement expands to {(void(0), std::make_shared<int>()), (void(1), std::make_shared<int>())...}. void(...) is not strictly necessary, but Clang emits a warning otherwise.


But if it was my code, I'd write a more generic helper instead:

#include <utility>

template <typename R, typename N, typename F, N ...I>
[[nodiscard]] R GenerateForEach(std::integer_sequence<N, I...>, F &&func)
{
return {(void(I), func(std::integral_constant<N, I>{}))...};
}

template <typename R, auto N, typename F>
[[nodiscard]] R Generate(F &&func)
{
return (GenerateForEach<R, decltype(N)>)(std::make_integer_sequence<decltype(N), N>{}, std::forward<F>(func));
}

Then:

auto array_ = Generate<std::array<std::shared_ptr<int>, 4>, 4>([](auto){return std::make_shared<int>();});

While the lambda discards the index in this case, it often ends up being useful, especially given that in [](auto index){...}, index.value is constexpr.

Initialize a class with brace-enclosed initializer list

Initializer lists are more appropriate for situations where the size of the list is dynamic. In your case, where the size of Vec is a template parameter (i.e. static), you're probably better off with variadic parameters:

template <unsigned C>
struct Vec
{
Vec(){}

template<typename ... V>
Vec(V ... args)
{
static_assert(sizeof...(V) == C, "");
//...
}
};

then these will work:

Vec<3> myVec2;
myVec2 = {1, 2, 3};
Vec<3> myVec3 = {1,2,3};

But this one won't, as it explicitly requires an std::initializer_list:

Vec<3> myVec1{{1,2,3}};

How to pass in a brace-enclosed initializer list to a function?

Braced-init-list has no type and cause template argument deduction failing.

Non-deduced contexts

In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.


  1. The parameter P, whose A is a braced-init-list, but P is not std::initializer_list, a reference to one (possibly cv-qualified), or a reference to an array:

You can specify the template argument as std::initializer_list explicitly to bypass the deduction,

sayIt<std::initializer_list<std::string>>({"foo", "bar"});

Or add another overload taking std::initializer_list.

template <typename T>
void sayIt(std::initializer_list<T> stuff) {
sayIt<decltype(stuff)>(stuff);
}

call constructor of class with brace enclosed initilizer list

There's actually more constructors in your question than you let on. Here's two more, courtesy of your compiler:

A(A const&) = default;
A(A &&) = default;

Now the interesting bit is what happens when you write another initializer, A{1, 2, 3}. That calls your first user defined c'tor. Nothing novel there. But when you write A({1, 2, 3}), overload resolution has two options. Either construct a temporary A{1, 2, 3} and copy it. Or construct a vector for the other user provided c'tor.

In both cases it's a user defined conversion sequence. Neither is better than the other, so you get a nice flashing error due to ambiguity.

The solution? Either use brace initialization A{{1,2,3}}; or supply an initializer_list constructor, to better match the initializer list argument.


array initialization requires a brace-enclosed initializer list

If you have access to a C++11 compiler, here's one solution.

Remove the ( and ) from the initializer of value. Use:

Matrix4x4::Matrix4x4(T aa, T ba, T ca,
T ab, T bb, T cb,
T ac, T bc, T cc)
: value{ { aa, ba, ca, 0 },
{ ab, bb, cb, 0 },
{ ac, bc, cc, 0 },
{ 0, 0, 0, 1 } }
{
}

Brace-enclosed initializer for matrix constructor fail in type std::complex double

Your problem is not related to the initializer list. The problem is that this code

#include <iostream>
#include <complex>

int main()
{
std::complex<double> x = (1,2);
std::cout << x;
}

Is not doing what you expect it to do. Output is

(2,0)

Because (1,2) is the comma operator at work. std::complex<double> x = (1,2); is the same as std::complex<double> x = 2;.

You need to use curly braces for initialization:

#include <iostream>
#include <complex>

int main()
{
std::complex<double> x = {1,2};
std::cout << x;
}

Output

(1,2)

PS I would strongly advise you to use a std::vector<T> to hold the data rather than a T*. Currently copying a xxx will cause undefined behavior, due to not following the rule of 3/5.



Related Topics



Leave a reply



Submit