2

It's taken me a while, but I've finally constructed a minimal example with illustrates the problem I'm having.

#include <memory> #include <vector> class Thing { }; class Box { public: std::vector<std::unique_ptr<Thing>> Things; static Box MakeBox() {Box b; return b;} }; 

My real program is obviously quite a lot more complicated than this.

GCC 4.8.3 happily compiles this. It also compiles the real application, which works perfectly.

Visual Studio 2012 insists that this code is not correct, giving me error C2248 on line 606 of vc\include\xmemory0. If I wade through several miles of compiler output, I discover the real source of the error is line 11 in the above example. (The line that defines Things.) VS also refuses to compile my real application, with the same error.

So, is this code correct or not? If it's not correct, then why does GCC accept it? And how to I make it correct? If it is correct, then why won't VS compile it? Is there some way I can unconditionally force VS to actually compile my program?


Output from VS:

1>------ Build started: Project: TestUniquePtr, Configuration: Debug Win32 ------ 1> Main.cpp 1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(606): error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>' 1> with 1> [ 1> _Ty=Thing 1> ] 1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\memory(1447) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr' 1> with 1> [ 1> _Ty=Thing 1> ] 1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(605) : while compiling class template member function 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)' 1> with 1> [ 1> _Ty=std::unique_ptr<Thing> 1> ] 1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(751) : see reference to function template instantiation 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)' being compiled 1> with 1> [ 1> _Ty=std::unique_ptr<Thing> 1> ] 1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\type_traits(743) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled 1> with 1> [ 1> _Ty=std::unique_ptr<Thing> 1> ] 1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\vector(655) : see reference to class template instantiation 'std::is_empty<_Ty>' being compiled 1> with 1> [ 1> _Ty=std::allocator<std::unique_ptr<Thing>> 1> ] 1> d:\projects\c++\testuniqueptr\testuniqueptr\main.cpp(11) : see reference to class template instantiation 'std::vector<_Ty>' being compiled 1> with 1> [ 1> _Ty=std::unique_ptr<Thing> 1> ] ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ========== 
16
  • 1
    Why are you using VS2012? Commented Oct 16, 2018 at 15:46
  • 1
    @MathematicalOrchid That's incorrect. All recent version of VS support Windows XP. You just have to set the platform toolset to an XP compatible one. When you install VS it either comes with it or as an option you can check when you install it. I've recently used it with VS 2015 and I've seen it as available with VS 2017. Unless you mean it's the latest VS that runs on XP. That might be true. Commented Oct 16, 2018 at 15:48
  • 3
    VS 2012 has laughable C++11 support. For full support you need VS 2015 (update 3 I think) or 2017. Commented Oct 16, 2018 at 15:52
  • 1
    @MathematicalOrchid In case you need to convince anyone to upgrade: msdn.microsoft.com/en-us/library/hh567368.aspx Commented Oct 16, 2018 at 16:09
  • 1
    This link can be useful: msdn.microsoft.com/en-us/library/hh567368.aspx Commented Oct 16, 2018 at 16:19

2 Answers 2

4

Your problem isn't std::unique_ptr but std::vector.

Your compiler comes with an old version of std::vector that requires the element type to be copyable.

The return by value (return b;) should invoke a move of the vector, but your std::vector doesn't implement move.

std::unique_ptr is moveable but not copyable, therefore it doesn't meet the pre-C++11 requirements for being used in std::vector... a requirement which still applies to VC++ 2012.

Your best option is to use a newer compiler and standard library. One that supports move semantics on std::vector.

Otherwise you might make some progress by eliminating the copy of std::vector, for example, by having MakeBox fill in an output argument rather than returning a new object.

static void MakeBox(Box& b) { /* fill Things in b provided by caller */ } 

That's probably an exercise in futility though, because whenever the vector needs to grow, it has to relocate the existing elements to new storage, and with incomplete move support, it will try to copy those.

Sign up to request clarification or add additional context in comments.

2 Comments

I vaguely remember these issues now. Even thought of lack of NRVO and C++17's guaranteed copy elision but not of a missing move constructor :D
I don't believe this is the answer. MSVS2012 doesn't auto generate move constructors so Box isn't moveable.
3

The problem is that Box has no move constructor, thus returning a Box requires it to have a copy constructor (which it can't because unique_ptr is not copyable). All you have to do is define a move constructor for Box:

Box::Box(Box&& other) : Things(std::move(other.Things)) { } 

With more recent editions, the compiler will generate the move constructor for you.

2 Comments

How does this work if Box has a dozen other members? Do I have to explicitly remember to move all of those too? (Seems VS doesn't support the = default syntax yet.)
Yes. There's no way around that really (aside from refactoring to reduce the number of members).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.