Klammern in C ++ werden an vielen Stellen verwendet: z. B. in Funktionsaufrufen und Gruppierungsausdrücken, um die Priorität von Operatoren zu überschreiben. Abgesehen von illegalen zusätzlichen Klammern (wie z. B. Argumentlisten für Funktionsaufrufe) ist eine allgemeine, aber nicht absolute Regel von C ++, dass zusätzliche Klammern niemals schaden :
5.1 Primäre Ausdrücke [expr.prim]
5.1.1 Allgemeines [expr.prim.general]
6 Ein Ausdruck in Klammern ist ein primärer Ausdruck, dessen Typ und Wert mit denen des eingeschlossenen Ausdrucks identisch sind. Das Vorhandensein von Klammern hat keinen Einfluss darauf, ob der Ausdruck ein Wert ist. Der Ausdruck in Klammern kann in genau denselben Kontexten wie diejenigen verwendet werden, in denen der eingeschlossene Ausdruck verwendet werden kann, und mit derselben Bedeutung, sofern nicht anders angegeben .
Frage : In welchen Kontexten ändern zusätzliche Klammern die Bedeutung eines C ++ - Programms, außer dass die Priorität des Operators überschrieben wird?
ANMERKUNG : Ich betrachte die Beschränkung der Zeiger-zu-Element- Syntax auf &qualified-id
ohne Klammern als außerhalb des Gültigkeitsbereichs liegend, da sie die Syntax einschränkt, anstatt zwei Syntaxen mit unterschiedlichen Bedeutungen zuzulassen. In ähnlicher Weise schützt die Verwendung von Klammern in Präprozessor-Makrodefinitionen auch vor unerwünschter Operatorrangfolge.
quelle
&(C::f)
, ist der Operand von&
immer nochC::f
, nicht wahr?expr.unary.op/4
: Ein Zeiger auf ein Mitglied wird nur gebildet, wenn ein expliziter&
Operand verwendet wird und sein Operand eine qualifizierte ID ist, die nicht in Klammern steht.()
gegenüber dem Zeiger-zu-Mitglied-Selektor::*
Antworten:
TL; DR
Zusätzliche Klammern ändern die Bedeutung eines C ++ - Programms in den folgenden Kontexten:
decltype
AusdrückenArgumentabhängige Namenssuche verhindern
Wie in Anhang A der Norm dargelegt, ist a
post-fix expression
der Form(expression)
einprimary expression
, aber keinid-expression
und daher keinunqualified-id
. Dies bedeutet, dass die argumentabhängige Namenssuche bei Funktionsaufrufen des Formulars im(fun)(arg)
Vergleich zum herkömmlichen Formular verhindert wirdfun(arg)
.3.4.2 Argumentabhängige Namenssuche [basic.lookup.argdep]
Aktivieren des Kommaoperators in Listenkontexten
Der Kommaoperator hat in den meisten listenartigen Kontexten (Funktions- und Vorlagenargumente, Initialisierungslisten usw.) eine besondere Bedeutung. Klammern des Formulars
a, (b, c), d
in solchen Kontexten können den Kommaoperator im Vergleich zum regulären Formular aktivieren,a, b, c, d
bei dem der Kommaoperator nicht gilt.5.18 Kommaoperator [expr.comma]
Mehrdeutigkeitsauflösung von ärgerlichen Parsen
Die Abwärtskompatibilität mit C und seiner Deklarationssyntax für arkane Funktionen kann zu überraschenden Parsing-Ambiguitäten führen, die als ärgerliche Parses bezeichnet werden. Im Wesentlichen wird alles, was als Deklaration analysiert werden kann, als eine Deklaration analysiert , obwohl auch eine konkurrierende Analyse gelten würde.
6.8 Mehrdeutigkeitsauflösung [stmt.ambig]
8.2 Mehrdeutigkeitsauflösung [dcl.ambig.res]
Ein berühmtes Beispiel hierfür ist die Most Vexing Parse , ein Name, der von Scott Meyers in Punkt 6 seines Effective STL- Buches populär gemacht wurde :
Dies deklariert eine Funktion
data
, deren Rückgabetyp istlist<int>
. Die Funktionsdaten nehmen zwei Parameter an:dataFile
. Es ist Typ ististream_iterator<int>
. Die KlammerndataFile
sind überflüssig und werden ignoriert.istream_iterator<int>
.Durch Platzieren zusätzlicher Klammern um das erste Funktionsargument (Klammern um das zweite Argument sind unzulässig) wird die Mehrdeutigkeit behoben
C ++ 11 verfügt über eine Klammer-Initialisierer-Syntax, mit der solche Analyseprobleme in vielen Kontexten umgangen werden können.
Referenz in
decltype
Ausdrücken ableitenIm Gegensatz zu
auto
Typ Abzugdecltype
ermöglicht referenceness (L - Wert und R - Wert - Referenzen) abgeleitet werden. Die Regeln unterscheiden zwischendecltype(e)
unddecltype((e))
Ausdrücken:7.1.6.2 Einfache Typspezifizierer [dcl.type.simple]
Die Regeln für
decltype(auto)
haben eine ähnliche Bedeutung für zusätzliche Klammern in der rechten Seite des initialisierenden Ausdrucks. Hier ist ein Beispiel aus den C ++ - FAQ und den damit verbundenen Fragen und AntwortenDie erste gibt zurück
string
, die zweite gibt zurückstring &
, was eine Referenz auf die lokale Variable iststr
.Verhindern von Fehlern im Zusammenhang mit Präprozessormakros
Es gibt eine Vielzahl von Feinheiten mit Präprozessor-Makros in ihrer Interaktion mit der eigentlichen C ++ - Sprache, von denen die häufigsten unten aufgeführt sind
#define TIMES(A, B) (A) * (B);
, um unerwünschte Operatorprioritäten zu vermeiden (z. B.TIMES(1 + 2, 2 + 1)
wenn 9 ergibt, aber 6 ohne die Klammern um(A)
und ergibt(B)
assert((std::is_same<int, int>::value));
die sonst nicht kompiliert würden(min)(a, b)
(mit dem unerwünschten Nebeneffekt, dass auch ADL deaktiviert wird)quelle
if
/ verwendet werden,while
wenn der Ausdruck eine Zuweisung ist. ZBif (a = b)
- Warnung (meinten Sie==
?), Währendif ((a = b))
- keine Warnung.(min)(a, b)
(mit bösem MACROmin(A, B)
) Teil der argumentabhängigen Verhinderung der Namenssuche?In Programmiersprachen bedeuten "zusätzliche" Klammern im Allgemeinen, dass sie die syntaktische Analysereihenfolge oder -bedeutung nicht ändern. Sie werden hinzugefügt, um die Reihenfolge (Vorrang des Operators) zu klären, damit die Benutzer den Code lesen können. Ihre einzige Auswirkung besteht darin, den Kompilierungsprozess geringfügig zu verlangsamen und menschliche Fehler beim Verständnis des Codes zu verringern (was wahrscheinlich den gesamten Entwicklungsprozess beschleunigt) ).
Wenn eine Reihe von Klammern tatsächlich die Art und Weise ändert, wie ein Ausdruck analysiert wird, sind sie per Definition nicht extra. Klammern , die ein illegalen / ungültig Parsen in eine juristischen Frage nicht „extra“ drehen, aber das kann ein schlechtes Sprachdesign hinweisen.
quelle