Déclarer et initialiser une variable dans une instruction conditionnelle ou de contrôle en C++
dans" 15197092020 "de Stroustrup," the C++ Programming Language: Special Edition (3e éd.) , Stroustrup écrit que la déclaration et l'initialisation des variables dans les conditions des déclarations de contrôle est non seulement permise, mais encouragée. Il écrit qu'il l'encourage, car il réduit la portée des variables de l'étendue qu'ils sont nécessaires pour. Donc quelque chose comme cela...
if ((int i = read(socket)) < 0) {
// handle error
}
else if (i > 0) {
// handle input
}
else {
return true;
}
...est un bon style de programmation et pratique. La variable i
n'existe que pour le bloc d'énoncés if
pour lequel elle est nécessaire et sort ensuite du champ d'application.
cependant, cette fonctionnalité du langage de programmation ne semble pas être supportée par g++ (version 4.3.3 de la compilation spécifique à Ubuntu), ce qui me surprend. Peut-être que j'appelle g++ avec un drapeau qui l'éteint (les drapeaux que j'ai appelés sont -g
et -Wall
). Ma version de g++ renvoie la compilation suivante erreur lors de la compilation avec ces options:
socket.cpp:130: error: expected primary-expression before ‘int’
socket.cpp:130: error: expected `)' before ‘int’
sur des recherches ultérieures, j'ai découvert que je ne semblais pas être le seul avec un compilateur qui ne supporte pas cela. Et il semblait y avoir une certaine confusion dans cette question quant à savoir exactement quelle syntaxe était censée être standard dans la langue et quels compilateurs compilent avec elle.
donc la question Est, quels compilateurs supportent cette fonctionnalité et quels drapeaux doivent être mis pour pour compiler? Est-ce une question d'être de certaines normes et pas dans d'autres?
Aussi, juste par curiosité, les gens sont généralement d'accord avec Stroustrup que c'est un bon style? Ou s'agit-il d'une situation où le créateur d'une langue a une idée en tête qui n'est pas nécessairement soutenue par la communauté de la langue?
9 réponses
il est permis de déclarer une variable dans la partie de contrôle d'un bloc imbriqué, mais dans le cas de if
et while
, la variable doit être initialisée à une valeur numérique ou booléenne qui sera interprété comme la condition. ne peut pas être inclus dans une expression plus complexe!
Dans le cas particulier que vous montrez, il ne semble pas que vous pouvez trouver un moyen de se conformer malheureusement.
je pense personnellement que c'est bonne pratique de garder les variables locales aussi près que possible de leur durée de vie réelle dans le code, même si cela semble choquant lorsque vous passez de C à C++ ou de Pascal à C++ - nous étions habitués à voir toutes les variables à un seul endroit. Avec certains habitude, vous trouverez plus lisible, et vous n'avez pas à chercher ailleurs pour trouver de la déclaration. Par ailleurs, vous savez qu'il n'est pas utilisé avant ce point.
Edit:
cela étant dit, je ne trouve pas ça une bonne pratique de mélanger trop en une seule instruction, et je pense que c'est un avis partagé. Si vous affectez une valeur à une variable, puis l'utilisez dans une autre expression, le code sera plus lisible et moins déroutant en séparant les deux parties.
donc plutôt que d'utiliser ceci:
int i;
if((i = read(socket)) < 0) {
// handle error
}
else if(i > 0) {
// handle input
}
else {
return true;
}
je préférerais que:
int i = read(socket);
if(i < 0) {
// handle error
}
else if(i > 0) {
// handle input
}
else {
return true;
}
je le considère comme un bon style lorsqu'il est utilisé avec éventuellement null pointeur:
if(CObj* p = GetOptionalValue()) {
//Do something with p
}
de cette façon, que p soit déclaré, c'est un pointeur valide. Pas de danger d'accès au pointeur.
d'un autre côté au moins en VC++ c'est la seule utilisation supportée (c'est-à-dire vérifier si l'assignation est vraie)
j'utilise const autant que possible dans ces situations. Au lieu de votre exemple, je ferais:
const int readResult = read(socket);
if(readResult < 0) {
// handle error
}
else if(readResult > 0)
{
// handle input
}
else {
return true;
}
donc bien que la portée ne soit pas contenue, elle n'a pas vraiment d'importance, puisque la variable ne peut pas être modifiée.
j'ai rencontré un similaire problème :
le problème semble être les parenthèses autour de la déclaration int
. Cela devrait fonctionner si vous pouvez exprimer l'affectation et le test sans eux, i.e.
if (int i = read(socket)) {
devrait fonctionner, mais cela signifie que le test est != 0
, ce qui n'est pas ce que vous voulez.
Alors que vous pouvez utiliser une déclaration comme une expression booléenne, vous ne pouvez pas placer une déclaration au moyen d'une expression. Je ne peux m'empêcher de penser que vous lisez mal ce que dit Bjarne.
la technique est utile et désirable surtout pour les variables de contrôle de pour les boucles, mais dans ce cas je crois est mal avisé et ne sert pas la clarté. Et bien sûr, cela ne fonctionne pas! ;)
if( <type> <identifier> = <initialiser> ) // valid, but not that useful IMO
if( (<type> <identifier> = <initialiser>) <operator> <operand> ) // not valid
for( <type> <identifier> = <initialiser>;
<expression>;
<expression> ) // valid and desirable
Dans votre exemple, vous avez appelé un fonction avec des effets secondaires dans un conditionnel, qui IMO est une mauvaise idée indépendamment de ce que vous pourriez penser de déclarer la variable là.
ajoute à ce que RedGlyph et Ferruccio ont dit. Peut-être pouvons-nous faire ce qui suit pour déclarer encore dans une déclaration conditionnelle pour limiter son utilisation:
if(int x = read(socket)) //x != 0
{
if(x < 0) //handle error
{}
else //do work
{}
}
else //x == 0
{
return true;
}
ils le réparent en c++17:
if (int i = read(socket); i < 0)
où if
peut avoir une déclaration d'initialiseur.
voir http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0305r0.html
pour compléter les bonnes réponses des autres, vous pouvez toujours limiter la portée de la variable par des accolades:
{
const int readResult = read(socket);
if(readResult < 0) {
// handle error
}
else if(readResult > 0)
{
// handle input
}
else {
return true;
}
}
bien qu'ils ne soient pas directement liés à la question, tous les exemples donnent la première place au traitement des erreurs. Puisqu'il y a 3 Cas (>0 -> data, ==0 -> connection closed et <0 -> error), cela signifie que le cas le plus commun d'obtenir de nouvelles données nécessite deux tests. La vérification de >0 Tout d'abord réduirait le nombre prévu d'essais de près de la moitié. Malheureusement L'approche" if(int x = read(socket)) " donnée par White_Pawn nécessite encore 2 tests pour le cas des données, mais la proposition C++17 pourrait être utilisée pour tester >0.