2012-12-05 17 views
12

Mam następujący kodC++ gromadzić z ruchu zamiast kopii

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

Chciałbym tu jest uniknięcie kopiowania ciągów. Niestety nie jest to realizowane przez wdrożenie akumulacji vs2012. Wewnętrznie akumuluj wywołania innej funkcji _Akumuluj, a funkcja rvalue zostaje utracona w procesie.

To ja zamiast wywołać funkcję _Accumulate jak tak

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

mogę uzyskać zamierzony wzrost wydajności.

Czy należy zaktualizować bibliotekę std, aby uwzględnić argumenty rVue?

Czy jest jakiś inny sposób, w jaki mogę używać akumulacji, aby osiągnąć to, czego chcę, bez oszukiwania za dużo?

+1

Twój 'test' nie jest rvalue. 's1' w lambda nie jest rvalue –

Odpowiedz

4

Sprawdzenie jednego z ostatniego postu C++ 11 projektów (N3337.pdf) widzimy, że efekt std :: akumuluj jest określona jako

oblicza swój wynik poprzez inicjowanie ACC akumulator o wartość początkowa init, a następnie zmienia ją za pomocą acc = acc + * i lub acc = binary_op (acc, * i) dla każdego iteratora w zakresie [pierwszy, ostatni) w kolejności.

Więc średnia rzeczywiście zabrania implementacje używające std :: przejść do starej wartości akumulatora tak:

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

który jest nieszczęśliwy w swojej sprawie.

Opcja (1): Zastosuj ten ruch-świadomy gromadzić się.

Option (2): Trzymać przy użyciu funktor jak

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

pamiętać, że nie użycie rvalue rodzaje powrotne odniesienie tutaj. Jest to zamierzone, ponieważ może uniknąć zwisających problemów z odniesieniami, na przykład w przypadku, gdy ostatnie przeciążenie jest wybierane, a "a" jest inicjowane w odniesieniu do obiektu tymczasowego. Wszystkie operatora + przeciążenia dla ciągów również celowo zwracają wartość.

Oprócz tego możesz chcieć użyć std :: copy w połączeniu ze std :: stringstream i iteratorem strumienia wyjściowego.

Uzupełnienie: Alternatywny mutating_string_adder z jakiegoś częściowego idealny do korespondencji:

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

' mutating_string_adder' - drugi operator(), a jest lwartością, dlatego ruch nic nie robi. Jednak nie jestem pewien, czy pierwszy operator() powinien użyć przenieść. –

+0

@ BЈовић: Celem std :: move jest przekształcenie lwartości w wartość r. Tak, oczywiście, std :: move robi coś. Gdybym usunął std :: move a w instrukcji return, zwracana wartość byłaby skonstruowana jako copy, a nie move. Nie ma potrzeby używania std :: move dla pierwszego przeciążenia, ponieważ a + b już jest rwartością. – sellibitze

+0

Akumulator działający z naciskiem na ruchy działał bez żadnych kopii, a ten sam zarezerwowany obszar był cały czas używany. –

Powiązane problemy