C++: how is construction by initialization list done? - c++

C++ allows instantiating a class, setting the public members' value via an initialization list, as the example below shows for b1:
class B {
public:
int i;
std::string str;
};
B b1{ 42,"foo" };
B b2();
; however, if I provide a constructor
B(int k) { }
, it won't compile.
So, what's going on behind the hood?
Is it that when no constructor is provide, the compiler will provide one? But how could it provide one with initialization list? I thought it will just provide a "blank" constructor taking no input, as the example shows for b2. Or does it provide both?

But how could it provide one with initialization list?
No, it won't.
Note that for list initialization B b1{ 42,"foo" };, aggregate initialization is performed.
If T is an aggregate type, aggregate initialization is performed.
And B is an aggregate type,
An aggregate is one of the following types:
array type
class type (typically, struct or union), that has
no private or protected non-static data members
no user-provided, inherited, or explicit constructors (explicitly defaulted or deleted constructors are allowed)
no virtual, private, or protected base classes
no virtual member functions
That's why B b1{ 42,"foo" }; works well.
And If you provide a user-defined constructor, B becomes non-aggregate type, then aggregate initialization won't work again. In this case, you can only initialize B like B b1{42}; or B b2(42);, which will call the appropriate constructor.
BTW: After providing a user-defined constructor (taking one parameter), the implicitly-declared default constructor won't be declared by the compiler again. It means B b2; or B b2{}; won't work again.
BTW2: B b2(); might be a function declaration. See Most vexing parse.

Related

C++ : Variadic Function + Private Constructor Loophole(?) [duplicate]

Let's say I have a type and I want to make its default constructor private. I write the following:
class C {
C() = default;
};
int main() {
C c; // error: C::C() is private within this context (g++)
// error: calling a private constructor of class 'C' (clang++)
// error C2248: 'C::C' cannot access private member declared in class 'C' (MSVC)
auto c2 = C(); // error: as above
}
Great.
But then, the constructor turns out to not be as private as I thought it was:
class C {
C() = default;
};
int main() {
C c{}; // OK on all compilers
auto c2 = C{}; // OK on all compilers
}
This strikes me as very surprising, unexpected, and explicitly undesired behavior. Why is this OK?
The trick is in C++14 8.4.2/5 [dcl.fct.def.default]:
... A function is user-provided if it is user-declared and not explicitly defaulted or
deleted on its first declaration. ...
Which means that C's default constructor is actually not user-provided, because it was explicitly defaulted on its first declaration. As such, C has no user-provided constructors and is therefore an aggregate per 8.5.1/1 [dcl.init.aggr]:
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).
You're not calling the default constructor, you're using aggregate initialization on an aggregate type. Aggregate types are allowed to have a defaulted constructor, so long as it's defaulted where it's first declared:
From [dcl.init.aggr]/1:
An aggregate is an array or a class (Clause [class]) with
no user-provided constructors ([class.ctor]) (including those inherited ([namespace.udecl]) from a base class),
no private or protected non-static data members (Clause [class.access]),
no virtual functions ([class.virtual]), and
no virtual, private, or protected base classes ([class.mi]).
and from [dcl.fct.def.default]/5
Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall provide implicit definitions for them ([class.ctor] [class.dtor], [class.copy]), which might mean defining them as deleted. A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed. [ Note: Declaring a function as defaulted after its first declaration can provide efficient execution and concise definition while enabling a stable binary interface to an evolving code base. — end note ]
Thus, our requirements for an aggregate are:
no non-public members
no virtual functions
no virtual or non-public base classes
no user-provided constructors inherited or otherwise, which allows only constructors which are:
implicitly declared, or
explicitly declared and defined as defaulted at the same time.
C fulfills all of these requirements.
Naturally, you may be rid of this false default construction behavior by simply providing an empty default constructor, or by defining the constructor as default after declaring it:
class C {
C(){}
};
// --or--
class C {
C();
};
inline C::C() = default;
Angew's and jaggedSpire's' answers are excellent and apply to c++11. And c++14. And c++17.
However, in c++20, things change a bit and the example in the OP will no longer compile:
class C {
C() = default;
};
C p; // always error
auto q = C(); // always error
C r{}; // ok on C++11 thru C++17, error on C++20
auto s = C{}; // ok on C++11 thru C++17, error on C++20
As pointed out by the two answers, the reason the latter two declarations work is because C is an aggregate and this is aggregate-initialization. However, as a result of P1008 (using a motivating example not too dissimilar from the OP), the definition of aggregate changes in C++20 to, from [dcl.init.aggr]/1:
An aggregate is an array or a class ([class]) with
no user-declared or inherited constructors ([class.ctor]),
no private or protected direct non-static data members ([class.access]),
no virtual functions ([class.virtual]), and
no virtual, private, or protected base classes ([class.mi]).
Emphasis mine. Now the requirement is no user-declared constructors, whereas it used to be (as both users cite in their answers and can be viewed historically for C++11, C++14, and C++17) no user-provided constructors. The default constructor for C is user-declared, but not user-provided, and hence ceases to be an aggregate in C++20.
Here is another illustrative example of aggregate changes:
class A { protected: A() { }; };
struct B : A { B() = default; };
auto x = B{};
B was not an aggregate in C++11 or C++14 because it has a base class. As a result, B{} just invokes the default constructor (user-declared but not user-provided), which has access to A's protected default constructor.
In C++17, as a result of P0017, aggregates were extended to allow for base classes. B is an aggregate in C++17, which means that B{} is aggregate-initialization that has to initialize all the subobjects - including the A subobject. But because A's default constructor is protected, we don't have access to it, so this initialization is ill-formed.
In C++20, because of B's user-declared constructor, it again ceases to be an aggregate, so B{} reverts to invoking the default constructor and this is again well-formed initialization.

