12

次のコードがあります

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 が呼び出され、その過程で右辺値の機能が失われます。

代わりに _Accumulate 関数を呼び出します

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

意図したパフォーマンスの向上が得られます。

右辺値引数を考慮に入れるために std ライブラリを書き直す必要がありますか?

あまりにも不正行為をせずに私が望むものを達成するために蓄積を使用できる他の方法はありますか?

4

1 に答える 1

4

最近投稿された C++11 ドラフト (N3337.pdf) の 1 つを確認すると、std::accumulate の効果が次のように指定されていることがわかります。

アキュムレータ acc を初期値 init で初期化して結果を計算し、[first,last) の範囲内の反復子 i ごとに acc = acc + *i または acc = binary_op(acc, *i) で順番に変更します。

したがって、標準では実際には、次のように古いアキュムレータ値に 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);
  }
};
于 2012-12-06T09:50:52.773 に答える