C++, Variablendeklaration im ‘if’-Ausdruck

Lesezeit: 1 Minute

Benutzer-Avatar
Neutrino

Was ist denn hier los?

if(int a = Func1())
{
    // Works.
}

if((int a = Func1()))
{
    // Fails to compile.
}

if((int a = Func1())
    && (int b = Func2()))
)
{
    // Do stuff with a and b.
    // This is what I'd really like to be able to do.
}

Abschnitt 6.4.3 des Standards von 2003 erklärt, wie Variablen, die in einer Bedingung einer Auswahlanweisung deklariert sind, einen Gültigkeitsbereich haben, der sich bis zum Ende der Unteranweisungen erstreckt, die von der Bedingung gesteuert werden. Aber ich sehe nicht, wo es etwas darüber aussagt, dass keine Klammern um die Deklaration gesetzt werden können, und es sagt auch nichts über nur eine Deklaration pro Bedingung aus.

Diese Einschränkung ist auch dann störend, wenn nur eine Deklaration in der Bedingung erforderlich ist. Bedenken Sie.

bool a = false, b = true;

if(bool x = a || b)
{

}

Wenn ich den ‘if’-Body-Bereich mit x auf false eingeben möchte, benötigt die Deklaration Klammern (da der Zuweisungsoperator einen niedrigeren Vorrang als das logische OR hat), aber da Klammern nicht verwendet werden können, ist die Deklaration von x außerhalb erforderlich den Körper, wodurch diese Erklärung in einem größeren Umfang als gewünscht preisgegeben wird. Offensichtlich ist dieses Beispiel trivial, aber ein realistischerer Fall wäre einer, in dem a und b Funktionen sind, die Werte zurückgeben, die getestet werden müssen

Ist das, was ich tun möchte, nicht standardkonform, oder sprengt mein Compiler nur meine Eier (VS2008)?

  • “Wenn ich die Schleife mit betreten möchte” <-- Ihre Beispiele haben if. if ist keine Schleife, sondern eine Bedingung.

    – crashmstr

    20. Oktober 2011 um 13:48 Uhr

  • @crashmstr: stimmt, aber die Bedingungen für while sind die gleichen wie für if.

    – Mike Seymour

    20. Oktober 2011 um 14:08 Uhr

  • Geht das nicht mit dem Komma-Operator? Ich meine: if (int a = foo(), int b = bar(), a && b)? Wenn der Kommaoperator nicht überladen ist, sagt der Standard, dass die Ausdrücke von links nach rechts ausgewertet werden und der Ergebniswert der letzte Ausdruck ist. Es funktioniert mit for Schleifeninitialisierung, warum nicht hier?

    – Archie

    20. Oktober 2011 um 14:46 Uhr


  • @Archie: Ich habe es gerade versucht, ich konnte es nicht zum Laufen bringen. Vielleicht kannst du ein funktionierendes Beispiel geben?

    – James Johnston

    20. Oktober 2011 um 15:10 Uhr

  • @JamesJohnston: Ich habe es gerade auch versucht, und es scheint nicht zu funktionieren. Diese Idee kam einfach aus meinem Kopf, ich wurde von How vorgeschlagen if funktioniert, und es scheint eine falsche Annahme zu sein.

    – Archie

    20. Oktober 2011 um 15:52 Uhr

Benutzer-Avatar
James Johnston

Ich glaube, Sie haben das Problem bereits angedeutet. Was soll der Compiler mit diesem Code machen?

