如果可能,C++ 通过右值引用传递参数,否则复制左值引用
C++ pass parameter by rvalue reference if possible, otherwise copy the lvalue reference
使用右值引用,许多冗余副本可能会被省略,但这似乎需要我多次编写相同的函数(一次用于右值引用,一次用于 const
左值引用)。但是标准库好像有些函数只需要声明一次。
例如:
#include <iostream>
#include <tuple>
void foo(int&& x){
x = 2;
}
int main()
{
int x = 1;
foo(x); // compile error
std::make_tuple(x); // ok
std::cout << x << std::endl;
}
调用foo(x)
是编译错误,因为我无法从int
隐式转换为int&&
。但是我很困惑为什么 std::make_tuple
会起作用。 reference 表示它只接受右值引用参数。当传递给它的值是一个 ravlue 引用时,它似乎也不会制作副本,但是当在我上面的示例中使用时它会制作副本(正如大多数人所期望的那样)。
如何让 foo
像这样工作?
这对你有用吗?
#include <iostream>
#include <tuple>
void foo(int&& x){
std::cout<<"rvalue"<<std::endl;
x = 2;
}
void foo(int& x){
std::cout<<"lvalue"<<std::endl;
x = 2;
}
int main()
{
int x = 1;
foo(x); // no compile error anymore
foo(std::move(x)); // now r-value is being used
std::make_tuple(x); // ok
std::cout << x << std::endl;
}
The reference says that it only accepts rvalue reference parameters.
不是,这是forwarding reference,根据传入参数的值类别,它既可以作为左值引用也可以作为右值引用
How can I make foo
work like this?
声明转发引用的要点是(1)类型推导是必要的,这意味着你需要在这里制作foo
一个函数模板; (2) 参数 x
对于模板参数 T
具有 T&&
的确切形式。例如
template <typename T>
void foo(T&& x){
x = 2;
}
然后
int x = 1;
foo(x); // lvalue passed, T is deduced as int&, parameter's type is int&
foo(1); // rvalue passed, T is deduced as int, parameter's type is int&&
请注意,std::make_tuple
也是如此,即使它使用的是模板参数包。最好记住,即使转发引用看起来像右值引用,但它们是不同的东西。
顺便说一句:std::forward 通常与转发引用一起使用以保留参数的值类别,例如当转发到其他功能时。
std::make_tuple
之所以有效,是因为该函数实际上并不像 foo
那样采用右值引用。 std::make_tuple
采用 T&&
形式的对象,并且由于 T
是模板类型,它使其成为转发引用,而不是右值引用。转发引用可以绑定到左值和右值,而右值引用只能采用右值。要使 foo
相同,您需要
template<typename T>
void foo(T&& bar)
{
bar = 2;
}
差异是 &&
运算符与模板参数一起使用时相当微妙的重载的结果:
template<typename foo>
void bar(foo &&baz)
这不是右值引用。它是转发参考。解析模板后,baz
将成为左值或右值引用,具体取决于调用情况。
这就是为什么您会看到 C++ 库提供看似单一的模板,它在左值和右值上下文中都有效。
但转发引用仅出现在模板中。在非模板声明中:
void bar(int &&baz)
这始终是右值引用,并且只能在右值上下文中使用。
使用右值引用,许多冗余副本可能会被省略,但这似乎需要我多次编写相同的函数(一次用于右值引用,一次用于 const
左值引用)。但是标准库好像有些函数只需要声明一次。
例如:
#include <iostream>
#include <tuple>
void foo(int&& x){
x = 2;
}
int main()
{
int x = 1;
foo(x); // compile error
std::make_tuple(x); // ok
std::cout << x << std::endl;
}
调用foo(x)
是编译错误,因为我无法从int
隐式转换为int&&
。但是我很困惑为什么 std::make_tuple
会起作用。 reference 表示它只接受右值引用参数。当传递给它的值是一个 ravlue 引用时,它似乎也不会制作副本,但是当在我上面的示例中使用时它会制作副本(正如大多数人所期望的那样)。
如何让 foo
像这样工作?
这对你有用吗?
#include <iostream>
#include <tuple>
void foo(int&& x){
std::cout<<"rvalue"<<std::endl;
x = 2;
}
void foo(int& x){
std::cout<<"lvalue"<<std::endl;
x = 2;
}
int main()
{
int x = 1;
foo(x); // no compile error anymore
foo(std::move(x)); // now r-value is being used
std::make_tuple(x); // ok
std::cout << x << std::endl;
}
The reference says that it only accepts rvalue reference parameters.
不是,这是forwarding reference,根据传入参数的值类别,它既可以作为左值引用也可以作为右值引用
How can I make
foo
work like this?
声明转发引用的要点是(1)类型推导是必要的,这意味着你需要在这里制作foo
一个函数模板; (2) 参数 x
对于模板参数 T
具有 T&&
的确切形式。例如
template <typename T>
void foo(T&& x){
x = 2;
}
然后
int x = 1;
foo(x); // lvalue passed, T is deduced as int&, parameter's type is int&
foo(1); // rvalue passed, T is deduced as int, parameter's type is int&&
请注意,std::make_tuple
也是如此,即使它使用的是模板参数包。最好记住,即使转发引用看起来像右值引用,但它们是不同的东西。
顺便说一句:std::forward 通常与转发引用一起使用以保留参数的值类别,例如当转发到其他功能时。
std::make_tuple
之所以有效,是因为该函数实际上并不像 foo
那样采用右值引用。 std::make_tuple
采用 T&&
形式的对象,并且由于 T
是模板类型,它使其成为转发引用,而不是右值引用。转发引用可以绑定到左值和右值,而右值引用只能采用右值。要使 foo
相同,您需要
template<typename T>
void foo(T&& bar)
{
bar = 2;
}
差异是 &&
运算符与模板参数一起使用时相当微妙的重载的结果:
template<typename foo>
void bar(foo &&baz)
这不是右值引用。它是转发参考。解析模板后,baz
将成为左值或右值引用,具体取决于调用情况。
这就是为什么您会看到 C++ 库提供看似单一的模板,它在左值和右值上下文中都有效。
但转发引用仅出现在模板中。在非模板声明中:
void bar(int &&baz)
这始终是右值引用,并且只能在右值上下文中使用。