Подтвердить что ты не робот

Не указатель typedef функций-членов не разрешен?

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

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

дополнительная квалификация "База::" на члене "Обратный звонок"

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

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

4b9b3361

Ответ 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)() абсолютно необходимым и фундаментальным, поскольку он вписывается в другой синтаксис в язык.

Ответ 2

Чтобы быть точным, два 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

Ответ 3

Позвольте пропустить часть "функция" на секунду. В С++ мы имеем типы int, int* и int Foo::*. Это регулярное целое число, указатель на целое число и указатель на целочисленный член. Нет целочисленного элемента четвертого типа.

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