Consider a 'contract' function used for checking values of arguments etc:
template< class T >
const T& AssertNotEmpty( const T& val )
{
//raise hell if val empty/0/...
return val;
}
Which for example can be used as follows:
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)
Now enter C++11 where we want Bar98 to take the constructor argument by value and move from it:
class Bar11
{
public:
Bar11( std::shared_ptr< int > val ) :
myVal( AssertNotEmpty( std::move( val ) ) )
{}
private:
std::shared_ptr< int > myVal;
};
For this to work AssertNotEmpty needs a rewrite which I rather naively thought would work by making use of a universal reference:
template< class T >
T&& AssertNotEmpty( T&& val )
{
//...
return std::move( val );
}
This seems fine for all cases except the last one (1), where VS gives warning C4239: nonstandard extension used : 'return' : conversion from 'std::shared_ptr<int>' to 'std::shared_ptr<int> &'
.
As far as I know this is because the compiler sees AssertNotEmpty( x )
which is AssertNotEmpty( T& && x )
which collapses into AssertNotEmpty( T& )
and you cannot move from T&
, please correct me if I'm wrong.
To fix this I added the universal reference as an overload that is enabled only for non-lvalue references to force the compiler to select the const reference one also when it encounters a plain lvalue reference like in (1):
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 );
}
Seems to work as intended and the compiler selects the correct one in all cases I tried, but is this the 'correct' C++11 way to solve this? Are there any possible pitfalls? Isn't there a solution not requiring duplication?