Как работает параметр шаблона std :: function? (реализация)

На домашней странице Бьярна Страуструпа ( FAQ по C ++ 11 ):

struct X { int foo(int); };

std::function f;
f = &X::foo; //pointer to member

X x;
int v = f(&x, 5); //call X::foo() for x with 5

Как это сделать Работа? Как std :: function вызывает функцию-член foo ?

Параметр шаблона: int (X *, int) , is & X :: foo преобразован из указателя на функцию-член в указатель на функцию-не ?!

(int(*)(X*, int))&X::foo //casting (int(X::*)(int) to (int(*)(X*, int))

Чтобы уточнить: я знаю, что нам не нужно приводить какие-либо указатель на использование std :: function , но я не знаю, как внутренности std :: function обрабатывают эту несовместимость между указателем на функцию-член и указатель на функцию, не являющуюся членом . Я не Не знаю, как стандарт позволяет нам реализовать что-то вроде std :: function !

53
задан Gyorgy Szekely 15 November 2015 в 06:11
поделиться

4 ответа

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

Ниже приводится простой (и неполный) пример реализации чего-то вроде std :: function :

template<class T> class Function { };

// Parse the function type
template<class Res, class Obj, class... ArgTypes>
class Function<Res (Obj*, ArgTypes...)> {
    union Pointers {
        Res (*func)(Obj*, ArgTypes...);
        Res (Obj::*mem_func)(ArgTypes...);
    };

    typedef Res Callback(Pointers&, Obj&, ArgTypes...);

    Pointers ptrs;
    Callback* callback;

    static Res call_func(Pointers& ptrs, Obj& obj, ArgTypes... args) {
        return (*ptrs.func)(&obj, args...);
    }

    static Res call_mem_func(Pointers& ptrs, Obj& obj, ArgTypes... args) {
        return (obj.*(ptrs.mem_func))(args...);
    }

  public:

    Function() : callback(0) { }

    // Parse the function type
    Function(Res (*func)(Obj*, ArgTypes...)) {
        ptrs.func = func;
        callback = &call_func;
    }

    // Parse the function type
    Function(Res (Obj::*mem_func)(ArgTypes...)) {
        ptrs.mem_func = mem_func;
        callback = &call_mem_func;
    }

    Function(const Function& function) {
        ptrs = function.ptrs;
        callback = function.callback;
    }

    Function& operator=(const Function& function) {
        ptrs = function.ptrs;
        callback = function.callback;
        return *this;
    }

    Res operator()(Obj& obj, ArgTypes... args) {
        if(callback == 0) throw 0; // throw an exception
        return (*callback)(ptrs, obj, args...);
    }
};

Использование:

#include <iostream>

struct Funny {
    void print(int i) {
        std::cout << "void (Funny::*)(int): " << i << std::endl;
    }
};

void print(Funny* funny, int i) {
    std::cout << "void (*)(Funny*, int): " << i << std::endl;
}

int main(int argc, char** argv) {
    Funny funny;
    Function<void(Funny*, int)> wmw;

    wmw = &Funny::print; // void (Funny::*)(int)
    wmw(funny, 10); // void (Funny::*)(int)

    wmw = &print; // void (*)(Funny*, int)
    wmw(funny, 8); // void (*)(Funny*, int)

    return 0;
}
34
ответ дан 7 November 2019 в 08:53
поделиться

g ++, похоже, имеет объединение, которое может содержать любой указатель на функцию, указатель на член или указатель void, который, вероятно, указывает на функтор. Добавьте перегрузки, которые соответствующим образом отмечают, какой член объединения является действительным, и тяжелое приведение к супу, и тогда он работает ...

2
ответ дан 7 November 2019 в 08:53
поделиться

Это не указатели на функции. Для этого существует std :: function. Он обертывает любые вызываемые типы, которые вы ему даете. Вы должны проверить boost :: bind - он часто используется, чтобы сделать указатели функций-членов вызываемыми как (this, args).

1
ответ дан 7 November 2019 в 08:53
поделиться

Как это происходит (я считаю), оставлено неопределенным (но у меня здесь нет копии стандарта).

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

Но я думаю, вы хотели бы знать, как работают функторы, и они относительно просты. Вот небольшой пример.

Функторы:

Это объекты, которые действуют как функции.
Они очень полезны в коде шаблона, поскольку часто позволяют взаимозаменяемо использовать объекты или функции.Однако самое замечательное в функторах - это то, что они могут удерживать состояние (своего рода замыкание для бедняков).

struct X
{
     int operator()(int x) { return doStuff(x+1);}
     int doStuff(int x)    { return x+1;}
};

X   x;  // You can now use x like a function
int  a = x(5);

Вы можете использовать тот факт, что функтор удерживает состояние, чтобы хранить такие вещи, как параметры или объекты, или указатель на методы-члены (или любую их комбинацию).

struct Y // Hold a member function pointer
{
    int (X::*member)(int x);
    int operator(X* obj, int param) { return (obj->*member)(param);}
};
X  x;
Y  y;
y.member = &X::doStuff;
int a = y(&x,5);

Или даже пойти дальше и связать параметры. Итак, теперь все, что вам нужно предоставить, - это один из параметров.

struct Z
{
    int (X::*member)(int x);
    int  param;
    Z(int (X::*m)(int), int p) : member(m), param(p) {}

    int operator()(X* obj)  { return (obj->*member)(param);}
    int operator()(X& obj)  { return (obj.*member)(param);}
};

Z z(&X::doStuff,5);

X x;
int a = z(x);
3
ответ дан 7 November 2019 в 08:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: