23

In C++11, we can get an efficiency boost by using std::move when we want to move (destructively copy) values into a container:

SomeExpensiveType x = /* ... */;
vec.push_back(std::move(x));

But I can't find anything going the other way. What I mean is something like this:

SomeExpensiveType x = vec.back(); // copy!
vec.pop_back(); // argh

This is more frequent (the copy-pop) on adapter's like stack. Could something like this exist:

SomeExpensiveType x = vec.move_back(); // move and pop

To avoid a copy? And does this already exist? I couldn't find anything like that in n3000.

I have a feeling I'm missing something painfully obvious (like the needlessness of it), so I am prepared for "ru dum". :3

Raedwald
  • 46,613
  • 43
  • 151
  • 237
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • What implications does the move method have on variables that go out of scope? For instance, if I create an object, add it to a member container, then the object goes out of scope... Since no copy was made, is the object in the member container still defined? – DigitalZebra Jan 26 '10 at 22:19
  • Yes, it was moved into the container. You'll want to google rvalue references if you're not sure how they work. – GManNickG Jan 26 '10 at 22:21
  • sweet thanks... I'll have to dig into this some more lol. At face value it seems like this would cause problems. – DigitalZebra Jan 26 '10 at 22:33

4 Answers4

21

I might be total wrong here, but isn't what you want just

SomeExpensiveType x = std::move( vec.back() ); vec.pop_back();

Assuming SomeExpensiveType has a move constructor. (and obviously true for your case)

leiz
  • 3,984
  • 2
  • 23
  • 17
  • 1
    I thought of that, but I'm wary about moving something from the container like that. Now that I think about it more, it does seem perfectly legal... :| – GManNickG Jan 26 '10 at 22:08
  • Hmm. I'm not sure that's correct. Surely vec.back() would have to have the move semantics, i.e. return an && – ScaryAardvark Jan 26 '10 at 22:20
  • Mind you, I've thought about it a bit more and you may well be right. – ScaryAardvark Jan 26 '10 at 22:21
  • 3
    @Scary: `back` returns a reference, and using `std::move` would turn that into an rvalue-reference. – GManNickG Jan 26 '10 at 22:22
  • 3
    @GMan: no worries, `move` is a service provided by the class, not the container. The object in the container is still guaranteed to remain valid. – Potatoswatter Jan 26 '10 at 22:42
  • @Potate: That's the conclusion I've reached as well. For some reason, I was thinking moving it out would somehow invalidate the container. But like you said, it's clearly an operation independent of the container. Thanks, guys. – GManNickG Jan 26 '10 at 22:53
  • Technically, I don't think this is allowed, but it'll probably at least normally work. The problem is that anything in a container is required to be a valid object, but from the time of the `move()` to the `pop_back()`, what you have in the container is no longer really valid -- it's basically a zombie object. In a few cases, most objects of a class are expensive, but it's *possible* to create one cheaply -- and in this case, you can (sometimes) create a cheap object, swap it with the object in the container, then pop off the cheap object. Not ideal by any stretch though. – Jerry Coffin Jan 27 '10 at 00:07
  • 4
    @Jerry: No, a "moved-from" object is still valid. It exists, it is just typically reset to a very simple state. Its lifetime has not expired, and its destructor has not been called, but it *will* be called. The object is perfectly valid as far as the language is concerned – jalf Jan 27 '10 at 03:00
  • @jalf:I suspect that's the intent, but I'm not sure it's required, at least in the current draft of the standard. The closest I can see is (Table 33): "[ Note: There is no requirement on the value of rv after the construction.—end note ]" That implies that rv *has* a value, which you *could* read as meaning that rv must still be a valid object -- but then again, 1) it's a pretty weak implication at best, and 2) it's only in a note anyway. – Jerry Coffin Jan 27 '10 at 04:17
  • @Jerry: That's the exact thought process I had. (And I feel better not being alone on it :P) But zombie states are valid states, I think what the standard means by "no requirement of rvalue" means no *special* requirements; we still have to make sure the rvalue is a useless but usable object. – GManNickG Jan 27 '10 at 15:03
  • Wow. "There is no requirement on the value of rv after the construction" seems a pretty misleading way to describe something that is going to have a destructor run on it. – Daniel Earwicker Apr 08 '10 at 06:48
  • 3
    @Jerry @jalf @Daniel: It should be noted that what the standard says has changed. From the FCD, Table 34 & 36 (moved from 33, not relevant) says: "Note: rv remains a valid object. Its state is unspecified—end note ]" So it's explicit: a moved object needs to be able to destruct safely, but nothing else is specified. Glad they cleared that up. – GManNickG Jul 27 '10 at 23:21
  • @Jerry @GMan : A moved-from object should be valid in the sense that it is at least destructible. Also, if the type supports assignment, it should be assignable in the "moved-from" state as well. IMHO, that's the bare minimum. Any additional guarantees are up to the class designer. I would consider any class for which this doesn't hold to be broken. – sellibitze Oct 01 '10 at 19:58
  • I'm curious as to whether or not should we also define move assignment operator for such type too? – haxpor Dec 10 '19 at 18:53
