【问题标题】:How to use a std::vector<unique_ptr<T>> as default parameter如何使用 std::vector<unique_ptr<T>> 作为默认参数
【发布时间】:2014-04-24 23:03:46
【问题描述】:

编辑2:

澄清:

这个问题源于一个实际上与默认参数本身无关的问题,而是复制和移动构造函数。 我接受了实际回答问题的答案(因此,如果您因为问题标题而出现在这里,请阅读它)并将解释为什么它最初对我不起作用。

出了什么问题?

所以问题,如“编辑:”下所述,实际上相当简单:

对包含 std::vector&lt;std::unique_ptr&lt;T&gt;&gt; 的类的赋值将破坏 VisualStudio 2013 中的编译(未使用其他版本进行测试),并且错误消息非常神秘。

cmets 中的假设是 VC 编译器存在错误并试图调用不存在的复制构造函数。

操,现在怎么办?

这个假设实际上是正确的,但不是我最初理解的那样。

实际上,VCC 确实试图调用MyClass 的移动构造函数,它确实隐式定义了它。 但是,这就是问题所在,它没有正确地定义它:

当显式定义移动构造函数MyClass(MyClass &amp;&amp; a)时,我们实际上可以通过编写如下代码来模仿编译器的行为:

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

使用此代码会生成 与使用隐式定义完全相同的错误消息,我猜 您可以立即看到这里出了什么问题:这个移动构造函数实际上试图调用 foos_ 的复制构造函数,这当然是不可能的,因为它又不能为其内容调用复制构造函数,因为它们是键入std::unique_ptr,其中没有复制构造函数,原因很明显。

当使用此代码时,

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

一切正常,因为现在调用了 std::vector 的移动构造函数,因此调用了其内容的移动构造函数。

那么该怪谁呢?

可能性一:

它实际上是一个编译器错误,源于模板解析问题。

如果需要,编译器希望隐式定义一个移动构造函数,如果类定义中有不可复制的类型,并且如果在代码中对该类进行了赋值,它就会这样做。

如果满足这两个条件,它会继续定义移动构造函数,但现在似乎并不关心std::vector 模板的实际类型,只关心类本身,它确实定义了一个副本constructor, so the VCC tries to use it, which fails because of the missing copy constructor in std::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&lt;unique_ptr&lt;Foo&gt;&gt;); 这样对这个构造函数的调用会成功并按预期运行。但我想将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) because assignment to unique_ptrs 绝对不是“不可能”; (2) 用= 构造不是“赋值”; (3) 可以进行移动
  • @0x499602D2 这个人:Error 1 error C2280: 'std::unique_ptr&lt;Foo,std::default_delete&lt;_Ty&gt;&gt;::unique_ptr(const std::unique_ptr&lt;_Ty,std::default_delete&lt;_Ty&gt;&gt; &amp;)' : attempting to reference a deleted function c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0 593
  • @LightnessRacesinOrbit 我说的是直接分配,std::move() 没有任何魔法,但似乎一切都有例外。
  • 如果默认参数初始化为默认构造的vector&lt;unique_ptr&lt;int&gt;&gt;,则不会发生该错误,因为它会发生移动。如果您从左值构造实例,则会发出错误,因为无法复制 unique_ptrs。您必须改为使用std::move()。

标签: c++ vector unique-ptr default-parameters


【解决方案1】:

编写构造函数重载,它根本不接受向量并默认初始化你的向量(到一个空向量):

MyClass() : foos_{}
{}

【讨论】:

  • 我试过了,它适用于其他类,但不幸的是不适用于这一类。是否需要满足任何先决条件才能使其发挥作用?
  • 好的,在解决了一个与默认构造函数没有任何关系的问题之后,您的解决方案确实像预期的那样工作,即使默认参数现在也可以工作。我已经编辑了我的问题以解释究竟是什么问题。
  • Visual Studio 在使用隐式转换到 unique_ptr 时也会阻止编译,请参见:code.google.com/p/infectorpp/source/browse/trunk/include/… 第 152 行 .. 你有什么解决方法吗? :((同样的错误:“试图引用已删除的函数”它当然可以在 GCC 和 Clang 上编译并运行良好)
猜你喜欢
  • 1970-01-01
  • 2019-11-07
  • 2018-02-02
  • 2015-10-11
  • 2022-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多