"temporary of type 'A' has protected destructor", but its type is B

In the following code, compiled with Clang 8.0.0+ and -std=c++17, creating a derived class instance using B{} gives an error error: temporary of type 'A' has protected destructor. Why is A appearing in this message when the temporary has type B (and thus should have a public destructor)?
https://godbolt.org/z/uOzwYa
class A {
protected:
A() = default;
~A() = default;
};
class B : public A {
// can also omit these 3 lines with the same result
public:
B() = default;
~B() = default;
};
void foo(const B&) {}
int main() {
// error: temporary of type 'A' has protected destructor
foo(B{});
// ^
return 0;
}
This is a subtle issue of aggregate initialization before C++20.
Before C++20, B (and A) are aggregate types:
(emphasis mine)
no user-provided, inherited, or explicit constructors (explicitly defaulted or deleted constructors are allowed) (since C++17) (until C++20)
Then
If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default member initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates).
So B{} constructs a temporary object via aggregate initialization, which will initialize the base subobject directly with empty list, i.e. perform aggregate initialization to construct the A base subobject. Note that the constructor of B is bypassed. The problem is that in such context, the protected desctructor can't be called to destroy the directly constructed base subobject of type A. (It doesn't complain about the protected constructor because it's bypassed by the aggregate initialization of A too.)
You can change it to foo(B()); to avoid aggregate initialization; B() performs value-initialization, the temporary object will be initialized by B's constructor, then anything is fine.
BTW since C++20 you code will work fine.
no user-declared or inherited constructors (since C++20)
B (and A) are not aggregate types again. B{} performes list initialization, and then the temporary object is initialized by B's constructor; the effect is just same as B().

Why doesn't a class having private constructor prevent inheriting from this class? How to control which classes can inherit from a certain base?

