如果可能,C++按右值引用传递参数,否则复制左值引用

C++ pass parameter by rvalue reference if possible, otherwise copy the lvalue reference

本文关键字:引用 参数 复制 C++ 如果      更新时间:2023-10-16

对于右值引用,可以省略许多冗余副本,但这似乎需要我多次编写相同的函数(一个用于右值引用,一个用于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会起作用感到困惑。 引用说它只接受右值引用参数。 当传入的值是 ravlue 引用时,它似乎也不会制作副本,但是当按照我上面的示例使用时,它会制作副本(正如大多数人所期望的那样(。

我怎样才能foo这样工作?

引用说它只接受右值引用参数。

不,这是转发引用,根据传入参数的值类别,它可以用作左值引用和右值引用。

如何使foo像这样工作?

声明转发引用的要点是(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 通常与转发引用一起使用,以保留参数的值类别,例如将其转发到其他函数时。

这种差异是由于 && 运算符与模板参数一起使用时相当微妙的重载造成的:

template<typename foo>
void bar(foo &&baz)

这不是右值引用。它是一个转发引用。解析模板后,baz将是左值或右值引用,具体取决于调用情况。

这就是为什么您将看到C++库提供了看似单个模板的内容,该模板在左值和右值上下文中都有效。

但转发引用仅发生在模板中。在非模板声明中:

void bar(int &&baz)

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

std::make_tuple工作,因为该函数实际上不像foo那样采用右值引用。 std::make_tuple采用T&&形式的对象,由于T是模板类型,因此使其成为转发引用,而不是右值引用。 转发引用可以绑定到左值和右值,其中右值引用只能采用右值。 要使foo相同,您需要

template<typename T>
void foo(T&& bar)
{
    bar = 2;
}

这对你有用吗?

#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;
}