Question

EDIT2:

To clarify:

This question originated from a problem that actually had nothing to do with default parameters themselves, but copy- and move-constructors. I accepted the answer that actually answers the question (so if you are here because of the question title, read it) and will explain why it didn't work for me initially.

What was the problem?

So the problem, as described under "EDIT:", is actually fairly simple:

An assignment to a class containing a std::vector<std::unique_ptr<T>> will break compilation in VisualStudio 2013 (not tested with other versions) and the error messages are extremely cryptic.

Assumptions in the comments were that the VC compiler had a bug and tried to call a copy constructor which didn't exist.

Shit, what now?

This assumption was in fact true, but not in the sense I first understood it.

Actually, the VCC does in fact try to call the move constructor of MyClass, which it does implicitly define. But, and this is where the problem lies, it doesn't define it correctly:

When defining the move constructor MyClass(MyClass && a) explicitly, we can actually imitate the behaviour of the compiler by writing our code like this:

MyClass(MyClass && a)
    : foos_(a.foos_)
{}

Using this code generates the exact same error messages as using the implicit definition, and I guess you can immediately see what's wrong here: this move constructor actually tries to call the copy constructor of foos_, which of course isn't possible, because it in turn can't call a copy constructor for its contents, as those are of type std::unique_ptr which doesn't have a copy constructor for obvious reasons.

When instead using this code,

MyClass(MyClass && a)
    : foos_(std::move(a.foos_))
{}

everything works out perfectly fine, because now the move constructor of std::vector is called and thus the move constructor for its contents.

So who's to blame?

Possibility 1:

It is in fact a compiler-bug and originates from a template resolving issue.

The compiler wants to implicitly define a move-constructor if needed, and it does so if there are non-copyable types in the class definition and if an assignment to that class is ever made in the code.

If these two conditions are satisfied, it proceeds to define the move-constructor, but now doesn't seem to care about the actual type of the std::vector template, only about the class itself, which does indeed define a copyconstructor, so the VCC tries to use it, which fails because of the missing copy constructor in std::unique_ptr`.

Or, it just skips the definition of the move-constructor entirely and tries to use the copy-constructor, which leads to the same error.

Possibility 2:

Something fishy in the Microsoft STL implementation. This is only a clue and I couldn't explain how it worked exactly, but it seems to be a possibility to me regardless.

How to avoid this mess?

Easy, define your own move constructor as shown above.


EDIT:

It seems to come down to one particular problem, the original answer is posted below.

In Visual Studio (2013), create a fresh Win32 console application, don't change any settings and make this your main .cpp:

// ConsoleApplication2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <vector>
#include <memory>

class Foo { };

class MyClass
{
public:
    MyClass(std::vector<std::unique_ptr<Foo>> foos) :
        foos_(std::move(foos))
    {};

    std::vector<std::unique_ptr<Foo>> foos_;
};

int _tmain(int argc, _TCHAR* argv[])
{
    auto test = MyClass(std::vector<std::unique_ptr<Foo>>()); //remove this, and all works fine!
    return 0;
}

Trying to compile it will result in the following error (It definitely works with gcc!):

1>  ConsoleApplication2.cpp
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0(593): error C2280: 'std::unique_ptr<Foo,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : attempting to reference a deleted function
1>          with
1>          [
1>              _Ty=Foo
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory(1486) : see declaration of 'std::unique_ptr<Foo,std::default_delete<_Ty>>::unique_ptr'
1>          with
1>          [
1>              _Ty=Foo
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0(592) : while compiling class template member function 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)'
1>          with
1>          [
1>              _Ty=std::unique_ptr<Foo,std::default_delete<Foo>>
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0(723) : see reference to function template instantiation 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)' being compiled
1>          with
1>          [
1>              _Ty=std::unique_ptr<Foo,std::default_delete<Foo>>
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\type_traits(572) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled
1>          with
1>          [
1>              _Ty=std::unique_ptr<Foo,std::default_delete<Foo>>
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\vector(650) : see reference to class template instantiation 'std::is_empty<_Alloc>' being compiled
1>          with
1>          [
1>              _Alloc=std::allocator<std::unique_ptr<Foo,std::default_delete<Foo>>>
1>          ]
1>          c:\users\felix\source\repos\infinite whitewursht\infinitewhitewursht\consoleapplication2\consoleapplication2.cpp(18) : see reference to class template instantiation 'std::vector<std::unique_ptr<Foo,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>' being compiled
1>          with
1>          [
1>              _Ty=Foo
1>          ]

Suppose I have a constructor like this:

MyClass(vector<unique_ptr<Foo>> foos) :
    foos_(std::move(foos))
{};

With this simple setup, everything compiles fine. A call to this constructor like MyClass(vector<unique_ptr<Foo>>); succeeds and behaves as expected. But I'd like to have foos as a default parameter.

How can I achieve a default value for foos?

This is what I came up with:

MyClass(vector<unique_ptr<Foo>> foos = vector<unique_ptr<Foo>>()) :
    foos_(std::move(foos))
{};

But unfortunately, this doesn't work. I don't know why, it would be nice if somebody could shed some light on this.

Next two tries, which are workarounds, not actual default parameters:

MyClass() :
    foos_() //or foos_(vector<unique_ptr<Foo>>())
{};

Don't work either. Both these approaches lead to an error message from the compiler and a lengthy output which most interesting part is this:

c:\users\ username \source\repos\myProject\myProject\MyClass.h(47) : see reference to class template instantiation

Where the 47 is the line number of the actual vector definition in MyClass:

vector<unique_ptr<GameObject>> foos_;

So my guess would be that this really is about me doing a huge mistake with the initialization.

Also, I am compiling on VS2013.

The whole error:

GameObject.cpp
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0(593): error         C2280: 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : attempting to reference a deleted function
1>          with
1>          [
1>              _Ty=int
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory(1486) : see declaration of 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr'
1>          with
1>          [
1>              _Ty=int
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0(592) : while compiling class template member function 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)'
1>          with
1>          [
1>              _Ty=std::unique_ptr<int,std::default_delete<int>>
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0(723) : see reference to function template instantiation 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)' being compiled
1>          with
1>          [
1>              _Ty=std::unique_ptr<int,std::default_delete<int>>
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\type_traits(572) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled
1>          with
1>          [
1>              _Ty=std::unique_ptr<int,std::default_delete<int>>
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\vector(650) : see reference to class template instantiation 'std::is_empty<_Alloc>' being compiled
1>          with
1>          [
1>              _Alloc=std::allocator<std::unique_ptr<int,std::default_delete<int>>>
1>          ]
1>          c:\users\felix\source\repos\infinite whitewursht\infinitewhitewursht\infinitewhitewursht\gameobject.h(47) : see reference to class template instantiation 'std::vector<std::unique_ptr<int,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>' being compiled
1>          with
1>          [
1>              _Ty=int
1>          ]
Was it helpful?

Solution

Write constructor overload which takes no vector at all and initializes your vector by default (To an empty vector):

MyClass() : foos_{}
{}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top