Ответ 1
Существует метод запоминания того, как построить такой тип. Сначала попробуйте читать указатели, начиная с их имени и читайте справа налево.
Как объявить этот материал без помощи?
Массивы
T t[5];
представляет собой массив из 5 T. Чтобы сделать T тип функции, вы записываете возвращаемый тип влево, а параметры вправо:
void t[5](void);
будет быть массивом из 5 функций, возвращающих пустоту и не принимающих никаких параметров. Но сами функции не могут быть заполнены массивами! Это не объекты. Только указатели на них могут.
Что насчет
void * t[5](void);
Это все еще неправильно, так как это просто изменит тип return как указатель на void. Вы должны использовать круглые скобки:
void (*t[5])(void);
и это действительно сработает. t представляет собой массив из 5 указателей на функции, возвращающие пустоту, и не принимает никаких параметров.
Отлично! Как насчет массива указателей на аранжировки? Это очень похоже. Тип элемента появляется слева, а размер справа. Опять же, нужны скобки, потому что иначе массив станет многомерным массивом целых указателей:
int (*t[5])[3];
Что это! Массив из 5 указателей на массивы из 3 int.
Как насчет функций?
То, что мы только что узнали, верно и в отношении функций. Пусть объявляет функцию, берущую int, которая возвращает указатель на другую функцию, не принимающую параметр и возвращающую void:
void (*f(int))(void);
нам нужны скобки снова по той же причине, что и выше. Теперь мы можем вызвать его и снова вызвать возвращаемую функцию.
f(10)();
Возврат указателя на функцию, возвращающую другой указатель на функцию
Как насчет этого?
f(10)(true)(3.4);
? Другими словами, как бы могла выглядеть функция, возвращающая int, возвращающая указатель на функцию, берущую bool, возвращающую указатель на функцию double и возвращающую пустоту? Ответ заключается в том, что вы просто вставляете их:
void (*(*f(int))(bool))(double);
Вы можете делать это бесконечно. Действительно, вы также можете вернуть указатель на массив так же, как указатель на функцию:
int (*(*f(int))(bool))[3];
Это функция, в которой int возвращает указатель на функцию bool, возвращающую указатель на массив из 3 int
Что он должен делать с константой?
Теперь, когда выше описано, как создавать сложные типы из основных типов, вы можете поместить const
в те места, где теперь вы знаете, где они принадлежат. Просто подумайте:
T c * c * c ... * c name;
T
- это основной тип, который мы заканчиваем тем, что указываем в конце. c
означает либо const, либо не const. Например
int const * const * name;
объявит имя для указателя типа указателю на константу int. Вы можете изменить name
, но вы не можете изменить *name
, который будет иметь тип
int const * const
и ни **name
, который был бы типа
int const
Позвольте применить это к указателю функции выше:
void (* const t[5])(void);
Это фактически объявит массив содержать постоянные указатели. Поэтому после создания (и инициализации) массива указатели являются const, потому что const
появился после звезды. Обратите внимание, что мы не можем поставить const
перед звездой в этом случае, так как нет указателей на постоянные функции. Функции просто не могут быть const, поскольку это не имеет смысла. Таким образом, следующее недопустимо:
void (const * t[5])(void);
Заключение
С++ и C способ объявления функций и массивов на самом деле на самом деле немного запутан. Сначала вы должны обдумать это, но если вы это понимаете, вы можете написать очень компактные декларации функций, используя его.