user3759055
user3759055

Reputation: 39

Why can't I mix C++ universal references with standard reference?

I'm trying to understand C++11's universal references, and wrote the following code:

#include <cstdio>                                                                                                                                                                                                                                            

template <typename L, typename R>                                                                                                                                                                                                                            
static void Run(L&& lhs, const R& rhs) {                                                                                                                                                                                                                     
  lhs += rhs;                                                                                                                                                                                                                                                
}                                                                                                                                                                                                                                                            

static void Run2(int&& lhs, const int& rhs) {                                                                                                                                                                                                                
  lhs += rhs;                                                                                                                                                                                                                                                
}                                                                                                                                                                                                                                                            

int main() {                                                                                                                                                                                                                                                 
  int a = 0;                                                                                                                                                                                                                                                 
  int b = 3;                                                                                                                                                                                                                                                 
  Run(a, b);                                                                                                                                                                                                                                                 
  printf("%d\n", a);                                                                                                                                                                                                                                         
  // This does not compile.                                                                                                                                                                                                                                  
  Run2(a, b);                                                                                                                                                                                                                                                
  printf("%d\n", a);                                                                                                                                                                                                                                         
  return 0;                                                                                                                                                                                                                                                  
}

Note that Run() works, but the line calling Run2() will fail to compile. I can't really figure out why. The error I get is no known conversion from 'int' to 'int &&' for 1st argument.

I'm sure the compiler is right, but I can't figure out under why. It seems to be Run() and Run2() are doing the same things right?

Btw, changing Run2() to be templated with a single parameter also does not work.

Upvotes: 0

Views: 460

Answers (4)

Javier Mart&#237;n
Javier Mart&#237;n

Reputation: 2605

"Universal references" can only happen in contexts where the type of the reference is deduced, such as in a template type context T&&. In this case, T can either be deduced as int for an rvalue argument, and so you have int&& as the argument type; or as int& for your lvalue argument, which would give the type int& && that per reference collapsing rules becomes just int&. In your Run2 function, the type is directly int&& and so it cannot bind to any lvalue unless you use std::move.

Upvotes: 5

T33C
T33C

Reputation: 4429

Run(a,b) calls a function where typename L==int& and R==int

so static void Run(L&& lhs, const R& hrs)

is template instantiated as

static void Run(int & && lhs, const int& hrs)

or actually because of reference collapsing, it becomes

static void Run(int& lhs, const int& hrs)

Run2 expects an rvalue reference for lhs which it can only get from casting or being given an unnamed variable (temporary). In, your case, you could call the function using a cast:

Run(std::move(a), b)

Upvotes: 1

neshkeev
neshkeev

Reputation: 6476

The first one is a universal reference (as it described in the book of Scott Meyers Effective Modern C++) and the second one is rvalue reference

In order to make it work you need to call it like this:

Run2(std::move(a), b); 

Upvotes: 1

Kerrek SB
Kerrek SB

Reputation: 477150

Rvalue references cannot bind to lvalues.

The special rule for creating forwarding references is that a deduced template parameter T may be deduced as T = U& when it appears in a function parameter T&&, which makes the function parameter type be U& (thanks to reference collapsing from U& &&). However, the types of the parameters of Run2 are not deduced, so the first parameter is directly an rvalue reference.

Changing Run2 to a function template with a single template parameter means that the argument deduction will fail, since there is no consistent set of types that fits both arguments.

Upvotes: 2

Related Questions