Frage Das neue Schlüsselwort "auto"; Wann sollte es verwendet werden, um einen Variablentyp zu deklarieren? [Duplikat]


Mögliche Duplikate:
Wie viel ist zu viel mit C ++ 0x auto keyword 

Haben wir (als Gemeinschaft) genügend Erfahrung, um festzustellen, wann und / oder ob Auto missbraucht wird?

Was ich wirklich suche, ist ein Leitfaden für Best Practices

  • Wann ist Auto zu verwenden?
  • wenn es vermieden werden sollte

Einfache Faustregeln, die in 80% der Fälle schnell befolgt werden können.

Als Kontext wird diese Frage durch meine Antwort ausgelöst Hier


76
2017-08-01 15:09


Ursprung


Antworten:


Ich denke, wenn der Typ unter den Co-Programmierern sehr bekannt ist, die in Ihrem Projekt arbeiten (oder arbeiten würden), dann auto kann verwendet werden, wie im folgenden Code:

//good : auto increases readability here
for(auto it = v.begin(); it != v.end(); ++it) //v is some [std] container
{
      //..
}

Oder, allgemeiner gesagt,

//good : auto increases readability here
for(auto it = std::begin(v); it != std::end(v); ++it)//v could be array as well
{
      //..
}

Aber wenn der Typ nicht sehr bekannt und selten verwendet wird, dann denke ich auto scheint die Lesbarkeit zu reduzieren, wie hier:

//bad : auto decreases readability here
auto obj = ProcessData(someVariables);

Während im ersten Fall die Verwendung von auto scheint sehr gut zu sein und verringert die Lesbarkeit nicht und kann daher umfassend verwendet werden, aber im letzteren Fall verringert es die Lesbarkeit und sollte daher nicht verwendet werden.


Ein anderer Ort wo auto kann verwendet werden, wenn Sie verwenden new1 oder make_* Funktionen wie hier:

//without auto. Not that good, looks cumbersome
SomeType<OtherType>::SomeOtherType * obj1 = new SomeType<OtherType>::SomeOtherType();
std::shared_ptr<XyzType> obj2 = std::make_shared<XyzType>(args...);
std::unique_ptr<XyzType> obj2 = std::make_unique<XyzType>(args...);

//With auto. good : auto increases readability here
auto obj1 = new SomeType<OtherType>::SomeOtherType();
auto obj2 = std::make_shared<XyzType>(args...);
auto obj3 = std::make_unique<XyzType>(args...);

Hier ist es sehr gut, da es die Verwendung der Tastatur reduziert, ohne die Lesbarkeit zu verringern, da jeder die Art von Objekte erstellt werden, nur durch den Code zu sehen.

1. Vermeiden Sie die Verwendung new und Rohzeiger.


Irgendwann ist der Typ so irrelevant, dass die Kenntnis des Typs nicht einmal benötigt wird, wie in Ausdrucksvorlage; eigentlich, praktisch In solchen Fällen ist es unmöglich, den Typ (korrekt) zu schreiben auto ist eine Erleichterung für Programmierer. Ich habe eine Ausdruckvorlagenbibliothek geschrieben, die verwendet werden kann als:

foam::composition::expression<int> x;

auto s = x * x;       //square
auto c = x * x * x;   //cube
for(int i = 0; i < 5 ; i++ )
    std::cout << s(i) << ", " << c(i) << std::endl; 

Ausgabe:

0, 0
1, 1
4, 8
9, 27
16, 64

Vergleichen Sie nun den obigen Code mit dem Folgenden gleichwertig Code, der nicht verwendet wird auto:

foam::composition::expression<int> x;

//scroll horizontally to see the complete type!!
foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<int>, foam::composition::expression<int>, foam::operators::multiply>> s = x * x; //square
foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<int>, foam::composition::expression<int>, foam::operators::multiply> >, foam::composition::expression<int>, foam::operators::multiply>> c = x * x * x; //cube

for(int i = 0; i < 5 ; i++ )
    std::cout << s(i) << ", " << c(i) << std::endl; 

Wie Sie sehen können, in solchen Fällen auto macht dein Leben exponentiell einfacher. Die oben verwendeten Ausdrücke sind sehr einfach; Denken Sie über die Art einiger komplexerer Ausdrücke nach:

auto a = x * x - 4 * x + 4; 
auto b = x * (x + 10) / ( x * x+ 12 );
auto c = (x ^ 4 + x ^ 3 + x ^ 2 + x + 100 ) / ( x ^ 2 + 10 );

Die Art solcher Ausdrücke wäre noch größer und hässlicher, aber dank autokönnen wir nun den Compiler auf den Typ der Ausdrücke schließen lassen.


Die Grundlinie ist also: das Schlüsselwort auto Macht erhöhen oder verringern Klarheit und Lesbarkeit Ihres Codes, abhängig vom Kontext. Wenn der Kontext klarstellt was Art es ist, oder zumindest, wie es verwendet werden sollte (im Falle eines Standard-Container-Iterators), oder die Kenntnis des tatsächlichen Typs wird nicht einmal benötigt (wie in Expression-Templates) auto sollte sein benutztund wenn der Kontext nicht klar und nicht sehr häufig ist (wie im zweiten Fall oben), sollte es besser sein vermieden werden.


127
2017-08-01 15:13



Einfach. Verwenden Sie es, wenn Sie ist mir egalWas ist der Typ? Beispielsweise

for (auto i : some_container) {
   ...

Alles, was mich interessiert, ist das i ist was auch immer in dem Container ist.

Es ist ein bisschen wie Typedefs.

typedef float Height;
typedef double Weight;
//....
Height h;
Weight w;

Hier ist mir egal, ob h und w sind Floats oder Doubles, nur das sind sie welcher Typ auch immer geeignet ist, um Höhen und Gewichte auszudrücken.

Oder bedenke es

for (auto i = some_container .begin (); ...

Hier ist mir nur wichtig, dass es ein geeigneter Iterator ist, der unterstützt operator++()Es ist irgendwie wie Ente in dieser Hinsicht tippen.

Auch die Art von Lambdas kann nicht buchstabiert werden auto f = []... ist ein guter Stil. Die Alternative ist Casting std::function aber das kommt mit Overhead.

Ich kann mir nicht wirklich einen "Missbrauch" vorstellen auto. Das nächste, was ich mir vorstellen kann, ist, sich eine explizite Umwandlung in einen signifikanten Typ zu entgehen - aber Sie würden nicht verwenden auto Dafür würden Sie ein Objekt des gewünschten Typs konstruieren.

Wenn du kann Entfernen Sie etwas Redundanz in Ihrem Code, ohne Nebenwirkungen zu verursachen Muss sei gut, das zu tun.


14
2017-08-01 15:27



Ich würde die gleiche Regel wie für anwenden var in C #: benutze es großzügig. Es erhöht sich Lesbarkeit. Es sei denn, der Typ einer Variablen ist tatsächlich wichtig genug, um explizit angegeben zu werden, in welchen Fällen dies getan werden sollte (duh).

Dennoch behaupte ich, dass (insbesondere in statisch typisierten Sprachen) der Compiler die Typen für uns viel besser verfolgt als wir. Die meiste Zeit, die genau Typ ist sowieso nicht so wichtig (sonst würden Schnittstellen in der Praxis nicht funktionieren). Es ist wichtiger zu wissen, welche Operationen erlaubt sind. Der Kontext sollte uns das sagen.

Außerdem, auto kann eigentlich Bugs verhindern, indem ungewollte implizite Konvertierungen bei Initialisierungen verhindert werden. Im Allgemeinen die Aussage Foo x = y; führt eine implizite Konvertierung durch y ist nicht von der Art Foo und eine implizite Konvertierung existiert. Dies ist der Grund dafür, dass implizite Konvertierungen vermieden werden. Leider hat C ++ viel zu viele von ihnen bereits.

Schreiben auto x = y; wird dieses Problem verhindern allgemein gesagt.

Auf der anderen Seite sollte klar sein, dass, wenn ich Berechnungen durchführe, die diese oder jene Anzahl von Bytes in einer ganzen Zahl annehmen, muss der explizite Typ der Variablen bekannt sein und klar angegeben werden.

Nicht alle Fälle sind so klar, aber ich behaupte, dass die meisten sind, und das

  1. In den meisten Fällen ist es einfach zu erkennen, ob ein expliziter Typ bekannt sein muss und
  2. das Bedürfnis nach expliziten Typen ist vergleichsweise selten.

Eric Lippert, Hauptentwickler im C # -Compiler-Team, hat viel gesagt in Bezug auf var.


10
2017-08-01 15:28



Ich denke, die Antwort auf deine erste Frage ist eine Art Nein. Wir wissen genug, um einige Richtlinien für den Einsatz oder die Vermeidung zu formulieren auto, aber sie hinterlassen immer noch einige Fälle, in denen das Beste, was wir derzeit sagen können, ist, dass wir noch nicht viel über objektive Ratschläge geben können.

Der naheliegende Fall, in dem du fast bist haben es zu verwenden ist in einer Schablone, wenn Sie (zum Beispiel) den richtigen Typ haben möchten, um das Ergebnis irgendeiner Operation auf zwei generischen Parametern zu halten. In einem solchen Fall wäre die einzige Möglichkeit des Missbrauchs nicht wirklich Missbrauch auto selbst, aber ob die allgemeine Art der Operation, die Sie gerade machen (oder die Art der Vorlage, die Sie schreiben, usw.), ist etwas, das Sie besser vermeiden sollten.

Es gibt auch mindestens einige Situationen, in denen Sie eindeutig vermeiden müssen auto. Wenn Sie etwas wie einen Proxy-Typ verwenden, bei dem Sie von der Konvertierung von proxy-> target abhängig sind, um einen Teil des Jobs zu erledigen, auto erstellt (versucht) ein Ziel des gleichen Typs wie die Quelle, sodass die Konvertierung nicht stattfindet. In einigen Fällen kann dies die Konvertierung verzögern, in anderen Fällen wird es überhaupt nicht funktionieren (z. B. wenn der Proxy-Typ die Zuweisung nicht unterstützt, was oft der Fall ist).

Ein anderes Beispiel wäre, wenn Sie sicherstellen müssen, dass eine bestimmte Variable einen bestimmten Typ für eine externe Schnittstelle hat. Betrachten Sie beispielsweise die Netzwerkmaske auf eine IP-Adresse (v4). Nehmen wir an, Sie arbeiten mit den einzelnen Oktetts der Adresse (z. B. indem Sie jedes als ein unsigned char), so enden wir mit etwas wie octets[0] & mask[0]. Dank der C-Typ-Promotion-Regeln, auch wenn beide Operanden sind unsigned chars, das Ergebnis wird typischerweise sein int. Wir brauchen das Ergebnis zu sein unsigned char obwohl (d. h. ein Oktett) nicht ein int (in der Regel 4 Oktetts). In dieser Situation auto wäre mit ziemlicher Sicherheit unangemessen.

Das lässt aber immer noch viele Fälle übrig, in denen es ein Urteilsspruch ist. Mein eigenes Tendenz Für diese Fälle ist zu behandeln auto als Standard, und verwende nur einen expliziten Typ in Fällen, die mindestens ein bisschen wie der letztere Fall sind, den ich oben zitiert habe - selbst wenn ein bestimmter Typ nicht ist erforderlich für eine korrekte Bedienung, die ich wirklich wollen ein bestimmter Typ, auch wenn dies eine implizite Konvertierung beinhalten könnte.

Meine Vermutung (aber es ist nur eine Vermutung) ist, dass ich mit der Zeit werde wahrscheinlich tendieren noch mehr in diese Richtung. Da ich mich mehr an den Compiler gewöhnt habe, der Typen auswählt, werde ich feststellen, dass es eine ganze Reihe von Fällen gibt, in denen ich derzeit bin denken Ich sollte den Typ angeben, den ich wirklich nicht brauche und der Code wird einfach gut sein.

Ich vermute, dass viele von uns (und die älteren / erfahreneren wir sind, wahrscheinlich das Schlimmste, was wir tun werden) explizite Typen aus Gründen verwenden werden, die letztlich auf ein Gefühl von Leistung zurückgehen und glauben, dass unsere Wahl die Leistung verbessern wird . Teil der Zeit wir kann sogar richtig - aber wie die meisten von uns mit so viel Erfahrung herausgefunden haben, sind unsere Vermutungen oft falsch (besonders wenn sie auf impliziten Annahmen basieren), und Compiler und Prozessoren werden im Allgemeinen auch in solchen Dingen im Laufe der Zeit besser.


4
2017-08-01 16:52



Ich habe Sprachen mit voller Typinferenz verwendet. Ich sehe keinen Grund, nicht zu setzen auto überall ist es technisch möglich *. Tatsächlich habe ich vielleicht schon geschrieben auto i = 0;, woher int ist ein Zeichen kürzer als auto. Ich bin mir nicht einmal sicher, ob ich das getan habe, weil der Grund ist: Ich kümmere mich nicht um manifestiertes Tippen.

*: zum Beispiel auto int[] = { 0, 1, 2, 3 } funktioniert nicht.


2
2017-08-01 15:52



Verwenden Sie es nur mit langen sich wiederholenden Typen wie langen Vorlagen und Lambda-Funktionstypen. Versuchen Sie es zu vermeiden, wenn Sie die Dinge klarstellen können.


2
2017-08-01 15:14