class B {
private:
friend class C;
B() = default;
};
class C : public B {};
class D : public B {};
int main() {
C {};
D {};
return 0;
}
I assumed that since only class C is a friend of B, and B's constructor is private, then only class C is valid and D is not allowed to instantiate B. But that's not how it works. Where am I wrong with my reasoning, and how to achieve this kind of control over which classes are allowed to subclass a certain base?
Update: as pointed out by others in the comments, the snippet above works as I initially expected under C++14, but not C++17. Changing the instantiation to C c; D d; in main() does work as expected in C++17 mode as well.
This is a new feature added to C++17. What is going on is C is now considered an aggregate. Since it is an aggregate, it doesn't need a constructor. If we look at [dcl.init.aggr]/1 we get that an aggregate is
An aggregate is an array or a class with
no user-provided, explicit, or inherited constructors ([class.ctor]),
no private or protected non-static data members (Clause [class.access]),
no virtual functions, and
no virtual, private, or protected base classes ([class.mi]).
[ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors.  — end note ]
And we check of all those bullet points. You don't have any constructors declared in C or D so there is bullet 1. You don't have any data members so the second bullet doesn't matter, and your base class is public so the third bullet is satisfied.
The change that happened between C++11/14 and C++17 that allows this is that aggregates can now have base classes. You can see the old wording here where it expressly stated that bases classes are not allowed.
We can confirm this by checking the trait std::is_aggregate_v like
int main()
{
std::cout << std::is_aggregate_v<C>;
}
which will print 1.
Do note that since C is a friend of B you can use
C c{};
C c1;
C c2 = C();
As valid ways to initialize a C. Since D is not a friend of B the only one that works is D d{}; as that is aggregate initialization. All of the other forms try to default initialize and that can't be done since D has a deleted default constructor.
From What is the default access of constructor in c++:
If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted. An implicitly-declared default constructor is an inline public member of its class.
If the class definition does not explicitly declare a copy constructor, one is declared implicitly. [...] An implicitly-declared copy/move constructor is an inline public member of its class.
Constructors for classes C and D are generated internally by compiler.
BTW.: If you want to play with inheritance, please make sure you have virtual destructor defined.

I'm confused about this C++ constructor

I tried searching for an answer, but not sure exactly the best terms to use to describe this...
I am reading a book on SFML programming, and one of the examples has me confused with the usage of the constructor.
Lets say we have class A and class B. Class A has a member variable of type B (memberB). The constructor for A looks like:
A::A() : OtherMemberType(with, params), memberB()
{...}
Given that memberB is being initialized with the default constructor in the initialization list, what is the purpose of explicitly listing it in the list? Wouldn't the same effect be had without including it in the list?
Thanks
EDIT: Thanks for the answers. I have now learned the (basic) difference of value-initialization vs. default-initialization.
For more context, since the idea of "class B may be broken was brought up", here is the code example from the text SFML Game Development:
class Game
{
public:Game();
void run();
private:
void processEvents();
void update();
void render();
private:
sf::RenderWindow mWindow;
sf::CircleShape mPlayer;
};
Game::Game()
: mWindow(sf::VideoMode(640, 480), "SFML Application")
, mPlayer()
{
mPlayer.setRadius(40.f);
mPlayer.setPosition(100.f, 100.f);
mPlayer.setFillColor(sf::Color::Cyan);
}
So with that context, does anyone know some of the specifics of SFML? Is sf::CircleShape "broken", or is this a redundant call to the default constructor?
Adam
Initializing the member in the initializer list value-initializes it. Omitting it from the list default-initializes it,
If B is a non-aggregate and has a default constructor, there is no difference.
If B is an aggregate, then there may be a difference. default-initializing it means if it contains built-ins these may not get initialized. value-initializing it would eventually have the effect of zero-initializing its members.
This is an example where the semantics of the initialization would be different:
struct B
{
int i, j, k;
};
struct A
{
A() : b() {} // value-initializes b: b.i, b.j, b.k zero initialized
B b;
};
struct AA
{
AA() {} // default-initializes b: b.i, b.j, b.k have no initialization
B b;
};
By including it in the initialiser list, the member is value-initialised. If it weren't, it would be default-initialised. Whether there's a difference depends on the type.
If it's a class type with a declared default constructor, then there's no difference: that constructor will be used in either case.
Otherwise, value-initialisation will zero-initialise primitive types (and primitive members of class types), while in some circumstances default-initialisation will leave them uninitialised, with an indeterminate value.
UPDATE: In your specific case, the class does have a default constructor, so the explicit initialisation is redundant. But redundancy isn't necessarily a bad thing - it indicates that it's being deliberately value-initialised, not just forgotten about.
Given what mike and juan have said, I'd say that class B's implementation is broken iff it requires to be value-initialized like that unless it'd be reasonably expected to behave that way.
Generally, given a properly designed class - with a user-provided default constructor iff if has POD members - there should be no difference in behavior between value- and default-initializing the member of type B.
Some special classes may not perform zero-initialization of their members and they may lack a default constructor. std::array is one such class. They attempt to retain the performance of the raw type underlying their implementation. Members of such classes will require the value initialization.
There are several possibilities:
Class B is has the usual behavior the value initialization is superfluous. Specifically:
a) class B has no POD-typed members, and the non-POD typed member types are all implemented in line with possibility #1, or
b) class B's user-written default constructor initializes all POD-typed members as appropriate.
Class B has the semantics of a performance-optimized type, such as a numerical type or a replacement for raw C arrays. It lacks a default constructor and won't initialize unless you perform value initialization. Example: std::array<T> where T is POD.
Class B is a template parameter. In absence of any constraints on B, the value initialization is the only safe choice. B could be std::array, after all.
Class B is broken. Its members will be properly initialized if its instances are value-initialized. It needs to be fixed.

inheriting constructors of class virtually derived.

I came across this question which asks its output.
#include<iostream>
using namespace std;
class A{
public:
int i;
A(int j=3):i(j){}
};
class B:virtual public A{
public:
B(int j=2):A(j){}
};
class C:virtual public A{
public:
C(int j=1):A(j){}
};
class D:public B, public C {
public:
D(int j=0):A(j), B(j+1), C(j+2){}
};
int main()
{
D d;
cout<<d.i;
return 0;
}
There are few things which I did not understand.
Please clarify these doubts.I could not google it as I did not know what to search for.
Q1. As in the code a parameterised constructor is used.Just after the colon(:) we write the constructor of the parent class.How
A(int j=3):i(j){}
is used? As i is not a class.
Q2. In the class D, the constructor for the class is using the constructors for initialising the base classes.But as it can be seen that all the constructors modify variable i of the class A only.
Then what is the sequence of the constructor calling here.
I know when we do not call the constructor of the parent class, it is explicitly called and the order is well known, but what when we implicitly call the constructor like here.
Q3. Inspite of the parameters being initialized, the value which we send in the constructor seems to make a difference.Why is it so ?
A1. :i(j) in A(int j=3):i(j){} is an initializer list. Initializer lists can specify how parent classes and member variables are initialized. (j) is the initializer for i and behaves similarly to initialization for a local variable: int i(j);. (you may be more familiar with the initialization syntax int i = j; which is similar. You can't use the = syntax in initializer lists.)
A2. Virtual base classes are always initialized exclusively by the most derived class's constructor. So D's constructor initializes its A base class and when D's constructor calls the constructors for B and C those constructors do not reinitialize A.
A3. The syntax D(int j=0) does not initialize the parameter. Instead it declares a default value for the parameter which is ignored whenever you explicitly pass a value for that parameter.
The ctor-initializer-list contains the initializers for all subobjects. That means base class subobjects and member subobjects.
Subobjects are initialized in the order in which they appear in the class definition, always. it doesn't matter what order you put them in the ctor-initializer-list (although putting them in any other order is confusing when the order is ignored).
Inherited base subobject constructors are called by the constructor of the class which is directly derived... except for virtual base subobjects, which are called directly from the constructor for the most-derived object. Virtual base subobjects are constructed before anything else.
There's no such thing as "parameters being initialized". Those are default arguments, and they are ignored when an actual argument is provided.
1) The colon : can be used in a constructor to call the constructors of member variables with the given arguments:
public:
int i;
A(int j=3):i(j){}
means that A's member i will call its constructor with j as the argument.
2) Subobjects will be initialized in the order they appear in the class definition
class D:public B, public C
3) They weren't initialized, they were given default arguments.
Q1:
That code in the constructor between the signature of the function and before the opening bracket of the function body is called an initializer list.
The syntax of an initializer list is a listing of member variables of the class with corresponding initial values. The initial values are in parenthesis. The member variables are separate by comma's. The first variable comes after a colon.
An initializer list can also include a constructor to base class, which is what your B and C class do. Your 'A' class simply uses the form that initializes a member variable.
Q2:
To find the order that the constructors are executed in, put in some print statements. It will help you understand the order of construction. Also put in a destructor and put print statements in those too. I can't do all of your homework for you on this question. :)
Q3:
I guess by parameters being initialized you mean the default arguments? Default arguments are always always overridden if a value is actually passed into the function.