2010-11-11 18 views
35

Ostatnie pytanie sprawiło, że zastanawiałem się nad konstruktorami jawnych kopii. Oto przykładowy kod, który Próbowałem kompilacji pod Visual Studio 2005:Jawne zachowanie konstruktora kopiowania i praktyczne zastosowania

struct A 
{ 
    A() {} 
    explicit A(const A &) {} 
}; 

// #1 > Compilation error (expected behavior) 
A retByValue() 
{ 
    return A(); 
} 

// #2 > Compiles just fine, but why ? 
void passByValue(A a) 
{ 
} 

int main() 
{ 
    A a; 
    A b(a); // #3 > explicit copy construction : OK (expected behavior) 
    A c = a; // #4 > implicit copy construction : KO (expected behavior) 

    // Added after multiple comments : not an error according to VS 2005. 
    passByValue(a); 
    return 0; 
} 

Teraz pytania:

  • to # 2 dozwolony przez normę? Jeśli tak, to jaka jest odpowiednia sekcja opisująca tę sytuację?
  • Czy znasz jakieś praktyczne zastosowanie dla konstruktora jawnej kopii?

[EDIT] Właśnie znalazłem zabawny link na MSDN z dokładnie tej samej sytuacji, a tajemniczy wpis z głównych funkcji: „c jest kopiowany” (jakby to było oczywiste). Jak wskazał Oli Charlesworth: gcc nie kompiluje tego kodu i uważam, że nie ma racji.

+0

Nie myślę, że konstruktorzy jawnych kopii to dobry pomysł. Gdzie o nich czytałeś? – fredoverflow

+1

To wydaje się być poprawione w VC++ 2010 - daje błąd dla linii 'passByValue (a);'. – user200783

Odpowiedz

39

wierzę odpowiednie sekcje C++ 03 są §12.3.1 2:

An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (8.5) or where casts (5.2.9 , 5.4) are explicitly used. A default constructor may be an explicit constructor; such a constructor will be used to perform default-initialization or value-initialization (8.5).

i § 8.5 12:

The initialization that occurs in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and brace-enclosed initializer lists (8.5.1) is called copy-initialization and is equivalent to the form

T x = a; 

The initialization that occurs in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization and is equivalent to the form

T x(a); 

Wywołanie passByValue(a) obejmuje kopiowania inicjalizację, a nie bezpośredni inicjalizację, a tym samym powinien być błędem, zgodnie z C++ 03 § 12.3.1 2.

+2

Chociaż odpowiedzi MSalters i Oli są poprawne, przyjmuję to, ponieważ daje jasno do zrozumienia, że ​​VS jest błędne. Dzięki ! – icecrime

+0

Bardzo dobra odpowiedź, dzięki. – ttvd

4

Definicja passByValue jest w porządku, ponieważ nie ma instrukcji, która kopiuje obiekt A. W definicji retByValue znajduje się oczywiście instrukcja return, która kopiuje obiekt A.

+1

Nie jestem pewien, czy rozumiem twoją odpowiedź.Dla jasności: ograniczyłem kod do minimum, ale kompilator może nazwać 'passByValue (A())' z głównego, co, jak sądzę, wymaga niejawnego kopiowania. – icecrime

+1

@icecrime: Nie, nie można wywoływać 'passByValue()' w ten sposób ... –

+0

Moje rozumienie tego jest takie, że obiekt 'a' nigdy nie jest używany wewnątrz' passByValue() ', więc kompilator je ignoruje. – djeidot

2

Aby rozwinąć na odpowiedź @MSalters' (co jest poprawna), jeśli były, aby dodać passByValue(a); do funkcji main(), kompilator byłoby powinien narzekać.

Konstruktory jawnego kopiowania służą do zapobiegania temu, tj. Do zapobiegania niejawnemu kopiowaniu zasobów w wywołaniach funkcji i tak dalej (w zasadzie zmusza użytkownika do przekazania przez referencję, a nie wartości przekazywanej).

+0

Nie, nie będzie, passByValue (a); w main() kompiluje się dobrze, a ja, jako icecrime, wierzę, że istnieje niejawne kopiowanie obiektu. – davidnr

+0

@davidnr, hmm, kończy się niepowodzeniem w gcc z powodów wymienionych powyżej. – Nim

+0

@davidnr: W GCC otrzymuję komunikat 'error: no matching function dla wywołania 'A :: A (A &)',' error: initializing argument 1 of 'void passByValue (A)' ' –

3

Praktyczne zastosowanie, przed C++ 11, zrobienia konstruktora kopii exp dozwolona jest w przypadku, gdy jest to rzeczywiście częścią uniemożliwiającą kopiowanie klasy.

Niebezpieczeństwo polega na tym, że chociaż deklarujesz, że konstruktor kopii jest prywatny i nie implementujesz go, jeśli przypadkowo skopiujesz go w znajomym lub w samej klasie, kompilator go nie podniesie, a Ty " Pojawi się tylko trudny do znalezienia błąd łącza.

Wyjaśnienie tego również zmniejsza szansę, ponieważ kompilator może odebrać niezamierzoną kopię i wskazać właściwą linię, w której to robisz.

W C++ 11 (14) i nie ma potrzeby, aby to zrobić przy użyciu składni =delete jak byś się błąd kompilatora nawet jeśli kopiowanie w samej klasie lub w przyjaciela.

Powiązane problemy