TL; DR
Le parentesi aggiuntive cambiano il significato di un programma C ++ nei seguenti contesti:
- impedendo la ricerca del nome dipendente dall'argomento
- abilitare l'operatore virgola nei contesti elenco
- risoluzione dell'ambiguità di analisi fastidiose
- dedurre la referenzialità nelle
decltype
espressioni
- prevenire errori di macro del preprocessore
Prevenzione della ricerca del nome dipendente dall'argomento
Come dettagliato nell'Allegato A dello Standard, a post-fix expression
del modulo (expression)
è a primary expression
, ma non an id-expression
, e quindi non an unqualified-id
. Ciò significa che la ricerca del nome dipendente dall'argomento è impedita nelle chiamate di funzione del modulo (fun)(arg)
rispetto al modulo convenzionale fun(arg)
.
3.4.2 Ricerca del nome dipendente dall'argomento [basic.lookup.argdep]
1 Quando l'espressione postfissa in una chiamata di funzione (5.2.2) è un id non qualificato, è possibile cercare altri spazi dei nomi non considerati durante la consueta ricerca non qualificata (3.4.1), e in questi spazi dei nomi, la funzione si possono trovare dichiarazioni di modelli di funzione (11.3) non altrimenti visibili. Queste modifiche alla ricerca dipendono dai tipi di argomenti (e per gli argomenti del modello del modello, lo spazio dei nomi dell'argomento del modello). [ Esempio:
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
}
—End esempio]
Abilitazione dell'operatore virgola nei contesti di elenco
L'operatore virgola ha un significato speciale nella maggior parte dei contesti simili a elenchi (argomenti di funzioni e modelli, elenchi di inizializzatori ecc.). Le parentesi del modulo a, (b, c), d
in tali contesti possono abilitare l'operatore virgola rispetto alla forma regolare in a, b, c, d
cui l'operatore virgola non si applica.
5.18 Operatore virgola [expr.comma]
2 Nei contesti in cui alla virgola viene assegnato un significato speciale, [Esempio: negli elenchi di argomenti delle funzioni (5.2.2) e negli elenchi di inizializzatori (8.5) —end esempio] l'operatore virgola come descritto nella clausola 5 può apparire solo tra parentesi. [ Esempio:
f(a, (t=3, t+2), c);
ha tre argomenti, il secondo dei quali ha valore 5. —end esempio]
Risoluzione di ambiguità di analisi fastidiose
La retrocompatibilità con C e la sua sintassi di dichiarazione di funzione arcana può portare a sorprendenti ambiguità di analisi, note come analisi fastidiose. In sostanza, tutto ciò che può essere analizzato come dichiarazione verrà analizzato come uno , anche se si applicherebbe anche un'analisi concorrente.
6.8 Risoluzione dell'ambiguità [stmt.ambig]
1 Esiste un'ambiguità nella grammatica che coinvolge dichiarazioni-espressioni e dichiarazioni : un'istruzione-espressione con una conversione di tipo esplicita in stile funzione (5.2.3) come sottoespressione più a sinistra può essere indistinguibile da una dichiarazione in cui il primo dichiaratore inizia con un ( . In questi casi la dichiarazione è una dichiarazione .
8.2 Risoluzione dell'ambiguità [dcl.ambig.res]
1 L'ambiguità derivante dalla somiglianza tra un cast in stile funzione e una dichiarazione menzionata in 6.8 può verificarsi anche nel contesto di una dichiarazione . In quel contesto, la scelta è tra una dichiarazione di funzione con un insieme ridondante di parentesi attorno a un nome di parametro e una dichiarazione di oggetto con un cast in stile funzione come inizializzatore. Proprio come per le ambiguità menzionate in 6.8, la risoluzione è di considerare qualsiasi costrutto che potrebbe essere una dichiarazione una dichiarazione . [Nota: una dichiarazione può essere disambiguata esplicitamente da un cast in stile non funzionale, da un = per indicare l'inizializzazione o rimuovendo le parentesi ridondanti attorno al nome del parametro. —End note] [Esempio:
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
}
—End esempio]
Un famoso esempio di questo è The Most Vexing Parse , un nome reso popolare da Scott Meyers nell'articolo 6 del suo libro Efficace STL :
ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile), // warning! this doesn't do
istream_iterator<int>()); // what you think it does
Questo dichiara una funzione,, il data
cui tipo restituito è list<int>
. I dati della funzione accettano due parametri:
- Il primo parametro è denominato
dataFile
. Il suo tipo è istream_iterator<int>
. Le parentesi intorno dataFile
sono superflue e vengono ignorate.
- Il secondo parametro non ha nome. Il suo tipo è un puntatore a una funzione che non prende nulla e restituisce un file
istream_iterator<int>
.
Mettere parentesi extra attorno al primo argomento della funzione (le parentesi attorno al secondo argomento sono illegali) risolverà l'ambiguità
list<int> data((istream_iterator<int>(dataFile)), // note new parens
istream_iterator<int>()); // around first argument
// to list's constructor
C ++ 11 ha una sintassi di inizializzazione delle parentesi graffe che consente di eseguire il side-step di tali problemi di analisi in molti contesti.
Dedurre la referenzialità nelle decltype
espressioni
Contrariamente alla auto
deduzione di tipo, decltype
consente di dedurre la referenzialità (riferimenti lvalue e rvalue). Le regole distinguono tra decltype(e)
ed decltype((e))
espressioni:
7.1.6.2 Indicatori di tipo semplice [dcl.type.simple]
4 Per un'espressione e
, il tipo indicato dadecltype(e)
è definito come segue:
- se e
è un'espressione id senza parentesi o un accesso a un membro della classe senza parentesi (5.2.5), decltype(e)
è il tipo dell'entità chiamata da e
. Se non esiste una tale entità, o se e
nomina un insieme di funzioni sovraccaricate, il programma è mal formato;
- altrimenti, se e
è un xvalue, decltype(e)
è T&&
, dove T
è il tipo di e
;
- altrimenti, se e
è un lvalue, decltype(e)
è T&
, dove T
è il tipo di e
;
- altrimenti, decltype(e)
è il tipo di e
.
L'operando dello specificatore decltype è un operando non valutato (clausola 5). [ Esempio:
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&
—End esempio] [Nota: le regole per determinare i tipi che coinvolgono
decltype(auto)
sono specificate in 7.1.6.4. —End nota]
Le regole per decltype(auto)
hanno un significato simile per parentesi extra nell'RHS dell'espressione di inizializzazione. Ecco un esempio tratto dalle domande frequenti su C ++ e dalle domande e risposte correlate
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
Il primo restituisce string
, il secondo restituisce string &
, che è un riferimento alla variabile locale str
.
Prevenzione degli errori relativi alla macro del preprocessore
C'è una miriade di sottigliezze con le macro del preprocessore nella loro interazione con il linguaggio C ++ vero e proprio, le più comuni delle quali sono elencate di seguito
- utilizzando le parentesi attorno ai parametri della macro all'interno della definizione della macro
#define TIMES(A, B) (A) * (B);
al fine di evitare la precedenza di operatori indesiderati (ad esempio in TIMES(1 + 2, 2 + 1)
cui restituisce 9 ma restituisce 6 senza le parentesi intorno (A)
e(B)
- usando le parentesi intorno agli argomenti macro che contengono virgole:
assert((std::is_same<int, int>::value));
che altrimenti non verrebbero compilati
- utilizzo di parentesi attorno a una funzione per proteggere dall'espansione di macro nelle intestazioni incluse:
(min)(a, b)
(con l'effetto collaterale indesiderato di disabilitare anche ADL)
&(C::f)
, l'operando di&
è fermoC::f
, non è vero?