2016-02-28 16 views
6

Ho suonato in giro con i costruttori espliciti ed il loro comportamento, così ho creato questa classe:Come mai non riesco a usare costruttore esplicito per la costruzione di un tipo di ritorno

#include <iostream> 

class X 
{ 
public: 
    explicit X(void) 
    { 
     std::cout << "Default constructor\n"; 
    } 
    explicit X(X const& x) 
    { 
     std::cout << "Copy constructor\n"; 
    } 
    explicit X(X&& x) 
    { 
     std::cout << "Move constructor\n"; 
    } 
}; 

che è fondamentalmente solo uno stub per mettere alla prova esplicita costruttori. Poi ho voluto provare diverse situazioni. Così ho provato questo:

X foo(void) 
{ 
    X a{}; 
    return a; // ERROR: no matching constructor found! 
} 

int main() 
{ 
    X w{}; // Default Constructor 
    X x{w}; // Copy Constructor 
    X y{std::move(x)}; // Move Constructor 
    X z{foo()}; 
} 

E come potete vedere non posso tornare a all'interno foo(). So che tenta di inizializzare il tipo di ritorno Foo con il costruttore di copie, ma per qualche motivo non può usarlo.

Come mai non è possibile utilizzare il mio costruttore di copie fornito? So che la specifica explicit causa il problema, perché quando la rimuovo dal costruttore di copie funziona. Ma perché?

Ciò che mi confonde ancora di più è che posso effettuare le seguenti operazioni:

void bar(const X& a) { /* */ } 
bar(X{}); 

E non si lamenta. Ma non dovrebbe bar() costruire il suo parametro a allo stesso modo foo() costruisce il suo tipo di ritorno?

+1

chiamare 'bar' non chiama il costruttore, basta passare il riferimento (puntatore). dovresti vedere che i costruttori non copia sono chiamati. –

risposta

5

Quando si stanno tornando da foo:

X foo() 
{ 
    X a{}; 
    return a; 
} 

si sono implicitamente copiando a nel ritorno di foo. Ma il costruttore di copie è contrassegnato con explicit, pertanto non è consentito.

Non sono sicuro che ci sia mai un motivo per contrassegnare i costruttori copia/sposta explicit.

+0

Ma posso ancora usare define 'void bar (const X & a)' e usarlo come questo 'bar (X {});'. Non è anche una copia implicita? – hgiesel

+0

@henrikgiesel Nope. 'a' è un riferimento, che stai solo vincolando al' X {} 'temporaneo. – Barry

+0

Ok, ho finalmente ottenuto l'immagine quando ho riscritto 'X & foo()' e ha funzionato. (Certo che è stupido, ma mi ha aiutato a capire cosa stava succedendo) – hgiesel

0

Non puoi perché hai detto che non volevi che il compilatore lo usasse implicitamente quando lo hai dichiarato esplicito.

explicit X(X const& x) 

Ma x deve essere copiato nel valore di ritorno. Basta cambiarlo in

X(X const& x) 

e tutto funzionerà.

Live on Coliru

+0

* "So che la specifica esplicita causa il problema, perché quando lo rimuovo dal costruttore di copie funziona, ma perché?" * - OP. – LogicStuff

+1

Ciò significa che non c'è modo all'inferno che posso rendere 'foo()' restituire un oggetto 'Foo' intatto? – hgiesel

+0

Ad ogni modo per farlo funzionare con 'explicit'? –

1

Penso che tu abbia frainteso il significato di explicit. Un costruttore explicit NON VERRÀ UTILIZZATO PER CONVERSIONI/CASI DI TIPO IMPLICITO. Questo significa che il

X foo(void){ 
    X a{}; 
    return a; // ERROR: no matching constructor found! 
} 

non verrà compilato dal momento che hai già detto al compilatore di non usare il costruttore di copia in modo implicito.

Sono convinto che ciò che si desidera ottenere è "spostare" anziché copiare a. Finché c'è un costruttore di movimento (normale) nella tua classe, a verrà spostato anziché copiato in ogni caso: questo è il comportamento predefinito. In realtà, anche con C++ 99, la maggior parte dei compilatori è abbastanza intelligente da ottimizzare comunque questa copia.

Problemi correlati