4

For completeness (and anyone stumbling on this question without a C++1x compiler), an alternative that already exists today:

SomeExpensiveType x;
std::swap(x, vec.back()); 
vec.pop_back();

It just requires a specialization of std::swap to exist for the element type.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • 3
    Except don't use std::swap explicitly (as it can't be specialized properly for many types); use a using-declaration with an unqualified swap call. –  Oct 01 '10 at 10:56
  • 1
    @Roger Pate - or use `boost::swap` to get the same thing. – Daniel Earwicker Oct 01 '10 at 13:00
  • I'd rather type a single line than worry about who's going to complain about Boost, as someone inevitably does. :) –  Oct 01 '10 at 13:07
  • @sellibitze ...unless you realize that it's already 2010 and the standard isn't complete, which means the original meaning of the '0' in C++0x is obsolete. I would still call it C++0x like everyone else, but you can argue either way. – Tim Yates Oct 03 '10 at 03:42
  • Sometimes I put C++2x if I'm feeling less optimistic. – Daniel Earwicker Oct 04 '10 at 08:58
4
template<class C>
auto pop_back(C& c) -> typename std::decay<decltype(c.back())>::type
{
  auto value (std::move(c.back()));
  c.pop_back();
  return value;  // also uses move semantics, implicitly
  // RVO still applies to reduce the two moves to one
}
  • I find std::decay more appropriate than std::remove_reference. For example, it turns a "const MyClass&" into a "MyClass" (removing const). – sellibitze Oct 01 '10 at 19:42
  • I'm not 100% sure but a `typename` is possibly missing after `->` – sellibitze Oct 03 '10 at 02:19
  • Indeed it is. Why does it seem like this *should* be much simpler? –  Oct 03 '10 at 03:36
  • In some contexts typename is not necessary (for example, in the list of base classes). Since only a type can follow the arrow, typename is rather redundant. I simply wasn't sure whether this context requires typename or not. – sellibitze Oct 03 '10 at 09:53
  • @sellibitze: My interpretation of the draft and current gcc agree: required here. –  Oct 03 '10 at 11:50
-1

Generally for expensive types I think you'd want to push either a wrapper class or smart pointer into the container instead. This way you are avoiding expensive copies and instead only doing the cheap copies of the smart pointer or the wrapper class. You can also use raw pointers too if you want haha.

class ExpensiveWrapper
{
public:
   ExpensiveWrapper(ExpensiveClass* in) { mPtr = in; }

   // copy constructors here....

private:
   ExpensiveWrapper* mPtr;

};
DigitalZebra
  • 39,494
  • 39
  • 114
  • 146
  • 3
    A point of `move` semantics is to get rid of this method. Containers will only `move` their contents around instead of copying them. – GManNickG Jan 26 '10 at 22:12