Eine Variable im Bedingungsteil einer if-Anweisung definieren?

75

Ich war nur schockiert, dass dies erlaubt ist:

if( int* x = new int( 20 ) )
{
    std::cout << *x << "!\n";
    // delete x;
}
else
{
    std::cout << *x << "!!!\n";
    // delete x;
}
// std:cout << *x; // error - x is not defined in this scope

Ist dies nach dem Standard zulässig oder handelt es sich nur um eine Compiler-Erweiterung?


PS Da es dazu mehrere Kommentare gab - ignorieren Sie bitte , dass dieses Beispiel "schlecht" oder gefährlich ist. Ich weiß was. Dies ist nur das erste, was mir als Beispiel in den Sinn kam.

Kiril Kirov
quelle
1
Gute Frage, und Sie haben anscheinend einen C-Hintergrund im Kopf - +1 dafür. :)
@ H2CO3 - danke :) Aber was ist mit dem C? Du meinst, dass dies dort nicht erlaubt ist und das hat mich vielleicht denken lassen, dass es in C ++ nicht erlaubt ist?
Kiril Kirov
@ H2CO3 - Haha, danke :) Ich hatte nichts im Sinn, ich habe mich sogar gefragt, ob dies das gleiche in C ist :))
Kiril Kirov
2
da es nur Standard in C99 ist ...
Benötigte new (std::nothrow)Version - andernfalls wird Ihr Beispiel elseaufgrund eines std::bad_allocZuordnungsfehlers niemals aufgerufen.
PiotrNycz

Antworten:

82

Dies ist in der Spezifikation seit C ++ 98 zulässig.

Aus Abschnitt 6.4 "Auswahlanweisungen":

Ein Name, der durch eine Deklaration in einer Bedingung eingeführt wird (entweder durch den Typspezifizierer-seq oder den Deklarator der Bedingung), ist vom Deklarationspunkt bis zum Ende der von der Bedingung kontrollierten Unteranweisungen im Geltungsbereich.

Das folgende Beispiel stammt aus demselben Abschnitt:

if (int x = f()) {
    int x;    // ill-formed, redeclaration of x
}
else {
    int x;    // ill-formed, redeclaration of x
}
pb2q
quelle
20
Güte! Ich habe gerade festgestellt (dank dieses Zitats), dass der Name auch im Geltungsbereich war else. Irgendwie dachte ich immer, es wäre nur in der ifSektion verfügbar ...
Matthieu M.
19

Keine wirkliche Antwort (aber Kommentare eignen sich nicht gut für Codebeispiele), eher ein Grund, warum es unglaublich praktisch ist:

if (int* x = f()) {
    std::cout << *x << "\n";
}

Immer wenn eine API einen "Optionstyp" zurückgibt (für den zufällig auch eine boolesche Konvertierung verfügbar ist), kann dieser Konstrukttyp genutzt werden, sodass auf die Variable nur in einem Kontext zugegriffen werden kann, in dem es sinnvoll ist, ihren Wert zu verwenden. Es ist eine wirklich mächtige Redewendung.

Matthieu M.
quelle
Ich hatte dies aus Sicht der Optionstypen noch nie gesehen und würde sie beim Codieren in C ++ immer stark vermissen. Danke, dass du mein Leben zum Besseren verändert hast!
Hugomg
@Matthieu M. Ich verstehe Ihren Standpunkt zu einem Optionstyp nicht ganz. Könnten Sie die erwähnte Redewendung erweitern oder einen zusätzlichen Link angeben? Klingt ziemlich interessant. Im Moment benutze ich boost :: optional für solche Dinge. Wollen Sie einen einfachen Zeiger wie einen optionalen Typ verwenden und haben einen begrenzten Spielraum dafür oder steckt mehr dahinter
Martin
1
@Martin: Mit Optionstyp meine ich Typen, die Zeiger (oder boost::optional) mögen und einen "Sentinel" -Wert haben, um das Fehlen eines echten Werts anzuzeigen . Wenn sie auch eine Konvertierung in bool bereitstellen, deren Wert ausschließlich vom Vorhandensein / Fehlen eines im Typ enthaltenen realen Werts abhängt, funktioniert die Redewendung. Zum Beispiel: if (boost::optional<int> x = f()) { std::cout << *x << '\n'; }funktioniert auch.
Matthieu M.
18

