When I try to compile the following code:
class a {
int i;
public :
a(int);
};
class b {
a mya;
int j;
public:
b(int);
};
a::a(int i2) {
i=i2;
}
b::b(int i2) {
mya=a(i2);
j=2*i2;
}
int main() {
}
I get the following errors:
prog.cpp:21:12: error: no matching function for call to ‘a::a()
b::b(int i2) {
^
prog.cpp:17:1: note: candidate: ‘a::a(int)
a::a(int i2) {
^
prog.cpp:17:1: note: candidate expects 1 argument, 0 provided
prog.cpp:1:7: note: candidate: ‘constexpr a::a(const a&)’
class a {
^
prog.cpp:1:7: note: candidate expects 1 argument, 0 provided
prog.cpp:1:7: note: candidate: ‘constexpr a::a(a&&)
prog.cpp:1:7: note: candidate expects 1 argument, 0 provided
It seems that a constructor with no argument for the class a is expected. I do not understand why, the only time I create a object of type a, I call the constructor which takes an int as argument.
I understand that the solution would be to add a constructor without arguments for a. But why ?
Thank you for your answers, best regards,
Jerome
(All ISO Standard references below refer to N4659: March 2017 post-Kona working draft/C++17 DIS)
As per [class.base.init]/9, the mya
member of b
, which is of type a
, is default-initialized, but a
defines no default constructor:
In a non-delegating constructor, if a given potentially constructed subobject 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), then
- (9.1) if the entity is a non-static data member that has a default member initializer [...] the entity is initialized from its default member initializer as specified in [dcl.init];
- (9.2) otherwise, if the entity is an anonymous union or a variant member ([class.union.anon]), no initialization is performed;
- (9.3) otherwise, the entity is default-initialized.
Here, as mya
does is not declared along with a default member initializer, [class.base.init]/9.3 applies.
The example of [class.base.init]/9 even cover this particular case:
[...] [ Example:
struct A { A(); }; struct B { B(int); }; struct C { C() { } // initializes members as follows: A a; // OK: calls 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 };
— end example ]
You can resolve it either by providing a default member initializer for mya
, such that [class.base.init]/9.1 applies
class b {
a mya{42}; // default member initializer
int j;
public:
b(int);
};
or, use a member initializer list in the definition of the constructor of b
; b::b(int)
, such that [class.base.init]/7 applies:
The expression-list or braced-init-list in a mem-initializer is used to initialize the designated subobject (or, in the case of a delegating constructor, the complete class object) according to the initialization rules of [dcl.init] for direct-initialization. [ Example:
struct B1 { B1(int); /* ... */ }; struct B2 { B2(int); /* ... */ }; struct D : B1, B2 { D(int); B1 b; const int c; }; D::D(int a) : B2(a+1), B1(a+2), c(a+3), b(a+4) { /* ... */ } D d(10);
— end example ] [...]
thus direct-initializing the mya
member:
b::b(int i2) : mya(i2) {
// ^^^^^^^- member initializer list
j=2*i2;
}