如果可能,C ++通过rvalue引用传递参数,否则复制左值引用

时间:2017-03-23 13:36:43

标签: c++ function c++11 templates rvalue-reference

使用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像这样工作?

4 个答案:

答案 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_tupleT&&的形式获取对象,由于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)

这始终是一个右值引用,只能在右值上下文中使用。