Как использовать std:: vector<unique_ptr <T >> в качестве параметра по умолчанию
EDIT2:
Чтобы уточнить:
Этот вопрос возник из-за проблемы, которая на самом деле не имела ничего общего с параметрами по умолчанию, а с конструкторами копирования и перемещения. Я принял ответ, который фактически отвечает на вопрос (поэтому, если вы находитесь здесь из-за названия вопроса, прочитайте его) и объясню, почему он не сработал изначально для меня.
В чем была проблема?
Таким образом, проблема, описанная в "EDIT:", на самом деле довольно проста:
Присвоение классу, содержащему std::vector<std::unique_ptr<T>>
нарушит компиляцию в VisualStudio 2013 (не тестировалась с другими версиями), а сообщения об ошибках будут крайне загадочными.
Предположения в комментариях заключались в том, что компилятор VC имел ошибку и пытался вызвать конструктор копирования, которого не было.
Дерьмо, что теперь?
Это предположение на самом деле было правдой, но не в том смысле, в котором я впервые его понял.
На самом деле, VCC на самом деле пытается вызвать конструктор перемещения MyClass
, который он неявно определяет.Но, и вот в чем проблема, она не определяет ее правильно:
При определении конструктора перемещения MyClass(MyClass && a)
явно мы можем имитировать поведение компилятора, написав наш код следующим образом:
MyClass(MyClass && a)
: foos_(a.foos_)
{}
Использование этого кода генерируетте же самые сообщения об ошибках, что и неявное определение, и я думаю, вы можете сразу увидеть, что здесь не так: этот конструктор перемещения на самом деле пытается вызвать конструктор копирования foos_
что, конечно, невозможно, потому что он, в свою очередь, не может вызвать конструктор копирования для своего содержимого, так как они имеют типstd::unique_ptr
который не имеет конструктора копирования по понятным причинам.
Когда вместо этого используется этот код,
MyClass(MyClass && a)
: foos_(std::move(a.foos_))
{}
все работает отлично, потому что теперь конструктор перемещенияstd::vector
вызывается и, таким образом, конструктор перемещения для его содержимого.
Так кто же виноват?
Возможность 1:
На самом деле это ошибка компилятора, возникшая из-за проблемы с шаблоном.
Компилятор хочет неявно определить конструктор перемещения, если это необходимо, и делает это, если в определении класса есть не копируемые типы и если в коде когда-либо выполняется присвоение этому классу.
Если эти два условия выполняются, он переходит к определению конструктора перемещения, но теперь, похоже, не заботится о реальном типеstd::vector
шаблон, только о самом классе, который действительно определяет копиюconstructor, so the VCC tries to use it, which fails because of the missing copy constructor in
станд::unique_ptr`.
Или же он просто полностью пропускает определение конструктора перемещения и пытается использовать конструктор копирования, что приводит к той же ошибке.
Возможность 2:
Что-то подозрительное в реализации Microsoft STL. Это всего лишь ключ, и я не могу объяснить, как именно это работает, но мне кажется, что это возможно.
Как избежать этого беспорядка?
Легко, определите свой собственный конструктор перемещения, как показано выше.
РЕДАКТИРОВАТЬ:
Кажется, все сводится к одной конкретной проблеме, оригинальный ответ размещен ниже.
В Visual Studio (2013) создайте новое консольное приложение Win32, не меняйте никаких настроек и сделайте это своим основным.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;
}
Попытка скомпилировать приведет к следующей ошибке (она определенно работает с 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> ]
Предположим, у меня есть такой конструктор:
MyClass(vector<unique_ptr<Foo>> foos) :
foos_(std::move(foos))
{};
С этой простой настройкой все компилируется нормально. Вызов этого конструктора, какMyClass(vector<unique_ptr<Foo>>);
успешно и ведет себя как ожидалось. Но я бы хотел иметьfoos
в качестве параметра по умолчанию.
Как я могу получить значение по умолчанию дляfoos
?
Вот что я придумал:
MyClass(vector<unique_ptr<Foo>> foos = vector<unique_ptr<Foo>>()) :
foos_(std::move(foos))
{};
Но, к сожалению, это не работает. Я не знаю почему, было бы хорошо, если бы кто-то мог пролить свет на это.
Следующие две попытки, которые являются обходными, а не фактическими параметрами по умолчанию:
MyClass() :
foos_() //or foos_(vector<unique_ptr<Foo>>())
{};
Не работать тоже. Оба этих подхода приводят к сообщению об ошибке от компилятора и длинному выводу, наиболее интересная часть которого такова:
c: \ users \ username \ source \ repos \ myProject \ myProject \ MyClass.h (47): см. ссылку на создание шаблона класса
Где 47 - номер строки фактического определения вектора в MyClass
:
vector<unique_ptr<GameObject>> foos_;
Так что я думаю, что это действительно из-за того, что я сделал огромную ошибку с инициализацией.
Также я собираю на VS2013.
Вся ошибка:
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> ]
1 ответ
Напишите перегрузку конструктора, которая вообще не принимает вектор и инициализирует ваш вектор по умолчанию (в пустой вектор):
MyClass() : foos_{}
{}