Встретился интересный вопрос. Чтобы он был более понятен, то сначала продемонстрирую на простом примере.
Выражение вида
T(), где
T - это некоторый тип, называется инициализацией значением, и для скаларных типов это является инициализацией нулем.
Для примера в следующем предложении
int i = int();.
переменная
i инициализируется нулем, которой возвращает выражение
int().
Поэтому есть различие между двумя предложениями
int *p = new int; и
int *p = new int(); В первом случае значение выделенной памяти для объекта типа
int не определено, тогда как во втором случае память, выделенная под объект, инициализируется нулем. Многие программисты об этом различии даже не догадываются, поэтому будет полезно о его существовании еще раз напомнить.
Теперь, когда стало более ясно, о чем идет речь, сформулирую исходный вопрос.
Есть определение типа - указатель на функцию, возвращающую тип
void и не имеющую параметров. На самом деле не важно, какой тип функции рассматривается. Главное - это то, что определяется указатель на некоторую функцию.
Итак имеем
typedef void ( *pf )(); В этом предложении с помощью
typedef определен указатель на функцию, то есть имя
pf стало синонимом для типа
void ( * )(). Для краткости и удобства в дальнейшем описании новый тип обозначим буквой
T.
Теперь представим, что мы хоти объявить объект этого типа
T (или
pf - как вам удобнее), и инициализировать его по значению вызовом
T(), как это делалось с целочисленной переменной
i из примера выше. Когда имеется
typedef-определение, то сделать это очень просто:
pf our_pointer = pf(); Как видите, никаких проблем не возникло. Мы объявили объект с именем
our_pointer, который имеет тип
pf, а в данном случае
pf - это синоним для указателя на функцию вида
void ( * )(), и инициализировали его нулем, так как, как было уже указано, вызов выражения
T() для скалярных типов приводит к инициализации нулем.
Все просто. Но это просто лишь потому, что было
typedef-объявление, которое это нам все позволило так легко сделать. А как тоже самое проделать без typedef?
Казалось бы можно записать так
void ( *our_pointer )() = ( void ( * )() )(); Однако такая запись некорректна, так как конструкцию вида
T() можно вызывать лишь для простых спецификаторов типа, для определений типов с помощью
typedef, или для классов.
Что тогда делать?
На помощь приходит новый спецификатор стандарта С++ 2011
decltype. С помощью него можно решить данную проблему. Хотя код и выглядет вычурно, но тем не менее он не противоречит стандарту С++ 2011
void ( *our_pointer )() = decltype( our_pointer )(); На первый взгляд кажется, что написано нечто несуразное. Мы объявляем переменную, и еще не успев закончить предложение, уже ее имя используем для инициализации ее самой!
Но дело в том, что согласно стандарту переменная сразу же становится видимой после определения ее полного декларатора. В данном случае полным декларатором переменной
our_pointer является выражение
void ( *our_pointer )() в левой части предложения. Поэхтому в правой части после оператора
= имя
our_pointer уже считается определенным, а потому
decltype в состоянии вывести тип этой переменной. А делее все просто: мы получили тип нашего указателя и запустили его "конструктор" с помощью указания пустых круглых скобок после имени типа. Это приводит к инициализации нашего указателя нулем.
Конечно можно было бы и проще сделать, не мучаясь, сразу же присвоив 0 указателю. Но изначально вопрос ставился именно так: как инициализировать указатель на функцию с помощью выражения
T(), где
T - это тип этого указателя.
К сожалению приходится в очередной раз константировать, что MS VC++ 2010 этот код не компилирует, хотя никаких проблем с компиляцией этого кода не возникает у
онлайнового компилятора В этом можно убедиться, введя такой простой пример
#include <iostream>
int main()
{
void ( *our_pointer )() = decltype( our_pointer )();
std::cout << "our_pointer = " << our_pointer << std::endl;
return ( 0 );
}
В Майкрософт же я пошлю сообщение о баге, и подождем, что они ответят по этому поводу.