【发布时间】: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