2013-05-16 21 views
14

Często napotykam nużące konstrukty przenoszenia klas z wieloma zmiennymi składowymi. Wyglądają coś jak poniżej:Delegowanie do domyślnego konstruktora przenoszenia

A(A && rhs) : 
    a(std::move(rhs.a)), 
    b(std::move(rhs.b)), 
    c(std::move(rhs.c)), 
    d(std::move(rhs.d)) { 
    some_extra_work(); 
} 

Oznacza to, że wykonywanie wszystkich czynności związanych z konstruktora domyślnego przenieść, a następnie peform trochę przyziemne dodatkowe zadania. Idealnie byłoby delegować do domyślnego konstruktora ruchu, a następnie wykonać dodatkową pracę, jednak sam akt definiowania własnego konstruktora ruchu uniemożliwia zdefiniowanie domyślnej implementacji, co oznacza, że ​​nie ma nic do przekazania.

Czy istnieje dobry sposób na obejście tego anty-wzór?

+6

Trudno powiedzieć na takim poziomie abstrakcji, ale być może można oddzielić 'A' na dwie klasy i umieścić jeden do drugiego? Jeden będzie miał wszystkie domyślne konstruktory, a drugi wykona 'some_extra_work'. Sprawdź * Regułę zero *. – zch

+0

Nie jestem pewien czy to ma zastosowanie, ale czy coś takiego jak 'A (A && rhs, int): A (rhs) {}' nie działa? – Damon

+3

Pomocna może być pośrednia klasa bazowa z domyślnym konstruktorem ruchu. –

Odpowiedz

3

Aktualizacja: zignoruj ​​pierwszą część tej odpowiedzi i przejdź do końca, który ma lepsze rozwiązanie.

Wrap dodatkowa praca w nowego typu i dziedziczą po nim:

class A; 

struct EW 
{ 
    EW(EW&&); 
}; 

class A : private EW 
{ 
    friend class EW; 
public: 
    A(A&&) = default; 
}; 

EW::EW(EW&&) { A* self = static_cast<A*>(this); self->some_extra_work(); } 

Można też zrobić to z członkiem danych zamiast klasy bazowej, ale że trzeba trochę hackery korzystając offsetof (który jest niezdefiniowany dla niestandardowych typów układów) lub ręcznie walcowany odpowiednik za pomocą podstępnej arytmetyki wskaźnika. Korzystanie z dziedziczenia pozwala na użycie konwersji static_cast.

To nie będzie działać, jeśli some_extra_work() musi być zrobione po członkowie są inicjowane ponieważ klasy bazowe są inicjowane jako pierwszy.

Alternatywnie, jeśli dodatkowa praca działa na obiekcie rwartalnym, z którego się przechodzisz, powinieneś owijać te elementy typami, które działają automatycznie po przeniesieniu z, np. moja tidy_ptr typ, którego używam do wdrożenia Rule of Zero

class A 
{ 
    tidy_ptr<D> d; 
public: 
    A() = default; 
    A(const A&) = default; 
    A(A&& r) = default; // Postcondition: r.d == nullptr 
}; 
+0

Zastanawiam się, czy nie jest wywołany Ctor klasy bazowej * przed * członkiem ruchu ctors? – Xeo

+0

To jest tak, więc nie zadziała, jeśli dodatkowa praca opiera się na innych członkach. Kiedy napisałem, że nie przeczytałem komentarza OP mówiącego "klasyczny przykład some_extra_work() dla mnie jest" rhs.d = 0; "" –

+0

... druga część odpowiedzi działa w tym przypadku, ale –

Powiązane problemy