2012-12-05 3 views
12

Ich habe den folgenden CodeC++ mit Bewegung akkumulieren statt Kopie

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

Was würde Ich mag hier ist String Kopieren zu vermeiden. Leider wird dies durch die vs2012-Implementierung von accum nicht erreicht. Intern akkumulieren ruft eine andere Funktion _Accumulate auf und die Rvalue-Funktionalität geht dabei verloren.

Es nenne ich stattdessen die _Accumulate Funktion wie so

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

ich die beabsichtigte Leistungsgewinn erhalten.

Muss die Std-Bibliothek neu geschrieben werden, um rvalue-Argumente zu berücksichtigen?

Gibt es eine andere Art, wie ich mich ansammeln kann, um zu erreichen, was ich will, ohne zu viel zu betrügen?

+1

Ihre 'test' ist kein rvalue. Das 's1' in Lambda ist kein rvalue –

Antwort

4

Überprüfen einer der kürzlich erschienenen Beitrag C++ 11 Zugluft (N3337.pdf) können wir sehen, dass die Wirkung von std :: accumulate angegeben als

sein Ergebnis Ermittelt durch den Akkumulator mit dem Initialisieren acc Initialwert init und dann ändert es mit acc = acc + * i oder acc = binary_op (acc, * i) für jeden Iterator i im Bereich [first, last] in der Reihenfolge.

also die Standard verbietet eigentlich Implementierungen, die std :: für den alten Akku Wert wie folgt bewegen:

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

, die in Ihrem Fall ist bedauerlich.

Option (1): Implementieren Sie dieses Move-Aware-Paket selbst.

Option (2): Halten Sie einen Funktor wie

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

Beachten Sie, dass ich nicht hier Rückgabetypen Verwendung rvalue Referenz tat. Dies ist beabsichtigt, da es möglicherweise Probleme mit dem Dangling vermeidet, beispielsweise in dem Fall, in dem die letzte Überladung ausgewählt wird und 'a' initialisiert wird, um auf ein temporäres Objekt Bezug zu nehmen. Alle Operator + Überladungen für Strings werden auch absichtlich als Wert zurückgegeben.

Abgesehen davon könnten Sie std :: copy in Kombination mit std :: stringstream und einem Ausgabestreamiterator verwenden.

Nachtrag: Alternate mutating_string_adder mit einigen Teil perfekt Forwarding:

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); 
    } 
}; 
+0

' muting_string_adder' - der zweite Operator(), a ist Lvalue, deshalb macht move nichts. Ich bin mir jedoch nicht sicher, ob der 1. Operator() move verwenden soll. –

+0

@ BЈовић: Der Zweck von std :: move ist, einen Lvalue in einen Rvalue zu verwandeln. Also, sicher, std :: move macht etwas. Wenn ich in der return-Anweisung std :: move around entfernen würde, wäre der Rückgabewert copy constructed anstatt move constructed. Es ist nicht notwendig, std :: move für die erste Überladung zu verwenden, da a + b bereits ein rvalue ist. – sellibitze

+0

Die Bewegung bewusste Akkumulation funktionierte gut, ohne Kopien überall und der gleiche reservierte Bereich verwendet den ganzen Weg –