std::unique_ptr删除了函数,initializer_list驱动分配
std::unique_ptr deleted function, initializer_list - driven allocation
全部,
当我使用初始化器列表格式实例化窗口小部件数组时,指向成员变量窗口小部件实例的裸指针会编译,但在更改为std::unique_ptr<>之后gcc给出了一个关于已删除函数的编译错误。
$uname-
Linux。。3.5.0-21-generic#32 Ubuntu SMP 12月11日星期二18:51:59 UTC 2012 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
根据Jonathan的回答,以下代码没有抑制隐式移动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() {..}
。
也许有答案,但我发现在Main()中对wf[]使用c++11风格的迭代会导致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
将您从一个严重的错误中拯救出来。使用裸指针时,您的类型非常不安全,并且会导致未定义的行为,因为您没有复制构造函数,因此指针会被两个不同的对象复制,然后删除两次。Boom,你的程序有未定义的行为。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()) {}
// ...
最好的方法是使类可移动,而实现这一点的一个好方法是遵循为零的规则
- 使用std::multimap迭代器创建std::list
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- 如何在 C 中正确使用 libiconv 使其不会报告"Arg list too long"?
- C++中带有List类的迭代器Segfault
- 使用"std::unordereded_map"映射到"std::list"对象
- GCC对可能有效的代码抛出init list生存期警告
- 使用std::list创建循环链表
- C2664 无法从'initializer list'转换参数
- 使用 std::min "no matching function for call to ‘min(<brace-enclosed initializer list>)’"时出错
- 在C++中标记化"Braced Initializer List"样式字符串(使用 Boost?
- "默认参数":无法从'initializer list'转换为'std::initializer_list'
- 无法从'initializer-list'转换为用户控制器
- 如何修复<function-style-cast>错误:无法从'initializer list'转换为asdending比较<W>(模板函子)
- 递归调用中出现错误"[Error] expression list treated as compound expression in initializer [-fpermissive]"
- VS2015无法从'initializer list'转换为'std::string'错误
- 编译器错误:"Non-aggregates cannot be initialized with initializer list."
- 无法转换...从 '<brace-enclosed initializer list>' 到 地图
- 无法将'<brace-enclosed initializer list>'转换为'double'作为回报
- <function-style-cast> 错误:无法从'initializer list'转换为'std::thread'
- initializer语言 - list不能转换为const margin *