2016-07-01 30 views
1

Betrachten Sie das folgende:Vorlage Weiterleiten von Anrufen zu einer Methode Mitglied

template <typename type> class my_wrapper 
{ 
    type _; 

    template <typename... types, typename std :: enable_if <has_my_method_callable_with_the_following_types <type, types...> :: value> :: type * = nullptr> void my_method(types... items) 
    { 
    _.my_method(items...); 
    } 
}; 

Wo, wie Sie sich vorstellen können, has_my_method_callable_with_the_following_types eine Art SFINAE Struktur ist, die mir erlaubt, zu bestimmen, ob type eine Methode, die mit aufgerufen werden kann diese Arten.

Wie Sie sehen können, leitet das obige Beispiel grundsätzlich alle Anrufe an my_method bis _ weiter. Gut. Fast alle von ihnen. Was passiert, wenn ich tun:

class my_type 
{ 
    void my_method(int & x) 
    { 
    x++; 
    } 
}; 

my_wrapper <my_type> my_wrapped; 

int x; 
my_wrapped.my_method(x); 

klar, dass die oben nicht funktionieren, wie x auf die Funktion von Kopie übergeben werden würde, während my_type :: my_method es durch Verweis akzeptiert. Also frage ich mich: Gibt es eine Möglichkeit, dieses Problem zu umgehen? Ich könnte natürlich tun:

template <typename... types, typename std :: enable_if <has_my_method_callable_with_the_following_types <type, types & ...> :: value> :: type * = nullptr> void my_method(types & ... items) 
{ 
    _.my_method(items...); 
} 

Aber dann symmetrisch ich habe Probleme, wenn ich vorbei bin, sagen wir, int Literale, die ich durch Verweis nicht nehmen können, aber das wäre von einigen my_type :: my_method(int x) durchaus akzeptabel sein.

Wie kann ich dieses Problem umgehen? Ich möchte alle Anrufe nahtlos an my_wrapper <type> :: my_method zu type :: my_method weiterleiten.

Piraten Vorsicht: Ich kann Vererbung nicht verwenden, also bitte nicht vorschlagen, dass! :)

+3

Sie suchen nach dem sogenannten "perfekten Weiterleiten" – lorro

Antwort

2

Dies ist genau was perfekte Transport- und Speditions Referenzen für eingeführt wurden:

template < 
    typename... types, 
    typename std :: enable_if <has_my_method_callable_with_the_following_types <type, types...> :: value> :: type * = nullptr 
> void my_method(types&&... items) 
{ 
    _.my_method(std::forward<types>(items)...); 
} 

Wie das funktioniert:

Es gibt eine spezielle Regel in der Sprache, die besagt, dass, wenn eine T herzuleiten in einem T&& Konstrukt, und das für den Abzug verwendete Argument ist ein Lvalue vom Typ U, dann wird T als U& anstelle von U abgeleitet.

Der Nettoeffekt ist, dass, wenn ein Parameter eine Weiterleitung Referenz (T&& für ein abgeleitete T) ist, wird es entweder eine L-Wert Referenz sein (wenn das Argument ein L-Wert ist) oder eine R-Wert-Referenz (wenn das Argument ein rvalue). std::forward<T> wird dann je nach Bedarf zurück in lvalue oder rvalue umgewandelt.

+0

Was ist der Unterschied zwischen dem Aufruf von '_.my_method (std :: forward (items) ...)' und '_.my_method (items ...) '? –

+2

@MatteoMonti Vergessen Sie nicht, dass eine benannte rvalue-Referenz ein lvalue ist. Die Version ohne 'std :: forward' schlägt fehl, wenn 'my_method' eine rvalue-Referenz akzeptiert (oder nur eine Verschiebung nach Wert). – Angew