Variablen in einer if-Anweisung initialisieren

80

Ich habe gelesen, dass wir in C ++ 17 Variablen in solchen ifAnweisungen initialisieren können

if (int length = 2; length == 2)
    //execute something

Anstatt

int length = 2;
if (length == 2)
    //do something

Obwohl es kürzer ist, beeinträchtigt es die Lesbarkeit des Codes (insbesondere für Leute, die diese neue Funktion nicht kennen), was meiner Meinung nach eine schlechte Codierungspraxis für die Entwicklung großer Software ist.

Gibt es einen anderen Vorteil bei der Verwendung dieser Funktion als die Verkürzung des Codes?

Arne
quelle
38
Neben der Scope-Sache?
DeiDei
10
Ich denke, jemand hat vor einigen Jahren gesagt: "Ich habe gelesen, dass wir in C ++ 11 Lambda-Anweisungen wie diese erstellen können (...). Obwohl sie kürzer sind, wirkt sie sich auf die Lesbarkeit des Codes aus (insbesondere für Leute, die es nicht wissen." diese neue Funktion), die meiner Meinung nach eine schlechte Codierungspraxis für die Entwicklung großer Software ist. "
R2RT
9
Ich würde sagen, dass es genau die gleiche Länge hat, nicht kürzer.
user7860670
5
reine Meinung, daher keine Antwort: Es if (int length = 2; length == 2)ist vielleicht überraschend, dass Sie es das erste Mal sehen, aber es ist nichts Komplexes, das man nicht verstehen konnte, so dass es bereits beim zweiten Mal keine große Überraschung mehr sein wird und Dinge in dem Bereich deklariert werden, in den sie gehören einer der Hauptfaktoren, die zur Lesbarkeit beitragen. Imho Ihre Prämisse ist falsch;)
idclev 463035818
14
Die Sorge um die Lesbarkeit von Code für Leute, die die Sprache, in der der Code geschrieben ist, nicht kennen (was bedeutet, dass "diese neue Funktion nicht kennt" bedeutet), ist ein Wettlauf nach unten.

Antworten:

97

Es beschränkt den Umfang lengthauf das ifAllein. Sie erhalten also die gleichen Vorteile, die wir ursprünglich hatten, als wir schreiben durften

for(int i = 0; i < ... ; ++i) {
   // ...
}

Anstelle der Variablen undicht

int i;
for(i = 0; i < ... ; ++i) {
   // ...
}

Kurzlebige Variablen sind aus mehreren Gründen besser. Aber um ein paar zu nennen:

  1. Je kürzer etwas lebt, desto weniger Dinge müssen Sie beim Lesen nicht verwandter Codezeilen beachten. Wenn ies außerhalb der Schleife oder ifAnweisung nicht existiert , müssen wir uns nicht um den Wert außerhalb dieser Schleife kümmern. Wir müssen uns auch keine Sorgen machen, dass sein Wert mit anderen Teilen des Programms interagiert, die außerhalb des beabsichtigten Bereichs liegen (was passieren kann, wenn ioben in einer anderen Schleife wiederverwendet wird). Es erleichtert das Verfolgen und Überlegen von Code.

  2. Wenn die Variable eine Ressource enthält, wird diese Ressource jetzt für den kürzestmöglichen Zeitraum gespeichert. Und das ohne überflüssige geschweifte Klammern. Es wird auch klargestellt, dass die Ressource nur mit der Ressource zusammenhängt if. Betrachten Sie dies als ein motivierendes Beispiel

    if(std::lock_guard _(mtx); guarded_thing.is_ready()) {
    }
    

Wenn Ihre Kollegen die Funktion nicht kennen, bringen Sie sie bei! Programmierer zu beschwichtigen, die nicht lernen möchten, ist eine schlechte Ausrede, um Funktionen zu vermeiden.

Geschichtenerzähler - Unslander Monica
quelle
12
Ich werde diesen letzten Satz nehmen und ihn auf ein zwei Meter langes Poster klatschen.
Quentin
3
Außerdem sollten kurzlebige (eng begrenzte) Variablen Fehler reduzieren, da Sie eine Variable später im Code nicht versehentlich wiederverwenden können, sobald ihr Zweck erfüllt ist.
Galik
1
Oder mit einem schwachen Zeiger:if (auto p = ptr.lock(); p && p->foo()) bar(*p);
Deduplikator
1
3. Der Compiler darf den Stapelspeicher in mehreren Fällen wiederverwenden. (Wenn Sie i jemals als Referenz oder Zeiger auf eine externe Funktion übergeben, zum Beispiel.)
TLW
Die bessere Frage könnte sein: "Was ist der Vorteil davon gegenüber {int i = 2; if (i == 2) {...}}" (Beachten Sie den zusätzlichen Umfang.)
TLW
24

Gibt es einen anderen Vorteil bei der Verwendung dieser Funktion als die Verkürzung des Codes?

Sie reduzieren den variablen Bereich. Dies ist sinnvoll und erhöht die Lesbarkeit, da dadurch die Lokalität der Bezeichner gestärkt wird, über die Sie nachdenken müssen. Ich bin damit einverstanden, dass lange init-Anweisungen in ifAnweisungen vermieden werden sollten, aber für kurze Dinge ist es in Ordnung.

Beachten Sie, dass Sie das Ergebnis bereits in Pre-C ++ 17 initialisieren und verzweigen können:

int *get(); // returns nullptr under some condition

if (int *ptr = get())
    doStuff();

Dies hängt von der persönlichen Meinung ab, aber Sie können eine explizite Bedingung als besser lesbar betrachten:

if (int *ptr = get(); ptr != nullptr)
    doStuff();

