Template rvalue reference binds to an lvalue, but ordinary rvaluereference does not

K

K. Frank

Hello Group!

As I understand it, an rvalue reference is not allowed to bind
to an lvalue, and I see this behavior in simple test cases.
But when the rvalue reference is part of a template, it can.

Specifically:

void f (int&& arg) {}
template<typename T> void g (T&& arg) {}
void h() {
int i;
f (i); // fails -- error: cannot bind 'int' lvalue to 'int&&'
g (i); // succeeds
}

(To be clear, the line "g (i);" compiles, but "f (i);" does not.)

Why does the rvalue-reference argument in the template function
g bind more liberally than that in the regular function f? (And
why would this difference be a good thing?)

I suppose this would have something to do with template deduction,
but I haven't been able to make any progress with the standard on
this.


Thanks for any insight.


K. Frank
 
S

Spike

Hello Group!

As I understand it, an rvalue reference is not allowed to bind
to an lvalue, and I see this behavior in simple test cases.
But when the rvalue reference is part of a template, it can.

Specifically:

void f (int&& arg) {}
template<typename T> void g (T&& arg) {}
void h() {
int i;
f (i); // fails -- error: cannot bind 'int' lvalue to 'int&&'
g (i); // succeeds
}

(To be clear, the line "g (i);" compiles, but "f (i);" does not.)

Why does the rvalue-reference argument in the template function
g bind more liberally than that in the regular function f? (And
why would this difference be a good thing?)

I suppose this would have something to do with template deduction,
but I haven't been able to make any progress with the standard on
this.
This article explains everything:
http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers

S.
 
K

K. Frank

Hi Spike!


The article to which you link, "Universal References in C++11—Scott Meyers,"
is very useful, and does answer my question.

Quoting from that article:

template<typename T>
void f(T&& param); // here, “&&”does not mean rvalue reference

Well, so there you have it!

I have been blundering around in the standard, and haven't yet succeeded
in finding the standard's explanation of all of this. Any pointers?

Also, I still don't have a good grasp of why this "inconsistency"
exists.

Again, quoting from the Meyers article:

During type deduction for a template parameter that is a universal
reference, lvalues and rvalues of the same type are deduced to have
slightly different types. In particular, lvalues of type T are deduced
to be of type T& (i.e., lvalue reference to T), while rvalues of type
T are deduced to be simply of type T.

I suppose that this is (part of) the explanation. But this feels to
me rather like implementation. It seems like "lvalues of type T are
deduced to be of type T&" is using "type T&" for something it really
isn't, perhaps because the standard authors (or compiler writers)
didn't want to introduce (into the "implementation") the extra bit of
machinery necessary to make type deduction "work properly."

Is the "inconsistency" between the template and non-template &&
really just a question of implementation convenience, or is there
more to it?


Thanks for any additional wisdom.


K. Frank
 
V

Victor Bazarov

[..]
I have been blundering around in the standard, and haven't yet succeeded
in finding the standard's explanation of all of this. Any pointers?
[..]

[temp.deduct.call]/3 might shed some light. Also see [dcl.init.ref], in
whose guts the inability to initialize an rvalue ref with an lvalue is
probably buried.

V
 
K

K. Frank

Hello Victor!

[..]
I have been blundering around in the standard, and haven't yet succeeded
in finding the standard's explanation of all of this. Any pointers?
[..]

[temp.deduct.call]/3 might shed some light.

Quoting:

If P is an rvalue reference to a cv-unqualified template parameter
and the argument is an lvalue, the type “lvalue reference to A” is
used in place of A for type deduction.

Yes, thanks, that seems to be it.

And note the little land mine planted by the phrase "cv-unqualified,"
called out in the example:

template <class T> int g(const T&&);
int i;
// ...
int n3 = g(i); // error: would call g<int>(const int&&), which
// would bind an rvalue reference to an lvalue

(whereas if g were "int g(T&&)" -- i.e., without the const, then
"g(i)" would have been legal). Yikes!
Also see [dcl.init.ref], in
whose guts the inability to initialize an rvalue ref with an lvalue is
probably buried.

My word, "buried," indeed!

I suppose this is it:

In the second case, if the reference is an rvalue reference and
the second standard conversion sequence of the user-defined
conversion sequence includes an lvalue-to-rvalue conversion, the
program is ill-formed.


Thank you, Victor.


K. Frank
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top