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).
Related
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.
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).
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.
The compiler seems to have no problem with this. Can I safely assume that any object I create of this type will have these defaults values?
struct ColorProperties
{
bool colorRed = true;
bool colorBlue = false;
bool isRectangle = true;
};
ColorProperties myProperties;
Will myProperties automatically contain element values as noted by the struct?
Yes, you can. It's C++11 feature. Really it's equal to
struct ColorProperties {
ColorProperties()
: colorRed(true), colorBlue(false), isRectangle(true)
{}
//
};
You can read about this proposal here
Quotes from standard.
n3376 12.6.2/8
In a non-delegating constructor, if a given non-static data member or base class is not designated by a
mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no
ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
— if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized
as specified in 8.5;
struct A {
A();
};
struct B {
B(int);
};
struct C {
C() { }
A a;
const B b; // error: B has no default constructor
int i; // OK: i has indeterminate value
int j = 5; // OK: j has the value 5
};
I have the following class definitions in c++:
struct Foo {
int x;
char array[24];
short* y;
};
class Bar {
Bar();
int x;
Foo foo;
};
and would like to initialize the "foo" struct (with all its members) to zero in the initializer of the Bar class. Can this be done this way:
Bar::Bar()
: foo(),
x(8) {
}
... ?
Or what exactly does the foo(x) do in the initializer list?
Or is the struct even initialized automatically to zero from the compiler?
First of all, you should (must !) read this c++ faq regarding POD and aggregates. In your case, Foo is indeed a POD class and foo() is a value initialization :
To value-initialize an object of type
T means:
if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor
for T is called (and the initialization is ill-formed if T has no accessible default constructor);
if T is a non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized;
if T is an array type, then each element is value-initialized;
otherwise, the object is zero-initialized
So yes, foo will be zero-initialized. Note that if you removed this initialization from Bar constructor, foo would only be default-initialized :
If no initializer is specified for an
object, and the object is of (possibly
cv-qualified) non-POD class type (or
array thereof), the object shall be
default-initialized; if the object is
of const-qualified type, the
underlying class type shall have a
user-declared default constructor.
Otherwise, if no initializer is
specified for a nonstatic object, the
object and its subobjects, if any,
have an indeterminate initial
value;
In standard C++ you need to make a ctor for Foo.
struct Foo {
Foo(int const a, std::initializer_list<char> const b, short* c)
: x(a), y(c) {
assert(b.size() >= 24, "err");
std::copy(b.begin(), b.begin() + 24, array);
}
~Foo() { delete y; }
int x;
char array[24];
short* y;
};
class Bar {
Bar() : x(5), foo(5, {'a', 'b', ..., 'y', 'z'},
new short(5)) { }
private:
int x;
Foo foo;
};
In C++0x you may use uniform initialization list, but still you need dtor for Foo:
class Bar {
Bar() : x(5), foo{5, new char[24]{'a', 'b', ..., 'y', 'z'},
new short(5)} { }
~Bar() { delete[] foo.array; delete foo.y;}
}
private:
int x;
Foo foo;
};
To default initialize foo (as Bar() : foo(), x(8) { }) you need to give Foo a default ctor.