Enable default initializer list constructor - c++

I believe modern C++ initializer lists are very useful for initializing objects, to the point of removing the need for defining your own constructor:
struct point
{
float coord[3];
};
point p = {1.f, 2.f, 3.f}; // nice !
However, this doesn't work when my class inherits from another class:
template<typename T>
class serializable
{
protected:
serializable() = default;
...
// other stuff
}
struct point : public serializable<point>
{
float coord[3];
};
point p = {1.f, 2.f, 3.f}; // Doesn't work :(
I tried adding point() = default; to my point class, but that didn't work either. How can I still initialize point with an initializer list?

Your original case relied upon aggregate initialization [dcl.init.list]:
List-initialization of an object or reference of type T is defined as follows:
...
— Otherwise, if T is an aggregate, aggregate initialization is performed
Where an aggregate and aggregate initialiazation are, from [dcl.init.aggr], emphasis mine:
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or
protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list
are taken as initializers for the members of the aggregate, in increasing subscript or member order. Each
member is copy-initialized from the corresponding initializer-clause.
But now, since point has a base class (serializable<point>), point is no longer an aggregate and no longer supports aggregate initialization.
The solution is to simply provide such a constructor to initialize point:
struct point : public serializable<point>
{
template <typename... T>
point(T... ts)
: coord{ts...}
{ }
float coord[3];
};

Related

Initializing class members with {}

Someone gave me (part of) the following code:
struct MyStruct
{
int x = {};
int y = {};
};
I never saw this syntax before, what does initialization with {} mean?
This is default member initializer (since C++11),
Through a default member initializer, which is a brace or equals initializer included in the member declaration and is used if the member is omitted from the member initializer list of a constructor.
The initialization itself is copy-list-initialization (since C++11), as the effect, the data member x and y would be value-initialized (and zero-initialized as built-in type) to 0.
Since the C++11 standard there are two ways to initialize member variables:
Using the constructor initialization list as "usual":
struct Foo
{
int x;
Foo()
: x(0)
{
}
};
Use the new inline initialization where members are getting their "default" values using normal initialization syntax:
struct Foo
{
int x = 0;
};
Both these ways are for many values and types equivalent.

Initialization with empty curly braces

First attempt and everything works fine:
class Base {
public:
Base() {std::cout << "default ctor!\n"; }
};
...
Base b{};
Base b_one = {};
Another way of implementation(add explicit):
class Base {
public:
explicit Base() {std::cout << "default ctor!\n"; }
};
...
Base b{};
Base b_one = {}; // error! Why?
I have read on cppreference that in both cases default initialization would be used and no diffences.
From list initialization:
Otherwise, If the braced-init-list is empty and T is a class type with a default constructor, value-initialization is performed.
From value initialization:
if T is a class type with no default constructor or with a user-provided or deleted default constructor, the object is default-initialized;
I have read on cppreference that in both cases default initialization would be used and no diffences.
No they're not the same. To be precise, Base b{}; is direct-list-initialization, while Base b_one = {}; is copy-list-initialization; for copy-list-initialization, only non-explicit constructor may be called.
(emphasis mine)
direct-list-initialization (both explicit and non-explicit
constructors are considered)
copy-list-initialization (both explicit and non-explicit constructors
are considered, but only non-explicit constructors may be called)

Using initializer lists through inheritance

Is there a way to use initializer lists to initialize Bar?
struct Foo {
int i[2];
};
struct Bar : Foo {};
Foo f{0, 1}; // OK
Bar b{0, 1}; // error: no matching function for call to
// ‘Bar::Bar(< brace-enclosed initializer list>)’
Foo is an aggregate, and aggregates can be initialised using braced-enclosed lists performing aggregate initialisation. That is why Foo can be initialised using {0, 1} even without a constructor.
However, whether Bar is also an aggregate depends on the version of C++ which you're using. In C++14 and earlier, aggregates cannot have base classes, so Bar is not an aggregate. It therefore cannot be initialised using aggregate initialisation and would need an appropriate constructor. [Live example]
In C++17, the definition of "aggregate" was broadened to also include classes with non-virtual public base classes, so in C++17, Bar is an aggregate and indeed, can be initialised using aggregate initialisation: [Live example]
Since Foo is an aggregate type it's being initialized through aggregate initialization, which does not support base classes initialization as of c++14. But the good news are that it will be supported in c++17 :)
Example taken from cppreference:
// aggregate
struct base1 { int b1, b2 = 42; };
// non-aggregate
struct base2 {
base2() : b3(42) {}
int b3;
};
// aggregate in C++17
struct derived : base1, base2 { int d; };
derived d1{ {1, 2}, { }, 4}; // d1.b1 = 1, d1.b2 = 2, d1.b3 = 42, d1.d = 4
derived d2{ { }, { }, 4}; // d2.b1 = 0, d2.b2 = 42, d2.b3 = 42, d2.d = 4
It compiles, but with C++17 where aggregates can have base classes. This allows for list initialization of base data members. see here for more:
http://en.cppreference.com/w/cpp/language/aggregate_initialization
The effects of aggregate initialization are:
Each direct public base, (since C++17) array element, or non-static class member, in order of array subscript/appearance in the
class definition, is copy-initialized from the corresponding clause of
the initializer list.

initializer list constructor error with CRTP

I'm wetting my feet with C++11 and am really confused why this doesn't work:
template <class T>
struct A {
size_t size() const { return sizeof(T); }
};
struct B : A<B> {
int x;
int y;
};
B var {1, 5};
I'm using gcc 4.8.2 and get an error saying:
no matching function for call to 'B(<brace-enclosed initializer list>)'
It works just fine when I don't derive from A, so does the derivation somehow change the POD-ness of my struct B?
Aggregate-initialization requires your type to be an aggregate. An aggregate cannot have base classes:
An aggregate is an array or a class (Clause 9) with no user-provided
constructors (12.1), no private or protected non-static data members
(Clause 11), no base classes (Clause 10), and no virtual functions
(10.3).

Why do non-static data member initializers defeat uniform initialization syntax?

If all of your class/struct data members lack initializers, you can use uniform initialization syntax to construct the object.
struct foo
{
int i;
float f;
};
...
foo bar{ 5, 3.141f };
But if one or more members have initializers, uniform initialization syntax becomes invalid.
struct foo
{
int i;
float f = 0;
};
...
foo bar{ 5, 3.141f }; // Compiler error.
I surmise that the addition of a data member initializer automatically implements one or more default constructors and suppresses the default implementation of the initialization_list constructor. Is that the intended standard? Why does it work this way?
Yes, this is intended by the standard. What you are attempting here is aggregate initialization. Unfortunately, your second foo is no longer considered an aggregate due to the equal initializer of f. See 8.5.1 [dcl.init.aggr] (emphasis mine):
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equalinitializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
Because you have an equal initializer for the member f, you will need to provide a custom constructor to support the syntax you are after:
struct foo
{
int i;
float f = 0;
constexpr foo(int i, float f) : i(i), f(f) { }
};
...
foo bar{ 5, 3.141f }; // now okay
As to why this was specified in the standard, I have no idea.