I would like to know where I can find some documentation about the following behavior:
class Foo {
public:
Foo(int argX) : Foo(argX, defaultYValue) {}
Foo(int argX, int argY) : x(argX), y(argY) {};
private:
const int x;
const int y;
const int defaultYValue = -1;
}
Might it be possible that y value is undefined ? Or is there some documentation in the standard that tells that it works (I did noticed that the default member initialization is discarded if it is otherwise overridden inside the constructor)
PS: this was discovered while forgetting the static for defaultYValue.
CodePudding user response:
Yes, the code has undefined behavior. When using a delegating constructor it is the delegating constructor that will initialize the class members. When you pass defaultYValue
to the delegating constructor, it has not yet be initialized so you are passing an uninitialized value to the delegate, and said delegate uses that value to set y
.
This is called out by [class.base.init]/7
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.
CodePudding user response:
As pointed out in the other answers at the time you use defaultYValue it has not yet been initialized. You can fix that by making it satic:
class Foo {
public:
Foo(int argX) : Foo(argX, Foo::defaultYValue) {}
Foo(int argX, int argY) : x(argX), y(argY) {};
private:
const int x;
const int y;
static const int defaultYValue = -1;
}
But never write 2 constructors when one will suffice:
class Foo {
public:
Foo(int argX, int argY = defaultYValue) : x(argX), y(argY) {};
private:
const int x;
const int y;
static const int defaultYValue = -1;
}
But really why define a constant when it is only ever used once? It's clear from the use that it is the default value for y. There is no additional information gained from naming it defaultYValue
:
class Foo {
public:
Foo(int argX, int argY = -1) : x(argX), y(argY) {};
private:
const int x;
const int y;
}
CodePudding user response:
I would like to know where I can find some documentation about the following behavior:
This is called delegation and the constructor Foo::Foo(int)
is called a delegating constructor.
From class.base.init-6
A mem-initializer-list can delegate to another constructor of the constructor's class using any class-or-decltype that denotes the constructor's class itself. If a mem-initializer-id designates the constructor's class, it shall be the only mem-initializer; the constructor is a delegating constructor, and the constructor selected by the mem-initializer is the target constructor. The target constructor is selected by overload resolution. Once the target constructor returns, the body of the delegating constructor is executed. If a constructor delegates to itself directly or indirectly, the program is ill-formed, no diagnostic required.
Might it be possible that y value is undefined ?
Yes, the program has undefined behavior.
From class.base.init-7:
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.
This means that the target constructor Foo::Foo(int, int)
is used to initialize the data members. But since you're passing a data member defaultYValue
which has not yet been initialized, we have undefined behavior.