构造函数调用return语句

Ale*_*wer 13 c++ language-lawyer c++17

请考虑以下示例:

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

X foo() {
    X result;
    return result;
}

int main() {
    foo();
}
Run Code Online (Sandbox Code Playgroud)

Clang和GCC对该计划是否有效表示不同意见.GCC尝试在调用期间初始化临时函数时调用move构造函数,该函数foo()已被删除,从而导致编译错误.Clang处理这个很好,即使有-fno-elide-constructors.

任何人都可以解释为什么GCC被允许在这种情况下调用移动构造函数?不是result左值?

Sto*_*ica 11

我要引用C++ 17(n4659),因为那里的措辞最为明确,但之前的修改说的相同,对我来说不太清楚.这是[class.copy.elision]/3,强调我的:

在以下复制初始化上下文中,可能会使用移动操作而不是复制操作:

  • 如果return语句中的表达式是一个(可能带括号的)id-expression,它指定一个对象,该对象具有在最内层封闭函数或lambda表达式的body或parameter-declaration-clause中声明的自动存储持续时间,或者

  • [...]

首先执行重载决策以选择副本的构造函数,就好像该对象是由rvalue指定的一样.如果第一个重载决策失败或未执行,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值. [注意:无论是否发生复制省略,都必须执行此两阶段重载决策.如果未执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数. - 结束说明]

所以这就是为什么实际上Clang和GCC都会尝试先调用此举.行为上的差异是因为Clang以不同的方式遵循粗体文本.超载解决方案发生了,发现了一个移动,但称它是不正确的.所以Clang再次执行它并找到副本c'tor.

GCC只是停在其轨道上,因为在重载决议中选择了删除的功能.

我相信Clang在这里是正确的,如果有的话,在精神上.尝试移动返回的值并作为回退进行复制是此优化的预期行为.我觉得海湾合作委员会不应该停止,因为它找到了一个删除的移动c'tor.如果它是一个被定义删除的默认移动,那么编译器都不会.该标准意识到情况下的潜在问题([over.match.funcs]/8):

定义为已删除的默认移动特殊函数([class.copy])将从所有上下文中的候选函数集中排除.

  • 我认为铿锵有误.删除与丢失不一样,如果选择此选项,则故意"中断".选择已删除的重载时,重载分辨率不会失败.Gcc,英特尔和MSVC同意 - 我一年前提交了http://bugs.llvm.org/show_bug.cgi?id=31025,但没有人评论它. (3认同)
  • 什么@Cubbi说.如果["最佳可行功能存在并且是唯一的"](https://timsong-cpp.github.io/cppwp/over.match#3.sentence-1),则重载分辨率会成功.而且我不相信"预期的行为"; 将删除的函数作为陷阱处理在同样合理的情况下也与核心语言在类似情况下的行为一致.毕竟,如果移动构造函数触发了`static_assert`,则编译器不会回溯. (2认同)

Bar*_*rry 8

来自[class.copy.elision]:

在以下复制初始化上下文中,可以使用移动操作而不是复制操作:如果return语句中的表达式是(可能带括号的)id-expression,它指定在body [...中声明的自动存储持续时间的对象. ..]

首先执行重载决策以选择副本的构造函数,就好像该对象是由rvalue指定的一样.如果第一个重载决策失败或未执行,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值.

return result;我们正好在该段落中提到的情况下 - result是一个id-expression命名一个具有在体内声明的自动存储持续时间的对象.因此,我们首先执行重载决策,就好像它是一个右值.

重载决议将找到两个候选人:X(X const&)X(X&&).后者是优选的.

现在,重载决策失败意味着什么?来自[over.match]/3:

如果存在一个最好的可行函数并且是唯一的,则重载解析成功并将其作为结果产生.否则重载解析失败并且调用格式错误.

X(X&&)是一个独特的,最好的可行功能,因此重载分辨率成功.这个复制上下文对我们来说有一个额外的标准,但我们也满足它,因为这个候选者的第一个参数的类型是(可能是cv-qualified)rvalue引用X.两个盒子都经过检查,所以我们就此止步.作为左值,我们不再继续执行重载决策,我们已经选择了我们的候选者:移动构造函数.

一旦我们选择它,程序就会失败,因为我们试图调用已删除的函数.但只是在那一点上,而不是更早.候选者不会被排除在过载集之外被删除,否则删除重载将不会有用.

这是铿锵声31025.