Può essere che Alex Stepanov aveva il paradigma di programmazione funzionale, ma si troveranno che sia std::accumulate
e std::for_each
passaggio loro operandi intorno (la funzione e il valore accumulato) per valore, piuttosto che facendo riferimento. Così:
class MyFunctor
{
Y val;
public:
MyFunctor() : val() {}
void operator()(X const& x)
{
// do something to modify val based on x
}
Y getValue() const { return val; }
};
Ora, se si tenta:
MyFunctor f;
for_each(coll.begin(), coll.end(), f);
Y y = f.getValue();
Non funzionerà perché for_each
si occupa di copie di f
. Ovviamente si potrebbe avere internamente un'istanza di shared_ptr<Y>
che quindi punta alla stessa istanza. Puoi anche fare in modo che val all'interno di MyFunctor sia un riferimento, crearlo all'esterno del loop e passarlo a MyFunctor.
Tuttavia la lingua che si lascia solo fare:
Y y = for_each(coll.begin(), coll.end(), MyFunctor()).getValue();
bello e comodo, il tutto in una sola riga.
di fare lo stesso con std::accumulate
sarebbe stato fatto in questo modo:
class MyFunctor2
{
public:
Y operator()(Y y, X const& x) const
{
// create a new Y based on the old one and x
...
}
};
Y y = std::accumulate(coll.begin(), coll.end(), Y(), MyFunctor2());
è possibile utilizzare una funzione (o in C++ 11 a lambda) al posto di un funtore. Nota che qui il functor non ha uno stato e tu passi l'oggetto inizializzato come parametro, che può essere temporaneo.
Ora sappiamo che Y è copiabile. std::accumulate
utilizza by value
su Y, non una modifica sul posto. Per inciso, quando sul posto modificare realmente è più efficiente, c'è una soluzione senza scrivere un nuovo algoritmo (es accumulate2 che utilizza + = o modifica riferimento) utilizzando una firma funzione di:
Y * func(Y* py, X const &); // function modifies *py in-place then returns py
quindi chiamando:
Y y;
std::accumulate(coll.begin(), coll.end(), &y, func);
"Sappiamo" che il valore restituito sarà & y. Possiamo fare uso di questo se vogliamo accedere a un membro di Y in un posto, ad es.
Y y;
Z z = std::accumulate(coll.begin(), coll.end(), &y, func)->getZ();
Per inciso, una differenza chiave per la copia in for_each
e la copia in accumulate
è la complessità/numero di copie si farà. Con for_each
ci saranno al massimo 2 copie del tuo funtore: uno come parametro nella funzione e uno nel ritorno. Dico "al massimo" perché Return Value Optimization potrebbe ridurre la seconda di queste copie. Con accumulate
copia con ogni elemento della raccolta, cioè O(N)
piuttosto che con tempo costante. Quindi, se la copia è leggermente costosa, la doppia copia nel funtore non sarà una spesa importante che itera un piccolo numero di volte su raccolte di grandi dimensioni, mentre per accumulare sarebbe (e il suggerimento sarebbe il trucco puntatore).
Naturalmente, perché non ci ho pensato? Thx :) – larsmoa
Mi è stato detto una volta qui (da Charles Bailey, credo), che questo comportamento non è garantito. Non gli credevo; Pensavo che lo standard, sebbene non chiaro, fosse per lo più sensato in quel contesto. (Perché altrimenti avremmo una funzione?) Ma solo un avvertimento, forse questo è solo un comportamento del compilatore "standard" e non un comportamento standard. – GManNickG
@GMan: la restituzione della funzione è richiesta dallo standard (§25.1.1/2). –