2017-02-27 176 views
1
#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>&&)“auto v = f()”和“auto && v = f()”有什么区别?

+1

这不能在我的机器上编译。调用g()是不明确的。所以这个问题实际上应该是为什么会发生。 – zett42

+1

我强烈建议恢复编辑,您的更改会使现有答案无效。或者在编辑中发布附录,而不更改原始代码 –

+0

@ M.M,根据您的建议更新了文章。 – xmllmx

回答

3

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

超载g你做的方式是一个非常糟糕的主意。如果参数是cv不合格的右值,那么调用将是不明确的,因为这两个g都可以接受具有身份转换序列的右值。然而,可以接受的是一个超载取T&,另一个取T&&

如果您想转发f()的值类别,请不要像您所做的那样将其复制/移动到v1。这破坏了价值类别信息。 v1将永远是一个左翼。

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

这里是什么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 
} 
+0

_v2是一个右值reference_ ...实际上它不是,见M.M.的答案。 – zett42

+0

@ zett42为了保护这个答案 - 说“v2'是...”是不明确的; v2 *实体*是右值引用,但v2 *表达式*是左值。我同意第一段可以稍微澄清一点,因为对于那些不了解这种区别的人来说,这没什么意义。 –

2

有叫返回值有回传值功能相关联的对象。 f()的返回值是vector<int>

在C++ 11和C++ 14,其中f()通过返回值:

  • auto v1 = f();初始化vector<int>,这将称为v1,从返回值,使用复制/移动的构造。这是一个复制elision上下文。
  • 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)); 
+0

到目前为止这么好,但为什么g()模糊不清?如果我删除forward(),它就不再是模糊了。然后,如预期的那样,总是会调用第一个重载。 – zett42

+0

@ zett42类型'T'的rvalue与'T'的匹配与'T &&' –

+0

相等但不应该forward()返回一个左值引用? – zett42

相关问题