2013-07-08 19 views
5

Mam hierarchiczną hierarchię klas, gdzie nie ma domyślnego konstruktora, ani nie kopiuję konstruktorów. Dwaj konstruktorzy mam to „ruch” jednego i drugiego, który bierze odniesienie lwartości do obiektu:Jak zaimplementować konstruktor ruchu dla dziedziczenia w kształcie rombu?

struct base { 
    base(base&&) = default; 
    base(member_type&& m): member_(std::move(m)) {} 
    member_type member_; 
}; 

struct virt_1: virtual base { 
    virt_1(virt_1&& rhs): base(std::move(rhs)) {} 
    virt_1(member_type&& m): base(std::move(m)) {} 
}; 

struct virt_2: virtual base { 
    virt_2(virt_2&& rhs): base(std::move(rhs)) {} 
    virt_2(member_type&& m): base(std::move(m)) {} 
}; 

struct concrete: virt_1, virt_2 { 
    concrete(concrete&& rhs) // ??? 
}; 

Zresztą nie używamy hierarchii w kształcie rombu, jest to możliwe do wykonania konstruktora ruch dla betonu klasy ?

Dzięki!

+1

Brzmi bardzo podatnie na błędy. Konstruktor wirtualnej bazy jest wywoływany z najbardziej pochodnej klasy i nie powinno być żadnego powodu, dla którego nie mógłby wywołać konstruktora ruchu. Ale jakiekolwiek dalsze wyprowadzenie i istnieje spora szansa, że ​​ktoś o tym zapomni, lub zrobi to źle. Zasadą jest, że wirtualne klasy bazowe nie powinny zawierać danych lub przynajmniej konstruktorów innych niż domyślny konstruktor, aby uniknąć takich problemów. –

+0

@JamesKanze Mam świadomość, że 'base' nie powinien zawierać danych lub przynajmniej mieć domyślny konstruktor, ale nie może tak być w moim kontekście. Czy jest jednak jakakolwiek szansa, że ​​kompilator złoży skargę, jeśli ktoś zapomni zadzwonić do wirtualnego konstruktora bazowego, kiedy zacznie czerpać więcej? – piwi

+0

@piwi, nie, nie ma szans, ponieważ 'base' nie ma domyślnego konstruktora, więc najbardziej wyprowadzony typ ** musi ** konstruować go jawnie –

Odpowiedz

8

Co jest złego w pytaniu kompilatora o dostarczenie implementacji?

concrete(concrete&&) = default; 

bym określić virt_1 i virt_2 move konstruktorów jako domyślnie też.

można napisać go jeśli naprawdę chciał:

concrete(concrete&& rhs) 
: base(std::move(rhs)), virt_1(std::move(rhs)), virt_2(std::move(rhs)) 
{ } 

lub jeśli naprawdę jak wpisując:

concrete(concrete&& rhs) 
    : base(static_cast<base&&>(rhs)), 
     virt_1(static_cast<virt_1&&>(rhs)), 
     virt_2(static_cast<virt_2&&>(rhs)) 
    { } 

W inicjalizatory podstaw virt_1 i virt_2 są bezużyteczne, ponieważ tylko wywołaj konstruktor base, a ponieważ jest to wirtualna baza, nie będą tego robić, gdy go wywoła concrete, ale ze względu na twoje wybory konstruktorów nie możesz ich domyślnie zbudować i są wymagane do zainicjowania ich za pomocą rvalue, chociaż nic z tym nie zrobią.

+1

Pytanie: czy to nie byłoby niebezpieczne? Wydaje się, że porusza się wiele razy z tego samego obiektu ... –

+0

Myślę, że mój problem leży w obu konstruktorach konkretnej klasy; Będę szukał z domyślnym konstruktorem ruchu – piwi

+1

@AndyProwl, nie, zobacz ostatni dodany akapit. Konstruktor 'base' zostanie wywołany tylko raz (przez konstruktor dla najbardziej wyprowadzonego typu), więc wszystkie inne konstruktory nie robią nic z ich parametrem rvalue. Będzie tylko jeden ruch (pamiętaj, że wywoływanie 'std :: move' w rzeczywistości niczego nie przesuwa, a ruch ma miejsce tylko wtedy, gdy coś zaakceptuje parametr rvalue i zdecyduje się z niego ruszyć.) –

1

Pewnie. Każdy konstruktor z bazą virtual gdzieś w hierarchii jest odpowiedzialny za zainicjowanie tej bazy. Zasadniczo każda klasa poniżej bazy virtual w hierarchii dziedziczy uprawnienia do zainicjowania bazy.

W tym przypadku domyślny konstruktor ruchu powinien postępować właściwie. Polecam również określenie concrete : private virtual base, aby wyjaśnić, co się dzieje.

Here to działająca wersja demonstracyjna.

+0

Może również przetestować operatora przypisania ruchu. –

+1

Klasa nie musi dziedziczyć bezpośrednio z bazy. Ale to on jest odpowiedzialny za wywoływanie konstruktorów dowolnych wirtualnych baz, nawet jeśli nie dziedziczy po nich. –

+0

@JamesKanze Oh, myślałem, że to błąd składniowy, aby mieć inicjator pamięci dla pośredniej podstawy. To nie jest. Mimo to, popraw mnie, jeśli się mylę, ale różnica polega tylko na składni/stylu. Bezcenne byłoby również dziedziczenie z bazy i nielegalne dziedziczenie z bazy bez "wirtualnego". – Potatoswatter

Powiązane problemy