简体   繁体   中英

Different behavior when choosing which template overload to use when using rvalue references

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 ?

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).

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 );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM