Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/qt/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 自动生成的移动构造函数导致非法行为_C++_Qt_Pointers_C++11_Move Semantics - Fatal编程技术网

C++ 自动生成的移动构造函数导致非法行为

C++ 自动生成的移动构造函数导致非法行为,c++,qt,pointers,c++11,move-semantics,C++,Qt,Pointers,C++11,Move Semantics,我问了一个我还没有接受答案的问题,因为我对这个问题的某些方面感到更加困惑,即使我开始掌握其他方面。特别是,我发现了一个令人惊讶的例子,其中g++和clangg++都生成了不正确的移动构造函数 问题摘要 g++和clang++显然违反了在显式定义析构函数时不生成移动构造函数的规则;为什么?这是一个错误,还是我误解了发生了什么 为了正确起见,这些(可能是非法的)移动构造函数应该使RHS指针成员无效,但它们不会。为什么不呢 似乎避免不必要行为的唯一方法是为在其析构函数中使用delete的每个类显式定

我问了一个我还没有接受答案的问题,因为我对这个问题的某些方面感到更加困惑,即使我开始掌握其他方面。特别是,我发现了一个令人惊讶的例子,其中g++和clangg++都生成了不正确的移动构造函数

问题摘要
  • g++和clang++显然违反了在显式定义析构函数时不生成移动构造函数的规则;为什么?这是一个错误,还是我误解了发生了什么
  • 为了正确起见,这些(可能是非法的)移动构造函数应该使RHS指针成员无效,但它们不会。为什么不呢
  • 似乎避免不必要行为的唯一方法是为在其析构函数中使用
    delete
    的每个类显式定义一个正确的移动构造函数。Qt库(版本5.4)是否执行此操作
第1部分:非法自动生成的构造函数? 考虑以下代码:

class NoMove
{
  public:
    ~NoMove() {}
};
int main()
{
  std::cout << "NoMove move-constructible? " <<
    std::is_move_constructible<NoMove>::value << std::endl;
}
…但是由于NoMove有一个显式定义的析构函数,我希望是这样。请注意,意外的构造函数生成不是由于析构函数是平凡的这一事实;当析构函数
delete[]
s生成数组(!!)时,我会得到同样的行为,我甚至能够编译需要有效移动构造函数的代码(!!!!!)。(见下面的例子)这里发生了什么?在这里自动生成移动构造函数合法吗?如果合法,为什么

第2部分:(可能非法)导致未定义行为的自动生成构造函数? 似乎需要在
delete
时提供安全的移动构造函数,但我只想确保我理解:当类包含指针成员并拥有底层数据时,在任何情况下,移动构造函数在将目标指针设置为旧值后使RHS指针无效都是不正确和不充分的吗

考虑以下示例,它类似于上面的
NoMove
示例,并且基于我的:

这可以很好地编译——表明,
DataType
有一个自动生成的移动构造函数。当然,当运行时,它会导致双重删除错误

现在,如果自动生成的move构造函数使RHS指针无效,这就没问题了。那么,如果可以在这里自动生成移动构造函数,为什么这样做不安全呢?实现此功能的移动构造函数很简单:

DataType(DataType&& rhs) :
  val{rhs.val}
{
  rhs.val = nullptr;
}
(对吗?我遗漏了什么吗?应该是
val{std::move(rhs.val)}
?)

这似乎是一个完全安全的自动生成功能;编译器知道
rhs
是一个r值,因为函数原型这么说,因此修改它是完全可以接受的。因此,即使
DataType
的析构函数没有
delete[]val
,似乎也没有任何理由不在自动生成的版本中使
rhs
无效,除非,我认为,这会导致微不足道的性能损失

因此,如果编译器自动生成这个方法——同样,它不应该自动生成,特别是因为我们可以使用
unique\u ptr
从标准库代码中轻松地获得这个精确行为——为什么它会错误地自动生成它呢

第3部分:在Qt中避免这种行为(特别是Qt5.4中的
QByteArray
) 最后,一个(希望如此)简单的问题:Qt5.4的堆分配类,如
QByteArray
(这就是我在原始问题中实际用作
数据类型的类)是否正确地实现了移动构造函数,从而使从拥有指针移动的所有指针无效

我甚至懒得问,因为Qt看起来很可靠,我还没有看到任何双删除错误,但考虑到这些错误的编译器生成的移动构造函数让我措手不及,我担心在一个实现良好的库中很容易出现错误的移动构造函数


与此相关的是,在
C++11
之前编写的没有显式移动构造函数的Qt库呢?如果我可以意外地强制一个自动生成的move构造函数在这种情况下表现出错误,那么有人知道,比如说,使用符合C++11的编译器编译Qt3是否会在这样的用例中导致未定义的销毁行为吗?

问题在于,您混淆了
是可构造的
还是“有一个move构造函数”<代码>是否可移动\u可构造
不测试t是否有移动构造函数。它测试是否可以从类型为
T
的右值构造
T
。而
const T&
可以绑定到
T
rvalue

您看到的是自动生成的复制构造函数
T(const T&)
正在执行其工作,并且失败得很惨

我希望移动构造函数和复制构造函数都不应该自动生成

你的链接讨论了移动构造函数。它没有提到复制构造函数,如果不声明它,它总是隐式声明的

现在,如果您声明了一个移动操作,隐式声明的复制构造函数将被定义为deleted,但您没有这样做,因此它被定义为default并执行memberwise复制。[class.copy]/p7:

如果类定义没有显式声明副本 构造函数,则隐式声明一个。如果类定义 声明移动构造函数或移动赋值运算符 隐式声明的复制构造函数定义为已删除;否则,, 它被定义为默认值(8.4)。如果 类具有用户声明的复制赋值运算符或用户声明的 析构函数


我投票结束这个问题,因为老兄。每个问题一个问题。@Barry,这就……离题了?你真的读了整件事吗?这些问题非常复杂
class DataType
{
  public:
    DataType()
    {
      val = new int[35];
    }
    ~DataType()
    {
      delete[] val;
    }
  private:
    int* val;
};

class Marshaller
{
  public:
    Marshaller()=default;
    DataType toDataType() &&
    {
      return std::move(data);
    }
  private:
    DataType data;
};

void DoMarshalling()
{
  Marshaller marshaller;
  // ... do some marshalling...
  DataType marshalled_data{std::move(marshaller).toDataType()};
}
DataType(DataType&& rhs) :
  val{rhs.val}
{
  rhs.val = nullptr;
}