Smart way to construct class member std::vector<std::unique_ptr<AClass> >
Asked Answered
T

2

1

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?

Theoretician answered 24/7, 2013 at 11:52 Comment(1)
I know this question is really old but I had the same problem and just figured it out!Provencal
S
1

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;
};
Scranton answered 24/7, 2013 at 12:4 Comment(9)
what does the utility library do in this example?Theoretician
I'm still getting /usr/bin/../lib/c++/v1/memory:1681:31: error: call to implicitly-deleted copy constructor of 'std::__1::unique_ptr<A, std::__1::default_delete<A> >' errors from my compiler. The only difference with what you're doing is that my equivalent of you bar, called A` is derived; it's only member is a virtual function. Is this a compiler problem?Theoretician
@Karrek SB also, my version of your _v is declared public; I don't know if this is a problem, I thought I should mention it...Theoretician
It might well be a compiler problem. This should work. Try it on a recent compiler (e.g. here).Scranton
I was worried you'd say that.. I'm working on Mac and have had problems with compilers and C++11 syntax, particularly with library linking. clang has solved most of these for me, except the one I show here now, and I have all but the most recent version of the compiler... It's been more than inconvenient. Are you working or have had any experience with osx? Should I ask someone at 'clang' about this?Theoretician
@Plamen: A recent Clang also works fine on this code, but you should have at least 3.3, I believe.Scranton
Thanks for all your help, @Karrek SB! I have tried with clang 3.2 and g++-mp-4.8, i.e gcc v4.8. This is the error I have with gcc: error: declared here unique_ptr(const unique_ptr&) = delete; There's a lot more, I can put it into the question if it'll help... Everything else compiles fine, just the problem above.Theoretician
Update: the code compiles fine, so long as I don't actually construct the object foo, so pretty useless, really.Theoretician
let us continue this discussion in chatTheoretician
P
1

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;
};
Provencal answered 31/1, 2015 at 1:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.