2016-02-28 12 views
6

I bawił się z wyraźnymi konstruktorów i ich zachowanie, więc stworzyłem tę klasę:Dlaczego nie mogę używać wyraźny konstruktora do skonstruowania typ zwracany

#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"; 
    } 
}; 

który jest w zasadzie tylko zalążek aby przetestować jawne konstruktorzy. Potem chciałem wypróbować kilka sytuacji. Więc próbowałem to:

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()}; 
} 

I jak widać nie mogę wrócić a wewnątrz foo(). Wiem, że próbuje zainicjować typ zwrotu Foo z konstruktorem kopiującym, ale z jakiegoś powodu nie może go użyć.

Jak to możliwe, że nie można użyć mojego dostarczonego konstruktora kopii? Wiem, że specyfikacja explicit powoduje problem, ponieważ po usunięciu go z konstruktora kopiowania działa. Ale dlaczego?

Co myli mnie jeszcze bardziej to, że mogę wykonać następujące czynności:

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

Nie narzekam. Ale czy nie powinien on konstruować jego parametru a w ten sam sposób, w jaki konstruuje swój typ zwracania?

+1

Wywołanie 'bar' nie wywołuje w ogóle konstruktora, po prostu przekazuje referencję (wskaźnik). powinieneś zobaczyć wywołania no copy konstruktorów. –

Odpowiedz

5

Po powrocie z foo:

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

Jesteś niejawnie kopiowania a do zwrotu foo. Ale konstruktor kopii jest oznaczony jako explicit, więc jest niedozwolony.

Nie jestem pewien, czy kiedykolwiek istnieje powód, by oznaczyć konstruktorów kopiujących/przenoszących explicit.

+0

Ale nadal mogę użyć definicji 'void bar (const X & a)' i użyć jej w ten sposób 'bar (X {});'. Czy nie jest to również niejawna kopia? – hgiesel

+0

@henrikgiesel Nie. 'a' jest referencją, która jest tylko wiążąca dla tymczasowego' X {} '. – Barry

+0

OK, w końcu dostałem zdjęcie, gdy przepisałem 'X & foo()' i zadziałało. (Oczywiście, że to głupie, ale pomogło mi zrozumieć, co się dzieje) – hgiesel

0

Nie możesz, ponieważ powiedziałeś, że nie chcesz, aby kompilator używał go bezwarunkowo, gdy zadeklarowałeś go jako jawny.

explicit X(X const& x) 

Ale x musi zostać skopiowany do wartości zwracanej. Po prostu zmień go na

X(X const& x) 

i wszystko będzie działać.

Live on Coliru

+0

* "Wiem, że jawna specyfikacja powoduje problem, ponieważ po usunięciu go z konstruktora kopiowania działa, ale dlaczego?" * - OP. – LogicStuff

+1

Oznacza to, że nie ma mowy, aby w piekle uzyskać 'foo()' zwrócić nienaruszony obiekt 'Foo'? – hgiesel

+0

W każdym razie, aby działało z "wyraźnym"? –

1

Myślę, że źle zrozumiałeś znaczenie explicit. Konstruktor explicit NIE BĘDZIE UŻYWANY DO KONWERSJI/ODPOWIEDZI TYPU IMPLICIT. To oznacza, że ​​kompilator nie będzie kompilował, aby nie używać domyślnie konstruktora kopii.

Uważam, że to, co chcesz osiągnąć, to "przenieś", a nie kopiuj a. Dopóki istnieje konstruktor (normalny) ruch w twojej klasie, a zostanie przeniesiony, a nie skopiowany - to jest domyślne zachowanie. W rzeczywistości, nawet w przypadku C++ 99, większość kompilatorów jest na tyle sprytna, aby i tak zoptymalizować tę kopię.

Powiązane problemy