考虑一个用于检查参数值等的“合同”函数:
template< class T >
const T& AssertNotEmpty( const T& val )
{
//raise hell if val empty/0/...
return val;
}
例如可以如下使用:
void foo( const std::shared_ptr< int >& val )
{
AssertNotEmpty( val );
//use *val
}
class Bar98
{
public:
Bar98( const std::shared_ptr< int >& val ) : myVal( AssertNotEmpty( val ) ) {}
private:
std::shared_ptr< int > myVal;
};
std::shared_ptr< int > x;
//...
AssertNotEmpty( x ); //(1)
现在进入 C++11,我们希望 Bar98 按值获取构造函数参数并从中移出:
class Bar11
{
public:
Bar11( std::shared_ptr< int > val ) :
myVal( AssertNotEmpty( std::move( val ) ) )
{}
private:
std::shared_ptr< int > myVal;
};
为此,AssertNotEmpty 需要重写,我相当天真地认为可以通过使用通用引用来工作:
template< class T >
T&& AssertNotEmpty( T&& val )
{
//...
return std::move( val );
}
这似乎适用于所有情况,除了最后一种 (1),其中 VS 给出warning C4239: nonstandard extension used : 'return' : conversion from 'std::shared_ptr<int>' to 'std::shared_ptr<int> &'
. 据我所知,这是因为编译器看到AssertNotEmpty( x )
哪个是AssertNotEmpty( T& && x )
哪个崩溃AssertNotEmpty( T& )
而您无法从中移动T&
,如果我错了,请纠正我。
为了解决这个问题,我将通用引用添加为仅对非左值引用启用的重载,以强制编译器在遇到 (1) 中的普通左值引用时也选择 const 引用:
template< class T >
const T& AssertNotEmpty( const T& val )
{
//...
return val;
}
template< class T >
T&& AssertNotEmpty( T&& val, typename std::enable_if< !std::is_lvalue_reference< T >::value, int >::type* = 0 )
{
//...
return std::move( val );
}
似乎按预期工作,编译器在我尝试的所有情况下都选择了正确的,但这是解决这个问题的“正确”C++11 方法吗?有没有可能的陷阱?没有不需要重复的解决方案吗?