Es ist Standard, auch in der alten C ++ 98-Version der Sprache:

Geben Sie hier die Bildbeschreibung ein


quelle
7

Definition einer Variablen in dem bedingten Teil einer while, ifund switchAussage ist Standard. Die relevante Klausel ist 6.4 [stmt.select] Absatz 1, der die Syntax für die Bedingung definiert.

Übrigens ist Ihre Verwendung sinnlos: Wenn dies newfehlschlägt, wird eine std::bad_allocAusnahme ausgelöst .

Dietmar Kühl
quelle
3
Es war nur ein Beispiel. Ich weiß, es ist schlecht. Es war nur das erste, was mir in den Sinn kam.
Kiril Kirov
Ist es optimal, eine Variable im '' '' while '' '' Teil zu definieren?
Julen
2

Hier ist ein Beispiel, das die nicht typische Verwendung einer in einer if- Bedingung deklarierten Variablen demonstriert .

Der Variablentyp ist int &sowohl in Boolesche konvertierbar als auch in den Zweigen then und else verwendbar .

#include <string>
#include <map>
#include <vector>
using namespace std;

vector<string> names {"john", "john", "jack", "john", "jack"};
names.push_back("bill"); // without this push_back, my g++ generated exe fails :-(
map<string, int> ages;
int babies = 0;
for (const auto & name : names) {
    if (int & age = ages[name]) {
        cout << name << " is already " << age++ << " year-old" << endl;
    } else {
        cout << name << " was just born as baby #" << ++babies << endl;
        ++age;
    }
}

Ausgabe ist

john was just born as baby #1
john is already 1 year-old
jack was just born as baby #2
john is already 2 year-old
jack is already 1 year-old
bill was just born as baby #3

Leider kann die Variable in der Bedingung nur mit der Deklarationssyntax '=' deklariert werden.

Dies schließt andere möglicherweise nützliche Fälle von Typen mit einem expliziten Konstruktor aus.

Zum Beispiel wird das nächste Beispiel mit einem std::ifstreamnicht kompiliert ...

if (std::ifstream is ("c:/tmp/input1.txt")) { // won't compile!
    std::cout << "true: " << is.rdbuf();
} else {
    is.open("c:/tmp/input2.txt");
    std::cout << "false: " << is.rdbuf();
}

Bearbeitet Januar 2019 ... Sie können jetzt emulieren, was ich erklärt habe, konnte nicht getan werden ...

Dies funktioniert für bewegliche Klassen wie ifstream in C ++ 11 und sogar für nicht kopierbare Klassen seit C ++ 17 mit Kopierelision.

Bearbeitet im Mai 2019: Verwenden Sie auto, um die Ausführlichkeit zu verringern

{
    if (auto is = std::ifstream ("missing.txt")) { // ok now !
        std::cout << "true: " << is.rdbuf();
    } else {
        is.open("main.cpp");
        std::cout << "false: " << is.rdbuf();
    }
}
struct NoCpy {
    int i;
    int j;
    NoCpy(int ii = 0, int jj = 0) : i (ii), j (jj) {}
    NoCpy(NoCpy&) = delete;
    NoCpy(NoCpy&&) = delete;
    operator bool() const {return i == j;}
    friend std::ostream & operator << (std::ostream & os, const NoCpy & x) {
        return os << "(" << x.i << ", " << x.j << ")";
    }
};
{
    auto x = NoCpy(); // ok compiles
    // auto y = x; // does not compile
    if (auto nocpy = NoCpy (7, 8)) {
        std::cout << "true: " << nocpy << std::endl;
    } else {
        std::cout << "false: " << nocpy << std::endl;
    }
}
thierry.bravier
quelle