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