perché `std :: initializer_list` spesso viene passato per valore?

In quasi tutti i post che vedo su SO, che coinvolgono uno std::initializer_list , le persone tendono a passare std::initializer_list base al valore. Secondo questo articolo:

http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

si dovrebbe passare per valore, se si vuole fare una copia dell’object passato. Ma copiare uno std::initializer_list non è una buona idea, come

Copiare uno std::initializer_list non copia gli oggetti sottostanti. Non è garantito che l’array sottostante esista al termine della durata dell’object della lista inizializzatore originale.

Quindi, perché un’istanza di esso spesso viene passata in base al valore e non da, diciamo const& , che garantisce che non è una copia inutile?

È passato per valore perché è economico. std::initializer_list , essendo un thin wrapper, è molto probabilmente implementato come una coppia di puntatori, quindi la copia è (quasi) economica come passare per riferimento. Inoltre, in realtà non stiamo eseguendo una copia, stiamo eseguendo (di solito) una mossa poiché nella maggior parte dei casi l’argomento è comunque costruito da un temporaneo. Tuttavia, questo non farà la differenza per le prestazioni: spostare due puntatori costa quanto copiarli.

D’altra parte, l’ accesso agli elementi di una copia può essere più veloce poiché evitiamo un ulteriore dereferenziamento (quello del riferimento).

Probabilmente per gli stessi motivi gli iteratori sono quasi sempre passati di valore: copiare un iteratore è considerato “economico”. Nel caso di initializer_list , c’è anche il fatto che la maggior parte delle istanze saranno temporanee con distruttori banali, quindi il compilatore può costruirle direttamente dove pone l’argomento della funzione, senza alcuna copia. Infine, c’è il fatto che, come gli iteratori, è probabile che la funzione chiamata voglia modificare il valore, il che significa che dovrebbe copiarlo localmente se fosse passato da un riferimento a const.

MODIFICARE:

Solo per generalizzare: la libreria standard fa in generale l’ipotesi che sia meglio passare iteratori, initializer_lists e oggetti funzionali per valore. Di conseguenza, dovresti assicurarti che tutti gli iteratori, iterator_lists o gli oggetti funzionali che hai progettato siano economici da copiare, e dovresti assumere nel tuo codice che sono economici da copiare. La regola tradizionale su quando utilizzare i riferimenti a const e quando utilizzare il valore dovrebbe probabilmente essere modificata per riflettere questo:

Utilizzare pass per riferimento a const per tipi di class diversi da iterator, initializer_lists o oggetti funzionali; utilizzare il pass in base al valore altrimenti.