【问题标题】:Can an operator to recognize rvalue?运算符可以识别右值吗?
【发布时间】:2013-02-08 10:15:05
【问题描述】:

我有一些 STL 容器类型 T,比如 template< typename F > using T = std::vector< F >;。我希望能够编写以下代码:

typedef std::string F;
T< F > v(2, "a"), w(3, "b");
v += std::move(w);
std::cout << "v : ";
std::copy(std::begin(v), std::end(v), std::ostream_iterator< F >(std::cout, "|"));
std::cout << std::endl << "w : ";
std::copy(std::begin(w), std::end(w), std::ostream_iterator< F >(std::cout, "|"));
std::cout << std::endl;

并获得输出:

v : a|a|b|b|b|
w : |||

即我希望能够通过“std::move-ing”将源w 的所有内容附加到目标v 的末尾(从&lt;utility&gt; 一个接一个或从@987654329 @ by range),因此所有源的元素都保持为空,并且只需要调用w.clear();

是否可以让操作符识别右值引用?说:

T & ::operator += (T &, T &&);

或者还有什么我想要的吗?

【问题讨论】:

  • error: no match for 'operator+=' in 'foo += std::move&lt;std::vector&lt;std::basic_string&lt;char&gt; &gt;&amp;&gt;((* &amp; bar))'
  • int main () { std::vector&lt; std::string &gt; foo = {"1", "2", "3", "4"}; std::vector&lt; std::string &gt; bar = {"5", "6"}; foo += std::move(bar); return 0;}
  • 那是因为你只是尝试使用operator+=,你并没有真正写它!
  • 既然没有这样的操作符,那么我们可以将问题归结为容器是用户自定义的。我不能将operator += 声明为非成员函数,所以情况就是这样。
  • operator= 必须是非静态成员函数,但operator+= 不必是

标签: c++ c++11 move-semantics rvalue-reference


【解决方案1】:

是的,这会起作用,但是T 不是一个类型,所以你不能这样写操作符。 T 是一个模板,所以操作符需要类似于

template<typename U>
  T<U>& operator+=(T<U>&, T<U>&&);

一个可能的实现是:

template<typename U>
  T<U>& operator+=(T<U>& lhs, T<U>&& rvalue)
  {
    std::move(rvalue.begin(), rvalue.end(), std::back_inserter(lhs));
    rvalue.clear();
    return lhs;
  }

并为左值重载:

template<typename U>
  T<U>& operator+=(T<U>& lhs, const T<U>& lvalue)
  {
    std::copy(lvalue.begin(), lvalue.end(), std::back_inserter(lhs));
    return lhs;
  }

要使其适用于任何类型(我认为这是一个坏主意,它应该被限制为只匹配您想要的类型)试试这个:

template< class T, class U >
  inline
  typename std::enable_if< std::is_lvalue_reference< U >::value, T& >::type
  operator += (T& lhs, U&& lvalue)
  { /* copy from lvalue */ }

template< class T, class U >
  inline
  typename std::enable_if< !std::is_lvalue_reference< U >::value, T& >::type
  operator += (T& lhs, U&& rvalue)
  { /* move from rvalue */ }

【讨论】:

  • 但我会做一些优化:template&lt; class T, class U &gt; inline T &amp; operator += (T &amp; lhs, U &amp;&amp; rhs) { if (((void *)&amp;lhs != (void *)&amp;rhs) &amp;&amp; !rhs.empty()) { std::move(std::begin(rhs), std::end(rhs), std::back_inserter(lhs)); rhs.clear(); } return lhs; }
  • 如何实现下一个行为:对于非临时对象或非std::move(*)-ed 对象我想使用其他版本的运算符,它不清除源?天真的运算符重载不会导致任何结果。无论如何都会调用带有T &amp;&amp; 的版本。我应该使用boost::enable_if&lt;&gt;吗?
  • 为什么是优化?你多久做一次自我分配?多久从一个右值?答案应该是几乎从不和从不 ...不可能给自己分配一个临时的,如果你写a += std::move(a)你就是个傻瓜! “优化”只是添加了额外的代码来减慢它。
  • 你的重载一定是写错了,T&lt;F&gt;&amp;&amp;重载不能为非右值调用
  • 不使用通用参考。查看我的更新答案
猜你喜欢
  • 1970-01-01
  • 2021-10-22
  • 1970-01-01
  • 2015-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-16
  • 2021-04-06
相关资源
最近更新 更多