views:

323

answers:

4

In C++0x, we get an efficiency boost concerning containers with std::move:

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

+9  A: 

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
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... :|
GMan
ScaryAardvark
Mind you, I've thought about it a bit more and you may well be right.
ScaryAardvark
@Scary: `back` returns a reference, and using `std::move` would turn that into an rvalue-reference.
GMan
@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
@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.
GMan
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
@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
@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
@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.
GMan
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
GMan
@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
A: 

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;

};
Polaris878
A point of `move` semantics is to get rid of this method. Containers will only `move` their contents around instead of copying them.
GMan
+3  A: 

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
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.
Roger Pate
@Roger Pate - or use `boost::swap` to get the same thing.
Daniel Earwicker
I'd rather type a single line than worry about who's going to complain about Boost, as someone inevitably does. :)
Roger Pate
There is no C++1x.
sellibitze
@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
Sometimes I put C++2x if I'm feeling less optimistic.
Daniel Earwicker
+2  A: 
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
}
Roger Pate
sellibitze
@sellibitze: You're right, that is more appropriate; thanks!
Roger Pate
I'm not 100% sure but a `typename` is possibly missing after `->`
sellibitze
Indeed it is. Why does it seem like this *should* be much simpler?
Roger Pate
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
@sellibitze: My interpretation of the draft and current gcc agree: required here.
Roger Pate