Using initializer lists through inheritance - c++

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.

Related

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

Enable default initializer list constructor

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

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.

Constructor for Struct with Private Members

Consider the following code:
class A
{
private:
struct B { private: int i; friend class A; };
public:
static void foo1()
{
B b;
b.i = 0;
}
static void foo2()
{
B b = {0};
}
};
Why does foo1 work but not foo2? Is not the struct initializer constructor visible for class A? Is there anyway to make this work in C++11?
(Note, removing the private makes foo2 working.)
Why does foo1 work but not foo2? Is not the struct initializer constructor visible for class A?
B b = {0};
Does not work because B is not an Aggregate. And it is not an Aggregate because it has an non-static private data member. If you remove the private specifier, B becomes an Aggregate and hence can be initialized in this manner.
C++03 Standard 8.5.1 Aggregates
Para 7:
If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be value-initialized (8.5).
[Example:
struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form int(), that is,0. ]
C++03 standard 8.5.1 §1:
An aggregate is an array or a class (clause 9) with no user-declared
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 a structure has c-tor, why can't I statically initialize it?

My question: When a structure has c-tor, why can't I statically initialize it ?
My compiler claims :
type `myStruct' must be initialized by constructor, not by `{...}'
Why is that ? I'm using gcc version 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)
To illustrate, here is the struct that is rejected by the compiler.
struct myStruct
{
int a;
double b;
myStruct() { a= 0; b = 0.0; }
}
void main()
{
myStruct ms = {7, 7.7}; // Now this compiler does not accept.
}
The inclusion of a user-defined c-tor means it's no longer an aggregate type. This would also be the case if there was no user-defined c-tor for the struct itself, but you have a non-static data-member of the struct that is not a POD or aggregate type.
Because the language specifies it that way...
The reason is that the constructor is the designated way to initialize the object to a valid state, so just dumping values into fields directly makes no sense.The ideas is that you either have a collection of values, or a self contained object but what you want to do would make it a little of both.
Only aggregates may be initialized with an initializer list. Per 8.5.1:1, the inclusion of a user-provided constructor prevents a struct or class from being an aggregate:
c++11
8.5.1 Aggregates [dcl.init.aggr]
1 - An aggregate is an array or a class (Clause 9) with no user-provided constructors [...]
In C++03,
c++03
8.5.1 - Aggregates [dcl.init.aggr]
1 - An aggregate is an array or a class (clause class) with no user-declared constructors [...]
Aggregates are distinct from PODs (c++11 9:10); not all aggregates are POD and not all PODs are aggregates; a class with a user-provided destructor could be an aggregate but not a POD, while a class with a non-copy non-default constructor could be POD but not an aggregate.
Demonstration:
#include <type_traits>
#include <iostream>
struct non_pod_aggregate { int i, j; ~non_pod_aggregate() {} };
struct non_aggregate_pod { int i, j; non_aggregate_pod(int) {}
non_aggregate_pod() = default; };
int main() {
std::cout << std::is_pod<non_pod_aggregate>::value << '\n'; // false
std::cout << std::is_pod<non_aggregate_pod>::value << '\n'; // true
non_pod_aggregate{0, 0};
// non_aggregate_pod{0, 0}; // does not compile
}
In C++03, all PODs (c++03 9:4) are aggregates, but it is still possible to have aggregates that are not PODs; as above, a user-provided destructor is enough to disqualify a struct from being POD.
In C++03 list-initialization only works for aggregates. You need to change your code to call the constructor:
myStruct ms;
If you want to be able to specify values for all of the members, you need to add a constructor taking enough arguments:
struct myStruct
{
int a;
double b;
myStruct() : a(), b() { }
myStruct(int a, double b) : a(a), b(b) { }
};
void main()
{
myStruct ms(7, 7.7);
}