Как читать так много звезд и круглых скобок в шаблонном объявлении функции-указателя?
Из Введение в функцию С++ 11: возврат возвращаемых типов
В статье утверждается, что
template <class T> class tmp {
public:
int i;
};
auto foo()->auto(*)()->tmp<int>(*)(){
return 0;
}
эквивалентно
template <class T> class tmp{
public:
int i;
};
tmp<int> (*(*foo())())() {
return 0;
}
Я не понимаю сложную функцию во втором примере кода. Где я должен смотреть вначале? Я думаю, это foo
. Но stat рядом с foo
будет определять foo
как указатель...
Основываясь на первом примере кода, я преобразую фрагмент как
tmp<int> (*)() (*)() foo(){ return 0;}
Итак, foo - это функция, которая возвращает 0, но возвращаемый тип является сложным: его тип возврата - это функциональный указатель, возвращаемый тип которого снова является указателем функции, тип возврата которого tmp<int>
.
Ответы
Ответ 1
В дополнение к ответу @Vittorio существует правило по часовой стрелке, чтобы помочь нам дешифровать сложные типы:
Начиная с неизвестного элемента, двигайтесь по спирали/по часовой стрелке; при встрече со следующими элементами заменяйте их соответствующими английскими утверждениями:
-
[X]
или []
Массив X
размер... или массив undefined размер...
-
(type1, type2)
Функция, передающая тип1 и возвращаемый тип2...
-
*
указатель для...
Продолжайте делать это по спирали/по часовой стрелке до тех пор, пока все маркеры не будут закрыты. Всегда сначала разрешайте что-либо в скобках!
Здесь:
+-----------+
| +------+ |
| | >-v | |
temp<int> (*(*foo())())()
| | ^---+ | |
| ^--------+ |
+--------------+
foo
- это функция, возвращающая указатель на функцию, возвращающую указатель на функцию, возвращающую temp<int>
.
И теперь, @UKmonkey просто переименовал это правило. С++ Guru Snail Rule или CGSR для краткости:
/ /
L_L_
/ \
|00 | _______
|_/ | / ___ \
| | / / \ \
| |_____\ \_ / /
\ \____/ /_____
\ _______________/______\.............................
Ответ 2
Где я должен смотреть вначале?
Честно говоря, вы должны просто взглянуть на https://cdecl.org/, который описывает int (*(*foo())())();
как:
объявить foo как функцию, возвращающую указатель на функцию, возвращающую указатель на функцию, возвращающую int
И затем поймите, что это С++ 11, и у нас есть действительно хороший синтаксис для объявления псевдонимов указателей функции:
using A = int(*)(); // pointer to function returning int
using B = A(*)(); // pointer to function returning pointer to function returning int
B foo(); // function returning pointer to function returning pointer to function returning int
На самом деле нет причин писать такие объявления сегодня.
Ответ 3
cdecl - полезный онлайн-инструмент для демистификации сложных деклараций C.
Вставка int (*(*foo())())()
возвращает:
объявить foo как функцию, возвращающую указатель на функцию, возвращающую указатель на функцию, возвращающую int
Я заменил tmp<int>
на int
, поскольку инструмент не поддерживает шаблоны.
Ответ 4
Правильное форматирование кода может помочь вам понять:
template <class T>
class tmp {
public:
int i;
};
auto foo() -> auto(*)() -> tmp<int>(*)() {
return 0;
}
template <class T>
class tmp{
public:
int i;
};
tmp<int> (*
( *foo() )()
)() {
return 0;
}
Часть template class
остается той же, поэтому я не буду подробно останавливаться на ней. Посмотрим на функцию foo
.
В первом коде возвращаемое значение foo()
равно auto(*)() -> tmp<int>(*)()
, которое является указателем на функцию, возвращающую другой указатель, который указывает на функцию, возвращающую tmp<int>
.
Как вы всегда можете определить указатель на функцию, например:
base_type_t (*pointer_name)(parameter_list);
Рекурсия pointer_name
с помощью функции (т.е. func_name()
) может объявить функцию, возвращаемое значение которой является таким указателем:
base_type_t (*func_name())(parameter_list);
~~~~~~~~~~~
Итак, теперь (*func_name())(parameter_list)
может служить другой функции. Вернемся к синтаксису определения указателя функции:
base_type_t (*(*func_name())(parameter_list))(parameter_list);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Очистите список параметров (они пусты) и замените идентификаторы на правильные типы:
base_type_t (*(*func_name())(parameter_list))(parameter_list);
tmp<int> (*(* foo ())( /* Empty */ ))( /* Empty */ );
// Turns to
tmp<int> (*(*foo())())();
Как уже говорили другие, https://cdecl.org/ - хороший анализатор кода, хотя он может дать вам еще одно предложение, которое не так просто понять.