使用右值引用时选择要使用的模板重载时的不同行为

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

以下代码:

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

输出:

test1( args ) called
test1( base, args ) called
test2( args ) called
test2( args ) called

编译器在传递 Base 时调用了更具体的 test1 版本。但是,在 test2 的情况下它没有做这样的事情 - 从未调用过 Base 的重载。为什么编译器尊重 test1 的特殊性,而不是 test2

http://en.cppreference.com/w/cpp/language/template_argument_deduction http://en.cppreference.com/w/cpp/language/overload_resolution

在此代码中,Base() 是一个右值,其他参数也是:

test2( Base(), 1, 2, 3 );

此函数不将对 Base 的右值引用作为参数(即使它是可转换的):

 void test2( Base const &, T && ... )

因此推导和重载决策结果选择了这个函数:

 void test2( T && ... )

哪个看起来更合适。

要获得我认为您想要的输出,您需要对 Base (a "forwarding reference") 的非 const 右值引用:

 void test2( Base &&, T && ... )

或 Base 的 const 左值作为已存在函数的参数:

 const Base b;
 test2( b, 1, 2, 3 );

重载 test1(Base const&, T...) 优于 test1(T const...) 因为它更专业,编译器不必在第一种情况下对第一个参数进行任何类型推导。

现在转到 test2:您传递一个 Base() 右值,编译器必须在将其绑定到 T&&Base const& 之间做出选择。 T&& 是首选,因为 T 的类型被推断为 Base 并且结果 Base&&Base const&.

相比完全匹配

一个经验法则是前向引用(或者,正如 Scott Meyers 所说的,通用引用)是贪婪的并且倾向于绑定到任何东西,你在使用它们时应该小心。特别是,避免使用前向引用重载构造函数,因为它们可能 "disable" 甚至是你的复制构造函数(参见 Scott Meyers 的 Effective Modern C++ 中的第 26 条)。