Что такое делегат C ++? - PullRequest
132 голосов
/ 05 марта 2012

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

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

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

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

Ответы [ 6 ]

155 голосов
/ 05 марта 2012

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


Вариант 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 C ++Делегат (на Код проекта ).

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);
}
34 голосов
/ 05 марта 2012

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

Вот пример:

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 );
}
16 голосов
/ 29 марта 2013

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

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

  2. Все другие реализации идут на уступки в отношении переносимости или соответствия стандарту в большей или меньшей степени (пожалуйста, проверьте, осмотрев различные реализации делегата здесь и в проекте кода).Мне еще предстоит увидеть реализацию, которая не использует wild reinterpret_casts, «прототипы» вложенного класса, которые, как мы надеемся, выдают указатели на функции того же размера, что и передаваемый пользователем, трюки компилятора, такие как сначала forward forward, затем затем typedef, затем снова объявляют,на этот раз наследование от другого класса или аналогичных теневых методов.Несмотря на то, что это большое достижение для разработчиков, которые его создали, это все еще печальное свидетельство того, как развивается С ++.

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

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

6 голосов
/ 23 марта 2012

Очень просто, делегат обеспечивает функциональность того, как ДОЛЖЕН работать указатель на функцию. Есть много ограничений указателей на функции в C ++. Делегат использует некоторую скрытность шаблона для создания функции-указателя-типа-класса-шаблона, которая работает так, как вы этого хотите.

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

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

3 голосов
/ 03 марта 2016

Опция для делегатов в C ++, которая здесь не упоминается, заключается в том, чтобы сделать это в стиле 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);
    ...
1 голос
/ 25 сентября 2014

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

...