Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

With rvalue references, many redundant copies may be elided, but that seems to require me to write the same function multiple times (one for an rvalue reference, one for a const lvalue reference). But the standard library seems to only need to declare some functions once.

For example:

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

Calling foo(x) is a compile error, because I cannot convert implicitly from int to int&&. But I am perplexed as to why std::make_tuple would work. The reference says that it only accepts rvalue reference parameters. It also seems to not make copies when the value passed into it is an ravlue reference, but it would make a copy (as most would expect) when used as in my sample above.

How can I make foo work like this?

like image 828
Bernard Avatar asked Dec 11 '22 12:12

Bernard


1 Answers

The reference says that it only accepts rvalue reference parameters.

No, this is forwarding reference, which could serve as both lvalue reference and rvalue reference, according to the value category of the passed-in argument.

How can I make foo work like this?

The point of declaring a forwarding reference is (1) type deduction is necessary, that means you need to make foo a function template here; (2) the parameter x has the exact form of T&& for the template parameter T. e.g.

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

then

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&&

Note this is true for std::make_tuple too, even it's using template parameter pack. And better to bear in mind that even forwarding reference looks like rvalue reference but they're different things.

BTW: std::forward is usually used with forwarding reference to preserve the value category of the argument e.g. when forwarding it to other functions.

like image 93
songyuanyao Avatar answered May 19 '23 19:05

songyuanyao