if (!((1 == 0) && (bool a = false))) {
    // what is "a" initialized to?

Der “&&”-Operator ist ein kurzgeschlossenes logisches UND. Das heißt, wenn der erste Teil (1==0) stellt sich als falsch heraus, dann der zweite Teil (bool a = false) nicht ausgewertet werden, da bereits bekannt ist, dass die endgültige Antwort falsch sein wird. Ob (bool a = false) nicht ausgewertet wird, was dann später mit dem verwendeten Code zu tun ist a? Würden wir die Variable einfach nicht initialisieren und undefiniert lassen? Würden wir es auf den Standardwert initialisieren? Was wäre, wenn der Datentyp eine Klasse wäre und dies unerwünschte Nebenwirkungen hätte? Was wäre wenn statt bool Sie haben eine Klasse verwendet und sie hatte keinen Standardkonstruktor, sodass der Benutzer muss Parameter angeben – was tun wir dann?

Hier ist ein weiteres Beispiel:

class Test {
public:
    // note that no default constructor is provided and user MUST
    // provide some value for parameter "p"
    Test(int p);
}

if (!((1 == 0) && (Test a = Test(5)))) {
    // now what do we do?!  what is "a" set to?

Scheint, als ob die Einschränkung, die Sie gefunden haben, vollkommen vernünftig erscheint – sie verhindert, dass diese Art von Mehrdeutigkeiten auftreten.

  • Guter Punkt. Möglicherweise möchten Sie das Kurzschließen explizit erwähnen, falls das OP oder andere nicht damit vertraut sind.

    – Chris Cooper

    20. Oktober 2011 um 13:52 Uhr

  • Daran hatte ich nicht gedacht. Obwohl in dem von Ihnen bereitgestellten Beispiel der Kurzschluss verhindert, dass der Gültigkeitsbereich der bedingten Anweisung eingegeben wird, ist in diesem Fall der Teil des Ausdrucks, der die nicht verarbeitete Variable deklariert, kein Problem, da sein Gültigkeitsbereich auf den der bedingten Anweisung beschränkt ist. In welchem ​​Fall wäre es nicht besser, wenn der Compiler nur in den Fällen einen Fehler auslöst, in denen die Möglichkeit besteht, dass der Gültigkeitsbereich der bedingten Anweisung eingegeben wird, wenn ein Teil des Ausdrucks, der eine Variable deklariert, nicht verarbeitet wurde? Was in den Beispielen, die ich gegeben habe, nicht der Fall war.

    – Neutrino

    20. Oktober 2011 um 14:35 Uhr

  • @Neutrino Auf den ersten Blick klingt deine Idee ein bisschen wie das SAT-Problem, das zumindest im allgemeinen Fall nicht so einfach zu lösen ist.

    – Christian Rau

    20. Oktober 2011 um 14:37 Uhr

  • Was Sie über all die Probleme mit mehreren Variablendeklarationen in der if-Bedingung erklären und die Tatsache, dass Sie sie nur eingeschränkt verwenden können, lässt mich fragen, warum um alles in der Welt diese Art von Deklaration überhaupt eingeführt wurde. Ich hatte nie je hatte das Bedürfnis nach einer solchen Syntax, bevor ich sie in einem Codebeispiel sah. Ich finde diese Syntax ziemlich ungeschickt und ich denke, dass die Deklaration der Variablen vor dem if-Block viel lesbarer ist. Wenn Sie den Gültigkeitsbereich dieser Variablen wirklich einschränken müssen, können Sie einen zusätzlichen Block um den if-Block setzen. Ich habe diese Syntax nie verwendet.

    – Giorgio

    20. Oktober 2011 um 15:53 ​​Uhr

  • Ich persönlich fände es ziemlich elegant, den Gültigkeitsbereich der von Ihnen verwendeten Variablen auf genau den Gültigkeitsbereich des Anweisungsblocks beschränken zu können, der sie verwenden muss, ohne auf hässliche Maßnahmen wie extra verschachtelte Scoping-Klammern zurückgreifen zu müssen.

    – Neutrino

    20. Oktober 2011 um 16:25 Uhr

Ab C ++ 17, was Sie versucht haben ist endlich möglich:

if (int a = Func1(), b = Func2(); a && b)
{
    // Do stuff with a and b.
}

Beachten Sie die Verwendung von ; von statt , die Erklärung und den tatsächlichen Zustand zu trennen.

  • Hübsch! Ich hatte immer den Verdacht, meiner Zeit voraus zu sein.

    – Neutrino

    30. Mai 2017 um 10:15 Uhr

  • Muss jetzt nur noch While-Schleifen unterstützen.

    – Nullphase

    12. Mai 2021 um 5:05 Uhr

  • @ZeroPhase „for“-Anweisungen mit einem leeren Inkrementfeld stellen dieses Konstrukt bereits bereit

    – Yves Lhuillier

    22. Juni 2021 um 8:54 Uhr


  • Ist Func2() genannt wenn a ist Null (Kurzschlussverhalten)?

    – phinz

    31. August 2021 um 16:17 Uhr

  • Nach dem, was ich in der Dokumentation gelesen habe, ja: Zuerst werden alle Initialisierungen durchgeführt, dann wird der Zustand überprüft.

    – fwyzard

    4. September 2021 um 6:13 Uhr

Benutzer-Avatar
Mike Seymour

Der Zustand in einem if oder while Aussage kann entweder ein sein Ausdruckoder eine einzelne Variable Erklärung (mit Initialisierung).

Ihr zweites und drittes Beispiel sind weder gültige Ausdrücke noch gültige Deklarationen, da eine Deklaration nicht Teil eines Ausdrucks sein kann. Obwohl es nützlich wäre, Code wie Ihr drittes Beispiel schreiben zu können, würde dies eine erhebliche Änderung der Sprachsyntax erfordern.

Ich sehe nicht, wo etwas darüber steht, dass die Deklaration nicht in Klammern gesetzt werden kann, und es sagt auch nichts darüber aus, dass nur eine Deklaration pro Bedingung vorhanden ist.

Die Syntaxspezifikation in 6.4/1 gibt für die Bedingung Folgendes an:

condition:
    expression
    type-specifier-seq declarator = assignment-expression

Angabe einer einzigen Deklaration, ohne Klammern oder andere Verzierungen.

  • Gibt es dafür einen Grund oder Hintergrund?

    – Tomáš Zato – Wiedereinsetzung von Monica

    3. Dezember 2015 um 14:23 Uhr

Wenn Sie Variablen in einem engeren Geltungsbereich einschließen möchten, können Sie immer zusätzlich verwenden { }

//just use { and }
{
    bool a = false, b = true;

    if(bool x = a || b)
    {
        //...
    }
}//a and b are out of scope

Der letzte Abschnitt funktioniert bereits, man muss ihn nur etwas anders schreiben:

if (int a = Func1())
{
   if (int b = Func2())
   {
        // do stuff with a and b
   }
}

Benutzer-Avatar
basisch6

Hier ist eine hässliche Problemumgehung mit einer Schleife (wenn beide Variablen Ganzzahlen sind):

#include <iostream>

int func1()
{
    return 4;
}

int func2()
{
    return 23;
}

int main()
{
    for (int a = func1(), b = func2(), i = 0;
        i == 0 && a && b; i++)
    {
        std::cout << "a = " << a << std::endl;
        std::cout << "b = " << b << std::endl;
    }

    return 0;
}

Aber das wird andere Programmierer verwirren und es ist ziemlich schlechter Code, also nicht zu empfehlen.

Eine einfache Umschließung {} Block (wie bereits empfohlen) ist viel einfacher zu lesen:

{
    int a = func1();
    int b = func2();

    if (a && b)
    {
        std::cout << "a = " << a << std::endl;
        std::cout << "b = " << b << std::endl;
    }
}

Benutzer-Avatar
DukeBrymin

Zu beachten ist auch, dass die Ausdrücke innerhalb des größeren if-Blocks

if (!((1 == 0) && (bool a = false)))

nicht unbedingt von links nach rechts ausgewertet werden. Ein ziemlich subtiler Fehler, den ich damals hatte, hatte mit der Tatsache zu tun, dass der Compiler tatsächlich von rechts nach links statt von links nach rechts testete.

  • Heutzutage schreibt C99 jedoch vor, dass && und || werden von links nach rechts ausgewertet.

    – b0fh

    3. Juli 2013 um 20:25 Uhr

  • Ich denke, eine Rechts-nach-Links-Bewertung der Argumente war aufgrund der Kurzschlusslogik nie möglich. Es wurde immer für Dinge wie einen Test für Pointer und Pointee in einem einzigen Ausdruck verwendet, wie if(p && p->str && *p->str) .... Von rechts nach links wäre tödlich und nicht subtil gewesen. Bei anderen Betreibern – sogar Zuordnung! — und Funktionsaufrufargumente haben Sie Recht, aber nicht mit den Kurzschlussoperatoren.

    – Peter – Setzen Sie Monica wieder ein

    17. März 2017 um 13:11 Uhr


1013390cookie-checkC++, Variablendeklaration im ‘if’-Ausdruck

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy