"auto v = f()" 和 "auto&& v = f()" 有什么区别?

What's the difference between "auto v = f()" and "auto&& v = f()"?

#include <vector>

using namespace std;

vector<int> f()
{
    return{};
}

// Update: Below is not compilable
void g(vector<int>)
{}

// Update: Below is the my initial intent.
/*
void g(const vector<int>&)
{}
*/

void g(vector<int>&&)
{}

int main()
{
    auto       v1 = f();
    auto&& v2 = f();

    g(forward<vector<int>>(v1));
    g(forward<vector<int>>(v2));
}

C++11 是否保证 g(forward<vector<int>>(v1)) 会调用 f(vector<int>)f(const vector<int>&)g(forward<vector<int>>(v2)) 会调用 f(vector<int>&&)

不同之处在于 v1 是向量,而 v2 是对向量的右值引用。

按照您的方式重载 g 是一个非常糟糕的主意。如果参数是一个 cv 非限定右值,那么调用将是不明确的,因为两个 g 都可以接受带有身份转换序列的右值。但是,一个重载采用 T& 而另一个采用 T&&.

是可以接受的

如果要转发 f() 的值类别,请不要像 v1 那样 copy/move 转发。这会破坏值类别信息。 v1 永远是左值。

此外,您没有正确使用std::forwardv1v2 都将被转换为右值引用,并且在这种情况下在重载决策下的行为方式相同。

下面是 std::forward 的正确用法:

void g(vector<int>&);
void g(vector<int>&&);
int main() {
    auto&& v1 = function_returning_lvalue_vector();
    auto&& v2 = function_returning_rvalue_vector();
    g(forward<decltype(v1)>(v1));  // calls lvalue overload
    g(forward<decltype(v2)>(v2));  // calls rvalue overload
}

有一个对象与名为 return 值 的 return 值函数关联。 f() 的 return 值是 vector<int>.

在 C++11 和 C++14 中,其中 f() returns 按值计算:

  • auto v1 = f(); 使用 copy/move 构造函数从 return 值初始化一个 vector<int>,这将被称为 v1。这是一个复制省略上下文。
  • auto&& v2 = f(); 使名称 v2 指定 return 值,并延长 return 值的生命周期。

如果编译器确实实现了复制省略,那么这两个代码具有相同的效果。从 C++17 开始,有了所谓的 "guaranteed copy elision",这两个代码将是相同的。

"the same" 除了下文讨论的 decltype(identifier) 的结果外,我的意思在所有方面都相同。


你的两个 g 调用没有区别。在这两种情况下,参数都是 std::vector 类型的左值。 表达式 v1v2 不 "remember" 它们是否最初是 return 值对象。

std::forward 仅在给定模板参数时有用,该模板参数是完美转发推导的结果(因此,可能是引用类型)。

建议使用decltypedecltype(identifier) 是一个特例,它确实回忆起 标识符 是如何在应用 auto 扣除之后声明的。

  • decltype(v1)vector<int>
  • decltype(v2)vector<int> &&

但现在我们有:

  • std::forward<decltype(v1)>vector<int> &&
  • std::forward<decltype(v2)>vector<int> &&

所以你还是分不清g的两种不同形式。

事实上,如评论中所述,根本无法调用 g。每一个电话都将是模棱两可的。在重载决议中,直接引用绑定是一种身份转换;类型 T 的 xvalue 参数是参数 TT&& 的相等匹配。 (类似地,类型 T 的左值参数将与参数 TT& 相等匹配)。

可以重载 g 以实现左值与右值重载。但是您也需要更改 v1 的初始化:

void g(vector<int> const &) {}
void g(vector<int> &&) {}

// ...
auto const& v1 = f();
auto&& v2 = f();

g( std::forward<decltype(v1)>(v1) );
g( std::forward<decltype(v2)>(v2) );