Функциональные указатели не поддерживают типы экземпляров в C ++ - PullRequest
3 голосов
/ 08 марта 2012

Я пытаюсь понять, как использовать функциональные указатели для сопоставления метода с экземплярами в C ++, такими как делегаты в C #.

class FunctionalPointers
{

public:

    static int IncrementCounter ( int *a, int b )
    {
        return *a += b; 
    }

    int NonStaticIncrementCounter ( int *a, int b )
    {
        return *a += b;
    }
};

//Declare a functional pointer as a separate type.
typedef int ( *AFunctionalPointer ) ( int*, int );

int _tmain(int argc, _TCHAR* argv[])
{
    int a = 10;
    int *ptr = &a;
    *ptr = 200;

    //This works as the methods are static
    AFunctionalPointer funcInstance = FunctionalPointers::IncrementCounter;
    int result = funcInstance( ptr, a );

    //But if I try to make the same call from an
    //instance of a non static method I get an error. Why ?
    FunctionalPointers *functionalPointer = new FunctionalPointers();
    //ERROR : Compiler says it's illegal operation.
    AFunctionalPointer funcClassInstanceType = *functionalPointer->IncrementCounter;

    int instanceResult = funcClassInstanceType( ptr, a );
    return 0;
}

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

Отображение метода экземпляра на делегат в C # очень возможно, как фрагмент кода ниже

class Program
{
    static void Main( string[] args )
    {
        int a = 200;
        int b = a;

        FunctionalPointer funcInstance = new FunctionalPointer();
        AFunctionalPointer degegateInstance = funcInstance.Increment;

        int result = degegateInstance( 200, 200 );
    }
}

public delegate int AFunctionalPointer( int a, int b );

class FunctionalPointer
{
    public int Increment ( int a, int b )
    {
        return a += b;
    }

    public int Decrement( int a, int b )
    {
        return a -= b;
    }
}

Мой вопрос,

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

Ответы [ 6 ]

4 голосов
/ 08 марта 2012

C ++ требует разных типов указателей для функций-членов. В C ++ FAQ есть весь раздел .

Вы можете получить C # -подобное поведение, используя оболочку std :: function из C ++ 11.

2 голосов
/ 08 марта 2012

Указатель на члена класса должен быть объявлен как:

int (ClassName::*FunctionPointer)(int, int);
1 голос
/ 08 марта 2012

Нестатические методы относятся к другому типу. И для этого есть веская причина: они должны работать на экземпляре класса! Вы можете объявить указатель на нестатический метод как таковой

int (FunctionalPointers::*pointer_to_non_static_method)(int*, int)
    = FunctionalPointers::NonStaticIncrementCounter;

Но вам понадобится объект, чтобы использовать его!

Другой вариант - перегрузить operator() в вашем классе:

class FunctionalPointers {
    ...
    int operator()(int* a, int b) { return *a += b; };
    ...
}

и вам все еще нужен объект для его использования:

FunctionalPointers f;
f(a,b);
1 голос
/ 08 марта 2012

Указатели на статические функции-члены, по сути, аналогичны указателям на функции. С другой стороны, указатели на нестатические функции-члены - это совершенно разные звери, потому что им нужен объект для вызова этой функции-члена.

Выражение C # funcInstance.Increment связывает объект с функцией-членом и возвращает вам что-то, что можно вызвать как обычную функцию. С ++ эквивалент:

#include <functional>
using namespace std::placeholders;

int main()
{
    int a = 10;
    int *ptr = &a;
    *ptr = 200;

    FunctionalPointers functionalPointer;

    auto funcClassInstanceType = std::bind(
      &FunctionalPointers::NonStaticIncrementCounter, functionalPointer, _1, _2);

    int instanceResult = funcClassInstanceType( ptr, a );
}
1 голос
/ 08 марта 2012

Указатели-члены используют другой синтаксис.Вот ваш код, модифицированный для его использования:

typedef int ( *AFunctionalPointer ) ( int*, int );
// Use this syntax to declare pointers to member functions
typedef int (FunctionalPointers::*InstFunctionalPointer) ( int*, int );

int main()
{
    int a = 10;
    int *ptr = &a;
    *ptr = 200;

    //This works as the methods are static
    AFunctionalPointer funcStatic = FunctionalPointers::IncrementCounter;
    int result = funcStatic( ptr, a );

    InstFunctionalPointer funcInstance = FunctionalPointers::NonStaticIncrementCounter;

    //Now when I try to make the same call from an
    //instance of a non static method I don't get an error.
    FunctionalPointers *functionalPointer = new FunctionalPointers();
    // Use this syntax to call member functions through a member pointer
    int instanceResult = (functionalPointer->*funcInstance)(ptr, a );
    return 0;
}
1 голос
/ 08 марта 2012

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

В C ++ 11 одна опция - std::function:

typedef std::function<int(int*,int)> delegate;
using namespace std::placeholders;

FunctionalPointers fp; // no need for "new" here
delegate d = [&](int * p, int a){fp.IncrementCounter(p,a);};

int result = d(ptr, a);

В C ++ 03 вы можете использовать boost::function и boost::bind для достижения того же самого или написать свой собственный класс с перегруженным operator(), например:

class Delegate {
public:
    typedef int FunctionalPointers::*member_fn(int*,int);

    Delegate(FunctionalPointers * fp, member_fn fn) : fp(fp), fn(fn) {}
    int operator()(int * p, int a) {return (fp->*fn)(p,a);}

private:
    FunctionalPointers * fp;  // Object to call member function of
    member_fn fn;             // Member function to call
};

FunctionalPointers fp;
Delegate d(&fp, &FunctionalPointers::IncrementCounter);

int result = d(ptr, a);

хотя вам понадобится немного больше работы, чтобы получить полиморфное поведение или std::function.

...