Не указатель typedef функций-членов не допускается? - PullRequest
17 голосов
/ 15 сентября 2011

После получения ответа на на этот вопрос я обнаружил, что есть два допустимых способа определения типа указателя на функцию.

typedef void (Function) ();
typedef void (*PFunction) ();

void foo () {}

Function * p = foo;
PFunction  q = foo;

Теперь я предпочитаю Function * p PFunction q, но, очевидно, это не работает для функций указателя на член. Рассмотрим этот надуманный пример.

#include <iostream>

struct Base {
    typedef void (Base :: *Callback) ();
                        //^^^ remove this '*' and put it below (i.e. *cb)
    Callback cb;

    void go () {
        (this->*cb) ();
    }

    virtual void x () = 0;

    Base () {
        cb = &Base::x;
    }
};

struct D1 : public Base {
    void x () {
        std :: cout << "D1\n";
    }
};

struct D2 : public Base {
    void x () {
        std :: cout << "D2\n";
    }
};  

int main () {
    D1 d1;
    D2 d2;
    d1 .go ();
    d2 .go ();
}

Но если я изменю его на новый предпочтительный стиль: typedef void (Base :: Callback) () и Callback * cb, я получу ошибку компилятора в точке typedef

дополнительная квалификация 'Base ::' на участника 'Callback'

Демонстрация ошибки .

Почему это не разрешено? Это просто недосмотр или это вызовет проблемы?

Ответы [ 3 ]

10 голосов
/ 15 сентября 2011

Для функций, не являющихся членами, тип, такой как 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 ).


Теперь расширьте этот аргумент member-data до member-function . Предположим, у вас есть 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)() абсолютно необходимым и фундаментальным, так как соответствует другому синтаксису в языке.

2 голосов
/ 15 сентября 2011

Если быть точным, два typedef в случае указателей, не являющихся членами, не совпадают:

typedef void function();
typedef void (*fptr)();

Первый определяет function как функцию, не принимающую аргументов и возвращающую void, в то время как второй определяет ftpr как указатель на функцию, не имеющую аргументов и возвращающую void.Вероятно, возникает путаница, поскольку тип функции будет неявно преобразован в тип указателя во многих контекстах.Но не все:

function f;            // declares void f();
struct test {
   function f;         // declares void test::f()
};
void g( function f );  // declares g( void (*f)() ): function decays to pointer to function in declaration
g( f );                // calls g( &f ): function decays to pointer to function
void f() {}            // definition of f
// function h = f;     // error: cannot assign functions
function *h = f;       // f decays to &f
0 голосов
/ 15 сентября 2011

Давайте пропустим часть «функция» на секунду. В C ++ у нас есть типы int, int* и int Foo::*. Это обычное целое число, указатель на целое число и указатель на целочисленный член. Четвертого типа "целочисленный член" не существует.

Точно то же самое относится и к функциям: просто нет типа «функция-член», хотя существуют типы функций, типы указателей на функции и типы указателей на функции-члены.

...