Как использовать 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_{}
{}
Другие вопросы по тегам