Указатель на функцию

Jackill

Как получить указатель на функцию-член класса? Другими словами, почему этот код

#include <iostream>
using namespace std;

class T1 {
public:
int f(int x) {return x;}
T1 {;}
};

int main {
T1 *t = new T1;
cout << t->f(5) << endl;
int (*pint) = t->f; // ошибка здесь
cout << p(5) << endl;
return 0;
}
не компилируется, в то время как этот

#include <iostream>
using namespace std;

int f(int x);

int main {
cout << f(5) << ' ';
int (*pint) = f;
cout << p(5) << endl;
return 0;
}

int f(int x) {
return x;
}
работает. В первом случае компилятор ругается так:

pf2.cxx: In function `int main':
pf2.cxx:13: error: argument of type `int (T1:int)' does not match `int (*int)'

yolki

тут где-то обсуждалось, что вроде как в С++ это сделать нельзя.
в Pascal-Delphi можно:

type
FuncPtr = function (x:integer): Integer;
FuncClsPtr = function (x:integer): Integer of object;
T=class
public
function Z(x:integer):Integer;
end;
...
var
x: FuncPtr;
y: FuncClsPtr;
C: T;
...
begin
x:=C.Z; // нельзя - ошибка компилятора
y:=C.Z; // можно
end.

kamputer


int (T1::*pint) = &T1::f;
cout << (t->*p5) << endl;

deestr

T1 *t = new T1;
мне кажется скобки лишние.

Marinavo_0507


class t1 = object
method f x : int = x
end

open Printf

let t = new t1;;
printf "%d\n" (t#f 5);;
let p = t#f;;
printf "%d\n" (p 5);;

Eugenia_2005

что это за язык?

Marinavo_0507

OCaml

Jackill

Спасибо, . Тогда я правильно понял, что из конструктора подобный указатель вызывается так:

#include <iostream>
using namespace std;

class T1 {
public:
int f(int x) {return x;}
T1;
};

T1::T1 {
int (T1::*pint) = &T1::f;
cout << (this->*p4) << endl;
}

int main {
T1 t;
cout << t.f(5) << endl;
return 0;
}
Компилятор не ругается, просто я бы хотел узнать, как это сделать корректно.

Landstreicher

Еще вопрос по теме. Пытаюсь писать такое

class A {
public:
virtual void f { }
};

int main
{
A a;
void (A::*p;
p = &a.f;
}
При компиляции возникает ошибка:

1.cpp: In function 'int main':
1.cpp:10: error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Say '&A::f'
Почему так? Я хочу получить указатель именно на тот f который у объекта. У потомков он может быть переопределен и я хочу получить другое значение.

Marinavo_0507

> Почему так? Я хочу получить указатель именно на тот f который у объекта. У потомков он может быть переопределен и я хочу получить другое значение.
Переопределённый метод не будет иметь смысл для объектов типа A,
то есть его нельзя присваивать переменной p.

rosali

На всякий случай, чтобы все понимали:

class A
{
public:
virtual void m { std::cout << "A::m\n"; }
};

class B : public A
{
public:
virtual void m { std::cout << "B::m\n"; }
};

int main
{
void (A::*p;
p = &A::m;
A a; (a.*p;
B b; (b.*p;
return(0);
}

A::m
B::m

Marinavo_0507

Хм. А как это реализовано?

Chupa

в указатель номер метода в VMT запихнули

Marinavo_0507

И чё, в рантайме проверять, какого сорта у нас указатель?
Во дела.

Chupa

это должно на этапе компиляции делаться
PS

18 p = &A::m;
(gdb) next
19 A a; (a.*p;
(gdb) print p
$1 = {__pfn = 0x1, __delta = 0}

kamputer

>И чё, в рантайме проверять, какого сорта у нас указатель?
>Во дела.
Необязательно. Например, это может быть указатель на генерируемую компилятором вспомогательную функцию, вызывающую функцию с адресом vtable + x (где х - номер метода)

rosali

Ну member-pointer это вообще сложная вещь. Есть даже опции компилятору, например, делать ли у всех классов m-p одного размера или пожадничать и т.д.
И чё, в рантайме проверять, какого сорта у нас указатель?
Это что ты имеешь в виду? В рантайме обычный virtual dispatch стоит, а что делать, метод-то виртуальный...

Dasha30

Зачем в рантайме? Все типы известны статически, VMT у потомков до определенного смещения совпадает. А для не потомков операция отсечется еще при компиляции.

Marinavo_0507

gcc походу проверяет в рантайме

rosali

Да я чего-то стормозил, действительно непонятно как еще делать.

void call(A & o, void (A::*p
{
(o.*p;
}


.type _Z4callR1AMS_FvvE, @function
_Z4callR1AMS_FvvE:
.LFB1521:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
subl $24, %esp
.LCFI2:
movl 12(%ebp %eax
movl 16(%ebp %edx
movl %eax, -8(%ebp)
movl %edx, -4(%ebp)
movzbl -8(%ebp %eax
xorb $1, %al
andb $1, %al
testb %al, %al
je .L2
movl -8(%ebp %eax
movl %eax, -12(%ebp)
jmp .L3
.L2:
movl -4(%ebp %eax
movl 8(%ebp %edx
addl %eax, %edx
movl -8(%ebp %eax
addl (%edx %eax
decl %eax
movl (%eax %eax
movl %eax, -12(%ebp)
.L3:
movl -4(%ebp %eax
addl 8(%ebp %eax
movl %eax, (%esp)
call *-12(%ebp)
leave
ret
Нужна же развилка, указатель на виртуальную функцию или не на виртуальную.

kamputer

В MSVC это устроено так:

void call(A & o, void (A::*p
{
mov ecx, dword ptr [esp+4]
jmp dword ptr [esp+8]

А по адресу, куда делается джамп, лежит заранее сгенерённый стаб, вызывающий нужный метод

VitMix

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

class C
{
public:
void f (int x);
};

void (*pf) (int x); // Указатель на функцию одного параметра
void (C::*pcf) (int x); // Указатель на функцию двух параметров, один из которых неявный

void g (void)
{
pf = &C::f; // Неверно
pcf = &C::f; // Верно
}
Оставить комментарий
Имя или ник:
Комментарий: