Когда дополнительные скобки имеют эффект, отличные от приоритета оператора?

Скобки в C ++ используются во многих местах: например, в вызовах функций и группировании выражений для переопределения приоритета оператора. Помимо незаконных дополнительных круглых скобок (например, вокруг списков аргументов вызова функций), общее, но не абсолютное правило C ++ заключается в том, что дополнительные круглые скобки никогда не повреждаются :

5.1 Первичные выражения [expr.prim]

5.1.1 Общие положения [expr.prim.general]

6 Выражение в скобках – это первичное выражение, тип и значение которого совпадают с типом заключенного выражения. Наличие скобок не влияет на то, является ли выражение lvalue. Выражение в скобках может использоваться в точно таких же контекстах, как те, в которых может использоваться замкнутое выражение, и с тем же значением, если не указано иное .

Вопрос : в каких контекстах дополнительные круглые скобки меняют смысл программы на C ++, кроме переопределения основного приоритета оператора?

ПРИМЕЧАНИЕ . Я рассматриваю ограничение синтаксиса указателя на член с &qualified-id без круглых скобок, чтобы быть вне области видимости, поскольку он ограничивает синтаксис, а не позволяет использовать два синтаксиса с разными значениями. Точно так же использование скобок в макропрофилях препроцессора также защищает от нежелательного приоритета оператора.

TL; DR

Дополнительные круглые скобки меняют смысл программы на C ++ в следующих контекстах:

  • предотrotation поиска зависимых от аргументов имен
  • включение оператора запятой в контекстах списка
  • двусмысленное разрешение досадных анализов
  • decltype референциальности в выражениях decltype
  • предотrotation макропроцессов препроцессора

Предотrotation поиска зависимых от аргументов имен

Как подробно описано в Приложении А стандарта, 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 } 

-End пример]

Включение оператора запятой в контексты контекста

Оператор запятой имеет особое значение в большинстве контекстов (аргументы функции и шаблона, списки инициализаторов и т. Д.). Скобки формы a, (b, c), d в таких контекстах могут включать запятый оператор по сравнению с регулярной формой a, b, c, d где оператор запятой не применяется.

5.18 Comma operator [expr.comma]

2 В контекстах, где запятая имеет особое значение, [Пример: в списках аргументов функций (5.2.2) и списках инициализаторов (8.5) -end example] оператор запятой, как описано в разделе 5, может отображаться только в круглых скобках. [ Пример:

 f(a, (t=3, t+2), c); 

имеет три аргумента, второй из которых имеет значение 5. -end example]

Неоднозначное разрешение досадных парсов

Обратная совместимость с синтаксисом объявления C и его тайной функции может привести к неожиданной parsingчивости parsingа, известной как досадные parsingки. По сути, все, что может быть проанализировано как объявление, будет анализироваться как единое целое , даже если будет применяться и конкурирующий синтаксический анализ.

6.8 Разрешение неоднозначности [stmt.ambig]

