skypjack
skypjack

Reputation: 50548

std::launder, std::vector and move constructible only types

For a feature request of a project of mine , I thought/I've been suggested to use std::launder to move elements around in a vector where the elements are move constructible only (no move assignment operator defined).
The goal is to move the last element to a given position i, then pop the former.

In other terms, the resulting code would be something like this (simplified version for the purpose):

void foo(std::vector<my_type> &vec, std::size_t i) {
    my_type *el = vec.data() + i;
    el->~my_type();
    new (std::launder(el)) my_type{std::move(vec.back())};
    vec.pop_back();
}

Where my_type is defined as:

struct my_type { const int v; };

The purpose of using std::launder is to set a barrier against the possible optimization due to the fact that v within my_type is const. Therefore to be able to recycle the memory reserved for vec[i] so as to replace the contained object with a different instance (actually, I'm theoretically moving the last item to a different position).

Is this a valid approach/solution or is this still UB, as it would happen if I did something similar without recurring to std::launder?

As far as I understood it, the purpose of std::launder is to allow users to do things like this one and thus it seems to me that the above snippet is legal. But I wouldn't be surprised if I was wrong, so I'd like to have a feedback by someone more experienced than me

--- EDIT

Similarly I want also to swap to elements of the same type.
The resulting code should be something like this:

const auto tmp = std::move(vec[i]);
vec[i].~object_type();
new (std::launder(&vec[i])) object_type{std::move(vec[j])};
vec[j].~object_type();
new (std::launder(&vec[j])) object_type{std::move(tmp)};

I'm quite confident that if std::launder fits with the first example, this one should work as well for similar reasons. Am I wrong?

Upvotes: 1

Views: 266

Answers (1)

T.C.
T.C.

Reputation: 137385

launder gets you a pointer to an object that's already there.

There's no object there. You just destroyed it in the previous line.

Even the call to launder itself is pure unadulterated UB.

For this kind of thing, launder is needed when you access the newly created object. Not when you create it.

Upvotes: 4

Related Questions