Außerdem ist es gefährlich, gegen die Lesbarkeit eines Features zu argumentieren, indem man sich auf die Tatsache bezieht, dass die Leute nicht daran gewöhnt sind. Die Leute waren irgendwann nicht mehr an kluge Zeiger gewöhnt, aber wir sind uns heute alle einig (ich denke), dass es gut ist, dass sie da sind.

lubgr
quelle
4
Sie können verwenden, if (auto p =get ())da der Operator bool definiert ist
sudo rm -rf Schrägstrich
19

Die neue Form der if-Anweisung hat viele Verwendungszwecke.

Derzeit wird der Initialisierer entweder vor der Anweisung deklariert und in den Umgebungsbereich übertragen, oder es wird ein expliziter Bereich verwendet. Mit dem neuen Formular kann ein solcher Code kompakter geschrieben werden, und die verbesserte Bereichssteuerung macht einige frühere fehleranfällige Konstruktionen etwas robuster.

Öffnen Sie den Standardvorschlag für die If-Anweisung mit dem Initialisierer

Geben Sie hier die Bildbeschreibung ein

Zusammenfassend vereinfacht diese Anweisung gängige Codemuster und hilft Benutzern, die Bereiche eng zu halten.

Ich hoffe, es hilft!

Abhishek Sinha
quelle
Könnten Sie bitte klarstellen, dass Sie aus dem Vorschlag zitieren? Besonders der zweite Absatz. Ich schlage Blockzitate vor.
StoryTeller - Unslander Monica
Danke @StoryTeller, ja, ich habe den zweiten Absatz aus dem Vorschlag von open-std zitiert, der in C ++ 17 enthalten ist.
Abhishek Sinha
10

Um den Umfang der Variablen zu minimieren, gibt es eine Redewendung, die eine Ressource nur dann definiert, wenn sie bei der Erstellung gültig ist (z. B. Dateistreamobjekte ):

if(auto file = std::ifstream("filename"))
{
    // use file here
}
else
{
    // complain about errors here
}

// The identifier `file` does not pollute the wider scope

Manchmal möchten Sie in der Lage sein, die Logik dieses Tests umzukehren, um den Fehler zur Primärklausel und die gültige Ressource zur elseKlausel zu machen. Dies war bisher nicht möglich. Aber jetzt können wir tun:

if(auto file = std::ifstream("filename"); !file)
{
    // complain about errors here
}
else
{
    // use file here
}

Ein Beispiel könnte eine Ausnahme auslösen:

if(auto file = std::ifstream(filename); !file)
    throw std::runtime_error(std::strerror(errno));
else
{
    // use file here
}

Einige Leute codieren gerne so, dass eine Funktion bei einem Fehler frühzeitig abgebrochen wird und ansonsten fortschreitet. Diese Redewendung stellt die Abbruchlogik physisch über die Fortsetzungslogik, die manche Menschen vielleicht natürlicher finden.

Galik
quelle
8

Es ist besonders nützlich für logische Ereignisse. Betrachten Sie dieses Beispiel:

char op = '-';
if (op != '-' && op != '+' && op != '*' && op != '/') {
    std::cerr << "bad stuff\n";
}

Scheint etwas rau. Wenn Sie OR, ANDmit Negationen nicht sehr vertraut sind , müssen Sie möglicherweise innehalten und über diese Logik nachdenken - die im Allgemeinen ein schlechtes Design ist. Mit dem if-initializationkönnen Sie Ausdruckskraft hinzufügen.

char op = '-';
if (bool op_valid = (op == '-') || (op == '+') || (op == '*') || (op == '/'); !op_valid) {
    std::cerr << "bad stuff\n";
} 

Die benannte Variable kann auch innerhalb der wiederverwendet werden if. Z.B:

if (double distance = std::sqrt(a * a + b * b); distance < 0.5){
    std::cerr << distance << " is too small\n";
}

Dies ist großartig, insbesondere angesichts der Tatsache, dass die Variable einen Gültigkeitsbereich hat und daher den Speicherplatz danach nicht verschmutzt.

Stapel Danny
quelle
2
Mir ist klar, dass es subjektiv ist, aber ich bevorzuge Ihre "raue" Version gegenüber der mit dem if-Initialisierer. Ich finde es viel einfacher zu lesen und zu verstehen.
Fabio sagt Reinstate Monica
@FabioTurati Ich nehme an, das liegt daran, dass Sie sehr vertraut damit sind und die andere Version neu ist. Aber im Laufe der Zeit erwarte ich, dass der if-Initialisierer etwas Ähnliches übertrifft.
Danny
7

Dies ist eine Erweiterung einer vorhandenen Funktion, die meiner Erfahrung nach die Lesbarkeit verbessert.

if (auto* ptr = get_something()) {
}

Hier erstellen wir beide die Variable ptrund testen, ob sie nicht null ist. Der Geltungsbereich von ptrist auf den Gültigkeitsbereich beschränkt. Es ist viel einfacher, sich davon zu überzeugen, dass jede Verwendung ptrgültig ist.

Aber was ist, wenn wir über etwas sprechen, das sich nicht auf booldiese Weise umwandelt ?

if (auto itr = find(bob)) {
}

Das geht nicht Aber mit dieser neuen Funktion können wir:

if (auto itr = find(bob); itr != end()) {
}

Fügen Sie eine Klausel hinzu, die besagt, wann diese Initialisierung gültig ist.

Im Wesentlichen erhalten wir eine Reihe von Token, die bedeuten: "Initialisieren Sie einen Ausdruck, und wenn er gültig ist, führen Sie einen Code aus. Wenn er nicht gültig ist, verwerfen Sie ihn."

Seit C ++ 98 ist es idiomatisch, den Zeigertest-Trick auszuführen. Sobald Sie das angenommen haben, ist diese Erweiterung natürlich.

Yakk - Adam Nevraumont
quelle