2016-02-04 13 views
5

W języku C++ 11 można jawnie ustawić domyślną funkcję składową członkowską, jeśli jej automatyczne generowanie zostało automatycznie zablokowane.Wymuszanie jawnie domyślnej funkcji generowania specjalnych funkcji składowych

Jednak jawne domyślne użycie funkcji specjalnej powoduje tylko cofnięcie niejawnego usunięcia spowodowanego przez ręczne zadeklarowanie niektórych innych funkcji specjalnego elementu (operacje kopiowania, destruktora itd.), Nie zmusza kompilatora do wygenerowania funkcji i kod jest uważany za dobrze uformowany, nawet jeśli funkcja nie może być w rzeczywistości wygenerowana.

Rozważmy następujący scenariusz:

struct A 
{ 
    A()   = default; 
    A (const A&) = default; 
    A (A&&)  = delete; // Move constructor is deleted here 
}; 

struct B 
{ 
    B()   = default; 
    B (const B&) = default; 
    B (B&&)  = default; // Move constructor is defaulted here 

    A a; 
}; 

Konstruktor ruch w B nie zostanie wygenerowany przez kompilator, ponieważ spowodowałoby błąd kompilacji (ruch konstruktora jest usunięte). Bez jawnego usunięcia konstruktora A konstruktor ruchu B byłby generowany zgodnie z oczekiwaniami (kopiowanie A, zamiast go przesuwać).

Próba przenieść taki obiekt dyskretnie użyć konstruktora kopii zamiast:

B b; 
B b2 (std::move(b)); // Will call B's copy constructor 

Czy istnieje sposób zmusić kompilator do generowania albo funkcję albo wydać błąd kompilacji, jeśli nie może? Bez tej gwarancji trudno jest polegać na domyślnych konstruktorach ruchu, jeśli pojedynczy usunięty konstruktor może wyłączyć ruch dla całych hierarchii obiektów.

+0

Nie powinieneś wiedzieć, czy członkowie, do których się dodajesz, są ruchome czy nie? – NathanOliver

+2

Mogą być ruchome, gdy klasa została pierwotnie zaimplementowana, jednak jeśli później zostaną dodane kolejne elementy, można pominąć wymóg ruchomy (szczególnie jeśli są one dodawane przez kogoś innego). –

+1

Należy zauważyć, że typ, który usuwa konstruktor ruchu, ale * nie * jego konstruktor kopii jest bardzo ... dziwaczny. Nie ma absolutnie nic do zyskania, robiąc to. Najlepiej więc zignorować tę sprawę, biorąc pod uwagę, że jest to wynikiem tego, że ktoś robi coś idiotycznego bez powodu. –

Odpowiedz

3

Istnieje sposób wykrywania typów takich jak A. Ale tylko wtedy, gdy typ jawnie usuwa konstruktor ruchu. Jeśli konstruktor ruchu zostanie domyślnie wygenerowany jako usunięty, nie będzie on uczestniczył w rozwiązywaniu przeciążenia. To dlatego B jest ruchome, chociaż nie jest to A. Bdefault s Konstruktor ruchu, co oznacza, że ​​zostaje niejawnie usunięty, więc kopiowanie się dzieje.

B jest zatem możliwy do przeniesienia. Jednak nie jest to A. Więc jest to prosta sprawa to:

struct B 
{ 
    static_assert(is_move_constructible<A>::value, "Oops..."); 

    B()   = default; 
    B (const B&) = default; 
    B (B&&)  = default; // Move constructor is defaulted here 

    A a; 
}; 

Teraz nie ma ogóle sposób, aby spowodować żadnego rodzaju, który zawiera kopiowaniem tylko typy robić, co chcesz. Oznacza to, że musisz statycznie potwierdzać każdy typ osobno; nie można wstawić pewnej składni w domyślnym konstruktorze ruchu, aby niepowodzenie przyniosło próby przeniesienia B.

Przyczyna tego musi wynikać częściowo ze zgodności wstecznej. Pomyśl o całym kodzie poprzedzającym C++ 11, który zadeklarował konstruktory kopiowania zdefiniowane przez użytkownika. Zgodnie z regułami generowania konstruktorów ruchu w C++ 11, wszystkie z nich miałyby usunięte konstruktory ruchu. Co oznacza, że ​​dowolny kod formularza T t = FuncReturningTByValue(); może zawieść, mimo że działał dobrze w C++ 98/03, wywołując konstruktor kopiowania. Tak więc problem poruszania się po kopii działał w ten sposób, wykonując te kopie zamiast przesuwać, jeśli konstruktor ruchu nie mógł zostać wygenerowany.

Ale ponieważ = default oznacza "rób to, co normalnie robisz", zawiera także to specjalne zachowanie w przypadku przeciążenia, które ignoruje niejawnie usunięty konstruktor ruchu.

Powiązane problemy