2

考虑一个用于检查参数值等的“合同”函数:

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

4

2 回答 2

5

我认为您不应该从该函数返回任何内容。但是,这可能会如您所愿。

template<class T>
auto AssertNotEmpty(T&& val) -> decltype(std::forward<T>(val))
{
    //...
    return std::forward<T>(val);
}
于 2013-01-09T15:19:57.920 回答
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想到了什么……

于 2013-01-09T15:27:16.897 回答