Чому 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;але я ніколи не бачив, щоб хтось насправді використовував такі оголошення. Отже, мене цікавить - яка мета такої можливості, оскільки на даний момент я бачу, що вона створює лише випадок, подібний до горезвісного "найбільш неприємного розбору", і не додає нічого корисного?


"Мета" можливості - це, мабуть, спростити парсер.
— молбдніло


1
@GSerg Забавно, як текст мого запитання відповідає на запитання з другої відповіді у вашому зв’язаному питанні, оскільки я наводжу приклад, коли дозволення таких декларацій призводить до несподіваних результатів :)
— Предельник


зайшов у цю пастку: не знав, що мьютекс коментували, а потім випадково написав неправильну декларацію в дужках. // std :: mutex std :: unique_lock <std :: mutex> (m_mutex);
— Подавати Лауріссену

Відповіді:


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

Чи однаково ця відповідь стосується С, а не лише С ++?
— kdbanman

" змінна типу функції " що? !!
— curiousguy

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: Якщо ви відредагували частину мого допису та змінили імена у даній схемі синтаксису, будь-ласка, відповідно змініть імена на 3 рядки нижче. В іншому випадку все ще існують старі назви "тип спереду" та "специфікація", і тому публікація не має жодного сенсу.
— Ethouris
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.