Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using move semantics with std::pair or std::tuple

Suppose you want to take advantage of move semantics, but one of your movable classes needs to be part of an std::pair. The purpose would be to create a function that returns an std::pair that can be treated as an rvalue, and forwarded along.

But I can't see how this can be done, unless an internal change to std::pair itself is made, to make it aware of move semantics.

Consider the following code:

struct Foo
{
 Foo() { }

 Foo(Foo&& f) { }

 private:

 Foo(const Foo& f) { } // do not allow copying
};

int main() 
{
 Foo f;
 std::pair<Foo, int> res = std::make_pair(f, 10); // fails due to private copy constructor
}

The problem is that std::make_pair, as well as the std::pair constructor itself, takes two objects and tries to make internal copies of them. This causes it to try and invoke the copy constructor. But in my example, I want to be able to move the new pair into res, and ensure that no copies are made. I would think this wouldn't be possible unless std::pair itself had the following constructor defined internally:

pair(T1&& t1, T2&& t2) : first(std::move(t1)), second(std::move(t2))

But it doesn't, at least not on the compiler I'm using (gcc 4.3.2). It may be that my compiler is simply out-of-date, and newer versions in fact will have this move-aware constructor. But my understanding of move semantics is somewhat flaky at the moment, so I'm not sure if I'm simply overlooking something here. So, is what I'm trying to accomplish possible, without actually reimplementing std::pair? Or is my compiler just out of date?

like image 823
Channel72 Avatar asked Nov 04 '10 14:11

Channel72


People also ask

Is std :: pair a tuple?

A tuple is an object capable to hold a collection of elements where each element can be of a different type. The class template needs the header <tuple> . std::tuple is a generalization of std::pair. You can convert between tuples with two elements and pairs.

Does std :: move do anything?

std::move. std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t . It is exactly equivalent to a static_cast to an rvalue reference type.

Is std :: move required?

std::move itself does "nothing" - it has zero side effects. It just signals to the compiler that the programmer doesn't care what happens to that object any more. i.e. it gives permission to other parts of the software to move from the object, but it doesn't require that it be moved.

Should I return std :: move?

std::move is totally unnecessary when returning from a function, and really gets into the realm of you -- the programmer -- trying to babysit things that you should leave to the compiler.


2 Answers

That's not the std::pair constructor that will be called, though. The std::pair move constructor will be called, and the move constructor should do exactly what you expect (N3126 20.3.5.2/6):

template<class U, class V> pair(pair<U, V>&& p);

Effects: The constructor initializes first with std::move(p.first) and second with std::move(p.second).

However, your example should fail because in std::make_pair(f, 10);, f is an lvalue and needs to be explicitly moved, otherwise it is copied. The following should work:

std::pair<Foo, int> res = std::make_pair(std::move(f), 10);
like image 158
James McNellis Avatar answered Oct 19 '22 12:10

James McNellis


GCC 4.3.2 must not have a complete implementation. pair (and tuple) shall have move constructors:

template<class U, class V> pair(U&& x, V&& y);
Effects: The constructor initializes first with std::forward(x) and second with std::forward(y).
template<class U, class V> pair(pair<U, V>&& p);
Effects: The constructor initializes first with std::move(p.first) and second with std::move(p.second).

(From [pairs.pair] in n3126)

like image 43
Steve M Avatar answered Oct 19 '22 12:10

Steve M