Вопрос о синтаксисе / семантике C ++: ссылка на функцию и ключевое слово typedef

Для чего typedef int (&rifii) (int, int) будет использоваться?

Что такое typedef перед этим "оператором"? Я хочу думать об этом как о

typedef (int (&rifii) (int, int)) [new name]

но [новое имя] там нет, как если бы вы

typedef int INTEGER;

Аналогичный вопрос для следующего синтаксиса:

typedef void (*PF) ();

PF edit_ops[ ] = { &cut, &paste, &copy, &search };
PF file_ops[ ] = { &open, &append, & close, &write };

PF* button2 = edit_ops;
PF* button3 = file_ops;

button2[2]( );

Что позволяет typedef? Это заставляет вас не набирать:

void (*PF) ();
(void (*PF) ()) edit_ops[ ] = { &cut, &paste, &copy, &search };
(void (*PF) ()) file_ops[ ] = { &open, &append, & close, &write };

(void (*PF) ())* button2 = edit_ops;
(void (*PF) ())* button3 = file_ops;

Если да, то что случилось со второй частью ([что вы хотите]) typedef, например:

typedef [what you have -- (FP)] [what you want]

Приветствуются разъяснения по этому поводу.


person Matthew Hoggan    schedule 01.08.2011    source источник
comment
возможный дубликат stackoverflow.com/questions/1591361/   -  person vines    schedule 02.08.2011
comment
Вопрос похож, и я просмотрел связанные темы перед публикацией, однако мой вопрос был конкретным, и я не смог экстраполировать ответ из других сообщений.   -  person Matthew Hoggan    schedule 02.08.2011


Ответы (2)


Typedef не работает как typedef [type] [new name]. Часть [new name] не всегда заканчивается.

Вы должны смотреть на это так: если [some declaration] объявляет переменную, typedef [same declaration] определит тип.

E.g.:

  • int x; объявляет переменную с именем x типа int -> typedef int x; определяет тип x как int.
  • struct { char c; } s; определяет переменную с именем s некоторого типа структуры -> typedef struct { char c; } s; определяет тип s как некоторый тип структуры.
  • int *p; объявляет переменную с именем p с указателем типа на int -> typedef int *p; определяет тип p как указатель на int.

А также:

  • int A[]; объявляет массив целых чисел с именем A -> typedef int A[]; объявляет тип A как массив целых чисел.
  • int f(); объявляет функцию с именем f -> typedef int f(); объявляет тип функции f как возвращающий int и не принимающий аргументов.
  • int g(int); объявляет имя функции g -> typedef int g(int); объявляет тип функции g как возвращающий int и принимающий один int.

В качестве отступления: обратите внимание, что все аргументы функции идут после нового имени! Поскольку эти типы также могут быть сложными, после [новое имя] может быть много текста. Печально, но факт.

Но это еще не указатели функций, а просто типы функций. Я не уверен, что какой-либо тип функции существует в C или C ++, но он полезен в качестве промежуточного шага в моем объяснении.

Чтобы создать настоящий указатель на функцию, мы должны добавить к имени '*'. Что, к сожалению, имеет неправильный приоритет:

  • typedef int *pf(); объявляет тип функции pf как возвращающий int *. Ой, это не то, что было задумано.

Поэтому используйте () для группировки:

  • typedef int (*pf)(); объявляет, что тип указателя на функцию pf возвращает int и не принимает аргументов.
  • typedef int (&rf)(); объявляет ссылочный тип функции rf как возвращающий int и не принимающий аргументов.

Давайте теперь посмотрим на ваши примеры и ответим на ваши вопросы:

typedef int (&rifii) (int, int); объявляет ссылочный тип функции rifii как возвращающий int и принимающий два аргумента int.

И очевидно (?) button2[2]( ); вызовет copy();.

Правильный синтаксис без typedef трудно написать правильно без компилятора и трудно прочитать даже с компилятором:

void (*edit_ops[])() = { &cut, &paste, &copy, &search }; 
void (*file_ops[])() = { &open, &append, & close, &write };

void (**button2)() = edit_ops;
void (**button3)() = file_ops;

button2[2]( );   

Вот почему все предпочитают typedefs при использовании указателей на функции.