1 В грамматике есть двусмысленность, включающая выражения-заявления и декларации : выражение-выражение с преобразованием явного типа в стиле функции (5.2.3) как его самое левое подвыражение может быть неотличимым от объявления, в котором первый декларатор начинается с ( В этих случаях заявление является декларацией .

8.2 Разрешение неоднозначности [dcl.ambig.res]

1 Неоднозначность, возникающая из-за сходства между приведением в стиле функции и декларацией, упомянутой в 6.8, может также возникать в контексте декларации . В этом контексте выбор осуществляется между объявлением функции с избыточным набором круглых скобок вокруг имени параметра и объявлением объекта с использованием функции-стиля в качестве инициализатора. Так же как и для двусмысленностей, упомянутых в 6.8, резолюция состоит в том, чтобы рассмотреть любую конструкцию, которая может быть объявлением декларацией . [Примечание. Объявление может быть явно устранено с помощью неэффективного стиля, путем: = указать инициализацию или удалить избыточные круглые скобки вокруг имени параметра. -End note] [Пример:

 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 пример]

Известным примером этого является « Самый неприятный парсер» , имя, популяризированное Скоттом Мейерсом в 6-м пункте его эффективной книги STL :

 ifstream dataFile("ints.dat"); list data(istream_iterator(dataFile), // warning! this doesn't do istream_iterator()); // what you think it does 

Это объявляет функцию, data , чей тип возврата – это list . Данные функции принимают два параметра:

  • Первый параметр называется dataFile . Это тип istream_iterator . dataFile вокруг dataFile являются излишними и игнорируются.
  • Второй параметр не имеет имени. Его тип – это указатель на функцию, которая ничего не принимает и возвращает istream_iterator .

Размещение дополнительных круглых скобок вокруг первого аргумента функции (круглые скобки вокруг второго аргумента являются незаконными) разрешит двусмысленность

 list data((istream_iterator(dataFile)), // note new parens istream_iterator()); // around first argument // to list's constructor 

В C ++ 11 есть синтаксис синтаксиса, который позволяет во многих контекстах решать такие проблемы синтаксического анализа.

Выделение референциальности в выражениях decltype

В отличие от вычитания типа auto , decltype позволяет decltype ссылки (значения lvalue и rvalue). Правила различают decltype(e) и decltype((e)) :

7.1.6.2 Спецификаторы простого типа [dcl.type.simple]

4 Для выражения e тип, обозначаемый decltype(e) , определяется следующим образом:

– если e – это несферизованное идентификационное выражение или unparenthesized доступ к члену classа (5.2.5), decltype(e) – это тип объекта, названного e . Если такой объект отсутствует или если e называет набор перегруженных функций, программа плохо сформирована;

– в противном случае, если e является значением x, decltype(e) является T&& , где T – тип e ;

– в противном случае, если e является lvalue, 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& 

-End example] [Примечание. Правила для определения типов с участием decltype(auto) указаны в 7.1.6.4. -End note]

Правила для decltype(auto) имеют аналогичное значение для дополнительных круглых скобок в RHS инициализирующего выражения. Вот пример из часто задаваемых вопросов по C ++ и связанных с ним вопросов и ответов

 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 .

Предотrotation макросов, связанных с макропроцессором

Существует множество тонкостей с макросами препроцессора в их взаимодействии с языком C ++, наиболее распространенные из которых перечислены ниже

  • используя круглые скобки вокруг параметров макроса внутри определения макроса #define TIMES(A, B) (A) * (B); чтобы избежать нежелательного приоритета оператора (например, в TIMES(1 + 2, 2 + 1) который дает 9, но даст 6 без круглых скобок вокруг (A) и (B)
  • используя круглые скобки вокруг макросов с запятыми внутри: assert((std::is_same::value)); которые иначе не компилировались бы
  • используя круглые скобки вокруг функции для защиты от макрорасширения в включенных заголовках: (min)(a, b) (с нежелательным побочным эффектом также отключение ADL)

В общем, в языках программирования «дополнительные» круглые скобки подразумевают, что они не меняют синтаксический порядок или значение синтаксического анализа. Они добавляются, чтобы уточнить порядок (приоритет оператора) для людей, читающих код, и их единственным эффектом было бы немного замедлить процесс компиляции и уменьшить человеческие ошибки в понимании кода (возможно, ускорить общий процесс разработки ).

Если набор круглых скобок фактически изменяет способ анализа, то они по определению не являются дополнительными. Скобки, которые превращают незаконный / недействительный анализ в юридический, не являются «лишними», хотя это может указывать на плохой дизайн языка.

Interesting Posts

Как пропустить заголовок из файлов CSV в Spark?

Как остановить MS Word от пропусков страниц?

Параллельные streamи, коллекторы и безопасность streamов

Как импортировать VMware Workstation 7 VM в Sun VirtualBox (последняя версия)?

Отключить сообщения при загрузке пакета

Как вернуть мой раздел Windows 7 после синего экрана?

Отслеживание информации о подключении HTTPS

Метод-цепочка в C #

Проверка сертификатов X509 с использованием Java APis

Существует ли многопользовательское приложение для удаленного рабочего стола для Mac OS X?

C # Linq-to-Sql – Если DataContext будет удален с помощью IDisposable

Как я могу скопировать большой файл с одной машины Windows на другую предельную скорость передачи?

Как взаимодействовать с бесшумной установкой msi? (Данные прогресса и отменить его)

Целевая 32-разрядная или 64-разрядная нативная DLL-память в зависимости от среды

Значение проверки существует в std :: map – C ++

Давайте будем гением компьютера.