Übergeben Sie lvalue an rvalue

8

Ich habe eine kleine 'blocking queue' Klasse gemacht. Es irritiert mich, dass ich redundanten Code für Werte erstellt habe, die in die enqueue -Memberfunktion übergeben wurden.

Hier sind die zwei Funktionen, die genau dasselbe tun (außer dass rvalue std :: move verwendet, um den rvalue in die tatsächliche Warteschlangensammlung zu verschieben), mit Ausnahme der Handles lvalue bzw. rvalue:

%Vor%

Meine Frage ist, gibt es eine Möglichkeit, diese beiden Funktionen zu kombinieren, ohne die Unterstützung für rvalue Referenzen zu verlieren.

    
TheAJ 02.02.2013, 23:59
quelle

3 Antworten

15

Dies ist ein klassisches Beispiel für die Notwendigkeit, perfekt vorwärts zu gehen. Tun Sie dies, indem Sie die Funktion templatieren (Elementvorlage, wenn es sich um eine Elementfunktion handelt):

%Vor%

Erklärung: Wenn Sie einen Lvalue T an enqueue übergeben, wird U auf T& ableiten, und der forward wird ihn als Lvalue weiterleiten, und Sie erhalten das Kopierverhalten von Ihnen wollen. Übergeben Sie einen R-Wert T an enqueue , leitet U auf T und% forward leitet ihn als R-Wert weiter und Sie erhalten das gewünschte Bewegungsverhalten.

Dies ist effizienter als der Ansatz "nach Wert", da Sie niemals eine unnötige Kopie oder Bewegung machen. Der Nachteil im Hinblick auf den Ansatz "nach Wert" ist, dass die Funktion alles akzeptiert, auch wenn es falsch ist. Sie können kaskadierte Fehler unter push erhalten oder nicht. Wenn dies ein Problem ist, können Sie enable_if enqueue einschränken, mit welchen Argumenten es instanziiert wird.

Aktualisierung basierend auf Kommentar

Basierend auf den folgenden Kommentaren sehe ich die Dinge wie folgt aus:

%Vor%

Das ist alles gut. Aber was, wenn Sie stattdessen versuchen, ein Double zu quadrieren:

%Vor%

Das funktioniert immer noch, weil das Double implizit in das int konvertiert werden kann. Aber was, wenn du nicht willst, dass es funktioniert? Dann könnten Sie Ihre enqueue folgendermaßen einschränken:

%Vor%

Jetzt:

%Vor%

ergibt:

%Vor%

Aber q.enqueue(1); funktioniert immer noch gut. I.e. Das Einschränken Ihrer Mitgliedervorlage ist eine Designentscheidung, die Sie treffen müssen. Was U soll enqueue akzeptieren? Es gibt keine richtige oder falsche Antwort. Dies ist ein technisches Urteil. Und es sind mehrere andere Tests verfügbar, die geeigneter sein können (z. B. std :: is_convertible, std :: is_constructible, etc.). Vielleicht ist die richtige Antwort für Ihre Anwendung überhaupt keine Einschränkung, wie oben beschrieben.

    
Howard Hinnant 03.02.2013, 00:36
quelle
7

Es scheint mir, dass enqueue(const&) und enqueue(&&) nur ein Sonderfall von enqueue_emplace sind. Jeder gute C ++ 11-ähnliche Container hat diese drei Funktionen und die ersten beiden sind ein Spezialfall der dritten.

%Vor%

Dies ist eine einfache, aber effiziente Lösung und sollte sich genauso wie Ihre ursprüngliche Oberfläche verhalten. Es ist auch dem Template-Ansatz überlegen, da Sie eine Initialisierungsliste in die Warteschlange stellen können.

Alter Beitrag: Tue einfach guten alten Wert:

%Vor%

enqueue "besitzt" seinen Parameter und möchte ihn in die Warteschlange verschieben. Wertüberschreitung ist das richtige Konzept, um dies zu sagen.

Es macht genau eine Kopie und einen Zug. Leider kann dies für T s, die keinen optimierten Move-Konstruktor haben, langsam sein. Aus diesen Gründen haben die Container in der Standardbibliothek immer zwei Überladungen. Auf der anderen Seite kann ein guter Compiler dies optimieren.

    
ipc 03.02.2013 00:06
quelle
2

Haben Sie sich std::forward angesehen? es könnte nur tun, was du fragst, wenn du ein kleines Templating in deine Funktion wirfst ...

    
ted 03.02.2013 00:33
quelle

Tags und Links