2012-12-05 10 views
12

Ho il seguente codiceC++ si accumulano con mossa invece di copia

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

Quello che vorrei è quello di evitare di copiare stringa. Sfortunatamente questo non è realizzato dall'implementazione di accumulo vs2012. Accumulo internamente chiama un'altra funzione _Accumula e la funzionalità rvalue si perde nel processo.

E io invece chiamare la funzione _Accumulate in questo modo

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

ottengo il guadagno di prestazioni previsto.

La libreria std deve essere riscritta per tenere in considerazione gli argomenti rvalue?

C'è qualche altro modo in cui posso usare accumulare per realizzare ciò che voglio senza imbrogliare troppo?

+1

vostro 'test' non è un rvalue. il 's1' in lambda non è un rvalore –

risposta

4

Controllo uno dei post recente C++ 11 bozze (N3337.pdf) possiamo vedere che è specificato l'effetto di std :: accumulare

Calcola il suo risultato inizializzando il secondo accumulatore con il il valore iniziale init e quindi lo modifica con acc = acc + * i o acc = binary_op (acc, * i) per ogni iteratore i nell'intervallo [first, last) nell'ordine.

Quindi, la norma in realtà proibisce implementazioni che utilizzano std :: muoversi per il vecchio valore dell'accumulatore in questo modo:

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

che è un peccato nel tuo caso.

Opzione (1): implementa questo spostamento consapevole accumulando te stesso.

Opzione (2): Continua a utilizzare un funtore come

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

Nota che ho fatto non tipi di ritorno di riferimento uso rvalue qui. Ciò è intenzionale in quanto potrebbe evitare problemi di riferimento, ad esempio nel caso in cui viene selezionato l'ultimo sovraccarico e 'a' inizializzato per fare riferimento a un oggetto temporaneo. Anche tutti gli operatori + sovraccarichi per stringhe ritornano intenzionalmente in base al valore.

Oltre a ciò, è possibile utilizzare std :: copy in combinazione con std :: stringstream e un iteratore di flusso di output.

Addendum: Alternate mutating_string_adder con un po 'di inoltro perfetta parziale:

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' - il 2o operatore(), a è lvalue, quindi spostare non fa nulla. Tuttavia, non sono sicuro che il primo operatore() debba usare move. –

+0

@ BЈовић: Lo scopo di std :: move è di trasformare un lvalue in un valore. Quindi, certo, std :: move fa qualcosa. Se ho rimosso std :: move around a nell'istruzione return, il valore restituito sarebbe stato costruito in copia anziché spostato. Non è necessario usare std :: move per il primo sovraccarico perché + b è già un valore. – sellibitze

+0

L'accumulano mossa-aware ha funzionato benissimo con nessuna copia ovunque e la stessa area riservata usato tutta la strada –

Problemi correlati