Mit GCC 4.8.0 haben wir einen Compiler, der den automatischen Rückzugstypabzug unterstützt und Teil von C ++ 14 ist. Mit -std=c++1y
kann ich das machen:
auto foo() { //deduced to be int
return 5;
}
Meine Frage ist: Wann sollte ich diese Funktion verwenden? Wann ist es notwendig und wann wird der Code sauberer?
Szenario 1
Das erste Szenario, an das ich denken kann, ist wann immer möglich. Jede Funktion, die auf diese Weise geschrieben werden kann, sollte sein. Das Problem dabei ist, dass der Code möglicherweise nicht immer besser lesbar ist.
Szenario 2
Das nächste Szenario besteht darin, komplexere Rückgabetypen zu vermeiden. Als sehr leichtes Beispiel:
template<typename T, typename U>
auto add(T t, U u) { //almost deduced as decltype(t + u): decltype(auto) would
return t + u;
}
Ich glaube nicht, dass dies jemals wirklich ein Problem sein würde, obwohl ich denke, dass es in einigen Fällen klarer sein könnte, wenn der Rückgabetyp explizit von den Parametern abhängt.
Szenario 3
Um Redundanz zu vermeiden:
auto foo() {
std::vector<std::map<std::pair<int, double>, int>> ret;
//fill ret in with stuff
return ret;
}
In C ++ 11 können wir manchmal nur return {5, 6, 7};
einen Vektor ersetzen, aber das funktioniert nicht immer und wir müssen den Typ sowohl im Funktionsheader als auch im Funktionskörper angeben. Dies ist rein redundant, und der automatische Abzug des Rückgabetyps erspart uns diese Redundanz.
Szenario 4
Schließlich kann es anstelle sehr einfacher Funktionen verwendet werden:
auto position() {
return pos_;
}
auto area() {
return length_ * width_;
}
Manchmal schauen wir uns jedoch die Funktion an, um den genauen Typ zu erfahren, und wenn sie dort nicht bereitgestellt wird, müssen wir zu einem anderen Punkt im Code gehen, z. B. wo pos_
deklariert wird.
Fazit
Welche dieser Szenarien erweisen sich bei diesen Szenarien tatsächlich als eine Situation, in der diese Funktion hilfreich ist, um den Code sauberer zu gestalten? Was ist mit Szenarien, die ich hier nicht erwähnt habe? Welche Vorsichtsmaßnahmen sollte ich treffen, bevor ich diese Funktion verwende, damit sie mich später nicht beißt? Gibt es etwas Neues, das diese Funktion auf den Tisch bringt und das ohne sie nicht möglich ist?
Beachten Sie, dass die mehreren Fragen dazu dienen sollen, Perspektiven zu finden, aus denen diese beantwortet werden können.
quelle
->decltype(t+u)
durch automatischen Abzug SFINAE tötet.Antworten:
C ++ 11 wirft ähnliche Fragen auf: Wann wird der Rückgabetypabzug in Lambdas verwendet und wann werden
auto
Variablen verwendet ?Die traditionelle Antwort auf die Frage in C und C ++ 03 lautete: "Über Anweisungsgrenzen hinweg machen wir Typen explizit, innerhalb von Ausdrücken sind sie normalerweise implizit, aber wir können sie mit Casts explizit machen". C ++ 11 und C ++ 1y führen Typabzugswerkzeuge ein, damit Sie den Typ an neuen Stellen weglassen können.
Entschuldigung, aber Sie werden dies nicht im Voraus lösen, indem Sie allgemeine Regeln festlegen. Sie müssen sich einen bestimmten Code ansehen und selbst entscheiden, ob er die Lesbarkeit verbessert, um überall Typen anzugeben: Ist es besser, wenn Ihr Code sagt: "Der Typ dieser Sache ist X", oder ist es besser für Ihr Code sagt: "Der Typ dieser Sache ist für das Verständnis dieses Teils des Codes irrelevant: Der Compiler muss es wissen und wir könnten es wahrscheinlich herausfinden, aber wir müssen es hier nicht sagen."
Da "Lesbarkeit" nicht objektiv definiert ist [*] und darüber hinaus je nach Leser unterschiedlich ist, haben Sie als Autor / Herausgeber eines Codes die Verantwortung, die von einem Styleguide nicht vollständig erfüllt werden kann. Selbst in dem Maße, in dem ein Styleguide Normen festlegt, bevorzugen unterschiedliche Personen unterschiedliche Normen und neigen dazu, etwas Unbekanntes als "weniger lesbar" zu empfinden. Daher kann die Lesbarkeit einer bestimmten vorgeschlagenen Stilregel häufig nur im Kontext der anderen vorhandenen Stilregeln beurteilt werden.
Alle Ihre Szenarien (auch die ersten) werden für den Codierungsstil einer Person Verwendung finden. Persönlich finde ich, dass der zweite der überzeugendste Anwendungsfall ist, aber ich gehe trotzdem davon aus, dass er von Ihren Dokumentationstools abhängt. Es ist nicht sehr hilfreich zu sehen, dass der Rückgabetyp einer Funktionsvorlage
auto
dokumentiert ist , während die Dokumentationdecltype(t+u)
eine veröffentlichte Schnittstelle erstellt, auf die Sie sich (hoffentlich) verlassen können.[*] Gelegentlich versucht jemand, objektive Messungen durchzuführen. In dem geringen Maße, dass jemand jemals statistisch signifikante und allgemein gültige Ergebnisse erzielt, werden sie von arbeitenden Programmierern zugunsten des Instinkts des Autors, was "lesbar" ist, völlig ignoriert.
quelle
1.0 + 27U
, behaupten wir das letztere, wenn wir schreiben(double)1.0 + (double)27U
, behaupten wir das erstere. Die Einfachheit der Funktion, der Grad der Vervielfältigung und die Vermeidungdecltype
könnten alle dazu beitragen, aber keine wird zuverlässig entscheidend sein.auto
im Allgemeinen.auto
Schlüsselwort fahren, wird der tatsächliche Rückgabetyp angezeigt.int*
, aber was tatsächlich von Bedeutung ist, wenn überhaupt, ist der Grund dafür,int*
dass diesstd::vector<int>::iterator_type
bei Ihren aktuellen Build-Optionen der Fall ist!Im Allgemeinen ist der Rückgabetyp der Funktion eine große Hilfe, um eine Funktion zu dokumentieren. Der Benutzer wird wissen, was erwartet wird. Es gibt jedoch einen Fall, in dem es meiner Meinung nach hilfreich sein könnte, diesen Rückgabetyp zu löschen, um Redundanz zu vermeiden. Hier ist ein Beispiel:
Dieses Beispiel stammt aus dem offiziellen Ausschusspapier N3493 . Der Zweck der Funktion
apply
besteht darin, die Elemente von astd::tuple
an eine Funktion weiterzuleiten und das Ergebnis zurückzugeben. Dieint_seq
undmake_int_seq
sind nur ein Teil der Implementierung und werden wahrscheinlich nur Benutzer verwirren, die versuchen zu verstehen, was sie tun.Wie Sie sehen können, ist der Rückgabetyp nichts anderes als ein
decltype
zurückgegebener Ausdruck. Daapply_
ich nicht dafür gedacht bin, von den Benutzern gesehen zu werden, bin ich mir nicht sicher, ob es nützlich ist, den Rückgabetyp zu dokumentieren, wenn er mehr oder weniger dem von ihm entsprichtapply
. Ich denke, dass in diesem speziellen Fall das Löschen des Rückgabetyps die Funktion lesbarer macht. Beachten Sie, dass genau dieser Rückgabetypdecltype(auto)
im Vorschlag zur Ergänzungapply
des Standards N3915 tatsächlich gelöscht und durch ersetzt wurde (beachten Sie auch, dass meine ursprüngliche Antwort älter ist als dieses Papier):In den meisten Fällen ist es jedoch besser, diesen Rückgabetyp beizubehalten. In dem oben beschriebenen speziellen Fall ist der Rückgabetyp ziemlich unlesbar und ein potenzieller Benutzer wird nichts davon haben, ihn zu kennen. Eine gute Dokumentation mit Beispielen ist weitaus nützlicher.
Eine andere Sache, die noch nicht erwähnt wurde: Während
declype(t+u)
die Verwendung des Ausdrucks SFINAE erlaubt ist , istdecltype(auto)
dies nicht der Fall (obwohl es einen Vorschlag gibt , dieses Verhalten zu ändern). Nehmen Sie zum Beispiel einefoobar
Funktion, die diefoo
Mitgliedsfunktion eines Typsbar
aufruft, wenn sie existiert, oder die Mitgliedsfunktion des Typs, wenn sie existiert, und nehmen Sie an, dass eine Klasse immer genaufoo
oderbar
aber nicht beide gleichzeitig hat:Das Aufrufen
foobar
einer Instanz vonX
wird angezeigt,foo
während das Aufrufenfoobar
einer Instanz vonY
angezeigt wirdbar
. Wenn Sie stattdessen den automatischen Rückgabetypabzug verwenden (mit oder ohnedecltype(auto)
), erhalten Sie keinen Ausdruck SFINAE und rufenfoobar
eine Instanz von entweder aufX
oderY
lösen einen Fehler bei der Kompilierung aus.quelle
Es ist niemals notwendig. Wann Sie sollten - Sie werden viele verschiedene Antworten darauf erhalten. Ich würde überhaupt nicht sagen, bis es tatsächlich ein akzeptierter Teil des Standards ist und von der Mehrheit der großen Compiler auf die gleiche Weise gut unterstützt wird.
Darüber hinaus wird es ein religiöses Argument sein. Ich persönlich würde sagen, dass das Nicht-Eingeben des tatsächlichen Rückgabetyps den Code klarer macht, für die Wartung viel einfacher ist (ich kann die Signatur einer Funktion überprüfen und wissen, was sie zurückgibt, anstatt den Code tatsächlich lesen zu müssen), und dies beseitigt die Möglichkeit, dass Sie denken, es sollte einen Typ zurückgeben, und der Compiler glaubt, dass ein anderer Probleme verursacht (wie es bei jeder Skriptsprache passiert ist, die ich jemals verwendet habe). Ich denke, Auto war ein riesiger Fehler und es wird um Größenordnungen mehr Schmerz als Hilfe verursachen. Andere werden sagen, Sie sollten es die ganze Zeit verwenden, da es zu ihrer Programmierphilosophie passt. In jedem Fall liegt dies außerhalb des Geltungsbereichs dieser Website.
quelle
#define
zu verwandeln , z. B. die Verwendung von s, um C ++ in VB umzuwandeln. Features haben im Allgemeinen einen guten Konsens innerhalb der Denkweise der Sprache darüber, was richtig verwendet wird und was nicht, je nachdem, was die Programmierer dieser Sprache gewohnt sind. Dieselbe Funktion kann in mehreren Sprachen vorhanden sein, aber jede hat ihre eigenen Richtlinien für die Verwendung.auto
ist reiner Segen. Es beseitigt viel Redundanz. Es ist einfach ein Schmerz, den Rückgabetyp manchmal zu wiederholen. Wenn Sie ein Lambda zurückgeben möchten, kann dies sogar unmöglich sein, ohne das Ergebnis in einem zu speichernstd::function
, was zu einem gewissen Overhead führen kann.auto
damit er immer mit dem Rückgabetyp der übergebenen Funktion übereinstimmt.auto
für den nachfolgenden Rückgabetyp.]Es hat nichts mit der Einfachheit der Funktion zu tun (wie ein jetzt gelöschtes Duplikat dieser Frage angenommen wird).
Entweder ist der Rückgabetyp fest (nicht verwenden
auto
) oder in komplexer Weise von einem Vorlagenparameter abhängig (auto
in den meisten Fällen verwendet, gepaart mit,decltype
wenn mehrere Rückgabepunkte vorhanden sind).quelle
Stellen Sie sich eine reale Produktionsumgebung vor: Viele Funktionen und Komponententests hängen alle vom Rückgabetyp von ab
foo()
. Angenommen, der Rückgabetyp muss aus irgendeinem Grund geändert werden.Wenn der Rückgabetyp
auto
überall ist und Aufruferfoo()
und verwandte Funktionenauto
beim Abrufen des zurückgegebenen Werts verwenden, sind die erforderlichen Änderungen minimal. Wenn nicht, könnte dies Stunden extrem mühsamer und fehleranfälliger Arbeit bedeuten.Als Beispiel aus der Praxis wurde ich gebeten, ein Modul von der Verwendung von Rohzeigern überall auf intelligente Zeiger umzustellen. Das Reparieren der Unit-Tests war schmerzhafter als der eigentliche Code.
Es gibt zwar andere Möglichkeiten, wie dies gehandhabt werden könnte, aber die Verwendung von
auto
Rückgabetypen scheint gut zu passen.quelle
decltype(foo())
?typedef
s- und alias-Deklarationen (dhusing
Typdeklarationen) in diesen Fällen besser sind, insbesondere wenn sie klassenbezogen sind.Ich möchte ein Beispiel geben, in dem der Rückgabetyp auto perfekt ist:
Stellen Sie sich vor, Sie möchten einen kurzen Alias für einen langen nachfolgenden Funktionsaufruf erstellen. Mit auto müssen Sie sich nicht um den ursprünglichen Rückgabetyp kümmern (möglicherweise ändert er sich in Zukunft), und der Benutzer kann auf die ursprüngliche Funktion klicken, um den tatsächlichen Rückgabetyp zu erhalten:
PS: Kommt auf diese Frage an.
quelle
Für Szenario 3 würde ich den Rückgabetyp der Funktionssignatur mit der lokalen Variablen drehen, die zurückgegeben werden soll. Dies würde es für Client-Programmierer klarer machen, wenn die Funktion zurückgegeben wird. So was:
Szenario 3 So verhindern Sie Redundanz:
Ja, es gibt kein Auto-Schlüsselwort, aber der Principal ist derselbe, um Redundanz zu vermeiden und Programmierern, die keinen Zugriff auf die Quelle haben, die Arbeit zu erleichtern.
quelle
vector<map<pair<int,double>,int>
und diesen dann zu verwenden.