2013-03-10 15 views
7

Rozumiem, że gdy obiekty są zwracane do wartości od funkcji, ich konstruktory kopiowania są wywoływane. Jeśli klasa ma usunięty konstruktor kopiowania, zwracanie przez wartość zakończy się niepowodzeniem.Dlaczego konstruktor kopiowania nie jest wywoływany, gdy zwracana wartość-wartość jest obiektem inicjowanym listą?

struct X { 
    X(const X &) = delete; 
}; 

X f() { 
    return X{}; 
} 

error: call to deleted constructor of 'X'

C++ 11 daje nam rozszerzonym inicjatorów. I czytałem gdzieś na SO zakładać, że ten

X f() { 
    return {}; 
} 

jest taka sama jak

X f() { 
    return X{}; 
} 

Więc dlaczego nie poniższy kod daje mi błąd? Przechodzi on i ja nawet dostać się do wywołania funkcji w głównym:

struct D { 
    D(const D &) = delete; 
}; 

D f() { return {}; } 

int main() 
{ 
    f(); 
} 

Here is a demo. Nie jest błąd. Uważam, że to dziwne, ponieważ uważam, że powinien zostać wywołany konstruktor kopii. Czy ktoś może wyjaśnić, dlaczego nie podano błędu?

+0

Co się stanie, jeśli usuniesz konstruktor ruchu? C++ 11 stwierdza, że ​​będzie on używany, jeśli będzie dostępny. –

+0

Co ciekawe, nie mogę uzyskać kodu do skompilowania na gcc 4.7.2 lub 4,8 migawki. [wersja demonstracyjna] (http://ideone.com/L94cwe). – juanchopanza

+0

@ CrazyEddie Po usunięciu błędu pojawia się błąd. Wow, to było nieoczekiwane. Pamiętasz to jako odpowiedź? –

Odpowiedz

12

A czytałem gdzieś na SO zakładać, że [...] jest taka sama jak [...]

Byli w błędzie. One są podobne, ale nie takie same.

Korzystając z listy wzmocnionych-init, można zainicjować wartość zwracaną w miejscu. Jeśli utworzysz tymczasowy, to co robisz, tworzy tymczasowy, a następnie kopiuje go do wartości zwracanej. Każdy kompilator warty swojej soli usunie go, ale konstruktor kopii musi być nadal dostępny.

Ale ponieważ lista wstępnie spreparowanych inicjuje wartość zwracaną w miejscu, nie ma potrzeby dostępu do konstruktora kopiowania.

od standardu, rozdział 6.6.3, P2

Oświadczenie zwrotny z usztywnione inicjalizacji liście inicjalizuje obiekt lub odniesienie do zwrotu od funkcji przez kopiowaniem liście inicjalizacji (8,5 .4) z określonej listy inicjalizatora.

Należy zauważyć, że "inicjowanie listy kopii" nie jest podobne do "inicjowania kopii"; nie wykonuje żadnego kopiowania i dlatego nie wymaga dostępnego konstruktora kopiowania. Jedyna różnica między "inicjowaniem listy kopii" a "inicjacją listy bezpośredniej" polega na tym, że pierwszy z nich będzie się dusił na konstruktorach explicit.

+0

Więc mówisz, że jeśli użyłeś listy "spreparowanych" w instrukcji return, to zasadniczo wymusza to kopiowanie/przenoszenie? –

+1

@SethCarnegie: Nie. Mówię, że jeśli używasz listy ze wzmocnionymi początkami, nie ma * kopiowania *. Copy elision nadal wymaga dostępnego konstruktora kopii; Zapamiętane listy inicjalizują wartość zwracaną w miejscu, bez operacji kopiowania/przenoszenia. –

+0

To ciekawe, nie wiedziałem. Czy mógłbyś podać część normy, która to określa? –

Powiązane problemy