0
#include <iostream>
#include <typeinfo>

struct C
{
  template<class T>
  C(T && t) { std::cout << typeid(T).name() << std::endl; }
};

struct D
{
  D(int && t) { }
};

int main() 
{
    int i = 1;
    std::cout << typeid(i).name() << std::endl;

    C c(i);     // OK
    D d(i);     // error
}

The line D d(i); fails to compile with:

foo.cc:22:7: error: cannot bind 'int' lvalue to 'int&&'

However, commenting it out, the generated output is:

i
i

which suggests that T was deduced as int. How does D(int &&) fail to bind but C(int &&) succeeds?

M.M
  • 138,810
  • 21
  • 208
  • 365
  • 3
    Deductions works differently and `T` becomes `int&`, does [this](http://stackoverflow.com/questions/8842312) help? – Daniel Frey Jul 24 '14 at 05:23
  • Note that `typeid` doesn't distinguish between `T` and `T&`. §5.2.8 [expr.typeid]/p4 - "If the type of the *type-id* is a reference to a possibly cv-qualified type, the result of the typeid expression refers to a `std::type_info` object representing the cv-unqualified referenced type." Use `std::is_same` to check if types are exactly identical. – T.C. Jul 24 '14 at 05:27
  • [Live example](http://coliru.stacked-crooked.com/a/0edd1beb589d0277) of what I said above. – Daniel Frey Jul 24 '14 at 05:29
  • Can you explain further about how `T` is deduced to `int&`? That would result in `C(int& && t)` which is illegal – M.M Jul 24 '14 at 05:31
  • 1
    @MattMcNabb Look up reference collapsing. – T.C. Jul 24 '14 at 05:33
  • 1
    @MattMcNabb I think that is explained in the answer to the Q/A I linked. I'm actually tempted to close this as a duplicate... – Daniel Frey Jul 24 '14 at 05:45
  • @DanielFrey It's explained by [this](http://thbecker.net/articles/rvalue_references/section_08.html) which I found following TC's suggestion. I didn't understand just by reading the linked answer, so YMMV. – M.M Jul 24 '14 at 05:47
  • [This answer](http://stackoverflow.com/a/24378637/241631) of mine explains the universal reference half. The `int&&` is just a plain rvalue reference to an `int`, so I don't think there's much to explain there. – Praetorian Jul 24 '14 at 05:51
  • In that case, here's [another link](http://stackoverflow.com/questions/14302849) that might help. – Daniel Frey Jul 24 '14 at 05:52
  • @Praetorian OK, looks good. So in Dietmar Kuhl's post, "An rvalue reference is never deduced" should read "An rvalue reference *must be* deduced" – M.M Jul 24 '14 at 05:53
  • 3
    @MattMcNabb No, I think what he's talking about is that the `U` is not deduced as `X&&`, but as `X`, and the `&&` from the parameter type gets added on. – Praetorian Jul 24 '14 at 05:57
  • @Praetorian I just noticed that if it is changed to `C(T const && t)`, then the reference collapsing never happens (it doesn't even bind to `int const i = 1;`) - why is that? (none of the answers linked so far seem to mention it) – M.M Jul 24 '14 at 21:48
  • @MattMcNabb Because it's not a *universal reference* anymore. The signature is very specific, the parameter must be `T&&` and the `T` must be deduced. – Praetorian Jul 24 '14 at 21:55
  • @Praetorian OK. Is there a rationale for not making `T const &&` be a universal reference? – M.M Jul 24 '14 at 22:04
  • 2
    @MattMcNabb Probably because universal references are mostly used in perfect forwarding scenarios where you forward the arguments to your function to their destination while maintaining the value category of the argument as it was passed to your function. You cannot move out of a `const&&` argument, it must be copied. So perfect forwarding wouldn't apply in that case. This is a guess, I don't know if that is the entire reason behind that, or why universal references have such a narrow definition. – Praetorian Jul 24 '14 at 22:21

1 Answers1

2

Answering my own question, based on all the great info provided in comments:

  • int && can only bind to rvalues
  • T&& can bind to rvalues and lvalues

The Standard doesn't have a specific term for "can bind to rvalues and lvalues", so Scott Meyers coined the term universal reference, and this article by him explains everything.

Rationale: the non-universal version is used for move semantics, but the universal version is required for perfect forwarding.

During the development of C++11, some argued that different syntax should be used for moving than for forwarding, however they lost and && was used for both, with T&& in a deduced context meaning forwarding, and && in other contexts meaning moving.

Info taken from this post by Jonathan Wakely; in fact my question could be closed as a duplicate of that quesiton.

Also, T const && is not a universal reference, probably because it is not useful for forwarding.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365