在C 14允许它的情况下,C 17禁止复制ELISION



考虑以下内容:

struct X {
    X() {}
    X(X&&) { puts("move"); }
};
X x = X();

在C 14中,尽管移动构造器具有副作用,但由于[class.copy]/31,

在以下情况下允许复制/移动操作的这种省略...当未绑定到参考的临时类对象(12.2(时 到具有相同CV UNQUALIFIED类型的类对象

在C 17中,该子弹被删除。相反,由于[dcl.Init]/17.6.1:

,保证这一举动可以得到振奋。

如果初始化器表达式为prvalue,并且源类型的CV UNCOLIFIFIFIFIFIFIFIFIEN版本相同 类是目的地的类,初始化器表达式用于初始化目标 目的。[示例: T x = T(T(T()));调用T默认构造函数以初始化x。 - 结束 示例]

到目前为止,我所说的事实是众所周知的。但是现在让我们更改代码,以读取:

X x({});

在C 14中,执行过载分辨率,并使用默认构造函数将{}转换为X临时性,然后移至x。复制省略规则允许此移动省略。

在C 17中,超载分辨率是相同的,但是现在[DCL.Init]/17.6.1不应用,C 14的子弹不再存在。没有初始化器表达式,因为初始化器是支撑式列表。相反,[dcl.init]/(17.6.2(适用:

否则,如果初始化是直接限制的,或者是副本限制 源类型的CV UNQUALIFIFIFIFIFIFIED版本与类别或派生类的类别相同 目的地,考虑到构造函数。列举了适用的构造函数(16.3.1.3(, 最好的方法是通过超负荷分辨率(16.3(选择。如此选择的构造函数称为 初始化对象,以初始化器表达式或表达列表作为其参数。如果不 构造函数适用,或者过载分辨率模棱两可,初始化是错误的。

这似乎需要调用移动构造函数,如果标准中其他地方有一个规则可以说是可以的,我不知道它在哪里。

作为T.C.指出,这与CWG 2327相似:

考虑一个例子:

struct Cat {};
struct Dog { operator Cat(); };
Dog d;
Cat c(d);

这将到11.6 [dcl.init]子弹17.6.2:

否则,如果初始化是直接限制的,或者是副本定位化,而源类型的CV UNCOLIFIED版本与目标类别或派生类别的类别相同,则构造仪为经过考虑的。列举了适用的构造函数(16.3.1.3 [over.match.ctor](,并且通过过载分辨率(16.3 [over.match](选择了最好的构造函数。选择的构造函数被调用以初始化对象,以初始化器表达式或 expression-list 作为其参数。如果不应用构造函数或过载分辨率模棱两可,则初始化是不形成的。

超载分辨率选择CAT的移动构造函数。初始化构造函数的Cat&&参数会导致临时,每11.6.3 [dcl.init.ref]子弹5.2.1.2。这排除了这种情况的复制责任的可能性。

这似乎是保证副本Elision的措辞更改的监督。在这种情况下,我们应该同时考虑构造函数和转换功能,就像我们对副本的化合物一样,但是我们需要确保不会引入任何新颖的问题或歧义。

是什么使它存在相同的潜在问题是我们具有错误的类型(在此示例中,在op, {}中(,这是错误的类型 - 我们需要将其转换为正确的类型(XCat(,而是转换为弄清楚如何做到这一点,我们需要执行超负荷分辨率。此已经使我们进入了移动构造函数 - 我们将RVALUE参考参数绑定到我们刚刚创建的新对象的构造。在这一点上,为时已晚。我们已经在那里。我们不能...备份,Ctrl-Z,中止流产,好吧。

正如我在评论中提到的,我不确定这在C 14中是否有所不同。为了评估X x({}),我们必须构造一个X,该CC_13我们绑定到移动构造函数的RVALUE参考参数 - 我们无法在此时进行移动,在我们甚至知道我们知道我们正在做的之前,参考绑定发生在一个动作。

最新更新