“auto v = f()”和“auto&& v = f()”之间有什么区别?

时间:2017-02-27 00:31:43

标签: c++ c++11 auto perfect-forwarding type-deduction

#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>&&)吗?

2 个答案:

答案 0 :(得分:3)

区别在于v1是向量,而v2是对向量的右值引用。

以你完成的方式重载g是一个非常糟糕的主意。如果参数是cv-unqualified rvalue,那么调用将是不明确的,因为g两个都可以接受带有身份转换序列的rvalue。但是,可以接受一次重载T&,另一次重载T&&

如果您要转发f()的值类别,请不要像在v1中那样复制/移动它。这会破坏价值类别信息。 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
}

答案 1 :(得分:2)

有一个与返回值函数相关联的对象,称为返回值f()的返回值为vector<int>

在C ++ 11和C ++ 14中,f()按值返回:

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

如果编译器确实实现了copy-elision,那么这两个代码具有相同的效果。从C ++ 17开始,通过所谓的“保证副本省略”,这两个代码将是相同的。

“相同”,除了下面讨论的decltype(identifier)的结果外,我的意思在所有方面都是一样的。

您的两个g来电之间没有区别。在这两种情况下,参数都是std::vector类型的左值。 表达式 v1v2不会“记住”它们最初是否为返回值对象。

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参数与T的参数T&&相等。 (类似地,T类型的左值参数与T}的参数T&相等。

有可能重载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) );