2015-05-05 19 views
8

Ho seguente classeriferimenti rvalue senza std :: muovono

class widget { 
// The methods only print their name, i.e. c'tor, destructor etc. 
public: 
    widget(); 
    widget(const widget&); 
    widget(widget&&); 
    ~widget(); 
    auto operator=(const widget&) -> widget&; 
    auto operator=(widget&&) -> widget&; 
}; 

che uso nel seguente codice

#include "widget.h" 

auto main() -> int { 

    widget c(std::move(widget())); 
    c = std::move(widget()); 

    return 0; 
}; 

Il comportamento risultante è comprensibile per me. Nella prima chiamata viene costruito un widget, quindi il costruttore di movimento viene richiamato e il distruttore viene chiamato sul widget temporaneo.

La seconda chiamata fa la stessa cosa, si aspetta di chiamare l'operatore di assegnazione del movimento invece del costruttore di movimento. Lasciando il metodo principale, il distruttore viene richiamato su c.


Ora arriva la parte interessante:

#include "widget.h" 

auto main() -> int { 

    widget c((widget())); 
    c = widget(); 

    return 0; 
}; 

Se lascio la chiamata al std::move, il primo caso smette di funzionare e si traduce in una sola chiamata al costruttore. Mentre il secondo caso funziona ancora come prima.

Cosa mi manca qui? Perché queste due chiamate di funzione trattano i loro parametri in modo diverso? Ho provato questo su gcc e clang.

+0

'(widghet())' è un valore di prvalore, cioè un valore, quindi viene spostato (se possibile). 'std :: move' non si sposta, cambia solo la categoria di valore di un'espressione. Se quell'espressione avesse comunque la corrispondente categoria di valore, sarebbe superfluo. – Columbo

+1

Le parentesi sono necessarie, altrimenti l'espressione viene valutata come puntatore di funzione a una funzione che non accetta parametri e restituisce un 'widget'. – mike

+2

Potrebbe essere che il compilatore stia ottimizzando alcune costruzioni e copie per te? –

risposta

12

widget() è una rvalue puro (prvalue), quindi in linea

widget c((widget())); // use widget c{widget()} or c{widget{}} for more clear code 

sarà messa. Tuttavia, il compilatore esegue solo copy/move elision. Compili con -fno-elide-constructors e vedrai le chiamate ai costruttori di mosse in tutta la loro gloria.

Ogni volta che si utilizza esplicitamente std::move per spostare un valore nominale, non si consente al compilatore di eseguire l'elision; è per questo che vedi il costruttore di mosse in azione nel primo snippet. Ecco perché è quasi sempre una cattiva idea cercare di "aiutare" il compilatore usando uno std::move come ritorno (a meno che non si desideri davvero restituire un riferimento rvalue).

+2

Sì, sono necessari gli extra parens. Altrimenti, è una dichiarazione di funzione, non una definizione di oggetto. –

+0

@BenjaminLindley hai ragione, l'ho modificato. Per qualche ragione ho erroneamente pensato che 'widget' sia un oggetto, non il nome della classe. – vsoftco

Problemi correlati