简体   繁体   English

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

[英]Are overloads for const and universal references necessary?

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: 现在进入C ++ 11,我们希望Bar98按值获取构造函数参数并从中移出:

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: 为了工作,AssertNotEmpty需要重写,我相当天真地认为通过使用通用引用可以工作:

  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> &' . 对于除最后一个(1)之外的所有情况,这似乎都很好,其中VS给出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. 据我所知,这是因为编译器看到AssertNotEmpty( x )AssertNotEmpty( T& && x ) ,它会折叠成AssertNotEmpty( T& )并且你无法从T&移动,如果我错了,请纠正我。

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): 为了解决这个问题,我添加了通用引用作为重载,仅对非左值引用启用,以便在遇到像(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 );
  }

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? 似乎按预期工作,编译器在我尝试过的所有情况下都选择了正确的,但这是解决这个问题的'正确'的C ++ 11方法吗? Are there any possible pitfalls? 有任何可能的陷阱吗? Isn't there a solution not requiring duplication? 是不是有一个不需要重复的解决方案?

I don't think you should be returning anything from that function. 我不认为你应该从该功能返回任何东西。 However, this might do what you intend. 但是,这可能会做你想要的。

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

This is not strictly an answer to your question, but I do not think that AssertNotEmpty should modify its argument nor return anything. 这不是你问题的严格答案,但我不认为AssertNotEmpty应该修改它的参数也不会返回任何东西。 You can still use it in constructors, thanks to the comma operator , like this: 你可以在构造函数中使用它,这要归功于逗号运算符 ,如下所示:

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;
};

Note that the extra parenthesis are required, so the two expressions are evaluated and the result is that of the last expression. 请注意,需要额外的括号,因此将评估两个表达式,结果是最后一个表达式的结果。

Otherwise, you should rename your function. 否则,您应该重命名您的功能。 Something like AssertNotEmptyThenMove comes to mind... AssertNotEmptyThenMove东西......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM