webentwicklung-frage-antwort-db.com.de

Wie viel kostet das automatische C++ 11-Schlüsselwort?

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 .

200
Alan Turing

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)
118

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.

54
Jon Purdy

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

47
spraff

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.

43
DarenW

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();
36
Gene Bushuyev

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 :

  1. Es schadet der Lesbarkeit
  2. Es gibt Bedenken hinsichtlich der automatischen Typkonvertierung (z. B. von Konstruktoren, Zuweisungen, Vorlagen-Zwischentypen, implizite Konvertierung zwischen Ganzzahlbreiten).

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."

35
Sean

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.
18
morotspaj

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!

9
rafak

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();
2
user2495422

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

2
Mantosh Kumar

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!

1
Ajay

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.

1
LainIwakura

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:

  • Sie haben das Gefühl, dass der Variablenname zusammen mit dem Initialisierungs-/Zuweisungsausdruck nicht genügend Informationen darüber liefert, was die Anweisung tut.
  • Sie haben das Gefühl, dass der Variablenname zusammen mit dem Initialisierungs-/Zuweisungsausdruck "irreführende" Informationen darüber liefert, was der Typ sein soll. Wenn Sie also raten müssten, was statt Auto kommt, könnten Sie eine Vermutung treffen - und das wäre falsch, und diese falsche Annahme hat später Auswirkungen auf den Code.
  • Sie möchten einen anderen Typ erzwingen (z. B. eine Referenz).

Und auch:

  • Geben Sie lieber einen aussagekräftigen Namen (der nicht den Typnamen enthält), bevor Sie auto durch den konkreten Typ ersetzen.
0
einpoklum