Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 / VS2010 : Returning containers of uncopyable but movable objects

Consider the following code:

#include <vector>
#include <boost/noncopyable.hpp>

struct A : private boost::noncopyable
{
  A(int num, const std::string& name)
    : num(num),
      name(name)
  {
  }

  A(A&& other)
    : num(other.num),
      name(std::move(other.name))
  {
  }

  int num;
  std::string name;
};

std::vector<A> getVec()
{
  std::vector<A> vec;
  vec.emplace_back(A(3, "foo"));
  // vec.emplace_back(3, "foo"); not available yet in VS10?

  return vec; // error, copy ctor inaccessible
}

int main ( int argc, char* argv[] )
{
  // should call std::vector::vector(std::vector&& other)
  std::vector<A> vec = getVec();

  return 0;
}

This does not compile under VS2010 because obviously A is noncopyable and thus std::vector<A> cannot be copied. Accordingly I cannot return a std::vector<A> from a function.

However it doesn't feel right to me that this sort of thing is not possible considering the concept of RVO. If Return Value Optimization was applied here, copy construction could be omitted and the call to getVec() would be valid.

So what would be the proper way to do this? Is this possible at all in VS2010 / C++11?

like image 272
Opossum Avatar asked Jul 23 '12 08:07

Opossum


2 Answers

If return vec; does not compile, VS2010 does not support move semantics fully yet. Normally, automatic variables are moved implicitly if returned from a function. Use return std::move(vec); as an interim workaround, and make a note in your head to get rid of the std::move in the future.

A full explanation can be found in this FAQ answer under the headline "Moving out of functions".

Also, your two-argument constructor makes a copy of the string argument which is passed by reference-to-const. I would suggest taking the argument by value instead and moving it into the member:

A(int num, std::string name) : num(num), name(std::move(name)) { }

This way, you minimize the number of necessary copies. See Want Speed? Pass by Value for details.

Also, since your move constructor doesn't do anything special, you can default it:

A(A&& other) = default;

This makes it more robust in the face of changes. Bugs seldomly hide in the code you don't write :)

like image 167
fredoverflow Avatar answered Sep 21 '22 12:09

fredoverflow


However it doesn't feel right to me that this sort of thing is not possible considering the concept of RVO.

Elision, the general term for stuff like named return value optimization, is an optimization. It is not required. The specification allows it, but doesn't force any implementation to do it even when it is allowed to.

As such, to enforce consistency between compilers that allow elision and those that don't, if elision is allowed by an operation, the compiler must still verify that the copy/move being elided would be possible given the current state of the code. So if a copy/move constructor is not accessible, the operation fails even if the compiler won't actually call it.

In this case, Visual Studio 2010 seems to be kind of confused in this regard. It does recognize that return vec; should move from vec. However, it seems that VS2010's std::vector implementation needs a move assignment operator to move; without one, it will attempt to copy.

like image 36
Nicol Bolas Avatar answered Sep 19 '22 12:09

Nicol Bolas