12

This is a followup from function template does not recognize lvalue

Lets play with the following code:

#include <iostream> template <class T> void func(T&&) { std::cout<<"in rvalue\n"; } template <class T> void func(const T&) { std::cout<<"in lvalue\n"; } int main() { double n=3; func<double>(n); func(n); } 

It prints:

in lvalue in rvalue 

I don't understand what's happening in the second call. How the compiler resolve the template parameter ? Why isn't there any ambiguity ?

6
  • 5
    You may wish to consult 14.8.2 in the Standard. Template argument deduction is a somewhat complex subject. The point is that the "best match" is chosen, and that T may be deduced to be a reference type. Commented Mar 4, 2014 at 9:14
  • "somewhat complex subject" ! I fully agree. By the way, is there a way to ask g++/clang++ to be verbose about what it's doing here ? Commented Mar 4, 2014 at 9:20
  • That innocent little subsection occupies 15 pages and develops a whole new mathematical notation :-S But ultimately it "does what you think", you just have to embrace template parameters as honest types in the context of a function signature. Commented Mar 4, 2014 at 9:23
  • 3
    I found this link helpful : isocpp.org/blog/2012/11/… Commented Mar 4, 2014 at 9:29
  • No, it doesn't print log, or what it does. But template warnings/errors are quite verbose, if that's what you mean. Commented Mar 4, 2014 at 9:30

1 Answer 1

16

When you say func<double>(n), there's no argument deduction, since you specify the argument, and so the choice is between func(double &&) and func(const double &). The former isn't viable, because an rvalue reference cannot bind to an lvalue (namely n).

Only func(n) performs argument deduction. This is a complex topic, but in a nutshell, you have these two possible candidates:

T = double &: func(T &&) --> func(double &) (first overload) T = double: func(const T &) --> func(const double &) (second overload) 

The first overload is strictly better, because it requires one less conversion of the argument value (namely from double to const double).

The magic ingredient is the "reference collapsing", which means that T && can be an lvalue reference when T is itself a reference type (specificaly, double & && becomes double &, and that allows the first deduction to exist).

Sign up to request clarification or add additional context in comments.

6 Comments

I think Herb Sutter's article Why Not Specialize Function Templates? also helps to understand this behavior.
@piwi Except that it's hopelessly out of date since the introduction of rvalue references. @Kerrek doesn't really make it clear enough: when template argument deduction is taking place, the && in func( T && ) is not an rvalue reference, but a universal reference, which can become either an rvalue reference (if the argument is an rvalue) or an lvalue reference (if the argument is an lvalue). Scott Meyer explains this well in isocpp.org/blog/2012/11/…; that's the article I'd recommend for this particular question.
@JamesKanze I am aware of the double meaning of &&, depending on the context, but you're right that Herb Sutter's article does not cover this; however, I found it useful to understand resolution rules when "mixing" templates and overloads. Thanks for the link!
@piwi It's definitely a useful article, and very well written. It just doesn't address the behavior in question here (mainly because the behavior didn't exist when the article was written).
@JamesKanze: I linked to another answer which already spelt out all the details, I didn't want to repeat them here...
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.