【问题标题】:non-STL function object callback usage非 STL 函数对象回调用法
【发布时间】:2012-04-15 15:55:54
【问题描述】:

在网上我找到了一些关于函数对象(被调用者)的示例,但它们没有演示如何将它们用作回调函数(调用它们,调用者)的参数,而只是使用 sort 时的使用方式或 for_each。因此,我自己做了一个:

class CBase
{
public:
 virtual int operator()(int a, int b)
 {
  return 10;
 }
};

class CSmaller: public CBase
{
public:
 int operator()(int a, int b)
 {
  return a < b;
 }

 int Compute(int a, int b)
 {
  return a < b;
 }
};

class CLarger: public CBase
{
public:
 int operator()(int a, int b)
 {
  return a > b;
 }

 int Compute(int a, int b)
 {
  return a > b;
 }
};

int caller(CBase &f, int x0, int x1) 
{
 return f(x0,x1);
}

int main()
{
 CLarger callee1; /*this callee is being called by caller*/
 CSmaller callee2; /*idem*/
 int q=caller(callee1,0,1);
 int z=caller(callee2,0,1);
 printf("%d\n",q);
 printf("%d\n",z);
}

那些 STL 函数对象(如更大的 Less)是否以这种方式实现(因为它通常只在一个通用名称/类“模板”之前)?即,它们(CLarger 和 CSmaller)是否有一个关系/公共锚点,每个类都扩展了一个虚函数,或者它们也可以是一些通用函数对象(只要它们用两个参数实现 () 运算符)?像这样(虽然没有测试):

template<class F>
float integrate(const F &f,float x0,float x1) 
{
 ..f(x)..;
}

float derive(const F &f,float x0,float x1) 
{
 ..f(x)..;
}

class CSquare 
{
public:
 float operator()(float x)
 {
  return x*x;
 }
};

class CTwoX 
{
public:
 float operator()(float x)
 {
  return 2*x;
 }
};

如果这是正确的方式,这些不相关的类将如何以非 STL 方式实现?我知道使用模板并保持底层机制(以这种方式它们是相关的),但只有类型不同。但是否需要这样的关系(即相同的锚点)?

在函数对象中使用重载 () 仅仅是为了方便吗?因为我也可以使用 CLarger 的 Compute。例如,我还可以将那些分离的类(它们使用 virtual 来实现不同的功能)合并到一个 CCompute 类中,其中成员为 Larger 和 Smaller。顺便说一句,我想我也可以将派生和集成放在一个类中。我的问题是否有道理,或者我的想法是否与 STL 理论相矛盾?

【问题讨论】:

  • 注意:STL != 标准库。

标签: c++ function object callback


【解决方案1】:

您可以采用任何一种方式:继承或模板。不过,两者都不是要求。 STL 函数是模板化的,这意味着只要表达式 f(x) 按要求工作,您就可以为 f 传递任何内容(例如,函数指针、函子——您称之为被调用者)。

注意:f(x) 只是一个示例,参数的确切数量取决于所使用的特定 STL 函数。

继承的优点是可以将常见的行为封装在一个基类中,并在运行时根据实际类型执行。

模板的优点是您可以将它们与任何类型一起使用(基于模板参数)——因此虽然您可以在每个类中重载operator(),但创建一个可以处理每个类的模板类可能会更容易类型(如果某些类型需要特殊处理,则使用模板特化)

请注意,当您使用模板时,它会为每个实际模板参数类型组合创建不同的类型

【讨论】:

  • 问题在于 stdlib 算法按值接受函子,因此如果尝试将函子从指针/引用传递给基类,则会发生对象切片,并且因此,虚函数不会表现出多态性。因此,模板优于继承。
【解决方案2】:

接受函数参数(例如,谓词)的 STL 算法通常不会关心参数是如何创建的,只要它匹配“类似函数”的接口即可;它需要做的就是通过模板参数推导来推导类型

template< typename FwdIter, typename Pred >
FwdIter find_if( FwdIter first, FwdIter last, Pred pred );

/* .. etc ... */

bool is_equal_to_one(int n) { return n == 1; }

int main()
{
    std::vector<int> foo;
    std::vector<int>::const_iterator iter;
    iter = std::find_if( foo.begin(), foo.end(), is_equal_to_one );
}

Pred 类型有太多不同的可能方式来实现,而无法以其他方式实现(函数、仿函数、std::function...)。 find_if 函数将依赖 Pred 是任何与 bool (*FwdIt) 的接口匹配的东西

【讨论】:

  • 注意:STL != 标准库。
猜你喜欢
  • 2018-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-02
  • 1970-01-01
  • 2010-09-29
相关资源
最近更新 更多