0

Ich habe eine Funktion foo nehmen einen Parameter als Referenz, und ich will es anders auf Rvalue und auf lvalue Referenzen zu handeln. (Es ändert sich nicht die refered-to-Wert, möchte ich hinzufügen) Ich weiß, dass, wenn ich schreibe:Wie erhalten Sie verschiedene Überladungen für Rvalue und Lvalue Referenzen mit einem Template-abgeleiteten Typ?

template <typename T> foo(T&& x); 

ich forwarding reference erklärt habe, anstatt rvalue Referenz, dass diese Art und Weise Sinn:

template <typename T> foo(const T& x); 
template <typename T> foo(T&& x); 

wahrscheinlich werde ich nicht bekommen, was ich will.

Meine Frage ist also: Was ist der richtige Weg, um unterschiedliches Verhalten zwischen den beiden Arten von Referenzen zu beeinflussen?

+2

Können Sie ein Beispiel dafür angeben, dass eine Weiterleitungsreferenz nicht Ihren Wünschen entspricht? – Barry

+0

@Barry: Zuweisen von Heap-Speicher für eine R-Wert-Referenz, Zuweisung von nichts für L-Wert-Referenz - damit sie in irgendeiner Art von Callback verwendet werden können. – einpoklum

+0

Ist das nicht der normale Weg, dies zu tun, wenn Benutzer einen Verweis auf einen Handler wünschen, übergeben sie ihn in einem std :: ref? –

Antwort

1

Sie können die Tag-Verteilung für C++ 11 oder if constexpr für C++ 17 verwenden. So ähnlich.

template <typename T> 
void foo(T&& x) 
{ 
    foo(std::addressof(x), typename std::is_lvalue_reference<T>::type{}); 
} 
void foo(void const *value, std::true_type) 
{ 
    // do something on lvalue 
} 
void foo(void const *value, std::false_type) 
{ 
    // do something on rvalue 
} 

Sie benötigen Informationen, obwohl später über diese Art zu reinterpret_cast, die Zeiger zu speichern. Oder besser, wie vorgeschlagen:

template <typename T> 
void foo(T&& x) 
{ 
    foo(std::forward<T>(x), typename std::is_lvalue_reference<T>::type{}); 
} 
template <typename T> 
void foo(T &&value, std::true_type) 
{ 
    // do something on lvalue 
} 
template <typename T> 
void foo(T &&value, std::false_type) 
{ 
    // do something on rvalue 
} 
+0

So, jetzt haben wir zwei Hilfsfunktionen ohne Typinformation? -1. – Barry

+0

Sie müssen hier überhaupt keine Zeiger oder Umwandlungen verwenden. –

+0

Ich stimme zu, besser eine andere Vorlage verwenden. –

7

Sie können eine L-Wert-Referenz Überlastung und eine Weiterleitung Referenz Überlastung:

template <typename T> void foo(T&) { ... } 
template <typename T> void foo(T&&) { ... } 

Für lvalues ​​die erste Überlast begünstigt wird. Für Rvalues ​​ist nur die zweite Überladung möglich.


Wenn das, was Sie wollen ein const lvalue Referenz Überlastung ist und eine nicht-const rvalue Referenz Überlastung, dann müssen Sie nur noch eine Einschränkung auf den Fall Forwarding Verweis hinzuzufügen:

template <typename T> void foo(T const&) { ... } 
template <typename T, REQUIRES(!std::is_reference<T>::value)> 
void foo(T&&) { ... } 

wo REQUIRES ist die Methode deiner Wahl. Nun, für unsere vier Fälle:

  • nicht-const, L-Wert: nur erste tragfähige
  • const lvalue ist: nur erste lebensfähig ist
  • nicht-const, rvalue: beide lebensfähig, 2. ist besser aufeinander abzustimmen
  • const rvalue: beide lebensfähig, 2. ist speziellere bessere Übereinstimmung
+0

Ich habe abgestimmt, weil dies der einfachste Weg ist. Aber das OP muss seine Richtigkeit überprüfen, [sonst wird das passieren] (http://coliru.stacked-crooked.com/a/45b60c6b9915ce3f). – StoryTeller

+0

Sie haben die Konstante aus der ersten Variante entfernt. War das absichtlich, und wenn ja - können Sie das im Lichte von @ StoryTellers Kommentar erklären? – einpoklum

+0

@StoryTeller Ich habe 'const' in der ersten Überlastung nicht? – Barry

1

Tag Dispatching die einfachste Lösung ist.

namespace details { 
    template <typename T> 
    void foo(std::true_type is_lvalue, const T& x) { 
    std::cout << x << " is an lvalue\n"; 
    } 
    template <typename T> 
    void foo(std::false_type is_lvalue, T&& x) { 
    std::cout << x << " is an rvalue\n"; 
    } 
} 
template <typename T> 
void foo(T&& t) { 
    return details::foo(
    typename std::is_lvalue_reference<T>::type{}, 
    std::forward<T>(t) 
); 
} 

SFINAE ist übertrieben ernst, wenn Sie nicht wirklich keine Version ausgewählt werden für die Überladungsauflösung Zwecke unterstützen wollen.

Verwandte Themen