![](/img/trans.png)
[英]Error Constructing std::map<T, unique_ptr<S>> from an initializer_list
[英]std::unique_ptr deleted function, initializer_list - driven allocation
全部,
当我使用初始化列表格式实例化一个小部件数组时,一个指向成员变量小部件实例的裸指针会编译,但在更改为 std::unique_ptr<> gcc 后会给出有关已删除函数的编译错误。
$ unname -a
Linux .. 3.5.0-21-generic #32-Ubuntu SMP Tue Dec 11 18:51:59 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
$ g++ --版本
g++ (Ubuntu/Linaro 4.7.2-5ubuntu1) 4.7.2
此代码给出以下编译器错误:
#include <stdlib.h>
#include <memory>
class Widget
{
public:
Widget() {}
};
class W1 : public Widget
{
public:
W1() {}
};
class W2 : public Widget
{
public:
W2() {}
};
class WFactory
{
public:
WFactory(const int i) : _w(new W1()) {}
WFactory(const char* s) : _w(new W2()) {}
~WFactory() { _w.reset(nullptr); }
// ~WFactory() { delete _w; } <--- for naked ptr
private:
// NOTE: does not compile
std::unique_ptr<Widget> _w;
// NOTE: does compile
// Widget* _w;
};
int main()
{
std::unique_ptr<Widget> a(new W1()); // <--- compiles fine
WFactory wf[] { 4, "msg" }; // <--- compiler error using unique_ptr<>
}
错误:
$ g++ -o unique_ptr -std=c++11 -Wall unique_ptr.cpp
unique_ptr.cpp: In function ‘int main()’:
unique_ptr.cpp:36:30: error: use of deleted function ‘WFactory::WFactory(const WFactory&)’
unique_ptr.cpp:22:7: note: ‘WFactory::WFactory(const WFactory&)’ is implicitly deleted because the default definition would be ill-formed:
unique_ptr.cpp:22:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Widget; _Dp = std::default_delete<Widget>; std::unique_ptr<_Tp, _Dp> = std::unique_ptr<Widget>]’
In file included from /usr/include/c++/4.7/memory:86:0,
from unique_ptr.cpp:2:
/usr/include/c++/4.7/bits/unique_ptr.h:262:7: error: declared here
unique_ptr.cpp:36:30: error: use of deleted function ‘WFactory::WFactory(const WFactory&)’
unique_ptr.cpp:36:14: warning: unused variable ‘wf’ [-Wunused-variable]
我对任何一个都不知所措:幕后的机制会产生删除的 fcxn; 或者更简单地说,为什么 std::unique_ptr<> 的表现力与裸 ptr 相比似乎受到限制。
我的问题是:
谢谢你。
编辑 1
根据您的回答,我很感激,我可以对 WFactory 进行以下更改:
(标记为不道德的代码)
class WFactory
{
public:
WFactory(const WFactory& wf)
{
(const_cast<WFactory&>(wf)).moveto(_w);
}
WFactory(const int i) : _w(new W1()) {}
WFactory(const char* s) : _w(new W2()) {}
~WFactory() { _w.reset(nullptr); }
void moveto(std::unique_ptr<Widget>& w)
{
w = std::move(_w);
}
private:
std::unique_ptr<Widget> _w;
};
现在程序编译并运行。 我感谢标准人员出于某种原因编写规范,因此我将我的结果发布为我手头的案例的善意专业化,我真的想强调 ptr 的独特性。
编辑 2
根据乔纳森的回复,以下代码不会抑制隐式移动 ctor:
class WFactory
{
public:
WFactory(const int i) : _w(new W1()) {}
WFactory(const char* s) : _w(new W2()) {}
private:
std::unique_ptr<Widget> _w;
};
请注意,根本没有~WFactory() {..}
。
也许有 ya-ans,但我发现在 Main() 中对 wf[] 使用 c++11 风格的迭代会导致 no-copy-ctor-for-WFactory 错误。 那是:
int Main()
..
WFactory wf[] { 4, "msg" };
for ( WFactory iwf : wf ) <---- compiler error again
// ..
for (unsigned i = 0; i < 2; ++i) <--- gcc happy
wf[i] // ..
}
我想新的 c++11 风格的迭代正在做一个对象复制是不言而喻的。
根据 C++11 标准的第 8.5.1/2 段:
当聚合由初始化列表初始化时,如 8.5.4 中所指定,初始化列表的元素被视为聚合成员的初始化,按递增的下标或成员顺序。 每个成员都是从相应的初始化子句复制初始化的。 [...]
因此,对于每个元素,复制初始化涉及创建目标类型的临时对象,然后使用该临时对象来复制构造数组的元素。
但是,您的类包含一个成员,其类型是unique_ptr
的实例,它是不可复制的。 这也使您的课程不可复制。
此外,虽然unique_ptr
是可移动的,但您的类不是,因为编译器隐式生成移动构造函数被显式定义的析构函数抑制。 如果不是这种情况(即,如果您为您的类显式定义了移动构造函数),则复制初始化将起作用(参见 8.5/15)。
尝试如下更改WFactory
的定义以查看:
class WFactory
{
public:
WFactory(const int i) : _w(new W1()) {}
WFactory(const char* s) : _w(new W2()) {}
WFactory(WFactory&& f) : _w(std::move(f._w)) {}
~WFactory() { _w.reset(nullptr); }
private:
std::unique_ptr<Widget> _w;
};
int main()
{
std::unique_ptr<Widget> a(new W1());
WFactory wf[] { 4, "msg" }; // OK
}
产生删除 fcxn 的幕后机制;
数组只能这样初始化,如果类型是可复制或可移动的,并且unique_ptr
不可复制,因此具有unique_ptr
成员的类默认不可复制,并且您的类型具有用户定义的析构函数,它禁止隐式移动构造函数,所以你的类型也不能移动。
或者更简单地说,为什么
std::unique_ptr<>
的表现力与裸 ptr 相比似乎受到限制。
unique_ptr
使您免于严重错误。 使用裸指针,您的类型非常不安全并且会导致未定义的行为,因为您没有复制构造函数,因此指针被复制然后被两个不同的对象删除两次。 繁荣,你的程序有未定义的行为。 unique_ptr
通过防止它被复制来修复你的类,这是安全和正确的。
您可以通过多种方式使其工作,最简单的是删除用户定义的析构函数,这使您的类可移动,并且数组初始化将编译。
或者,如果您出于其他原因需要用户定义的析构函数,您仍然可以通过编写用户定义的移动构造函数来显式移动_w
成员来使其工作。
如果由于某种原因这是不可能的,您可以通过添加默认构造函数使其工作,因此可以默认构造数组元素,然后移动分配给它们:
class WFactory
{
public:
WFactory() = default;
WFactory(const int i) : _w(new W1()) {}
WFactory(const char* s) : _w(new W2()) {}
private:
std::unique_ptr<Widget> _w;
};
int main()
{
WFactory wf[2];
wf[0] = WFactory(4);
wf[1] = WFactory("msg");
}
您编辑的版本是不道德的并且非常可疑,您不应该像那样抛弃const
并且不应该从左值移动,尤其是不要使用const
左值。 不要去那儿。 而是更改您使用该类的方式以避免需要复制它,或者编写一个有效的复制构造函数来对拥有的对象进行深层复制:
class Widget
{
public:
Widget() {}
virtual std::unique_ptr<Widget> clone() const = 0;
};
class W1 : public Widget
{
public:
W1() {}
virtual std::unique_ptr<Widget> clone() const
{ return std::unique_ptr<Widget>(new W1(*this)); }
};
class W2 : public Widget
{
public:
W2() {}
virtual std::unique_ptr<Widget> clone() const
{ return std::unique_ptr<Widget>(new W2(*this)); }
};
class WFactory
{
public:
WFactory(const int i) : _w(new W1()) {}
WFactory(const char* s) : _w(new W2()) {}
WFactory(const WFactory& w) : _w(w._w->clone()) {}
// ...
最好的方法是让类可移动,一个好的方法是遵循零规则
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.