Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
// объявление функции pf(), возвращающей указатель на тип bool
bool *pf(const string &, const string &);
Использование указателей на функциюПри использовании имени функции как значения функция автоматически преобразуется в указатель. Например, адрес функции lengthCompare() можно присвоить указателю pf следующим образом:
pf = lengthCompare; // pf теперь указывает на функцию lengthCompare
pf = &lengthCompare; // эквивалентное присвоение: оператор обращения к
// адресу необязателен
Кроме того, указатель на функцию можно использовать для вызова функции, на которую он указывает. Это можно сделать непосредственно, обращение к значению указателя там не обязательно:
bool b1 = pf("hello", "goodbye"); // вызов lengthCompare
bool b2 = (*pf)("hello", "goodbye"); // эквивалентный вызов
bool b3 = lengthCompare("hello", "goodbye"); // эквивалентный вызов
Преобразование указателя на один тип функции в указатель на другой тип функции невозможно. Однако для обозначения того, что указатель не указывает на функцию, ему можно присвоить nullptr (см. раздел 2.3.2) или целочисленное константное выражение, означающее нуль:
string::size_type sumLength(const string&, const string&);
bool cstringCompare(const char*, const char*);
pf = 0; // ok: pf не указывает на функцию
pf = sumLength; // ошибка: разные типы возвращаемого значения
pf = cstringCompare; // ошибка: разные типы параметров
pf = lengthCompare; // ok: типы функции и указателя совпадают точно
Указатели на перегруженные функцииКак обычно, при использовании перегруженной функции применяемую версию должен прояснить контекст, в котором она используется. Вот объявление указателя на перегруженную функцию:
void ff(int*);
void ff(unsigned int);
void (*pf1)(unsigned int) = ff; // pf1 указывает на ff(unsigned)
Компилятор использует тип указателя для выявления используемой версии перегруженной функции. Тип указателя должен точно соответствовать одной из версий перегруженной функции:
void (*pf2)(int) = ff; // ошибка: нет версии с точно таким списком
// параметров
double (*pf3) (int*) = ff; // ошибка: тип возвращаемого значения
// функций ff и pf3 не совпадают
Указатель на функцию как параметрПодобно массивам (см. раздел 6.2.4), нельзя определить параметры типа функции, но можно создать параметр, являющийся указателем на функцию. Как и в случае с массивами, можно создать параметр, который выглядит как тип функции, но обрабатывается как указатель:
// третий параметр имеет тип функции и автоматически обрабатывается как
// указатель на функцию
void useBigger(const string &s1, const string &s2,
bool pf(const string&, const string&));
// эквивалентное объявление: параметр явно определен как указатель
// на функцию
void useBigger(const string &s1, const string &s2,
bool (*pf)(const string&, const string&));
При передаче функции как аргумента это можно сделать непосредственно. Аргумент будет автоматически преобразован в указатель:
// автоматическое преобразование функции lengthCompare в указатель
// на нее
useBigger(s1, s2, lengthCompare);
Как можно заметить в объявлении функции useBigger(), написание указателей на тип функций быстро становится утомительным. Псевдонимы типа (см. раздел 2.5.1), а также спецификатор decltype (см. раздел 2.5.3) позволяют упростить код, который использует указатели на функции:
// Func и Func2 имеют тип функции
typedef bool Func(const string&, const strings);
typedef decltype(lengthCompare) Func2; // эквивалентный тип
// FuncP и FuncP2 имеют тип указателя на функцию
typedef bool(*FuncP)(const string&, const string&);
typedef decltype(lengthCompare) *FuncP2; // эквивалентный тип
Здесь при определении типов использовано ключевое слово typedef. И Func, и Func2 являются типами функций, тогда как FuncP и FuncP2 — типы указателя. Следует заметить, что спецификатор decltype возвращает тип функции; автоматического преобразования в указатель не происходит. Поскольку спецификатор decltype возвращает тип функции, при необходимости получить указатель следует добавить символ *. Можно повторно объявить функцию useBigger(), используя любой из этих типов:
// эквивалентные объявления useBigger с использованием псевдонимов типа
void useBigger(const string&, const string&, Func);
void useBigger(const string&, const string&, FuncP2);
Оба объявления объявляют ту же функцию. В первом случае компилятор автоматически преобразует тип функции, представленный именем Func, в указатель.
Возвращение указателя на функциюПодобно массивам (см. раздел 6.3.3), нельзя возвратить тип функции, но можно возвратить указатель на тип функции. Точно так же тип возвращаемого значения следует писать как тип указателя; компилятор не будет автоматически рассматривать тип возвращаемого значения функции как соответствующий тип указателя. Как и при возвращении массива, безусловно, проще всего объявить функцию, которая возвращает указатель на функцию, при помощи псевдонима типа:
using F = int(int*, int); // F - тип функции, а не указатель
using PF = int(*)(int*, int); // PF - тип указателя
Здесь для определения F как типа функции и PF как указателя на тип функции было использовано объявление псевдонима типа (см. раздел 2.5.1). Имейте в виду, что в отличие от параметров, имеющих тип функции, тип возвращаемого значения не преобразуется автоматически в тип указателя. Следует явно определить, что тип возвращаемого значения является типом указателя:
PF f1(int); // ok: PF - указатель на функцию; f1 возвращает указатель
// на функцию
F f1(int); // ошибка: F - тип функции; f1 не может возвратить функцию
F *f1(int); // ok: явное определение типа возвращаемого значения как
// указателя на функцию
Конечно, функцию f1() также можно объявить непосредственно:
int (*f1(int))(int*, int);
Читая это объявление изнутри наружу, можно заметить у f1 список параметров, таким образом, f1 — это функция. Имени f1 предшествует знак *, следовательно, функция f1() возвращает указатель. У типа самого указателя тоже есть список параметров, таким образом, указатель указывает на функцию. Эта функция возвращает тип int.
И наконец, следует обратить внимание на то, что объявления функций, которые возвращают указатель на функцию, можно упростить при помощи замыкающего типа возвращаемого значения (см. раздел 6.3.3):
auto f1(int) -> int (*)(int*, int);
Использование спецификаторов auto и decltype для типов указателей на функцииЕсли известно, какую функцию (функции) следует возвратить, можно использовать спецификатор decltype для упрощения записи типа возвращаемого значения в виде указателя на функцию. Предположим, например, что имеются две функции, обе возвращают тип string::size_type и имеют два параметра типа const string&. Можно написать третью функцию, которая получает параметр типа string и возвращает указатель на одну из следующих двух функций следующим образом:
string::size_type sumLength(const string&, const string&);
string::size_type largerLength(const string&, const string&);
// в зависимости от значения строкового параметра функция getFcn
// возвращает указатель на sumLength или largerLength
decltype(sumLength) *getFcn(const string &);
Единственная сложность в объявлении функции getFcn() — это необходимость помнить, что при применении спецификатора decltype к функции она возвращает тип функции, а не указатель на тип функции. Чтобы получить указатель, а не функцию, следует добавить знак *.
Упражнения раздела 6.7Упражнение 6.54. Напишите объявление функции, получающей два параметра типа int, и возвращающей тип int. Объявите также вектор, элементами которого является тип указателя на эту функцию.
Упражнение 6.55. Напишите четыре функции, которые добавляют, вычитают, умножают и делят два значения типа int. Сохраните указатели на эти значения в векторе из предыдущего упражнения.