移动构造函数是必需的,即使它不使用。为什么? [英] Move constructor is required even if it is not used. Why?

查看:188
本文介绍了移动构造函数是必需的,即使它不使用。为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么?为什么C ++要求类是可移动的,即使它不使用!
例如:

Why?! Why C++ requires the class to be movable even if it's not used! For example:

#include <iostream>
using namespace std;

struct A {
    const int idx;
    //   It could not be compileld if I comment out the next line and uncomment
    // the line after the next but the moving constructor is NOT called anyway!
    A(A&& a) : idx(a.idx) { cout<<"Moving constructor with idx="<<idx<<endl; }
   //  A(A&& a) = delete;
    A(const int i) : idx(i) { cout<<"Constructor with idx="<<i<<endl; }
    ~A() { cout<<"Destructor with idx="<<idx<<endl; }
};

int main()
{
    A a[2] = { 0, 1 };
   return 0;
}

输出是(move constructor不被调用!):

The output is (the move constructor is not called!):


idx = 0的建构函式
idx = 1的建构函式$
idx = 1的析构函式

具有idx = 0的析构函数

Constructor with idx=0
Constructor with idx=1
Destructor with idx=1
Destructor with idx=0

如果移动构造函数被删除,代码不能被编译em>使用删除的函数'A :: A(A&&)''但是如果构造函数没有被删除它不被使用
什么是愚蠢的限制? :
为什么我需要它?当我试图初始化一个包含unique_ptr字段的对象数组时,会出现实际的含义。
例如:

The code can not be compiled if moving constructor is deleted ('use of deleted function ‘A::A(A&&)’'. But if the constructor is not deleted it is not used! What a stupid restriction? Note: Why do I need it for? The practical meaning appears when I am trying to initialize an array of objects contains unique_ptr field. For example:

// The array of this class can not be initialized!
class B {
    unique_ptr<int> ref;
public:
    B(int* ptr) : ref(ptr)
        {  }
}
// The next class can not be even compiled!
class C {
    B arrayOfB[2] = { NULL, NULL };
}

你试图使用一个unique_ptr的向量。


好​​的。非常感谢大家。与所有那些复制/移动构造函数和数组初始化有很大的混乱。实际上,问题是关于编译器需要复制consrtuctor的情况,可以使用移动构件并且不使用它们。
所以我要在稍后创建一个新问题,当我得到一个普通的键盘。我会在这里提供链接。


我创建了更具体和清晰的问题 - 欢迎来到讨论它!

And it gets even worse if you are trying to use a vector of unique_ptr's.

Okay. Thanks a lot to everybody. There is big confusion with all those copying/moving constructors and array initialization. Actually the question was about the situation when the compiler requires a copying consrtuctor, may use a moving construcor and uses none of them. So I'm going to create a new question a little bit later when I get a normal keyboard. I'll provide the link here.

P.S. I've created more specific and clear question - welcome to discuss it!

推荐答案

A a[2] = { 0, 1 };

这是聚集初始化。 §8.5.1[dcl.init.aggr] / p2的标准规定

This is aggregate initialization. §8.5.1 [dcl.init.aggr]/p2 of the standard provides that


当聚集由初始化器列表初始化时,如8.5.4中所述,初始化器列表的元素被作为聚合成员的初始化器,增加下标或成员顺序。每个成员都从相应的 initializer-clause 复制初始化

§8.5[dcl.init] / p16反过来描述类类型对象的副本初始化的语义:

§8.5 [dcl.init]/p16, in turn, describes the semantics of copy initialization of class type objects:



  • [...]

  • 如果目标类型是cv-qualified)类类型:

    • 如果初始化是直接初始化,或者是复制初始化,其中源
      类型的cv非限定版本是与
      目的地的类相同的类或类的派生类,构造函数被考虑。适用的构造函数
      被枚举(13.3.1.3),最好的构造函数通过重载
      分辨率(13.3)来选择。调用这样选择的构造函数来初始化
      对象,初始化表达式或表达式列表作为其
      参数。如果没有构造函数应用,或者重载解析是
      模糊,则初始化是错误的。

    • 否则(即,对于剩余的复制初始化情况),用户定义可以从源
      类型转换为目标类型或(当使用转换函数时)
      到其派生类的转换序列如13.3.1.4,
      和最好的一个通过重载分辨率(13.3)来选择。如果
      转换不能完成或不明确,初始化是
      不成形。所选的函数以初始化器
      表达式作为其参数来调用;如果函数是一个构造函数,调用
      初始化
      目标类型的cv非限定版本的临时。临时是一个贬值。调用
      (这是构造函数的临时变量)的结果然后被用于
      direct-initialize,根据上面的规则,对象是
      副本的目的地-初始化。在某些情况下,允许
      实现通过直接将中间结果
      构造到正被初始化的对象中来消除该
      直接初始化中固有的复制;

  • [...]
  • If the destination type is a (possibly cv-qualified) class type:
    • If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.
    • Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type. The temporary is a prvalue. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.

code> 0 和 1 int c> A s,这里的复制初始化在第二个子句下。编译器在 A :: A中找到 int A 的用户定义转换(int)构造函数,调用它构造一个 A 类型的临时变量,然后使用该临时变量执行直接初始化。这反过来意味着编译器需要对一个构造函数执行重载解决,该构造函数使用临时类型 A ,它会选择已删除的移动构造函数,形成。

Since 0 and 1 are ints, not As, the copy initialization here falls under the second clause. The compiler find a user-defined conversion from int to A in your A::A(int) constructor, calls it to construct a temporary of type A, then performs direct initialization using that temporary. This, in turn, means the compiler is required to perform overload resolution for a constructor taking a temporary of type A, which selects your deleted move constructor, which renders the program ill-formed.

这篇关于移动构造函数是必需的,即使它不使用。为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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