使用rvalue引用时,可能会省略许多冗余副本,但这似乎要求我多次写入相同的函数(一个用于右值引用,一个用于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
像这样工作?
答案 0 :(得分:7)
参考文献说它只接受右值参考参数。
不,这是forwarding reference,根据传入参数的值类别,它可以作为左值引用和右值引用。
如何让
foo
像这样工作?
声明转发引用的重点是(1)类型推导是必要的,这意味着你需要在这里使foo
成为一个函数模板; (2)参数x
具有模板参数T&&
的确切形式T
。 e.g。
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
也是如此,即使它使用模板参数包也是如此。最好记住,甚至转发参考看起来像右值参考,但它们是不同的东西。
BTW:std::forward通常与转发引用一起使用以保留参数的值类别,例如:将其转发给其他功能时。
答案 1 :(得分:2)
std::make_tuple
有效,因为该函数实际上没有像foo
这样的右值引用。 std::make_tuple
以T&&
的形式获取对象,由于T
是模板类型,因此它使其成为转发引用,而不是右值引用。转发引用可以绑定到左值和右值,其中右值引用只能采用右值。要使foo
与您需要的相同
template<typename T>
void foo(T&& bar)
{
bar = 2;
}
答案 2 :(得分:1)
这对你有用吗?
#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;
}
答案 3 :(得分:1)
差异是&&
运算符与模板参数一起使用时相当微妙的重载的结果:
template<typename foo>
void bar(foo &&baz)
这不是右值参考。它是转发参考。解析模板后,baz
将是左值或左值引用,具体取决于调用情况。
这就是为什么您会看到C ++库提供看似单个模板的原因,它在左值和右值上下文中都有效。
但转发引用仅出现在模板中。在非模板声明中:
void bar(int &&baz)
这始终是一个右值引用,只能在右值上下文中使用。