Il compilatore C ++ ottimizzerà il codice di ritorno per valore?

Supponiamo che io usi Visual Studio o GCC moderno con -O2. Il compilatore creerà S all’interno di func() e poi lo copierà su my_result , o creerà my_result con il costruttore (5, 6, 5 + 6) senza creare S temporanea?

NOTA: la funzione func() e il suo utilizzo sono in file obj separati!

 struct S { S(int _x, int _y, int _z) : x(_x), y(_y), z(_z) { } int x, y, z; }; S func(int a, int b) { return S(a, b, a + b); } /// USAGE /// S my_result = func( 5, 6 ); 

I compilatori moderni spesso ottimizzano questo tipo di operazione. Vedi l’ ottimizzazione del valore di ritorno

È un’ottimizzazione che praticamente per definizione significa che è facoltativo per il compilatore e fino a ogni compilatore aprticolare per decidere cosa fare. Come puoi sapere di sicuro? Controlla lo sassembly del codice generato!

Ciò detto, la maggior parte dei compilatori dovrebbe fare questa ottimizzazione (ottimizzazione del valore di ritorno [RVO]) in quanto è relativamente facile da fare in questo caso (nessun rendimento multiplo, è un temporaneo senza nome, quindi non si ha aliasing, ecc.).

Mi sembra che il caso di test fornito sia abbastanza semplice da consentire l’ authorization da parte di RVO .

Dubito che il temporaneo sia ottimizzato. È ansible testarlo inserendo un’istruzione print nel costruttore e nel costruttore di copie e vedere cosa viene stampato in diverse impostazioni del compilatore.

Puoi testarlo da solo, perché l’ottimizzazione in questione ha differenze osservabili!

La maggior parte delle forms di ottimizzazione in C ++ seguono la regola as-if , che le rende difficili da rilevare. Tuttavia, in alcuni casi, è consentito l’eliding (saltando) copia e sposta costruttore, anche se la differenza si traduce in cambiamenti del comportamento osservabili.

In questo caso, aggiungi quanto segue a S:

 struct S { // ... S( S const& o ):x(ox), y(oy), z(oz) { std::cout << "copy ctor!\n"; } S& operator=( S const& o ) { x=ox; y=oy; z=oz; std::cout << "copy assign!\n"; return *this; } S( S && o ):x(std::move(ox)), y(std::move(oy)), z(std::move(oz)) { std::cout << "move ctor!\n"; } S& operator=( S const& o ) { std::tie( x,y,z ) = std::tie( std::move(ox),std::move(oy),std::move(oz) ); std::cout << "move assign!\n"; return *this; } } 

ed esegui il tuo codice. Con l'ottimizzazione zero otterrai copie e / o mosse.

Con qualsiasi livello di ottimizzazione non banale, le stampe spariranno, perché RVO (e, in casi correlati, NRVO) verrà eseguito, eliminando le copie. (Se il tuo compilatore non è C ++ 11, rimuovi i costruttori di spostamento sopra - l'ottimizzazione in C ++ 03 era ancora consentita)

In C ++ 11 è ansible build esplicitamente il valore restituito invece di affidarsi a NRVO / RVO tramite la syntax return {stuff} .

Si noti che RVO (ottimizzazione del valore di ritorno) e NRVO (denominato ottimizzazione del valore di ritorno) sono relativamente fragili e se si sta facendo affidamento su di essi entrambi devono capire come funzionano, cosa li fa rompere e qualsiasi stranezza che il particolare compilatore ha nel suo implementazione (se presente).