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.
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.
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.
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.
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.
Easy, define your own move constructor as shown above.
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> ]
Write constructor overload which takes no vector at all and initializes your vector by default (To an empty vector):
MyClass() : foos_{}
{}