Кароч, https://godbolt.org/z/EeY5sn, синтаксис плюсов это просто массивный ю гот пранкд, с первым апреля, достаём розовые костюмы и смеёмся.
Я напомню, что синтаксически typedef - всего лишь спецификатор, и можно невозбранно писать, например, примерно так:
int const typedef** const * volatile(i);
Delfigamer, "дурак ты, боцман, и шутки у тебя дурацкие".
Кстати, лично мне труднее всего читать именно декларации вида A *b[N], по причине достаточно занятной. Как известно, в Ц++ "декларация подражает использованию", а в использовании я никак не могу запомнить, как следует понимать *b[N] - то ли как (*b)[N], то ли как *(b[N]).
Sbtrn. Devil
A (*b)( ); // указатель на функцию A ( *b)[N]; // указатель на массив? или массив указателей?
Sbtrn. Devil
> декларация подражает использованию
А я вот, кстати, ни разу об этом не думал. Я думал, это у них просто по невнимательности такая лапша получилась; а так получается очень даже логично - декларации и выражения специально сделали в подражание друг другу - и как следствие, теперь они постоянно друг с другом путаются и мешаются.
В самом деле - хотели как лучше, а получился сипипи.
Aslan
> A (*b)[N]; // указатель на массив? или массив указателей?
Указатель на массив элементов типа A. Массив указателей - A *b[N]
Dmitry_Milk
А почему? А где в A *b[N] можно поставить скобки без изменения смысла?
Aslan
Sbtrn. Devil
> *(b[N])
Aslan
> А почему?
Из-за приоритетов операций. Операция индексации приоритетнее операции разадресации указателя.
Dmitry_Milk
Почему отличается от указатель на функцию? #469
Aslan
> приоритетнее () ?
Да. https://en.cppreference.com/w/cpp/language/operator_precedence.
Фактически, как уже сказали выше, в Си в декларациях попытались сделать так, чтоб было аналогично использованию.
То есть, ты можешь начинать с имени, "применяя" к нему операции в соответствии с их приоритетами, и в результате у тебя должно "получаться" значение с самым внешним типом:
A x; // если берем х, то получаем значение типа A
A *x; // если берем х, потом разадресуем его, то получаем значение типа A
A x[10]; // если берем х, потом применяем индексацию, то получаем значение типа A
A *x[10]; // если берем х, потом применяем индексацию, потом разадресуем, то получаем значение типа A
A (*x)[10]; // если берем х, разадресуем, потом применяем индексацию, то получаем значение типа A
A (*x)(B); // если берем х, разадресуем его, потом делаем вызов с аргументом типа B, то получаем значение типа A.
A *x(B); // если берем х, делаем вызов с аргументом типа B, потом разадресуем его, то получаем значение типа A.
Dmitry_Milk
Отлично! Так понятно. Спасибо
Dmitry_Milk
> A x; // если берем х, то получаем значение типа A
Поправка: значение получаем, если мы справа от оператора присваивания, если мы слева - получаем адрес. Далее следует немножко стандарт-проблем относительно того, что является "left-value", а затем для "правильного описания" в C++ вводится "&".
gudleifr
> если мы слева - получаем адрес
Не не, не надо путать l-value с адресом (хотя под капотом да, берется адрес). С точки зрения грамматики тип у самого l-value значения точно такой же, как и у r-value значения, а нифига не указатель на такой тип.
Тема в архиве.