TL;DR
Les parenthèses supplémentaires modifient la signification d'un programme C++ dans les contextes suivants :
- empêcher la recherche de noms en fonction des arguments
- activer l'opérateur virgule dans les contextes de liste
- résolution d'ambiguïtés de parses contrariants
- déduire la référentialité dans
decltype
expressions
- prévention des erreurs de macro du préprocesseur
Empêcher la recherche de noms en fonction des arguments
Comme le précise l'annexe A de la norme, une post-fix expression
de la forme (expression)
est un primary expression
mais pas un id-expression
et donc pas un unqualified-id
. Cela signifie que la recherche de noms dépendante des arguments est empêchée dans les appels de fonction de la forme (fun)(arg)
par rapport à la forme conventionnelle fun(arg)
.
3.4.2 Recherche de nom dépendant de l'argument [basic.lookup.argdep]
1 Quand l'expression postfixe dans un appel de fonction (5.2.2) est un unqualified-id d'autres espaces de noms qui n'ont pas été pris en compte lors de l'examen habituel de l'UE. habituelle (3.4.1) peuvent être recherchés, et dans ces espaces de noms, les déclarations de fonctions amies ou de modèles de fonctions de l'espace de nommage (11.3) non visibles autrement peuvent être trouvées. Ces modifications de la recherche recherche dépendent des types d'arguments (et pour les arguments de gabarit de fonction l'espace de nom de l'argument du modèle). [ Exemple :
namespace N {
struct S { };
void f(S);
}
void g() {
N::S s;
f(s); // OK: calls N::f
(f)(s); // error: N::f not considered; parentheses
// prevent argument-dependent lookup
}
-fin de l'exemple ]
Activation de l'opérateur virgule dans les contextes de liste
L'opérateur virgule a une signification particulière dans la plupart des contextes de type liste (arguments de fonctions et de modèles, listes d'initialisateurs, etc.) Les parenthèses de la forme a, (b, c), d
dans de tels contextes peut activer l'opérateur virgule par rapport à la forme régulière a, b, c, d
où l'opérateur virgule ne s'applique pas.
5.18 Opérateur virgule [expr.comma]
2 Dans les contextes où la virgule a une signification particulière, [ Exemple : sur les listes d'arguments de fonctions (5.2.2) et les listes d'initialisateurs (8.5) -fin de l'exemple ] l'opérateur de virgule tel que décrit dans la clause 5 peut apparaître uniquement entre parenthèses. [ Exemple :
f(a, (t=3, t+2), c);
a trois arguments, dont le deuxième a la valeur 5. -fin exemple ]
Résolution de l'ambiguïté des syntagmes contrariants
La rétrocompatibilité avec le C et sa syntaxe de déclaration de fonction obscure peut conduire à des ambiguïtés d'analyse surprenantes, connues sous le nom d'analyses vexantes. Essentiellement, tout ce qui peut être analysé comme une déclaration sera analysé comme une déclaration même si une analyse syntaxique concurrente s'appliquerait également.
6.8 Résolution des ambiguïtés [stmt.ambig].
1 Il y a une ambiguïté dans la grammaire impliquant les déclarations d'expression et les déclarations : Un énoncé d'expression avec une fonction de style de type fonction (5.2.3) dans sa sous-expression la plus à gauche peut être être indiscernable d'une déclaration dont le premier déclarant commence par un avec un (. Dans ces cas, la déclaration est une déclaration .
8.2 Résolution des ambiguïtés [dcl.ambig.res]
1 L'ambiguïté découlant de la similitude entre une fonction de type et une déclaration mentionnée en 6.8 peut également se produire dans le contexte de d'une déclaration . Dans ce contexte, le choix se fait entre une fonction avec une série redondante de parenthèses autour d'un nom de paramètre et une déclaration d'objet avec un cast de style fonction en tant qu'initialisateur. initialisateur. Comme pour les ambiguïtés mentionnées en 6.8, la méthode de résolution consiste à considérer toute construction qui pourrait être une déclaration une déclaration . [Note : Une déclaration peut être explicitement explicitement par un cast de style non fonctionnel, par un = pour indiquer l'initialisation ou en supprimant les parenthèses redondantes autour du nom du paramètre. nom du paramètre. -fin note ] [ Exemple :
struct S {
S(int);
};
void foo(double a) {
S w(int(a)); // function declaration
S x(int()); // function declaration
S y((int)a); // object declaration
S z = int(a); // object declaration
}
-fin de l'exemple ]
Un exemple célèbre de ce phénomène est le Parse la plus vexante un nom popularisé par Scott Meyers dans l'article 6 de son STL efficace livre :
ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile), // warning! this doesn't do
istream_iterator<int>()); // what you think it does
Ceci déclare une fonction, data
dont le type de retour est list<int>
. La fonction data prend deux paramètres :
- Le premier paramètre est nommé
dataFile
. Son type est istream_iterator<int>
. Le site parenthèses autour de dataFile
sont superflus et sont ignorés.
- Le deuxième paramètre n'a pas de nom. Son type est un pointeur vers une fonction prenant rien et retourne un
istream_iterator<int>
.
En plaçant des parenthèses supplémentaires autour du premier argument de la fonction (les parenthèses autour du deuxième argument sont illégales), l'ambiguïté sera résolue.
list<int> data((istream_iterator<int>(dataFile)), // note new parens
istream_iterator<int>()); // around first argument
// to list's constructor
Le C++11 dispose d'une syntaxe d'initialisation des accolades qui permet de contourner ces problèmes d'analyse dans de nombreux contextes.
Déduction de la référentialité dans decltype
expressions
Contrairement à auto
déduction de type, decltype
permet de déduire la référentialité (références lvalue et rvalue). Les règles distinguent entre decltype(e)
y decltype((e))
expressions :
7.1.6.2 Spécificateurs de type simples [dcl.type.simple]
4 Pour une expression e
, le type désigné par decltype(e)
est défini comme suit suivante :
- si e
est une expression id non parenthésée ou un accès à un membre de classe non parenthésé (5.2.5), decltype(e)
est le type de l'entité nommée par e
. Si une telle entité n'existe pas, ou si e
noms a ensemble de fonctions surchargées, le programme est mal formé ;
- sinon, si e
est une valeur x, decltype(e)
es T&&
, donde T
est le type de e
;
- sinon, si e
est une lvalue, decltype(e)
es T&
, donde T
est le type de e
;
- autrement, decltype(e)
est le type de e
.
L'opérande de l'option decltype est un opérande non évalué (Clause 5). [ Exemple :
const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 0; // type is const int&&
decltype(i) x2; // type is int
decltype(a->x) x3; // type is double
decltype((a->x)) x4 = x3; // type is const double&
-fin de l'exemple ] [ Note : Les règles pour déterminer les types impliquant decltype(auto)
sont spécifiés dans 7.1.6.4. -dernière note ]
Les règles pour decltype(auto)
ont une signification similaire pour les parenthèses supplémentaires dans le RHS de l'expression d'initialisation. Voici un exemple tiré du C++FAQ y cette question connexe
decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; } //A
decltype(auto) look_up_a_string_2() { auto str = lookup1(); return(str); } //B
La première retourne string
le second renvoie string &
qui est une référence à la variable locale str
.
Prévention des erreurs liées aux macros de préprocesseur
Les macros du préprocesseur présentent une multitude de subtilités dans leur interaction avec le langage C++ proprement dit, dont les plus courantes sont énumérées ci-dessous.
- l'utilisation de parenthèses autour des paramètres de la macro à l'intérieur de la définition de la macro
#define TIMES(A, B) (A) * (B);
afin d'éviter une préséance d'opérateur non souhaitée (par exemple dans TIMES(1 + 2, 2 + 1)
ce qui donne 9 mais donnerait 6 sans les parenthèses autour de (A)
y (B)
- l'utilisation de parenthèses autour des arguments de macro ayant des virgules à l'intérieur :
assert((std::is_same<int, int>::value));
qui autrement ne serait pas compilé
- l'utilisation de parenthèses autour d'une fonction pour se protéger contre l'expansion des macros dans les en-têtes inclus :
(min)(a, b)
(avec l'effet secondaire non désiré d'invalider également ADL)