Скобки в C ++ используются во многих местах: например, в вызовах функций и групповых выражениях для переопределения приоритета оператора. Помимо незаконных лишних скобок (например, вокруг списков аргументов вызова функции), общее, но не абсолютное правило C ++ состоит в том, что лишние скобки никогда не повредят:
5.1 Первичные выражения [expr.prim]
5.1.1 Общие [expr.prim.general]
6 Заключенное в скобки выражение — это первичное выражение, тип и
значения идентичны значениям вложенного выражения. Присутствие
скобок не влияет на то, является ли выражение lvalue.
Заключенное в скобки выражение может использоваться в точно таких же контекстах
как те, где можно использовать вложенное выражение, и с тем же
имея в виду, если не указано иное.
Вопрос: в каких контекстах дополнительные скобки меняют смысл программы на C ++, кроме переопределения базового приоритета оператора?
НОТА: Я считаю ограничение указатель на член синтаксис для &qualified-id
без скобок, чтобы выйти за рамки, потому что это ограничивает синтаксис вместо того, чтобы разрешить два синтаксиса с разными значениями. Точно так же использование круглые скобки внутри макроопределений препроцессора также защищает от нежелательного приоритета оператора.
Дополнительные скобки изменяют значение программы на C ++ в следующих контекстах:
decltype
выраженияКак подробно описано в Приложении А к Стандарту, post-fix expression
формы (expression)
это primary expression
, но не id-expression
и, следовательно, не unqualified-id
, Это означает, что зависимый от аргумента поиск имени предотвращается в вызовах функций вида (fun)(arg)
по сравнению с обычной формой fun(arg)
,
3.4.2 Поиск имени в зависимости от аргумента [basic.lookup.argdep]
1 Когда Постфиксное выражение в вызове функции (5.2.2) является
неквалифицированный-ID, другие пространства имен, не учитываемые во время обычного
можно искать неквалифицированный поиск (3.4.1), и в этих пространствах имен
декларация о функции друга или области шаблона в пространстве имен
(11.3) может быть найдено невидимым. Эти модификации к
поиск зависит от типов аргументов (и шаблона шаблона
аргументы, пространство имен аргумента шаблона). [ Пример:
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
}
— конец примера]
Оператор запятой имеет особое значение в большинстве спискообразных контекстов (аргументы функций и шаблонов, списки инициализаторов и т. Д.). Скобки в форме a, (b, c), d
в таком контексте можно включить оператор запятой по сравнению с обычной формой a, b, c, d
где оператор запятой не применяется.
5.18 Оператор запятой [expr.comma]
2 В тех случаях, когда запятая имеет особое значение, [Пример: в
списки аргументов функций (5.2.2) и списки инициализаторов
(8.5) — конец примера] оператор запятой, как описано в разделе 5, может
появляются только в скобках. [ Пример:
f(a, (t=3, t+2), c);
имеет три аргумента, второй из которых имеет значение 5. — конец примера
]
Обратная совместимость с C и его синтаксисом объявления загадочных функций может привести к неожиданным неоднозначностям при разборе, известным как неприятные синтаксические разборы. По существу, все, что может быть проанализировано как объявление, будет проанализировано как одно, хотя конкурирующий анализ также будет применяться.
6.8 Разрешение двусмысленности [stmt.ambig]
1 В грамматике существует двусмысленность, связанная с выражениями-выражениями
и декларации: Выражение-выражение с функциональным стилем
явное преобразование типов (5.2.3), поскольку его крайнее левое подвыражение может быть
неотличим от объявления, где начинается первый декларатор
с (. В этих случаях заявление является декларацией.
8.2 Разрешение неоднозначности [dcl.ambig.res]
1 Неоднозначность, возникающая из-за сходства функционального стиля
приведение и объявление, упомянутое в 6.8, также может происходить в контексте
декларации. В этом контексте выбор между функцией
объявление с избыточным набором скобок вокруг параметра
имя и объявление объекта со стилем функции, приведенным в качестве
инициализатор. Как и в случае двусмысленности, упомянутой в 6.8,
разрешение заключается в рассмотреть любую конструкцию, которая может быть
декларация декларация. [Примечание: объявление может быть явно
устраняет неоднозначность посредством приведения типа, не являющегося функцией, символом = для указания
инициализация или удаление лишних скобок вокруг
Имя параметра. —Конец примечания] [Пример:
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
}
— конец примера]
Известным примером этого является Most Vexing Parse, имя, популяризированное Скоттом Мейерсом в пункте 6 его Эффективный 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
Это объявляет функцию, data
тип возвращаемого значения list<int>
,
Функция data принимает два параметра:
dataFile
, Это тип istream_iterator<int>
,dataFile
лишние и игнорируются.istream_iterator<int>
,Размещение дополнительных скобок вокруг первого аргумента функции (круглые скобки вокруг второго аргумента недопустимы) устранит неоднозначность
list<int> data((istream_iterator<int>(dataFile)), // note new parens
istream_iterator<int>()); // around first argument
// to list's constructor
C ++ 11 имеет синтаксис скобки-инициализатора, который позволяет обойти такие проблемы анализа во многих контекстах.
decltype
выраженияВ отличие от auto
тип вычета, decltype
позволяет выводить ссылки (ссылки на lvalue и rvalue). Правила различают decltype(e)
а также decltype((e))
выражения:
7.1.6.2 Спецификаторы простых типов [dcl.type.simple]
4 Для выражения
e
, тип обозначенdecltype(e)
определяется как
следующим образом:— если
e
это не заключенное в скобки id-выражение или
доступ к элементу класса без скобок (5.2.5),decltype(e)
это тип
лица, названногоe
, Если такой организации нет, или еслиe
называет
множество перегруженных функций, программа некорректна;— иначе,
еслиe
это xvalue,decltype(e)
являетсяT&&
, гдеT
это типe
;—
в противном случае, еслиe
это значение,decltype(e)
являетсяT&
, гдеT
это тип
изe
;— иначе,
decltype(e)
это типe
,Операнд
спецификатор decltype — это неоцененный операнд (раздел 5). [ Пример:
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&
— конец примера] [Примечание: правила определения типов, включающие
decltype(auto)
указаны в 7.1.6.4. —Конечная записка]
Правила для decltype(auto)
имеют аналогичное значение для дополнительных скобок в RHS инициализирующего выражения. Вот пример из C ++ FAQ а также это связано Q&
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
Первое возвращение string
вторая возвращается string &
, который является ссылкой на локальную переменную str
,
Существует множество тонкостей с макросами препроцессора в их взаимодействии с самим языком C ++, наиболее распространенные из которых перечислены ниже.
#define TIMES(A, B) (A) * (B);
чтобы избежать нежелательного приоритета оператора (например, в TIMES(1 + 2, 2 + 1)
который дает 9, но даст 6 без скобок вокруг (A)
а также (B)
assert((std::is_same<int, int>::value));
который иначе не скомпилируется(min)(a, b)
(с нежелательным побочным эффектом от отключения ADL)В целом, в языках программирования «лишние» скобки подразумевают, что они не изменение порядка синтаксического разбора или значения. Они добавляются для уточнения порядка (приоритета операторов) в интересах людей, читающих код, и их единственный эффект будет заключаться в том, чтобы немного замедлить процесс компиляции и уменьшить количество человеческих ошибок при понимании кода (возможно, ускоряя весь процесс разработки). ).
Если набор скобок на самом деле изменения то, как выражение анализируется, то они по определению не дополнительно. Скобки, которые превращают неправильный / недействительный анализ в легальный, не являются «лишними», хотя это может указать на плохой языковой дизайн.