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

时间:2015-11-11 00:19:32

标签: templates c++11 rvalue-reference

以下代码:

#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

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

2 个答案:

答案 0 :(得分:3)

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

现在转到test2:您传递Base() rvalue,编译器必须在将其绑定到T&&Base const&之间进行选择。 T&&是首选,因为与T相比,Base的类型推断为Base&&,结果Base const&完全匹配。

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

答案 1 :(得分:1)

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

在这段代码中,Base()是一个rvalue,其他参数也是如此:

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

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

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

因此,演绎和重载决策会选择此功能:

 void test2( T && ... )

这似乎更合适。

要获得我认为你想要的输出,你需要一个非const的rvalue引用Base(a&#34;转发引用&#34;):

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

或Base的const左值作为现有函数的参数:

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