"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::forward
。 v1
和 v2
都将被转换为右值引用,并且在这种情况下在重载决策下的行为方式相同。
下面是 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
类型的左值。 表达式 v1
和 v2
不 "remember" 它们是否最初是 return 值对象。
std::forward
仅在给定模板参数时有用,该模板参数是完美转发推导的结果(因此,可能是引用类型)。
建议使用decltype
。 decltype(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) );
#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::forward
。 v1
和 v2
都将被转换为右值引用,并且在这种情况下在重载决策下的行为方式相同。
下面是 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
类型的左值。 表达式 v1
和 v2
不 "remember" 它们是否最初是 return 值对象。
std::forward
仅在给定模板参数时有用,该模板参数是完美转发推导的结果(因此,可能是引用类型)。
建议使用decltype
。 decltype(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) );