c ++用移动而不是复制来累积

Posted

技术标签:

【中文标题】c ++用移动而不是复制来累积【英文标题】:c++ accumulate with move instead of copy 【发布时间】:2012-12-05 13:56:29 【问题描述】:

我有以下代码

auto adder = [](string& s1, const string& s2)->string&&
   
      if (!s1.empty())
         s1 += " ";
      s1 += s2;
      return move(s1);
   ;

   string test;
   test.reserve(wordArray.size() * 10);
   string words = accumulate(wordArray.begin(), wordArray.end(), 
       move(test), adder);

我想在这里避免字符串复制。不幸的是,这不是通过累积的 vs2012 实现来实现的。内部累积调用另一个函数 _Accumulate 并且右值功能在此过程中丢失。

我改为这样调用 _Accumulate 函数

string words = _Accumulate(wordArray.begin(), wordArray.end(), 
    move(test), adder);

我得到了预期的性能提升。

是否必须重写 std 库以考虑右值参数?

有没有其他方法可以让我使用累积来完成我想要的,而不会作弊太多?

【问题讨论】:

您的test 不是右值。 lambda 中的 s1 不是右值 C++20 将操作指定为acc = move(acc) + rhs,这可以大大减少复制成本不高的类型的累积。例如,一个好的std::string 实现将有一个operator+(string&& lhs, T) 劫持lhs,附加到它,然后返回它(这是RVOable)。 【参考方案1】:

查看最近发布的一篇 C++11 草案(N3337.pdf)我们可以看到 std::accumulate 的效果被指定为

通过使用初始值 init 初始化累加器 acc 来计算其结果,然后 对范围内的每个迭代器 i 使用 acc = acc + *i 或 acc = binary_op(acc, *i) 对其进行修改 [first,last) 按顺序排列。

因此,标准实际上禁止使用 std::move 作为旧累加器值的实现,如下所示:

template <class InputIterator, class T, class BinOp>
T accumulate (InputIterator first, InputIterator last, T init, BinOp binop)

  while (first!=last) 
    init = binop(std::move(init), *first);
    ++first;
  
  return init;

这对你来说很不幸。

选项 (1):自己实施这种移动感知累积。

选项(2):继续使用仿函数

struct mutating_string_adder 
  string operator()(string const& a, string const& b) const return a+b;
  string operator()(string & a, string const& b)      const a += b; return std::move(a);
  string operator()(string && a, string const& b)     const a += b; return std::move(a);
;

请注意,我没有在这里使用右值引用返回类型。这是有意的,因为它可能会避免悬空引用问题,例如在拾取最后一个重载并将“a”初始化为引用临时对象的情况下。字符串的所有 operator+ 重载也有意按值返回。

除此之外,您可能希望将 std::copy 与 std::stringstream 和输出流迭代器结合使用。

附录:备用mutating_string_adder 部分完美转发:

struct mutating_string_adder 
  template<class T, class U>
  std::string operator()(T && a, U && b) const 
    return std::move(a) + std::forward<U>(b);
  
;

【讨论】:

mutating_string_adder - 第二个 operator(),a 是左值,因此 move 不做任何事情。但是,我不确定第一个 operator() 是否应该使用 move。 @BЈовић:std::move 的目的是将左值转换为右值。所以,当然,std::move 做了一些事情。如果我在 return 语句中删除了 a 周围的 std::move,则返回值将是复制构造的,而不是 move 构造的。第一次重载不需要使用 std::move ,因为 a+b 已经是一个右值。 移动感知累积运行良好,没有任何副本,并且一直使用相同的保留区域 第一个是正确的,但我认为你对第二个是错误的。为什么要把左值变成右值?这是可能的吗,传递给 s1 的对象将被移动,并且不再存在。因此,访问它将是 UB @BЈовић:我已经说过为什么在第二个 operator() 重载中使用 std::move 。抱歉,我没有看到任何问题。您认为在哪里可以访问已移动对象 - 除了可移动感知版本的累积分配?

以上是关于c ++用移动而不是复制来累积的主要内容,如果未能解决你的问题,请参考以下文章

如何将项目从 boost::variant 移动到多地图?

获取值的键而不是获取键值? [复制]

用批处理把文件夹下的控件全部移动到sys32并注册

delphi如何复制文件夹

在Linux系统系下vi操作中C语言编程,如何进行复制粘贴?求救!

linux下移动或者复制文件覆盖相同文件夹时,文件夹里面的每个文件都提示是不是覆盖