This is a followup on the answers for placement new on a class with reference field.
Calling std::vector<A>::data()
on type A
that has reference or const fields, returns a pointer to objects that may be changed through the original vector by placement new, which causes a const or reference field of an original object to be replaced, while still being managed by another pointer, returned via the call to data()
.
For example:
struct A {
const int i = 0;
};
int main() {
std::vector<A> vec = {{1}, {2}};
auto ptr = vec.data();
std::cout << ptr[1].i << std::endl; // 2
vec.pop_back();
vec.push_back({3}); // placement new, inside
std::cout << ptr[1].i << std::endl; // 3
}
C++17 tried to resolve such issues by introducing std::launder
but it was later agreed that while std::launder
may solve other issues, it doesn't really solve the problem for above use case as noted in NB US042.
Some questions - for C++ versions prior to C++20:
Since NB US042 was accepted as a change for C++20 spec, but not marked as a DR - would it be advised, according to the spec only, to avoid the use of
std::vector<A>::data()
on a typeA
that has reference or const fields as in above example?Or, the wording of the spec for
std::vector<>::data()
covers that, making it legal and leaving the implementability question to the library implementers?If it is the latter, what can the library do to make it legal?
If it can't really do anything useful to make it legal, is it UB before C++20?
If it is UB before C++20, why wasn't this change considered to be a candidate for a DR, same as p0593r6? Most probably compilers do the right thing anyway, why not mandate that retroactively?
pop_back
, the nextpush_back
is applied to a "fresh" (unoccupied) memory.std::launder
was introduced in c++17 to fix some problems where placement new is used on a memory that is currently occupied by a different object. I wonder if a realistic example of the problem ever exists, with.data()
. Let me reiterate: placement new on an unoccupied memory should work fine, why should it not?. – Donahuepop_back
, the nextpush_back
is applied to a "fresh" (unoccupied) memory" - that's incorrect. – Archaismpush_back
s will reallocate memory, if youpop_back
once and thenpush_back
once, that particularpush_back
will never allocate any memory for the vector. – Archaismpush_back
just afterpull_back
destroy or modify in an uncontrollable manner a vaild object? – Donahuepull_back
- I'll assume you meantpop_back
.pop_back
correctly destroyes the last element (calls appropriate destructos). The memory is still allocated. Then, if we callpush_back
a placementnew
call will be made to construct another object in the storage that was occupied by the object that we previoulsly destroyed. Not sure whether that answers your question. What do you mean by "destroy or modify in an uncontrollable manner"? – Archaismp+1
and the secondp+1
are two different pointers. One only ever refers to the old object, and gets destroyed at the end of the expression. The other one only ever refers to the new object.p
itself doesn't have any problem, the object it points to never changes. OTOH if you savep+1
to a named variable, this is (or should be) illegal regardless of whether there are any const or reference subobjects, because an invalidated pointer should never be revalidated back. – Bridieptr[1].i
so you may not see that the value was changed. – Rasbora