[c++0x] name deduction in overloading with rvalue references

C

Chris Fairles

Take the following snip (pretend A its a std::pair if you wish ;) )

template <class T1,class T2>
struct A {
A(A const&){cout<<"A(const&) ";}
A(A&&){cout<<"A(A&&) ";}
A(T1 const&, T2 const&){cout<<"A(T1,T2) ";}

template<class U1, class U2>
A(A<U1,U2> &&) {cout<<"A(A<U1,U2>&&) ";}
template<class U, class... Args>
A(U&&,Args&&...){cout<<"A(U&&,Args&&...) ";}
};

int main(){
A<int,int> a(1,1);
A<int,int> b(a);
}

The output (gcc 4.3.0 20070824 with -std=c++0x) is:
A(U&&,Args&&...) A(U&&,Args&&...)

If I was to guess I would have thought:
A(T1,T2) A(const&)

So, the literal 1's are rvalues of type int&& I guess. So when the
params are deduced for the templated ctor with the variadic parameter,
I'm guessing U = int, Args... = int. Since 1 is an rvalue, A(int&&,
int&&) is the best match.

Ok. The next one, 'a' is an Lvalue of type A<int,int>. U then is
deduced to type A<int,int>& (N2639 14.8.2.1.3 I guess). I take it the
&& is dropped but I'm not sure how during the deduction process. I
guess it looks at parameter type U, see's that the argument is type
U&& but the REAL argument type is an lvalue so it converts the entire
argument type to an lvalue reference (unlike the first case where
int&& is the argument type, the real argument type is rvalue of type
int, so int&& is valid).

So, I go and add remove_reference<>'s to the parameter types (U and
Args). Now, in the first case A(T1,T2) is picked. I'd take a stab and
say that in "remove_reference<U>::type &&", the remove_reference is
applied AFTER the argument type is deduced to be int&& ? So the
argument type is... int? And the constant lvalue reference is picked
over an lvalue of type int when the actual argument type is an rvalue
int?

My head spins when I try to figure this stuff out so I wouldn't mind
if someone shed some light on the deduction process in the two cases
(that is, with and without remove_reference).

Much appreciated,
Chris
 
H

Howard Hinnant

Chris Fairles said:
Take the following snip (pretend A its a std::pair if you wish ;) )

template <class T1,class T2>
struct A {
A(A const&){cout<<"A(const&) ";}
A(A&&){cout<<"A(A&&) ";}
A(T1 const&, T2 const&){cout<<"A(T1,T2) ";}

template<class U1, class U2>
A(A<U1,U2> &&) {cout<<"A(A<U1,U2>&&) ";}
template<class U, class... Args>
A(U&&,Args&&...){cout<<"A(U&&,Args&&...) ";}
};

int main(){
A<int,int> a(1,1);
A<int,int> b(a);
}

The output (gcc 4.3.0 20070824 with -std=c++0x) is:
A(U&&,Args&&...) A(U&&,Args&&...)

If I was to guess I would have thought:
A(T1,T2) A(const&)

So, the literal 1's are rvalues of type int&& I guess. So when the
params are deduced for the templated ctor with the variadic parameter,
I'm guessing U = int, Args... = int. Since 1 is an rvalue, A(int&&,
int&&) is the best match.

Ok. The next one, 'a' is an Lvalue of type A<int,int>. U then is
deduced to type A<int,int>& (N2639 14.8.2.1.3 I guess). I take it the
&& is dropped but I'm not sure how during the deduction process. I
guess it looks at parameter type U, see's that the argument is type
U&& but the REAL argument type is an lvalue so it converts the entire
argument type to an lvalue reference (unlike the first case where
int&& is the argument type, the real argument type is rvalue of type
int, so int&& is valid).

So, I go and add remove_reference<>'s to the parameter types (U and
Args). Now, in the first case A(T1,T2) is picked. I'd take a stab and
say that in "remove_reference<U>::type &&", the remove_reference is
applied AFTER the argument type is deduced to be int&& ? So the
argument type is... int? And the constant lvalue reference is picked
over an lvalue of type int when the actual argument type is an rvalue
int?

My head spins when I try to figure this stuff out so I wouldn't mind
if someone shed some light on the deduction process in the two cases
(that is, with and without remove_reference).

Catching up on my newsgroup reading...

A<int,int> a(1,1);

binds to:

A(U&&,Args&&...){cout<<"A(U&&,Args&&...) ";}

with U == int and Args == int. This is a better match than:

A(T1 const&, T2 const&){cout<<"A(T1,T2) ";}

because of the conversion to const int in each of the arguments. Note
that int does not get converted to type int&&. The '1's are just int.
It is because they are not const lvalues that is the deal killer.

Very similar story with :

A<int,int> b(a);

Because 'a' is non-const it prefers the A(U&&,Args&&...) constructor
with U == A<int, int>&, and Args = {}. When template deduction happens
against U&&, if the argument A bound is an lvalue, U is deduced as A&,
not A. This makes perfect forwarding work.

The way this should be programmed in C++0X is:

template<class U, class... Args>
requires Constructible<T1, U&&> && Constructible<T2, Args&&...>
A(U&&,Args&&...){cout<<"A(U&&,Args&&...) ";}

I believe the addition of the requires clause will give you your
expected result. However I am unfortunately currently unable to compile
this to assure that I am correct.

-Howard
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top