1
votes

This question combines unique_ptr as class member and move semantics fail to compile with clang and C++ std::vector in constructor.

My goal is to construct a wrapper

struct V_wrapper{
       std::vector<std::unique_ptr<AClass> > vec;
       V_wrapper(std::vector<std::unique_ptr<AClass> > v) : vec{std::move(v)} {}
};

Unfortunately this code does not compile, because the compiler (clang Apple LLVM version 4.2) attempts to copy construct the vector v which is not supported. On the other hand, if I design an intermediate wrapper for std::unique_ptr<AClass>, as follows

struct P_wrapper{
       std::unique_ptr<AClass> Ptr;
       P_wrapper(std::unique_ptr<AClass>& p) : Ptr(std::move(p)) {}
};

and write V_wrapper as follows

struct V_wrapper{
       std::vector<P_wrapper > vec;
       V_wrapper(std::vector<P_wrapper > v) : vec{std::move(v)} {}
};

then I have no problems. I think (emphasis) that the reason this works is that the constructor of the vector realizes that you should use the reference to move rather than trying to copy, just as in unique_ptr as class member and move semantics fail to compile with clang.

Unfortunately, this leads to the rather inconvenient construction procedure that I make the std::vector<std::unique_ptr<AClass> >, use it to construct the P_wrapper, and finally use that to construct the V_wrapper. I feel that the middle step should be totally redundant! In addition, it makes the interface much harder to read. The whole point of the wrapper in the first place was to hide the implementation of vec from the user, and now there's an inexplicable (not knowing the source code) object P_wrapper that's only used to construct another object....

I want to avoid this, and only have one wrapper. Is there any way of cutting out the middle man so I can go back to the first, much simpler implementation of V_wrapper?

2
I know this question is really old but I had the same problem and just figured it out!Randy the Dev

2 Answers

1
votes

Don't use brace initalizers gratuitously; std::vector has a constructor that consumes initializer lists. The obvious way to write this compiles fine for me:

#include <memory>    // for std::unique_ptr
#include <utility>   // for std::move
#include <vector>    // for std::vector

struct bar {};

struct foo
{
    using vtype = std::vector<std::unique_ptr<bar>>;
    foo(vtype v) : _v(std::move(v)) { }
private:
    vtype _v;
};
1
votes

You need to delete the default copy constructor and assignment operators. These functions are defined implicitly, and need to be explicitly deleted, as they will attempt to copy the vector and its contents (which is an illegal operation on a unique_ptr).

struct V_wrapper
{
public:
    V_wrapper(std::vector<std::unique_ptr<AClass> > v)
      : vec(std::move(v))
    {}

    // Delete default copy constructor + assignment operator
    V_wrapper(const V_wrapper &) = delete;
    V_wrapper& operator= (const V_wrapper &) = delete;
private:
    std::vector<std::unique_ptr<AClass> > vec;
};