clang 中奇怪的右值引用

Koz*_*mar 6 c++ clang rvalue-reference move-semantics c++17

下面的代码:

#include <iostream>

struct A {
  A() { std::cout << "()" << std::endl; }  
  A(A&&) { std::cout << "(A&&)" << std::endl; }  
  A(const A&) { std::cout << "(const A&)" << std::endl; }  
};

A fun (A&& a){
  return a;
}

int main(){
  A a;
  fun(std::move(a));
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

在 clang 16.0.3 (arm64-apple-darwin22.4.0 和 c++17) 中产生

()
(A&&)
Run Code Online (Sandbox Code Playgroud)

虽然大多数编译器给出

()
(const A&)
Run Code Online (Sandbox Code Playgroud)

哪一个是正确的?

use*_*570 1

A::A(A&&)由于按值返回时局部变量和参数的隐式移动规则,因此应在此处使用移动构造函数:

如果表达式是一个(可能带括号的)id 表达式,它命名一个变量,该变量的类型是

  • 非易失性对象类型或

  • 对对象类型的非易失性右值引用 (C++20 起)

并且声明了该变量

  • 在体内或

  • 作为参数

    最里面的封闭函数或 lambda 表达式,

然后执行两次重载解析以选择用于初始化返回值的构造函数,或者对于 co_return,选择 Promise.return_value() (C++20 起) 的重载:

  • 首先,就好像表达式是一个右值表达式(因此它可以选择移动构造函数),并且
  • 如果第一个重载决议失败或者
  • 它成功了,但没有选择移动构造函数(正式地,所选构造函数的第一个参数不是对(可能是 cv 限定的)表达式类型的右值引用)(C++20 之前)
  • 然后像往常一样执行重载决策,并将表达式视为左值(因此它可以选择复制构造函数)。

(强调我的)

在您的示例中,您有语句return a;并且a是最内层封闭函数的参数,因此重载解析可以执行两次。在第一步中a,将其视为右值表达式,因此可以在此处使用移动构造函数。

请注意,由于第一步已成功,因此无需执行第二步。