我有一些令我困惑的代码。特别是,当我尝试将某些内容添加到列表作为初始化列表时 - 它一直有效,直到我添加析构函数 - 然后它开始尝试查找复制构造函数。
这似乎并不是完全一致的行为。拿这个最小的例子:
#include <list>
int main()
{
class MemberType
{
public:
MemberType() {}
MemberType(MemberType&& copy) { }
};
struct ListItemType
{
MemberType x;
~ListItemType() {}
};
std::list<ListItemType> myList;
myList.push_back({MemberType()});
return 0;
}
由于push_back
尝试访问ListItemType
副本构造函数,因此无法在GCC和VS2015中进行编译:
main()::ListItemType::ListItemType(const main()::ListItemType&)
(根据我的理解)。这似乎有道理,因为列表push_back
将复制(因为没有移动构造函数),除非这是不行为,如果你删除析构函数。注释掉析构函数,编译按预期成功。
也就是说,即使使用析构函数,以下工作也很好 - 不需要复制或移动构造函数来满足它。这对我来说似乎是一样的行为。
ListItemType foo = { MemberType() };
最后,如果删除或注释掉MemberType
的移动构造函数 - 编译再次成功 - 意味着以下内容将编译。
#include <list>
int main()
{
class MemberType
{
public:
MemberType() {}
};
struct ListItemType
{
MemberType x;
~ListItemType() {}
};
std::list<ListItemType> myList;
myList.push_back({MemberType()});
return 0;
}
有人可以在这里解释一下这种行为吗?为什么push_back
尝试访问ListItemType
的副本构造函数 - 但仅当ListItemType
具有析构函数且MemberType
具有移动构造函数时?
答案 0 :(得分:27)
您正在观察的行为是由管理编译器生成隐式复制或移动构造函数的规则生成的:
如果未定义,则移动构造函数对于类隐式声明,如果:
如果未定义,则在以下情况下,对于类,复制构造函数将隐式删除:
在您的问题中,您有几种情况:
ListItemType
有一个析构函数MemberType
有一个移动构造函数由于存在析构函数,ListItemType
的隐式移动构造函数已被删除。因此push_back
必须使用复制构造函数将ListItemType
放入列表中。
在这种情况下,ListItemType
的复制构造函数不能被隐式声明为其数据成员之一(MemberType
)包含移动构造函数,这会阻止MemberType
的隐式复制构造函数正在生成。
ListItemType
没有析构函数MemberType
有一个移动构造函数可以为ListItemType
隐式生成移动构造函数,并将其用于将值移动到列表中。
ListItemType
有一个析构函数MemberType
没有移动构造函数可以生成ListItemType
和MemberType
的隐式复制构造函数,并将其用于将值复制到列表中。
最后,表达式ListItemType foo = { MemberType() };
是聚合初始化并遵循不同的规则。在任何一种情况下,MemberType
都将具有足以进行聚合初始化的移动或复制构造函数。