If one copies one std::vector
into another, or copies elements of a std::vector
in a larger (reserve
) or smaller (shrink_to_fit
) block of heap memory, what constructor of the element's type is called?
In the example program:
#include <vector>
#include <iostream>
struct A {
A() {}
A(A&) { std::cout << "A(A&) "; }
A(const A&) { std::cout << "A(const A&) "; }
};
int main() {
std::vector<A> v(1);
v.reserve(10);
auto w = v;
v.shrink_to_fit();
}
I would expect to see A(const A&) A(const A&) A(const A&)
output. But in reality the standard library implementations diverge:
libc++
prints A(const A&) A(A&) A(const A&)
,libstdc++
prints A(const A&) A(const A&) A(A&)
,A(A&) A(A&) A(A&)
.Online demo: https://gcc.godbolt.org/z/TTqxv9sd3
Is it right to assume that if the constructor from not-const
lvalue is called, then the user code is allowed modifying its argument (at least temporarily)?
I do not see any reason that the library would be required to ensure that the const
overload is used.
The container copy constructor requires per [container.reqmts]/12 that the element type is CopyInsertable from a lvalue v
of its type into the container regardless of the const
-qualification of v
. It also requires the postcondition ([container.alloc.reqmts]/2.4):
The value of v is unchanged and is equivalent to *p.
implying that if the non-const
copy constructor is used, then it must not result in the value of the original object being different after the call, at least.
It is a bit less clear to me whether modification of the argument is completely forbidden, because this is stated as postscondition only. I suspect that this is the intent though.