简体   繁体   中英

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.

"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 .

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.

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); 

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM