2015-02-08 13 views
5

Chciałbym użyć kompozycji i napisać dobre metody przekazywania dla każdego możliwego przeciążenia (noexcept, const, volatile) przy użyciu C++.Przekazywanie metod ze składem zamiast dziedziczenia (przy użyciu cech C++)

Chodzi o wykorzystanie cech w celu określenia, czy dana metoda została zadeklarowana {noexcept/const/volatile/etc.} oraz odpowiednie zachowanie.

Oto przykład tego, co chciałbym osiągnąć:

struct User{  
    UsedObject& obj; 
    User(UsedObject& obj) : obj(obj) {} 

    FORWARD_METHOD(obj, get); //here is where the forwarding happens 
}; 

struct UsedObject{ 
    string m{"Hello\n"}; 

    string& get(double d){ 
     cout << "\tUsed :const not called...\n"; 
     return m; 
    } 
    const string& get(double d) const{ 
     cout << "\tUsed :const called...\n"; 
     return m; 
    } 
}; 

Oto co mam tak daleko **:

// forward with noexcept attribute 
// I'm not 100% sure about : std::declval<std::add_lvalue_reference<decltype(obj)>::type 

template<typename... Args> 
constexpr decltype(auto) get(Args && ... args) 
noexcept(
     noexcept(std::declval<std::add_lvalue_reference<decltype(obj)>::type>().get( std::forward<Args>(args)... )) 
     and 
     std::is_nothrow_move_constructible<decltype(std::declval<std::add_lvalue_reference<decltype(obj)>::type>().get( std::forward<Args>(args)... ))>::value 
     ) 
{ 
    cout << "const called...\n"; 
    return obj.get(std::forward<Args>(args)...); 
} 

// forward with noexcept and const attributes 
// I'm not sure that this one behave properly. 

template<typename... Args> 
constexpr decltype(auto) get(Args && ... args) 
const noexcept(
     noexcept(std::declval< std::add_const<decltype(obj) &>::type >().get( std::forward<Args>(args)... )) 
     and 
     std::is_nothrow_move_constructible<decltype(std::declval< std::add_const<decltype(obj) &>::type >().get( std::forward<Args>(args)... ))>::value 
     ) 
{ 
    cout << "const not called...\n"; 
    using const_type = std::add_lvalue_reference<std::add_const<std::remove_reference<decltype(obj)>::type>::type>::type; 
    return const_cast<const_type>(obj).get(std::forward<Args>(args)...); 
} 

Należy pamiętać, że ta kwestia jest inna od następujących jeden, ponieważ wiem, że możemy używać cech C++ do sprawdzenia interfejsu obiektu: Composition: using traits to avoid forwarding functions?

** inspirowany wątkiem komentarzy z @David S dźwięk tutaj: When should I use C++ private inheritance?.

Odpowiedz

1

Zacznijmy od rozwiązania i wyjaśnij to po kawałku.

#define FORWARDING_MEMBER_FUNCTION(Inner, inner, function, qualifiers) \ 
    template< \ 
     typename... Args, \ 
     typename return_type = decltype(std::declval<Inner qualifiers>().function(std::declval<Args &&>()...)) \ 
    > \ 
    constexpr decltype(auto) function(Args && ... args) qualifiers noexcept(\ 
     noexcept(std::declval<Inner qualifiers>().function(std::forward<Args>(args)...)) and \ 
     (\ 
      std::is_reference<return_type>::value or \ 
      std::is_nothrow_move_constructible<return_type>::value \ 
     ) \ 
    ) { \ 
     return static_cast<Inner qualifiers>(inner).function(std::forward<Args>(args)...); \ 
    } 

#define FORWARDING_MEMBER_FUNCTIONS_CV(Inner, inner, function, reference) \ 
    FORWARDING_MEMBER_FUNCTION(Inner, inner, function, reference) \ 
    FORWARDING_MEMBER_FUNCTION(Inner, inner, function, const reference) \ 
    FORWARDING_MEMBER_FUNCTION(Inner, inner, function, volatile reference) \ 
    FORWARDING_MEMBER_FUNCTION(Inner, inner, function, const volatile reference) 

#define FORWARDING_MEMBER_FUNCTIONS(Inner, inner, function) \ 
    FORWARDING_MEMBER_FUNCTIONS_CV(Inner, inner, function, &) \ 
    FORWARDING_MEMBER_FUNCTIONS_CV(Inner, inner, function, &&) 

Wewnętrzna reprezentuje typ obiektu, do którego przekazujesz, a wewnętrzna reprezentuje jego nazwę. Kwalifikatory to połączenie const, volatile, & i & &, których potrzebujesz w funkcji członka.

Specyfikacja noexcept jest zaskakująco skomplikowana tylko dlatego, że musisz obsłużyć wywołanie funkcji, jak również skonstruować wartość zwracaną. Jeśli funkcja, którą przekazujesz, zwraca referencję, wiesz, że jest bezpieczna (referencje są zawsze niedopuszczalne do skonstruowania z tego samego typu), ale jeśli funkcja zwrócona przez wartość, musisz upewnić się, że konstruktor ruchu obiektu nie jest wyjątkiem.

Udało nam się to trochę uprościć, używając domyślnego argumentu template return_type, w przeciwnym razie musielibyśmy dwa razy przeliterować ten typ zwrotu.

Używamy static_cast w treści funkcji do prawidłowego dodawania cv i kwalifikatorów referencyjnych do zawartych typów. Nie jest to automatycznie wybierane przez kwalifikatory referencji dla funkcji.

Korzystanie dziedziczenie zamiast kompozycji

za pomocą prywatnych dziedziczenia, rozwiązanie wygląda bardziej jak to:

struct Outer : private Inner { 
    using Inner::f; 
}; 

Ma to tę zaletę,

  • Dokładność odczytu
  • Szybsza kompilacja razy
  • Szybsze kod w debugowania buduje (nic do inline)
  • Nie stosując swój constexpr głębokość rekurencji
  • Nie używając swój szablon instancji głębokość
  • Praca z powrotem non-ruchome typy według wartości
  • Praca z przekazaniem do konstruktorów
+0

Dziękuję za bardzo dobrą odpowiedź! Wiele się nauczyłem o metaprogramowaniu. Czy możemy używać go w ten sposób? 'FORWARDING_MEMBER_FUNCTIONS (std :: decay :: type, inner, function)' I ze wskaźnikiem dla członków: 'FORWARDING_MEMBER_FUNCTIONS (std :: decay :: type, * ptr , funkcja); ' ? –

+0

BTW, Co uniemożliwia C++ dostarczanie tej samej składni "use inner.function;" dla kompozycji? –

+0

@Julien__ Można zmienić makro, aby zaakceptować typ parametru. Wadą jest to, że musisz mieć deklarację zmiennych przed wywołaniem FORWARDING_MEMBER_FUNCTIONS, a jeśli wyraźnie określisz typ, to ograniczenie nie występuje. Co jest lepsze, zależy od Ciebie. Prawdopodobnie nie chciałbyś tego użyć ze wskaźnikami, ze względu na kwalifikatory referencji. Nic nie stoi na przeszkodzie, by C++ używało składni używania do przekazywania funkcji członkom, z wyjątkiem tego, że musiałbyś przekonać komisję, by to zaakceptowała. –

Powiązane problemy