Reputation: 5821
The following code:
#include <stdio.h>
class Base {};
template< typename... T >
void test1( T const & ... )
{
printf( "test1( args ) called\n" );
}
template< typename... T >
void test1( Base const &, T const & ... )
{
printf( "test1( base, args ) called\n" );
}
template< typename... T >
void test2( T && ... )
{
printf( "test2( args ) called\n" );
}
template< typename... T >
void test2( Base const &, T && ... )
{
printf( "test2( base, args ) called\n" );
}
int main()
{
test1( 1, 2, 3 );
test1( Base(), 1, 2, 3 );
test2( 1, 2, 3 );
test2( Base(), 1, 2, 3 );
}
outputs:
test1( args ) called
test1( base, args ) called
test2( args ) called
test2( args ) called
The compiler called a more specific version of test1
when Base
was passed. However, it did not do such thing in the case of test2
- the overload which takes Base
was never called. Why does the compiler honor the specificity with test1
, but not with test2
?
Upvotes: 4
Views: 74
Reputation: 56547
The overload test1(Base const&, T...)
is preferred vs test1(T const...)
because it is more specialized, the compiler doesn't have to do any type deduction for the first parameter in the first case.
Now moving on to test2
: you pass a Base()
rvalue, and the compiler has to choose between binding it to T&&
or to Base const&
. The T&&
is preferred, since the type of T
is deduced as Base
and the resulting Base&&
matches perfectly, compared to Base const&
.
A rule of thumb is that forward references (or, as Scott Meyers called them, universal references) are greedy and tend to bind to anything, and you should be careful when you use them. In particular, avoid overloading constructors with forward references, since they may "disable" even your copy constructor (see Item 26 from Effective Modern C++ by Scott Meyers).
Upvotes: 3
Reputation: 842
http://en.cppreference.com/w/cpp/language/template_argument_deduction http://en.cppreference.com/w/cpp/language/overload_resolution
In this code, Base() is an rvalue, and so are the other arguments:
test2( Base(), 1, 2, 3 );
This function does not take a rvalue reference to Base as a parameter (even though it is convertible):
void test2( Base const &, T && ... )
So deduction and overload resolution chooses this function as a result:
void test2( T && ... )
which seems more appropriate.
To get the output I think you want, you need a non-const rvalue reference to Base (a "forwarding reference"):
void test2( Base &&, T && ... )
or a const lvalue of Base as an argument to your already-existing function:
const Base b;
test2( b, 1, 2, 3 );
Upvotes: 1