【问题标题】:Why does adding a destructor change the copy constructor behavior of this struct? [duplicate]为什么添加析构函数会更改此结构的复制构造函数行为? [复制]
【发布时间】:2016-10-22 10:41:33
【问题描述】:

我有一些令我感到困惑的代码。特别是,当我尝试将某些内容作为初始化列表添加到列表中时——它会一直工作,直到我添加一个析构函数——然后它开始尝试找到一个复制构造函数。

这似乎不是完全一致的行为。举个最小的例子:

#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;
}

这在 GCC 和 VS2015 中编译失败,因为 push_back 尝试访问 ListItemType 复制构造函数:

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 具有移动构造函数?

【问题讨论】:

  • 简而言之:添加析构函数会抑制隐式移动构造函数的生成,因此复制/移动操作会退回到隐式复制构造函数

标签: c++ c++11


【解决方案1】:

您观察到的行为是由管理编译器是否生成隐式复制或移动构造函数的规则生成的:

隐式移动

如果未定义,则为类隐式声明移动构造函数,如果:

  • 该类没有用户定义的复制构造函数;和
  • 该类没有用户定义的复制赋值或移动赋值运算符;和
  • 该类没有用户定义的析构函数。

隐式复制

如果未定义,则在以下情况下为类隐式删除复制构造函数:

  • 该类有一个用户定义的移动构造函数;或
  • 其他与此处无关的原因...

在您的问题中,您有几种情况:

案例一

  • ListItemType 有一个析构函数
  • MemberType 有一个移动构造函数

由于析构函数的存在,ListItemType 的隐式移动构造函数已被删除。因此push_back 必须使用复制构造函数将ListItemType 放入列表中。

在这种情况下,ListItemType 的复制构造函数不能隐式声明为其数据成员之一 (MemberType) 包含移动构造函数,这会阻止生成 MemberType 的隐式复制构造函数。

案例2

  • ListItemType 没有析构函数
  • MemberType 有一个移动构造函数

可以为ListItemType 隐式生成移动构造函数,并用于将值移动到列表中。

案例3

  • ListItemType 有一个析构函数
  • MemberType 没有移动构造函数

可以生成ListItemTypeMemberType 的隐式复制构造函数,并用于将值复制到列表中。

最后,表达式ListItemType foo = { MemberType() }; 是聚合初始化,遵循不同的规则。在任何一种情况下,MemberType 都会有一个足以进行聚合初始化的移动或复制构造函数。

【讨论】:

猜你喜欢
  • 2012-06-29
  • 2015-04-28
  • 2019-03-15
  • 2020-01-14
  • 1970-01-01
  • 1970-01-01
  • 2011-04-17
  • 1970-01-01
  • 2021-05-14
相关资源
最近更新 更多