Синтаксис подписи функции-члена C++ typedef

Я хочу объявить определение типа для сигнатуры функции-члена. Определения глобальных функций выглядят следующим образом:

typedef int (function_signature)(int, int);
typedef int (*function_pointer) (int, int);

Но я не могу сделать то же самое для функции-члена:

typedef int (foo::memberf_signature)(int, int);   // memberf_pointer is not a member of foo
typedef int (foo::*memberf_pointer)(int, int);

Мне это кажется логичным, потому что foo:: — это синтаксис для доступа к члену в классе foo.

Как я могу ввести только подпись?


person 0xbadf00d    schedule 28.01.2011    source источник
comment
Просто из любопытства, почему вы пытаетесь это сделать?   -  person Derrick Turk    schedule 28.01.2011
comment
Я в замешательстве, последнее typedef не то, что вам нужно?   -  person GManNickG    schedule 28.01.2011
comment
Мне это кажется неоднородным. Можно определить тип функции, объявленной в глобальной области видимости, но невозможно определить тип метода. И да, я различаю сигнатуру и тип указателя на функцию.   -  person 0xbadf00d    schedule 01.02.2011
comment
да, синтаксис C++ должен разрешать int (foo::&memberf_reference)(int, int )   -  person CashCow    schedule 11.02.2015


Ответы (5)


Для вопросов, касающихся неудобного синтаксиса указателя функции, я лично использую шпаргалку: Учебное пособие по указателям функций (загружается здесь, спасибо Vector за указание на это).

Однако сигнатура функции-члена немного отличается от сигнатуры обычной функции, как вы уже поняли.

Как вы, наверное, знаете, функция-член имеет скрытый параметр this, тип которого необходимо указать.

// C++11 and above.
using Member = int (Foo::*)(int, int);

// C++03 and below.
typedef int (Foo::*Member)(int, int);

позволяет вам указать, что первым элементом, переданным в функцию, будет Foo* (и, таким образом, ваш метод действительно принимает 3 аргумента, если подумать, а не только 2.

Однако есть и другая причина, по которой вы должны указать тип.

Указатель функции может ссылаться на виртуальную функцию, и в этом случае все может стать довольно сложным. Поэтому сам размер представления в памяти меняется в зависимости от типа функции. Действительно, в Visual Studio размер указателя на функцию может в 1-4 раза превышать размер обычного указателя. В частности, это зависит от того, является ли функция виртуальной.

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

person Matthieu M.    schedule 29.01.2011
comment
@BЈовић: и это тоже была такая хорошая ссылка... о, хорошо, к счастью, я знал вторую статью ;) - person Matthieu M.; 25.10.2012
comment
Новая рабочая ссылка с загрузками для Учебник по указателям функций - person Vector; 14.02.2015
comment
Как это будет выглядеть в C++11 с использованием синтаксиса? - person paulm; 26.05.2019
comment
@paulm: Хороший вопрос, сообщение отредактировано. Это в основном то же самое, за исключением того, что имя извлекается, и поэтому его легче найти. - person Matthieu M.; 26.05.2019
comment
@MatthieuM. Это действительно? Вы уверены, что тип нестатической функции-члена/данных (а не тип указателя-члена на эту функцию/данные) равен Return(Class::*)(Args...), а не Return(Class::)(Args...)? Если да, то верно ли, что this* является причиной * в идентичном типе члена или указателя члена (принимая во внимание тот факт, что this* является указателем, который всегда является частью подписи)? - person KeyC0de; 22.09.2019
comment
@Nikos: Вы можете сами попробовать синтаксис в компиляторе: Return(Class::)(Args...) недопустимый синтаксис. this (не this*) всегда является указателем, несмотря на то, что он никогда не равен нулю, и передается как указатель на методы. Синтаксис вызова - (this->*method)(args...) или (ref.*method)(args...), что является еще одним уровнем странности... - person Matthieu M.; 22.09.2019

Вы можете выделить целевой класс в современном C++ (сообщение 11), используя качества определения типа псевдонимов шаблонов. То, что вам нужно, будет выглядеть так:

template<typename T>
using memberf_pointer = int (T::*)(int, int); 

Тем не менее, в момент объявления указатель на функцию-член, использующую этот синтаксис, должен указать целевой класс:

// D is a member function taking (int, int) and returning int
memberf_pointer<foo> mp = &foo::D; 
person Nikos Athanasiou    schedule 27.07.2015

Причина, по которой это не работает с вашим текущим синтаксисом, заключается в том, что приоритет оператора диктует, что вы ссылаетесь на функцию с именем foo::memberf_signature, а не на какой-либо тип.

Я не знаю точно, можете ли вы это сделать или нет, но я не смог придумать какую-либо комбинацию скобок, которая заставила бы код компилироваться с помощью g++ 4.2.

person Mark B    schedule 28.01.2011
comment
Может быть, просто ::memberf_signature? - person user470379; 28.01.2011

Меня устраивает:

#include <iostream>

class foo
  {
public:
  int g (int x, int y) { return x + y ; }
  } ;

typedef int (foo::*memberf_pointer)(int, int);

int main()
  {
  foo f ;
  memberf_pointer mp = &foo::g ;
  std::cout << (f.*mp) (5, 8) << std::endl ;
  }
person TonyK    schedule 28.01.2011
comment
Это typedef для указателя на функцию-член. Эта работа для меня тоже, но это не то, что мне нужно ;) - person 0xbadf00d; 29.01.2011
comment
Хорошо, теперь я вижу, что вы хотите. Но как вы планируете использовать этот typedef, если вам удастся его создать? - person TonyK; 29.01.2011

Ну, в принципе, это не может работать (по крайней мере, я не знаю, как использовать g++); При использовании компилятора borland C++ будет ключевое слово __closure.

Причина, по которой он не компилируется, заключается в том, что размер указателя функции (на машине x86) всегда занимает ‹‹32 бита>>; но если вы хотите указать на сигнатуру класса (интерфейса), sizeof должен быть 64-битным: 32-битный для указателя this (поскольку интерфейс класса находится в памяти только один раз) и 32-битный для фактической функции

Но ключевое слово __closure - это нестандартизированный "взлом" языка bcb...

person googling    schedule 28.01.2011
comment
Тот факт, что указатели на функции-члены и указатели на функции имеют разный размер, кажется мне неважным. Обычно short и double имеют разные размеры, но вы можете typedef использовать их оба. - person David Thornley; 28.01.2011
comment
Указатель this обычно передается как первый скрытый параметр. Я не слышал ни о каком компиляторе, использующем 64-битный указатель для преобразования указателя this в указатель на объект. - person Timo Geusch; 29.01.2011
comment
Это абсолютно неправильно. Указатель this хранится в регистре ECX (например, MSC) на целевой машине IA-32. В такой системе любой указатель всегда 32-битный. - person 0xbadf00d; 29.01.2011