是否需要使用const和通用引用的重载?

时间:2013-01-09 15:16:11

标签: c++ c++11 overloading

考虑用于检查参数值等的“契约”函数:

  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方法吗?有任何可能的陷阱吗?是否有解决方案不需要重复?

2 个答案:

答案 0 :(得分:5)

我认为你不应该从该功能返回任何东西。但是,这可能会达到您的目的。

template<class T>
auto AssertNotEmpty(T&& val) -> decltype(std::forward<T>(val))
{
    //...
    return std::forward<T>(val);
}

答案 1 :(得分:3)

这不是你问题的严格答案,但我不认为AssertNotEmpty应修改其论点,也不应返回任何内容。您仍然可以在构造函数中使用它,这要归功于逗号运算符,如下所示:

template< class T >
void AssertNotEmpty( T const& val )
{
  /* assert( val not empty ) */
}

class Bar11
{
public:
  Bar11( std::shared_ptr< int > val ) :
    myVal( ( AssertNotEmpty( val ), std::move( val ) ) )
  {}
private:
  std::shared_ptr< int > myVal;
};

请注意,需要额外的括号,因此会计算两个表达式,结果是最后一个表达式的结果。

否则,您应该重命名您的功能。想到AssertNotEmptyThenMove之类的东西......