【问题标题】:What is a C++ delegate?什么是 C++ 委托?
【发布时间】:2012-03-23 00:19:33
【问题描述】:

C++ 中委托的一般概念是什么?它们是什么,它们是如何使用的,它们的用途是什么?

我想先以“黑匣子”的方式了解它们,但了解一些关于这些东西的内脏的信息也会很棒。

这不是最纯粹或最干净的 C++,但我注意到我工作的代码库有很多。我希望对它们有足够的了解,因此我可以直接使用它们,而不必深入研究可怕的嵌套模板的可怕之处。

这两篇The Code Project 文章解释了我的意思,但不是特别简洁:

【问题讨论】:

  • 你说的是.NET下的托管C++吗?
  • delegate 不是 C++ 语言中的常用名称。您应该在问题中添加一些信息,以包括您阅读它的上下文。请注意,虽然该模式可能很常见,但如果您通常谈论 delegate,或者在 C++CLI 或具有特定实现 的任何其他库的上下文中,答案可能会有所不同委托.
  • 等 2 年 ?;)
  • 还在等待中...

标签: c++ delegates delegation


【解决方案1】:

在 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:lambda 表达式(仅限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++ DelegateThe Code Project)。

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);
}

【讨论】:

  • 伟大的名单,+1。然而,这里只有两个真正算作委托——捕获 lambdas 和从std::bind 返回的对象,它们都做同样的事情,除了 lambdas 不是多态的,因为它们可以接受不同的参数类型。
  • @J.N:为什么您建议不要使用函数指针,但似乎可以使用成员方法指针?它们完全一样!
  • @MatthieuM。 : 有道理。我认为函数指针是遗留的,但这可能只是我个人的喜好。
  • @SirYakalot 行为类似于函数的东西,但它可能同时保持状态并且可以像任何其他变量一样被操纵。例如,一种用途是采用一个带有两个参数的函数并强制第一个参数具有一定的值,从而创建一个具有单个参数的新函数(我的列表中的binding)。你不能用函数指针来实现这一点。
  • 它们本身不存在于 C++ 中。因此列表。
【解决方案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 );
}

【讨论】:

  • 哪个提供了触发调用的方法?代表?如何?函数指针?
  • 委托类将提供类似Execute() 的方法,该方法会触发对委托包装的对象的函数调用。
  • 我强烈建议在您的情况下覆盖调用运算符,而不是执行,而不是执行 - void CCallback::operator()(int)。这样做的原因是在泛型编程中,可调用对象应该像函数一样被调用。 o.Execute(5) 将不兼容,但 o(5) 很适合作为可调用模板参数。这个类也可以泛化,但我假设你为了简洁起见保持简单(这是一件好事)。除了挑剔之外,这是一个非常有用的类。
【解决方案3】:

对 C++ 委托实现的需求一直是 C++ 社区的尴尬。 每个 C++ 程序员都希望拥有它们,因此他们最终还是会使用它们,尽管有以下事实:

  1. std::function() 使用堆操作(对于严肃的嵌入式编程来说是遥不可及的)。

  2. 所有其他实现都会或多或少地在可移植性或标准一致性方面做出让步(请通过检查此处和 codeproject 上的各种委托实现来验证)。我还没有看到一个不使用野生 reinterpret_casts 的实现,嵌套类“原型”,它希望产生与用户传入的函数指针大小相同的函数指针,编译器技巧,如首先前向声明,然后 typedef 然后再次声明,这次继承自另一个类或类似的黑幕技术。虽然这对于构建它的实现者来说是一项伟大的成就,但它仍然是 C++ 如何发展的悲哀见证。

  3. 很少有人指出,现在超过 3 个 C++ 标准修订版,代表没有得到适当的解决。 (或者缺少允许直接委托实现的语言功能。)

  4. 通过标准定义 C++11 lambda 函数的方式(每个 lambda 具有匿名的、不同的类型),情况仅在某些用例中有所改善。但是对于在 (DLL) 库 API 中使用委托的用例,lambda 单独 仍然不可用。这里的常用技术是首先将 lambda 打包到 std::function 中,然后通过 API 传递它。

【讨论】:

  • 我已经根据其他地方看到的其他想法在 C++11 中完成了 Elbert Mai 的通用回调的一个版本,它似乎确实清除了您在 2) 中引用的大部分问题。对我来说,唯一剩下的烦人问题是我被困在客户端代码中使用宏来创建代表。可能有一个我还没有解决的模板魔术方法。
  • 我在嵌入式系统中使用没有堆的 std::function,它仍然有效。
  • std::function 不会总是动态分配
  • 这个答案比实际答案更像是咆哮
【解决方案4】:

非常简单,委托提供了函数指针应该如何工作的功能。 C++ 中的函数指针有很多限制。一个委托使用一些幕后的模板来创建一个模板类函数指针类型的东西,它可以按照你想要的方式工作。

ie - 您可以将它们设置为指向给定的函数,并且可以传递它们并随时随地调用它们。

这里有一些很好的例子:

【讨论】:

    【解决方案5】:

    这里没有另外提到的 C++ 中委托的一个选项是使用函数 ptr 和上下文参数来实现 C 风格。这可能是许多提出这个问题的人试图避免的模式。但是,该模式是可移植的、高效的,并且可用于嵌入式和内核代码。

    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 运行时等效于标准 C++ 中的函数对象。可以将整个函数用作参数(实际上是函数指针)。它主要与事件一起使用。委托代表事件处理程序履行的合同。它有助于函数指针如何工作。

      【讨论】:

        猜你喜欢
        • 2018-01-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-30
        • 1970-01-01
        相关资源
        最近更新 更多