Is initialization performed in the first statement int i;
or second statement i = 3;
of the function main
according to the C++ standard?
The first. The second statement is assignment, not initialization. The second statement marks the point "until that value is replaced ([expr.ass])" from your quotes of the standard.
If [initialization is the first statement] is so, is it really possible to separate storage allocation from object initialization?
Yes, but not in a such a simple example as yours. A common example that comes to mind is a std::vector
. Reserving capacity allocates storage space, but that storage is not initialized until an object is added to the vector.
std::vector<int> v; // Allocates and initializes the vector object.
v.reserve(1); // Ensures space has been allocated for an int object.
/*
At this point, the first contained element has space allocated, but has
not yet been initialized. If you want to do nutty things between allocation
and object initialization, this is the place to do it. Note that you are
not allowed to access the allocated space since it belongs to the vector.
You'd have to replicate the inner workings of a vector to do that...
*/
v.emplace_back(3); // Initializes the first contained object.
Quoting the standard
Short version:
There is nothing to quote because the standard does not explicitly prohibit all spurious actions. Compilers avoid spurious actions by their own volition.
Long version:
Strictly speaking, the standard does not guarantee that reserve()
does not initialize anything. The requirements imposed on reserve()
in [vector.capacity] are more focused on what must be done than on prohibiting spurious activity. The closest it comes to this guarantee is the requirement that the time complexity of reserve()
be linear in the size of the container (not in the capacity, but in the current size). This would make it impossible to always initialize everything that was reserved. However, a compiler could still choose to initialize a fixed number of reserved elements, say up to 10 million of them. As long as this limit is fixed, it counts as constant-time complexity, so is allowed by [vector.capacity].
Now let's get real. Compilers are designed to produce fast code, without introducing unnecessary, useless busywork. Compilers do not seek out the possibility of doing additional work simply because the standard does not prohibit it. Except for debug builds, no compiler is going to introduce an initialization when it it not required. The people who view the possibility as something worth considering are language lawyers who lose sight of the big picture. You don't pay for what you don't need. The question to ask here is not "Could you quote the standard supporting that no initialization happens?" but "Could you quote the standard supporting that no initialization is required?" Since the additional work of initialization is not required, it will not happen in practice.
Still, reality means little to some language lawyers, and this question does have that tag. To be thorough, I will demonstrate that it is "possible to separate storage allocation from object initialization" even if you happen to use a pathological, yet standards-compliant, compiler that was over-engineered by masochists. I need only one case to demonstrate "possible", so let's abandon int
for a more bizarre, yet fully legal, type.
The sole precondition for reserve()
is that the contained type can be move-inserted into the container. This precondition is satisfied by the following class.
class C {
// Default construction is not supported.
C() = delete;
public:
// Move construction is allowed, even outside this class.
C(C &&) = default;
};
I have designed this class to be rather hard to initialize. The only allowed construction is move-construction; in order to initialize an object of this type, you need to already have an object of this type. Who creates the first object? No one. No objects of this type can exist. However, one can still create a vector of these objects (an empty vector, but still a vector).
It is legal to define std::vector<C> v;
, and to follow that by a call to v.reserve(1);
. This allocates space (1 byte is needed on my system) for an object of type C
, and yet there is no possible initialization of this object. QED.