Sprite
Sprite

Reputation: 3763

How to move a non-copyable element from std::set to std::map using extract?

Consider the following:

#include <set>
#include <map>

struct MyClass
{
    MyClass(int i);
    MyClass(MyClass const&) = delete;
    ~MyClass();

    bool operator<(const MyClass& r) const { return v < r.v; }
    int v;
};

void map_set_move_test()
{
    std::set<MyClass> s1, s2;
    std::map<int, MyClass> m1;

    s1.emplace(123);

    s2.insert(std::move(s1.extract(s1.begin())));

    // This fails
    m1.insert(std::move(std::make_pair(1, std::move(s2.extract(s2.begin()).value()))));
}

I used std::set::extract to successfully move an element from std::set to another std::set, as in:

s2.insert(std::move(s1.extract(s1.begin())));

But the compiler doesn't allow moving the element to std::map this way:

m1.insert(std::move(std::make_pair(1, std::move(s2.extract(s2.begin()).value()))));

Any help would be appreciated. Link to compiler explorer.

Upvotes: 3

Views: 249

Answers (2)

einpoklum
einpoklum

Reputation: 131546

This is a problematic example, because MyClass is not merely non-copyable, but implicitly non-movable - which is probably not what you meant. (I think MyClass becomes immovable as a result of you explicitly deleting the copy constructor; see this answer.)

If you do make MyClass movable, like so:

class MyClass : NonCopyable
{
public:
    MyClass(int i) : v(i) {
        std::cout << "constructor" << std::endl;
    }
    MyClass(MyClass&& other) : v(other.v) {
        std::cout << "move constructor" << std::endl;
    }
    ~MyClass() {
        std::cout << "destructor" << std::endl;
    }
    bool operator<(const MyClass& r) const {
        return v < r.v;
    }

    int v;
};

all seems to go well, when you write, say.

m1.emplace(
    1,
    std::move( 
        s2.extract(s2.begin()).value() 
    )
);

You can see this working on GodBolt.


PS - As @DavisHerring suggests, you might want to use the try_emplace() method instead, if you don't want to overwrite an existing value.

Upvotes: 4

Davis Herring
Davis Herring

Reputation: 39818

The point of extract and the corresponding overload of insert is to manipulate node-based containers without memory allocation. (One interesting such manipulation is to change std::map keys.) That cannot work here because the nodes are different types (viz., one has a key and the other doesn’t), so you need to just use the value with a regular insert (or the more ergonomic try_emplace).

Upvotes: 3

Related Questions