Ich habe das neue auto
-Schlüsselwort verwendet, das im C++ 11-Standard für komplizierte Templatetypen verfügbar ist, wofür ich glaube, dass es entwickelt wurde. Aber ich benutze es auch für Dinge wie:
auto foo = std::make_shared<Foo>();
Und noch skeptischer für:
auto foo = bla(); // where bla() return a shared_ptr<Foo>
Ich habe nicht viel Diskussion zu diesem Thema gesehen. Es scheint, dass auto
überlastet werden könnte, da ein Typ häufig eine Form von Dokumentation und Vernunftsprüfungen ist. Wo zeichnen Sie die Linie in auto
und welche Anwendungsfälle werden für diese neue Funktion empfohlen?
Zur Klarstellung: Ich bitte nicht um eine philosophische Meinung; Ich frage nach der beabsichtigten Verwendung dieses Stichworts durch die Standardkommission, möglicherweise mit Kommentaren, wie diese beabsichtigte Verwendung in der Praxis umgesetzt wird.
Randbemerkung: Diese Frage wurde an SE.Programmer und dann wieder an Stack Overflow verschoben. Diskussionen hierzu finden Sie in dieser Meta-Frage .
Ich denke, man sollte das auto
-Schlüsselwort verwenden, wenn es schwierig ist, den Typ auf den ersten Blick zu schreiben, aber der Typ der rechten Seite eines Ausdrucks ist offensichtlich. Zum Beispiel mit:
my_multi_type::nth_index<2>::type::key_type::composite_key_type::\
key_extractor_Tuple::tail_type::head_type::result_type
um den zusammengesetzten Schlüsseltyp in boost::multi_index
zu erhalten, obwohl Sie wissen, dass es int
ist. Sie können nicht einfach int
schreiben, da dies in der Zukunft geändert werden könnte. Ich würde in diesem Fall auto
schreiben.
Wenn also das Schlüsselwort auto
die Lesbarkeit in einem bestimmten Fall verbessert, verwenden Sie es. Sie können auto
schreiben, wenn dem Leser klar ist, welchen Typ auto
darstellt.
Hier sind einige Beispiele:
auto foo = std::make_shared<Foo>(); // obvious
auto foo = bla(); // unclear. don't know which type `foo` has
const size_t max_size = 100;
for ( auto x = max_size; x > 0; --x ) // unclear. could lead to the errors
// since max_size is unsigned
std::vector<some_class> v;
for ( auto it = v.begin(); it != v.end(); ++it ) // ok, since I know
// that `it` has an iterator type (don't really care which one in this context)
Verwenden Sie auto
überall, wo Sie können - insbesondere const auto
, damit Nebenwirkungen weniger bedenklich sind. Sie müssen sich nicht um Typen kümmern, außer in den offensichtlichen Fällen. Sie werden jedoch weiterhin statisch für Sie überprüft, und Sie können einige Wiederholungen vermeiden. Wo auto
nicht machbar ist, können Sie decltype
verwenden, um Typen semantisch als Kontrakte basierend auf Ausdrücken auszudrücken. Ihr Code wird anders aussehen, aber es wird eine positive Änderung sein.
Einfach. Verwenden Sie es, wenn Sie egal was der Typ ist. Zum Beispiel
for (const auto & i : some_container) {
...
Ich kümmere mich hier nur darum, dass i
alles ist, was sich im Container befindet.
Es ist ein bisschen wie Typedefs.
typedef float Height;
typedef double Weight;
//....
Height h;
Weight w;
Dabei ist es mir egal, ob h
und w
Floats oder Doubles sind, nur dass sie von jedem Typ sind, der geeignet ist, um Höhen und Gewichte auszudrücken .
Oder überlegen Sie
for (auto i = some_container .begin (); ...
Hier kümmere ich mich nur darum, dass es sich um einen geeigneten Iterator handelt, der operator++()
unterstützt.
Auch die Art der Lambdas kann nicht geschrieben werden, also ist auto f = []...
ein guter Stil. Die Alternative ist das Casting auf std::function
, aber das ist mit Overhead verbunden.
Ich kann mir einen "Missbrauch" von auto
nicht wirklich vorstellen. Am ehesten kann ich mir vorstellen, dass Sie sich einer expliziten Konvertierung in einen signifikanten Typ entziehen - aber Sie würden auto
dafür nicht verwenden, Sie würden ein Objekt des gewünschten Typs konstruieren.
Wenn Sie können einige Redundanzen in Ihrem Code entfernen, ohne Nebenwirkungen einzuführen, dann muss es sei gut dazu.
Gegenbeispiele (aus den Antworten eines anderen entlehnt):
auto i = SomeClass();
for (auto x = make_unsigned (y); ...)
Hier ist uns der Typ egal, also sollten wir Someclass i;
und for(unsigned x = y;...
schreiben
Tue es. Verwenden Sie auto
überall, wo das Schreiben von Code einfacher ist.
Jedes neue Feature in einer beliebigen Sprache wird von einigen Programmierern überlastet. Nur durch mäßige Überbeanspruchung durch einige erfahrene Programmierer (nicht Noobs) lernen die anderen erfahrenen Programmierer die Grenzen der ordnungsgemäßen Verwendung. Extreme Überbeanspruchung ist in der Regel schlecht, kann jedoch von Vorteil sein, da eine solche Überbeanspruchung möglicherweise zu Verbesserungen der Funktion oder zu einer besseren Funktion führt, um sie zu ersetzen.
Aber wenn ich mit mehr als ein paar Zeilen am Code arbeite
auto foo = bla();
wenn der Typ null Mal angegeben wird, möchte ich diese Zeilen möglicherweise um Typen ändern. Das erste Beispiel ist großartig, da der Typ einmal angegeben wird und auto
es uns erspart, unordentlich vorgefertigte Typen zweimal zu schreiben. Hurra für C++++. Das explizite Anzeigen des Typs null, wenn er in einer nahegelegenen Zeile nicht leicht sichtbar ist, macht mich zumindest in C++ und seinen unmittelbaren Nachfolgern nervös. Für andere Sprachen, die auf einer höheren Ebene mit mehr Abstraktion, Polymorphismus und Generizität arbeiten sollen, ist das in Ordnung.
Ja, es kann zum Nachteil der Lesbarkeit überstrapaziert werden. Ich schlage vor, es in Kontexten zu verwenden, in denen exakte Typen lang sind oder nicht lesbar sind oder für die Lesbarkeit nicht wichtig sind und Variablen nur von kurzer Dauer sind. Beispielsweise ist der Iteratortyp normalerweise lang und nicht wichtig, sodass auto
funktionieren würde:
for(auto i = container.begin(); i != container.end(); ++i);
auto
schadet der Lesbarkeit nicht.
Ein anderes Beispiel ist der Parser-Regeltyp, der lang und gewunden sein kann. Vergleichen Sie:
auto spaces = space & space & space;
mit
r_and_t<r_and_t<r_char_t<char>&, r_char_t<char>&>, r_char_t<char>&> spaces =
space & space & space;
Wenn der Typ jedoch bekannt und einfach ist, ist es viel besser, wenn er explizit angegeben wird:
int i = foo();
eher, als
auto i = foo();
Bei C++ and Beyond 2012 im Ask Us Anything Panel gab es einen fantastischen Austausch zwischen Andrei Alexandrescu, Scott Meyers und Herb Sutter sprechen darüber, wann auto
zu verwenden und nicht zu verwenden ist. Fahren Sie mit Minute 25:03 fort, um eine 4-minütige Diskussion zu führen. Alle drei Sprecher geben hervorragende Punkte, die beachtet werden sollten, wenn auto
not verwendet werden soll.
Ich ermutige die Leute nachdrücklich, zu ihrer eigenen Schlussfolgerung zu kommen, aber mein Take Away war benutze auto
überall , es sei denn :
Die liberale Verwendung von explicit
hilft, die Sorge um das Letztere zu verringern, was dazu beiträgt, den Zeitaufwand für das Erstere zu minimieren.
Umformulieren, was Herb sagte: "Wenn Sie nicht X, Y und Z tun, verwenden Sie auto
. Erfahren Sie, was X, Y und Z sind, und verwenden Sie auto
überall sonst."
auto
kann in Kombination mit Ausdrucksvorlagen, die häufig von linearen Algebra-Bibliotheken wie Eigen oder OpenCV verwendet werden, sehr gefährlich sein.
auto A = Matrix(...);
auto B = Matrix(...);
auto C = A * B; // C is not a matrix. It is a matrix EXPRESSION.
cout << C; // The expression is evaluated and gives the expected result.
... // <code modifying A or B>
cout << C; // The expression is evaluated AGAIN and gives a DIFFERENT result.
Fehler, die durch diese Art von Fehlern verursacht werden, sind ein großer Schmerz für das Debuggen. Eine mögliche Abhilfe besteht darin, das Ergebnis explizit in den erwarteten Typ umzuwandeln, wenn Sie Auto für den Deklarationsstil von links nach rechts verwenden.
auto C = Matrix(A * B); // The expression is now evaluated immediately.
Ich verwende auto
ohne Einschränkung und hatte kein Problem. Ich benutze es sogar manchmal für einfache Typen wie int
. Dies macht C++ zu einer höheren Sprache für mich und erlaubt es, Variablen in C++ wie in Python zu deklarieren. Nach dem Schreiben von Python-Code schreibe ich manchmal sogar z.
auto i = MyClass();
anstatt
MyClass i;
In diesem Fall würde ich sagen, dass es sich um einen Missbrauch des auto
-Schlüsselworts handelt.
Oft macht es mir nichts aus, was der genaue Typ des Objekts ist, ich interessiere mich mehr für seine Funktionalität und da Funktionsnamen im Allgemeinen etwas über die Objekte sagen, die sie zurückgeben, auto
schadet nicht: z. auto s = mycollection.size()
, ich kann mir vorstellen, dass s
eine Art Ganzzahl ist, und in dem seltenen Fall, in dem ich mich für den genauen Typ interessiere, lassen Sie uns den Prototyp der Funktion prüfen (ich meine, ich bevorzuge es, zu prüfen, wann ich die Informationen brauche, eher als a priori, wenn Code geschrieben wird, nur für den Fall, dass er irgendwann nützlich wäre, wie in int_type s = mycollection.size()
).
Zu diesem Beispiel aus der akzeptierten Antwort:
for ( auto x = max_size; x > 0; --x )
In meinem Code verwende ich in diesem Fall noch auto
. Wenn x
unsigniert werden soll, verwende ich eine Dienstprogrammfunktion namens say make_unsigned
, die meine Bedenken klar zum Ausdruck bringt:
for ( auto x = make_unsigned(max_size); x > 0; --x )
haftungsausschluss: Ich beschreibe nur meine Verwendung, ich bin nicht kompetent für Ratschläge!
Eine Gefahr, die ich bemerkt habe, betrifft die Referenzen.
MyBigObject& ref_to_big_object= big_object;
auto another_ref = ref_to_big_object; // ?
Das Problem ist, dass another_ref eigentlich keine Referenz ist. In diesem Fall handelt es sich um MyBigObject anstelle von MyBigObject &. Am Ende kopieren Sie ein großes Objekt, ohne es zu merken.
Wenn Sie eine Referenz direkt von einer Methode erhalten, denken Sie möglicherweise nicht darüber nach, was sie eigentlich ist.
auto another_ref = function_returning_ref_to_big_object();
sie würden "auto &" oder "const auto &" benötigen
MyBigObject& ref_to_big_object= big_object;
auto& another_ref = ref_to_big_object;
const auto& yet_another_ref = function_returning_ref_to_big_object();
Ein Problem von major mit dem C++ - Programm besteht darin, dass Sie die nicht initialisierte Variable verwenden können. Dies führt uns zu unangenehmem nicht deterministischem Programmverhalten. Es ist zu beachten, dass der moderne Compiler jetzt entsprechende Warnmeldungen ausgibt, wenn das Programm die Verwendung dieser Funktion erschöpft.
Um dies zu veranschaulichen, betrachten Sie das folgende C++ - Programm:
int main() {
int x;
int y = 0;
y += x;
}
Wenn ich dieses Programm mit einem modernen Compiler (GCC) kompiliere, wird eine Warnung ausgegeben. Eine solche Warnung ist möglicherweise nicht sehr offensichtlich, wenn wir mit dem wirklich komplexen Produktionscode arbeiten.
main.cpp: In der Funktion 'int main ()':
main.cpp: 4: 8: warning: 'x' wird in dieser Funktion nicht initialisiert verwendet [-Wuninitialisiert]
y + = x;
^
================================================== =============================== __. Nun, wenn wir unser Programm ändern, das autoverwendet _, dann kompilieren wir folgendes:
int main() {
auto x;
auto y = 0;
y += x;
}
main.cpp: In der Funktion 'int main ()':
main.cpp: 2: 10: error: Die Deklaration von 'auto x' hat keinen Initialisierer
auto x; ^
Mit auto ist es nicht möglich, die nicht initialisierte Variable zu verwenden. Dies ist ein großer Vorteil, den wir (kostenlos) erhalten können, wenn wir auto verwenden.
Dieses Konzept und andere großartige moderne C++ - Konzepte werden von C++ - Experte Herb Shutter in seiner CppCon14 Diskussion erklärt:
Zurück zu den Grundlagen! Grundlagen des modernen C++ - Stils
Das auto
-Schlüsselwort kann nur für lokale Variablen verwendet werden, nicht für Argumente oder Klassen-/Strukturmitglieder. Daher ist es sicher und praktikabel, sie überall zu verwenden, wo Sie möchten. Ich benutze sie viel. Der Typ wird zur Kompilierzeit abgeleitet, der Debugger zeigt den Typ beim Debuggen an, die sizeof
meldet sie richtig, die decltype
würde den richtigen Typ angeben - es gibt keinen Schaden. Ich zähle auto
nie als überlastet!
Verwenden Sie auto
, wenn es sinnvoll ist, auf einen Typ zu schließen. Wenn Sie etwas haben, von dem Sie wissen, dass es eine Ganzzahl ist, oder Sie wissen, dass es eine Zeichenfolge ist, verwenden Sie einfach int/std :: string usw. Ich würde mir keine Sorgen machen, wenn Sie eine Sprachfunktion "überfordern", es sei denn, es kommt zur Lächerlichkeit oder verschleiert Code.
Das ist sowieso meine Meinung.
TL; DR: Siehe Daumenregel unten.
Die akzeptierte Antwort schlägt die folgende Faustregel vor:
Verwenden Sie
auto
immer dann, wenn Sie schwer sagen können, wie der Typ auf den ersten Blick geschrieben wird. Der Typ der rechten Seite eines Ausdrucks ist jedoch offensichtlich.
Aber ich würde sagen, das ist zu restriktiv. Manchmal kümmere ich mich nicht um die Typen, da die Aussage informativ genug ist, ohne dass ich mir die Zeit genommen habe, den Typ herauszufinden. Was meine ich damit? Betrachten Sie das Beispiel, das in einigen Antworten aufgetaucht ist:
auto x = f();
Was macht dieses Beispiel für den Missbrauch von auto
? Ist es meine Unkenntnis des Rückgabetyps von f()
? Nun, es kann in der Tat helfen, wenn ich es wüsste, aber - das ist nicht meine Hauptsorge. Viel schwieriger ist, dass x
und f()
ziemlich bedeutungslos sind. Wenn wir hätten:
auto nugget = mine_gold();
stattdessen kümmere ich mich normalerweise nicht darum, ob der Rückgabetyp der Funktion offensichtlich ist oder nicht. Beim Lesen der Aussage weiß ich, was ich mache, und ich weiß genug über die Semantik des Rückgabewerts, um nicht das Gefühl zu haben, dass ich auch dessen Typ wissen muss.
Meine Antwort lautet also: Verwenden Sie auto
, wenn der Compiler dies erlaubt, es sei denn:
Und auch:
auto
durch den konkreten Typ ersetzen.