【问题标题】:Does C++17 forbid copy elision in a case where C++14 allowed it?在 C++14 允许的情况下,C++17 是否禁止复制省略?
【发布时间】:2018-02-08 09:07:53
【问题描述】:

考虑以下几点:

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

在 C++14 中,尽管移动构造函数由于 [class.copy]/31 而存在副作用,但仍可以省略移动,

复制/移动操作的省略...在以下情况下是允许的...当尚未绑定到引用 (12.2) 的临时类对象将被复制/移动时 到具有相同 cv 非限定类型的类对象

在 C++17 中,此项目符号已被删除。相反,由于 [dcl.init]/17.6.1:

如果初始化表达式是prvalue并且源类型的cv-unqualified版本相同 class 作为目标的类,初始化表达式用于初始化目标 目的。 [示例: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 非限定版本与 目的地,构造函数被考虑。列举了适用的构造函数(16.3.1.3), 最好的一个是通过重载决议(16.3)选择的。如此选择的构造函数被调用 初始化对象,使用初始化表达式或表达式列表作为其参数。如果不 构造函数适用,或者重载决议不明确,初始化格式不正确。

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

【问题讨论】:

  • 这是核心问题 2327 的另一个变体。
  • @T.C.我在公开列表中找不到该问题
  • 我不太确定你的 C++14 结论。 {} 转换为 X... 类型的临时值,然后绑定到移动构造函数的引用。我认为您的初始报价不适用。
  • @Barry 如果要省略移动,那么肯定不会发生引用绑定。
  • @Brian 那是假设先行者。如果有绑定,则无法省略,因此因为省略而假设没有绑定有点过分。

标签: c++ language-lawyer copy-elision c++17


【解决方案1】:

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

考虑如下示例:

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

Dog d;
Cat c(d);

这转到 11.6 [dcl.init] 项目符号 17.6.2:

否则,如果初始化是直接初始化,或者如果是复制初始化,其中源类型的 cv 非限定版本与目标类的类或派生类相同,则构造函数是经过考虑的。枚举适用的构造函数 (16.3.1.3 [over.match.ctor]),并通过重载决议 (16.3 [over.match]) 选择最佳构造函数。调用如此选择的构造函数来初始化对象,使用初始化表达式或 expression-list 作为其参数。如果没有构造函数适用,或者重载决议不明确,则初始化格式不正确。

重载解析选择Cat的move构造函数。根据 11.6.3 [dcl.init.ref] 项目符号 5.2.1.2,初始化构造函数的 Cat&& 参数会产生一个临时结果。这排除了这种情况下复制省略的可能性。

这似乎是对保证复制省略的措辞更改的疏忽。在这种情况下,我们大概应该同时考虑构造函数和转换函数,就像我们对复制初始化所做的那样,但我们需要确保不会引入任何新问题或歧义。

使这成为相同潜在问题的原因是我们有一个类型错误的初始化程序(在 OP 中为{},在本例中为d) - 我们需要将其转换为正确的类型(XCat),但要弄清楚如何做到这一点,我们需要执行重载决议。这已经将我们带到了移动构造函数 - 我们将右值引用参数绑定到我们刚刚创建的新对象以实现这一点。在这一点上,要回避这一举动为时已晚。我们已经到了。我们不能...备份,ctrl-z,abort abort,好的重新开始。

正如我在 cmets 中提到的,我也不确定这在 C++14 中是否有所不同。为了评估X x({}),我们必须构造一个X,我们将其绑定到移动构造函数的右值引用参数——此时我们不能忽略移动,引用绑定在我们知道之前就发生了我们正在采取行动。

【讨论】:

  • 您在哪里找到了该缺陷报告?它似乎没有出现在公开的问题列表中。
  • @Brian 现在还没有,但应该在某个时候。它基于naturally, T.C.'s report
  • 我不同意执行重载解析需要引用绑定到实际发生的推理。我相信它只是确定应该调用移动构造函数,因为移动构造函数是最好的可行函数,但是编译器可以忽略它,因为引用绑定实际上还没有发生。
  • @Brian Public 现在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-14
  • 1970-01-01
  • 2011-08-18
  • 2016-01-27
  • 1970-01-01
  • 2018-05-04
相关资源
最近更新 更多