Почему C ++ позволяет нам заключать имя переменной в круглые скобки при объявлении переменной?


84

Например, такое объявление:

int (x) = 0;

Или даже это:

int (((x))) = 0;

Я наткнулся на это, потому что в моем коде оказался фрагмент, похожий на следующий:

struct B
{
};

struct C
{
  C (B *) {}
  void f () {};
};

int main()
{
  B *y;
  C (y);
}

Очевидно, я хотел создать объект, Cкоторый затем делал бы что-нибудь полезное в своем деструкторе. Однако, как это бывает, компилятор рассматривает C (y);как объявление переменной yс типом Cи, таким образом, выводит ошибку о yпереопределении. Интересно то, что если я напишу его как C (y).f ()или как-то в этом роде, C (static_cast<B*> (y))он будет компилироваться, как задумано. {}Конечно, лучший современный обходной путь - использовать при вызове конструктора.

Итак, как я понял после этого, можно объявлять такие переменные, как int (x) = 0;или даже, int (((x))) = 0;но я никогда не видел, чтобы кто-то действительно использовал такие объявления. Так что меня интересует - в чем цель такой возможности, потому что пока я вижу, что это только создает случай, похожий на пресловутый «самый неприятный синтаксический анализ», и не добавляет ничего полезного?


«Цель» возможности, вероятно, состоит в том, чтобы упростить синтаксический анализатор.
molbdnilo


1
@GSerg Забавно, как текст моего вопроса отвечает на вопрос из второго ответа в вашем связанном вопросе, поскольку я
привожу


попал в эту ловушку: не знал, что мьютекс закомментирован, а затем случайно написал неправильное объявление скобок. // std :: mutex std :: unique_lock <std :: mutex> (m_mutex);
Serve Laurijssen

Ответы:


80

Группировка.

В качестве конкретного примера рассмотрим, что вы можете объявить переменную типа функции, например

int f(int);

Как бы вы объявили указатель на такую ​​вещь?

int *f(int);

Нет, не работает! Это интерпретируется как возврат функции int*. Вам нужно добавить в круглые скобки, чтобы он правильно разбирался:

int (*f)(int);

То же самое и с массивами:

int *x[5];   // array of five int*
int (*x)[5]; // pointer to array of five int

13
И чтобы завершить ответ: запрет на конкретный случай, о котором спрашивает спрашивающий, потребует дополнительного правила особого случая. Текущее определение того, как ()работать с типом, едино для всего типа.
Джозеф Мэнсфилд

Таким образом, особый случай применяется к самому неприятному синтаксическому анализу. Это связано с тем, что синтаксис для инициализации переменных с аргументами конструктора был добавлен позже (я думаю, в спешке?)
AnArrayOfFunctions

1
@FISOCPP Хорошо. . да. C ++ появился после C. .
iheanyi

Относится ли этот ответ в равной степени к C, а не только к C ++?
kdbanman

" переменная типа функции " что? !!
любопытный парень

17

Обычно в таких объявлениях разрешается использовать круглые скобки, потому что объявление с синтаксической точки зрения всегда выглядит так:

<front type> <specification>;

Например, в следующем объявлении:

int* p[2];

«Тип фасада» есть int(нет int*), а «спецификация» есть * p[2].

Правило состоит в том, что вы можете использовать любое количество круглых скобок в части «спецификация», потому что иногда они неизбежно устраняют неоднозначность. Например:

int* p[2]; // array of 2 pointers to int; same as int (*p[2]);
int (*p)[2]; // pointer to an array of 2 ints

Указатель на массив - редкий случай, однако такая же ситуация у вас с указателем на функцию:

int (*func(int)); // declares a function returning int*
int (*func)(int); // declares a pointer to function returning int

Это прямой ответ на ваш вопрос. Если ваш вопрос касается утверждения вроде C(y), то:

  • Заключите все выражение в круглые скобки - (C(y))и вы получите то, что хотели
  • Этот оператор ничего не делает, кроме создания временного объекта, который перестает существовать после завершения этой инструкции (я надеюсь, что это то, что вы намеревались сделать).

1
Как я уже упоминал, изначально он что-то делал в деструкторе, я думаю, это довольно стандартная вещь, когда у вас есть некоторое количество функций «цепочки» для установки некоторых параметров, а затем выполнения всей работы в деструкторе. Тем не менее, спасибо за еще один обходной путь, я думаю, что написание {}в конце концов является наиболее подходящим.
Пределник

4
старайтесь не придумывать свою собственную грамматику и используйте ту, которая предусмотрена в стандарте. <front-type> <specification>вводит в заблуждение и неверно. Грамматика<declaration-specifier> <declarator>
Стив Кокс

Вы правы - в стандарт я не заглядывал, просто повторил правило из головы. На самом деле в C ++ 11 роль <declaration-specifier>может также играть autoключевое слово, так что это даже не всегда тип.
Ethouris

@Pravasi Meet: Если вы отредактировали часть моего сообщения и изменили имена в данной схеме синтаксиса, пожалуйста, также измените имена в трех строках ниже соответственно. В противном случае остались старые названия «тип фронта» и «спецификация», поэтому пост не имеет смысла.
Ethouris
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.