Что такое делегат С++?

Какова общая идея делегата на С++? Каковы они, как они используются и для чего они используются?

Я хотел бы сначала узнать о них в "черном ящике", но немного информации о кишках этих вещей было бы здорово.

Это не С++ в чистом или чистом виде, но я замечаю, что в базе кода, где я работаю, есть их в изобилии. Я надеюсь понять их достаточно, так что я могу просто использовать их и не должен вникать в ужасную вложенность шаблона.

Эти два проекта Code Project объясняют, что я имею в виду, но не особенно лаконично:

Ответ 1

У вас есть невероятное количество вариантов для достижения делегатов на С++. Вот те, которые пришли мне на ум.


Вариант 1: функторы:

Функциональный объект может быть создан путем реализации operator()

struct Functor
{
     // Normal class/struct members

     int operator()(double d) // Arbitrary return types and parameter list
     {
          return (int) d + 1;
     }
};

// Use:
Functor f;
int i = f(3.14);

Вариант 2: лямбда-выражения (C++11)

// Syntax is roughly: [capture](parameter list) -> return type {block}
// Some shortcuts exist
auto func = [](int i) -> double { return 2*i/1.15; };
double d = func(1);

Вариант 3: указатели на функции

int f(double d) { ... }
typedef int (*MyFuncT) (double d);
MyFuncT fp = &f;
int a = fp(3.14);

Вариант 4: указатель на функции-члены (самое быстрое решение)

См. Fast С++ Delegate (на Проект кода).

struct DelegateList
{
     int f1(double d) { }
     int f2(double d) { }
};

typedef int (DelegateList::* DelegateType)(double d);

DelegateType d = &DelegateList::f1;
DelegateList list;
int a = (list.*d)(3.14);

Вариант 5: std:: function

(или boost::function, если ваша стандартная библиотека не поддерживает его). Он медленнее, но он является наиболее гибким.

#include <functional>
std::function<int(double)> f = [can be set to about anything in this answer]
// Usually more useful as a parameter to another functions

Вариант 6: привязка (используя std:: bind)

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

struct MyClass
{
    int DoStuff(double d); // actually a DoStuff(MyClass* this, double d)
};

std::function<int(double d)> f = std::bind(&MyClass::DoStuff, this, std::placeholders::_1);
// auto f = std::bind(...); in C++11

Вариант 7: шаблоны

Принять что угодно, если оно соответствует списку аргументов.

template <class FunctionT>
int DoSomething(FunctionT func)
{
    return func(3.14);
}

Ответ 2

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

Вот пример:

template <class T>
class CCallback
{
public:
    typedef void (T::*fn)( int anArg );

    CCallback(T& trg, fn op)
        : m_rTarget(trg)
        , m_Operation(op)
    {
    }

    void Execute( int in )
    {
        (m_rTarget.*m_Operation)( in );
    }

private:

    CCallback();
    CCallback( const CCallback& );

    T& m_rTarget;
    fn m_Operation;

};

class A
{
public:
    virtual void Fn( int i )
    {
    }
};


int main( int /*argc*/, char * /*argv*/ )
{
    A a;
    CCallback<A> cbk( a, &A::Fn );
    cbk.Execute( 3 );
}

Ответ 3

Необходимость в реализациях делегатов С++ является долговременным смущением для сообщества С++. Каждый программист на С++ хотел бы иметь их, поэтому они в конечном итоге используют их, несмотря на то, что:

  • std::function() использует операции кучи (и недоступен для серьезного встроенного программирования).

  • Все другие реализации делают уступки в отношении переносимости или стандартного соответствия в большей или меньшей степени (пожалуйста, проверьте, проверяя различные реализации делегатов здесь и на codeproject). Я еще не видел реализацию, которая не использует wild reinterpret_casts, прототипы классов вложенных классов, которые, как мы надеемся, производят указатели функций того же размера, что и те, которые были переданы пользователем, трюки компилятора, такие как first forward declare, затем typedef, затем объявляют снова, на этот раз наследуя от другого класса или подобных теневых методов. Хотя это большое достижение для разработчиков, которые его построили, по-прежнему остается печальным свидетельством того, как развивается С++.

  • Лишь в редких случаях указывается, что теперь над 3 стандартными версиями С++ делегаты были неправильно рассмотрены. (Или отсутствие языковых функций, которые позволяют выполнять простые делегирования.)

  • С учетом того, что лямбда-функции С++ 11 определены стандартом (каждая лямбда имеет анонимный, другой тип), ситуация в некоторых случаях используется только в улучшении. Но для использования API-интерфейсов делегатов в (DLL), только lambdas все еще не пригодны для использования. Общая методика здесь состоит в том, чтобы сначала упаковать лямбда в функцию std::, а затем передать ее через API.

Ответ 4

Очень просто, делегат предоставляет функциональные возможности для работы с указателем функции. В С++ существует множество ограничений указателей на функции. Делегат использует некоторые закулисные шаблонные шаблоны для создания типа-типа-указателя типа шаблона, который работает так, как вам может захотеть.

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

Здесь есть несколько хороших примеров:

Ответ 5

Опция для делегатов на С++, которая здесь не упоминается иначе, - это сделать ее стилем C с помощью функции ptr и аргумента контекста. Это, вероятно, та же самая модель, которую многие задают этот вопрос. Но шаблон является портативным, эффективным и может использоваться во встроенном и коде ядра.

class SomeClass
{
    in someMember;
    int SomeFunc( int);

    static void EventFunc( void* this__, int a, int b, int c)
    {
        SomeClass* this_ = static_cast< SomeClass*>( this__);

        this_->SomeFunc( a );
        this_->someMember = b + c;
    }
};

void ScheduleEvent( void (*delegateFunc)( void*, int, int, int), void* delegateContext);

    ...
    SomeClass* someObject = new SomeObject();
    ...
    ScheduleEvent( SomeClass::EventFunc, someObject);
    ...

Ответ 6

Windows Runtime эквивалент функционального объекта в стандартном С++. В качестве параметра можно использовать всю функцию (на самом деле это указатель функции). Он в основном используется в сочетании с событиями. Делегат представляет собой контракт, который выполняют многие обработчики событий. Это облегчает работу указателя функций.