При чтении найдите место, чтобы начать читать. Читайте как можно правее, но соблюдайте группировку по (). Затем читайте влево как можно больше, опять же ограничиваясь grouping (). Закончив все внутри (), начните читать вправо, затем влево.

Применительно к void (*edit_ops[])() это означает, что

  1. edit_ops есть (идите вправо)
  2. массив (ударьте в конец группы, поверните налево)
  3. указателя (конец группировки)
  4. в функцию, принимающую (проанализируйте () справа)
  5. без аргументов (идите налево)
  6. возвращение пустоты

Для экспертов. Чтобы еще больше усложнить задачу, аргументы могут иметь имена (которые будут проигнорированы), поэтому может быть даже сложно найти, с чего начать синтаксический анализ! Например. typedef int (*fp)(int x); допустимо и то же самое, что и typedef int (*fp)(int);. Имена могут даже иметь () вокруг них: typedef int (*fp)(int (x)); Но, как мы видели, имена аргументов могут быть опущены, поэтому разрешено даже следующее: typedef int (*fp)(int ());. Это по-прежнему указатель на функцию, принимающий один int и возвращающий int. В случае, если вы хотите, чтобы ваш код был действительно трудным для чтения ...

person Sjoerd    schedule 02.08.2011
comment
Ваше объяснение помогает. Для пояснения: typedef void (* PF) (); объявляет тип (* PF), который определяется как указатель на функцию, который принимает 0 аргументов и ничего не возвращает. Итак, теперь у нас есть тип PF, если мы можем использовать его в наших выражениях (я мог бы иметь в виду операторы), например PF edit_ops [] = {...}. Не определяя новый тип, мы используем void (* edit_ops []) () = {...}. Это говорит о том, что мы создаем массив указателей на функции, который может быть одним из ...? Тип этого выражения - указатель на функцию? - person Matthew Hoggan; 02.08.2011
comment
@Matthew: Да, void (*edit_ops[]() = { ... }; определяет массив указателей на функции и инициализирует этот массив значениями cut, paste, copy и search. void (**button2)() = edit_ops; объявляет указатель на нулевой элемент этого массива. button2[2]( ); использует третий элемент массива и вызывает функцию, на которую он указывает. Что, оказывается, copy. - person Sjoerd; 02.08.2011
comment
@Matthew: В вашем первом предложении typedef void(*PF)() объявляет тип PF, не (*PF). PF уже является указателем. Последний оператор void(*x[])() = ... объявляет x массивом указателей на функции. - person Kerrek SB; 02.08.2011

изменить: извините, первый ответ не инициализировал fcn ptr.

typedef int (& rifii) (int, int) позволяет объявлять указатели на функции, которые возвращают int по ссылке и принимают два int в качестве параметров.

    rifi x,y,z;

int &ret_an_int_ref( int p1, int p2 ) { 
    static int retval=0;
    if( p1 > p2 ) retval = p1*p2;
    return retval;
}

    x = ret_an_int_ref;
    y = ret_an_int_ref;

int & an_int_ref = x(1,2);
int & another_int_ref=y(3,4);

z = x;

z(1,2); // will give the same answer as x(1,2);
person Harry Seward    schedule 01.08.2011
comment
Так что рифи теперь типаж? И переменные x, y и z относятся к этому типу? И, по словам Бьярна, возврат ссылки из функции - это грех, поэтому я предполагаю, что никогда этого не увижу? - person Matthew Hoggan; 02.08.2011
comment
Я не могу сказать со 100% уверенностью, является ли rifi типом, надеюсь, гуру может вмешаться. Он действует точно так же, как тип, может передаваться как тип и, вероятно, использоваться как параметр шаблона. Скорее всего, вы этого никогда не увидите, однако иногда для сложных типов данных лучше вернуться по ссылке. И оператор operator = всегда должен возвращаться по ссылке, чтобы глупые операторы, такие как A = A, не вызывали ошибку seg. - person Harry Seward; 02.08.2011
comment
Функция не возвращает ссылку на int, но это ссылка на функцию, возвращающую (простой) int. Ваша версия была бы typedef int &(rifii)(int,int); - person Sjoerd; 02.08.2011
comment
упс, извините за размещение плохой информации :( - person Harry Seward; 02.08.2011