Ответ 1
Для функций, не являющихся членами, тип, такой как typedef void(Function)()
, имеет несколько применений, но для функций-членов единственным приложением является объявление переменной, которая содержит указатель на функцию. Следовательно, кроме стилистических предпочтений, нет строгой необходимости разрешить этот синтаксис, и он был исключен из стандарта.
Фон
::
- это оператор разрешения области видимости, а синтаксис X::Y
зарезервирован для доступа к static
, если X
- тип класса. Итак, X::*Z
был другим синтаксисом, созданным для определения указателя на член.
Забудьте функцию-член какое-то время, просто подумайте об элементах-членах и посмотрите этот код:
struct X
{
int a;
};
int X::*pa = &X::a; //pointer-to-member
X x = {100}; //a = 100
cout << (x.*pa) << endl;
Он определяет данные указателя на элемент, а cout
использует его для печати значения a
объекта X
, и он печатает:
100
Демо: http://www.ideone.com/De2H1
Теперь подумайте, если было разрешено X::pa
(в отличие от X::*pa
), то вы написали выше:
int X::pa = X::a; //not &X::a
Увидев этот синтаксис, как бы вы сказали, является ли X::a
членом static
или нестационарным членом? Это одна из причин, почему в стандарте появился синтаксис "указатель-член" и равномерно применяется к нестационарным данным-членам, а также к нестатической функции-члену.
На самом деле вы не можете писать X::a
, вы должны написать &X::a
. Синтаксис X::a
приведет к ошибке компиляции (см. this).
Теперь распространите этот аргумент данных-членов на функцию-член. Предположим, что у вас есть typedef, определяемый как:
typedef void fun();
то как вы думаете, что делает следующий код?
struct X
{
fun a;
};
Ну, он определяет член a
типа fun
(который является функцией, не принимающей никаких аргументов и возвращающей void) и эквивалентен этому:
struct X
{
void a();
};
Удивлены? Читайте дальше.
struct X
{
fun a; //equivalent to this: void a();
};
void X::a() //yes, you can do this!
{
cout << "haha" << endl;
}
Мы можем использовать точно такой же синтаксис для ссылки на a
, который теперь является функцией-членом:
X x;
x.a(); //normal function call
void (X::*pa)() = &X::a; //pointer-to-member
(x.*pa)(); //using pointer-to-member
Сходство - это синтаксис с правой стороны: &X::a
. Независимо от того, относится ли a
к функции-члену или член-данным, синтаксис такой же.
Демо: http://www.ideone.com/Y80Mf
Вывод:
Как известно, мы не можем писать X::a
в RHS, независимо от того, является ли a
элементом-данными или член-функцией. Единственный допустимый синтаксис - это &X::f
, что делает необходимым, чтобы целевой тип (на LHS) также был указателем, что в свою очередь делает синтаксис void (X::*pa)()
абсолютно необходимым и фундаментальным, поскольку он вписывается в другой синтаксис в язык.