使用右值引用时选择要使用的模板重载时的不同行为
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 条)。
以下代码:
#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 条)。