默认移动构造函数/赋值和删除拷贝构造函数/赋值 [英] Default move constructor/assignment and deleted copy constructor/assignment

查看:1107
本文介绍了默认移动构造函数/赋值和删除拷贝构造函数/赋值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据标准,


如果类X的定义没有显式声明一个移动构造函数,作为默认值(如果且仅当



) - X没有用户声明的复制构造函数,



没有用户声明的复制赋值运算符,



- X没有用户声明的移动赋值运算符,



- X没有用户声明的析构函数。


b
$ b

 #include< utility> 

class Foo
{
public:
Foo()= default;
Foo(Foo const&)= delete;
};

int main()
{
Foo f;
Foo g(std :: move(f)); // compilation failed here
return 0;
}

所以看起来一个被删除的函数被认为是用户定义的,感觉(它不是它的默认实现)。但是,在这种特殊情况下,如何删除拷贝的构造函数/赋值乱丢默认移动构造函数/赋值?



我认为这个问题具有实际重要性,因为手动生成和esp。维护这样的默认函数是容易出错的,而同时,使用类的增加(正确的)如 std :: unique_ptr 可复制类比以前更常见的野兽。

解决方案

用户声明表示用户提供的(由用户定义),显式默认与隐式默认/删除(比如你的移动构造函数)相比,这是一个非常简单的方法。( = delete 在您的情况下,,移动构造函数隐式被删除,因为复制构造函数显式



删除的复制构造函数/赋值默认移动构造函数/赋值?


这不会,但是标准不区分这种情况和一个复杂的。



最简单的回答是,使用隐式定义的移动构造函数显式地删除复制构造函数可能在某些情况下是危险的,如果您拥有一个用户定义的析构函数,而且没有用户定义的复制构造函数(参见规则三/五/零)。现在,您可以认为用户定义的析构函数不会删除复制构造函数,但这只是一种无法删除的语言中的缺陷,因为它会破坏很多旧的(坏的)程序。引用Bjarne Stroustrup:


在一个理想的世界里,我认为我们将决定无代作为默认值, [...]另外,无默认操作策略导致编译时错误(我们应该有一个简单的方法来修复),而生成o


您可以在 N3174 = 10-0164



请注意,大多数用户遵循规则三/五/零,在我看来你应该。通过隐式删除默认的移动构造函数,标准是保护你的错误,并应该保护你很久以前在某些情况下删除复制构造函数(参见Bjarne的论文)。



进一步阅读,如果你有兴趣:






我认为这个问题具有实际的重要性,因为手动生成和esp。维护这样的默认函数是容易出错的,而同时,使用类的增加(正确的)如 std :: unique_ptr


将移动构造函数标记为显式默认值将会解决这个问题:

  class Foo {
public:
Foo()= default;
Foo(Foo const&)= delete;
Foo(Foo&&)= default;
};

你得到一个带有默认移动构造函数的不可复制对象,在我看来,这些显式声明比隐式的更好(例如,只删除移动构造函数 default ,而不删除复制构造函数)。


According to the standard,

If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if

— X does not have a user-declared copy constructor,

— X does not have a user-declared copy assignment operator,

— X does not have a user-declared move assignment operator, and

— X does not have a user-declared destructor.

Now the following fails to compile

# include <utility>

class Foo
{
public:
  Foo() = default;
  Foo(Foo const &) = delete;
};

int main()
{
  Foo f;
  Foo g(std::move(f)); // compilation fails here
  return 0;
}

So it seems that a deleted function is considered as user-defined, which makes sense (it is not its default implementation). However, in that particular case, how would deleted copy construtor/assignment mess default move constructor/assignment?

I think this question has practical importance because manual generation and esp. maintenance of such default functions is error prone, while at the same time, the (righteous) increase of the use of classes such as std::unique_ptr as class members made non-copyable classes much more common beasts than they used to be.

解决方案

user-declared means either either user-provided (defined by the user), explicitly defaulted (= default) or explicitly deleted (= delete) in contrast with implicitly defaulted / deleted (such as your move constructor).

So in your case, yes the move constructor is implicitly deleted because the copy-constructor is explicitly deleted (and thus user-declared).

However, in that particular case, how would deleted copy constructor/assignment mess default move constructor/assignment?

It would not, but the standard does not make the difference between this case and a complicated one.

The shortest answer is that having an implicitly defined move-constructor with an explicitly deleted copy-constructor might be dangerous in some cases, the same when you have a user-defined destructor and no user-defined copy-constructor (see rule of three/five/zero). Now, you can argue that a user-defined destructor does not delete the copy-constructor, but this is simply a flaw in the language which cannot be removed because it would break a lot of old (bad) program. To quote Bjarne Stroustrup:

In an ideal world, I think we would decide on "no generation" as the default and provide a really simple notation for "give me all the usual operations." [...] Also, a "no default operations" policy leads to compile time errors (which we should have an easy way to fix), whereas a generate o perations by default policy leads to problems that cannot be detected until run time.

You can read more about this in N3174=10-0164.

Note that most people follow the rule of three/five/zero, and in my opinion you should. By implicitly deleting the default move-constructor, the standard is "protecting" you from mistakes and should have protected you a long time before by deleting copy-constructor in some cases (see Bjarne's paper).

Further reading if you are interested:

I think this question has practical importance because manual generation and esp. maintenance of such default functions is error prone, while at the same time, the (righteous) increase of the use of classes such as std::unique_ptr as class members made non-copyable classes much more common beasts than they used to be.

Marking the move constructor as explicitly defaulted will solve this problem:

class Foo {
public:
  Foo() = default;
  Foo(Foo const &) = delete;
  Foo(Foo&&) = default;
};

You get a non-copyable object with a default move constructor, and in my opinion these explicit declarations are better than implicit ones (e.g. by only declaring the move constructor as default without deleting the copy-constructor).

这篇关于默认移动构造函数/赋值和删除拷贝构造函数/赋值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