web-dev-qa-db-fra.com

Définition d'une variable dans la partie condition d'une instruction if?

J'étais juste choqué que cela soit permis:

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

Alors, est-ce autorisé par la norme ou c'est juste une extension de compilateur?


P.S. Comme il y avait plusieurs commentaires à ce sujet - veuillez ignorer que cet exemple est "mauvais" ou dangereux. Je sais ce que. Ce n'est que la première chose qui m'est venue à l'esprit, par exemple.

69
Kiril Kirov

Ceci est autorisé par la spécification, depuis C++ 98.

De la section 6.4 "Déclarations de sélection":

Un nom introduit par une déclaration dans une condition (introduit soit par le spécificateur de type-seq soit par le déclarant de la condition) est dans la portée de son point de déclaration jusqu'à la fin des sous-instructions contrôlées par la condition.

L'exemple suivant provient de la même section:

if (int x = f()) {
    int x;    // ill-formed, redeclaration of x
}
else {
    int x;    // ill-formed, redeclaration of x
}
73
pb2q

Il est standard, même dans l'ancienne version C++ 98 du langage:

enter image description here

18
user529758

Ce n'est pas vraiment une réponse (mais les commentaires ne sont pas bien adaptés aux échantillons de code), plus une raison pour laquelle c'est incroyablement pratique:

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

Chaque fois qu'une API renvoie un type "option" (qui a également une conversion booléenne disponible), ce type de construction peut être exploité de sorte que la variable n'est accessible que dans un contexte où il est judicieux d'utiliser sa valeur. C'est un idiome vraiment puissant.

16
Matthieu M.

La définition d'une variable dans la partie conditionnelle d'une instruction while, if et switch est standard. La clause pertinente est 6.4 [stmt.select] paragraphe 1 qui définit la syntaxe de la condition.

BTW, votre utilisation est inutile: si new échoue, il lance un std::bad_alloc exception.

7
Dietmar Kühl

Voici un exemple démontrant l'utilisation non typique d'une variable déclarée dans une condition if.

Le type de variable est int & qui est à la fois convertible en booléen et utilisable dans les branches then et else.

#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;
    }
}

la sortie est

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

Malheureusement, la variable de la condition ne peut être déclarée qu'avec la syntaxe de déclaration "=".

Cela exclut d'autres cas de types éventuellement utiles avec un constructeur explicite.

Par exemple, l'exemple suivant utilisant un std::ifstream ne compile pas ...

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();
}

Édité en janvier 2019 ... vous pouvez maintenant imiter ce que j'ai expliqué ne pouvait pas être fait ...

Cela fonctionne pour les classes mobiles comme ifstream en C++ 11 et même pour les classes non copiables depuis C++ 17 avec élision de copie.

Modifié en mai 2019: utilisez l'automobile pour réduire la verbosité

{
    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;
    }
}
2
thierry.bravier