以下代码:
#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
?
答案 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 );