Nawiasy w C ++ są używane w wielu miejscach: np. W wywołaniach funkcji i wyrażeniach grupujących, aby przesłonić pierwszeństwo operatorów. Oprócz niedozwolonych dodatkowych nawiasów (takich jak listy argumentów wywołań funkcji), ogólna - ale nie bezwzględna - zasada C ++ mówi, że dodatkowe nawiasy nigdy nie zaszkodzą :
5.1 Wyrażenia podstawowe [wyr.prim]
5.1.1 Ogólne [wyr.prim.general]
6 Wyrażenie w nawiasach jest wyrażeniem podstawowym, którego typ i wartość są identyczne z wyrażeniem zawartym w nim. Obecność nawiasów nie wpływa na to, czy wyrażenie jest lwartością. Wyrażenia ujętego w nawiasy można używać w dokładnie takich samych kontekstach, jak te, w których można użyć wyrażenia ujętego w nawiasach, io tym samym znaczeniu, chyba że wskazano inaczej .
Pytanie : w jakich kontekstach dodatkowe nawiasy zmieniają znaczenie programu C ++, inne niż przesłanianie podstawowego pierwszeństwa operatorów?
UWAGA : Uważam, że ograniczenie składni wskaźnika do elementu członkowskiego do &qualified-id
bez nawiasów jest poza zakresem, ponieważ ogranicza składnię, a nie zezwala na dwie składnie o różnych znaczeniach. Podobnie, użycie nawiasów w definicjach makr preprocesora chroni również przed niepożądanym pierwszeństwem operatorów.
źródło
&(C::f)
, operand&
jest nadalC::f
, czyż nie?expr.unary.op/4
: wskaźnik do elementu członkowskiego jest tworzony tylko wtedy, gdy&
używany jest jawny, a jego operand jest kwalifikowanym identyfikatorem nieuwzględnionym w nawiasach.()
nad selektorem wskaźnika do elementu członkowskiego::*
Odpowiedzi:
TL; DR
Dodatkowe nawiasy zmieniają znaczenie programu C ++ w następujących kontekstach:
decltype
wyrażeniachZapobieganie wyszukiwaniu nazw zależnych od argumentów
Jak opisano szczegółowo w załączniku A do normy, a
post-fix expression
w formularzu(expression)
jest aprimary expression
, ale nieid-expression
, a zatem nieunqualified-id
. Oznacza to, że wyszukiwanie nazw zależnych od argumentów jest niemożliwe w wywołaniach funkcji formularza w(fun)(arg)
porównaniu z formą konwencjonalnąfun(arg)
.3.4.2 Wyszukiwanie nazw zależnych od argumentów [basic.lookup.argdep]
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 }
Włączanie operatora przecinka w kontekstach list
Operator przecinka ma specjalne znaczenie w większości kontekstów listowych (argumenty funkcji i szablonów, listy inicjalizujące itp.). Nawiasy formularza
a, (b, c), d
w takich kontekstach mogą umożliwić użycie operatora przecinka w porównaniu do zwykłego formularza, wa, b, c, d
którym operator przecinka nie ma zastosowania.5.18 Operator przecinka [wyr.comma]
f(a, (t=3, t+2), c);
Rozwiązanie niejednoznaczności irytujących analiz
Kompatybilność wsteczna z C i jego tajemną składnią deklaracji funkcji może prowadzić do zaskakujących niejednoznaczności analizowania, znanych jako irytujące analizy. Zasadniczo wszystko , co można przeanalizować jako deklarację, zostanie przeanalizowane jako jedna , nawet jeśli konkurencyjna analiza również miałaby zastosowanie.
6.8 Rozwiązywanie niejednoznaczności [stmt.ambig]
8.2 Rozwiązywanie niejednoznaczności [dcl.ambig.res]
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 }
Słynnym tego przykładem jest The Most Vexing Parse , nazwa spopularyzowana przez Scotta Meyersa w pozycji 6 jego książki Effective 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
To deklaruje funkcję
data
, której typem zwracanym jestlist<int>
. Dane funkcji mają dwa parametry:dataFile
. To jest typistream_iterator<int>
. Nawiasy wokółdataFile
są zbędne i są ignorowane.istream_iterator<int>
.Umieszczenie dodatkowych nawiasów wokół pierwszego argumentu funkcji (nawiasy wokół drugiego argumentu są niedozwolone) rozwiąże niejednoznaczność
list<int> data((istream_iterator<int>(dataFile)), // note new parens istream_iterator<int>()); // around first argument // to list's constructor
C ++ 11 ma składnię inicjującą nawiasy klamrowe, która pozwala na ominięcie takich problemów parsowania w wielu kontekstach.
Wyprowadzanie odwołań w
decltype
wyrażeniachW przeciwieństwie do
auto
dedukcji typu,decltype
umożliwia wywnioskowanie referencyjności (odniesień do lwartości i rwartości). Reguły rozróżniajądecltype(e)
idecltype((e))
wyrażenia:7.1.6.2 Proste specyfikatory typu [dcl.type.simple]
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&
Reguły dla
decltype(auto)
mają podobne znaczenie dla dodatkowych nawiasów po prawej stronie wyrażenia inicjującego. Oto przykład z często zadawanych pytań dotyczących języka C ++ oraz powiązanych pytań i odpowiedzidecltype(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
Pierwszy zwraca
string
, drugi zwracastring &
, czyli odwołanie do zmiennej lokalnejstr
.Zapobieganie błędom związanym z makrami preprocesora
Istnieje wiele subtelności z makrami preprocesora w ich interakcji z właściwym językiem C ++, z których najczęstsze są wymienione poniżej
#define TIMES(A, B) (A) * (B);
w celu uniknięcia niepożądanego pierwszeństwa operatora (np. wTIMES(1 + 2, 2 + 1)
którym daje 9, ale daje 6 bez nawiasów wokół(A)
i(B)
assert((std::is_same<int, int>::value));
które w przeciwnym razie nie zostałyby skompilowane(min)(a, b)
(z niepożądanym efektem ubocznym wyłączania ADL)źródło
if
/,while
jeśli wyrażenie jest przypisaniem. Np.if (a = b)
- ostrzeżenie (czy miałeś na myśli==
?), Podczas gdyif ((a = b))
- bez ostrzeżenia.(min)(a, b)
(ze złym MACROmin(A, B)
) jest częścią zapobiegania wyszukiwaniu nazw zależnym od argumentów?Ogólnie rzecz biorąc, w językach programowania „dodatkowe” nawiasy oznaczają, że nie zmieniają one kolejności składniowej analizy ani znaczenia. Są dodawane w celu wyjaśnienia kolejności (pierwszeństwa operatorów) z korzyścią dla osób czytających kod, a ich jedynym efektem byłoby nieznaczne spowolnienie procesu kompilacji i zmniejszenie liczby błędów ludzkich w zrozumieniu kodu (prawdopodobnie przyspieszenie całego procesu tworzenia ).
Jeśli zestaw nawiasów faktycznie zmienia sposób przetwarzania wyrażenia, to z definicji nie są one dodatkowymi. Nawiasy, które zamieniają niedozwoloną / nieprawidłową analizę na legalną, nie są „ekstra”, chociaż może to wskazywać na kiepski projekt języka.
źródło