函数在C++中永远不能是抽象的吗

Can a Functor never be abstract in C++?

本文关键字:抽象的 不能 永远 C++ 函数      更新时间:2023-10-16

通常,模板参数可以是抽象类,下面的程序也显示了这一点。但似乎排序中的比较函子一定不是抽象的。至少以下内容不能在VC++11和Oracle Studio 12上编译。

#include <vector>
#include <algorithm>

class Functor
{
public:
    virtual bool operator()(int a, int b) const = 0;
};

class MyFunctor: public Functor
{
public:
    virtual bool operator()(int a, int b) const { return true; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    vector<Functor> fv; // template of abstract class is possible
    vector<int> v;
    MyFunctor* mf = new MyFunctor();
    sort(v.begin(), v.end(), *mf);
    Functor* f = new MyFunctor();
    // following line does not compile: 
    // "Cannot have a parameter of the abstract class Functor"
    sort(v.begin(), v.end(), *f); 
    return 0;
}

现在,我想知道这是函子参数的一般性质,还是取决于STL实现?有办法得到我想做的事吗?

函数通常需要可复制。多态基类通常不可复制,抽象基类永远不可复制。

更新:感谢@ahenderson和@ltjax的评论,这里有一个非常简单的方法来生成一个包含原始多态引用的包装器对象:

#include <functional>
std::sort(v.begin(), v.end(), std::ref(*f));
//                            ^^^^^^^^^^^^

std::ref的结果是std::refrence_wrapper,这正是您所需要的:一个具有值语义的类,它包含对原始对象的引用。


函子被复制的事实让很多人感到困惑,他们想在函子内部积累一些东西,然后想知道为什么结果不正确。函子真的应该对外部对象进行引用。也就是说:

糟糕不会像你预期的那样工作;函子可能被任意复制:

struct Func1 {
    int i;
    Func1() : i(0) { }
    void operator()(T const & x) { /* ... */ }
};
Func1 f;
MyAlgo(myContainer, f); 

好:提供累加器;复制函子是安全的:

struct Func2 {
   int & i;
   Func2(int & n) : i(n) { }
   void operator()(T const & x) { /* ... */ }
};
int result;
MyAlgo(myContainer, Func2(result));

正如Kerrek所说,你不能直接这么做:

但只要间接一级,你就没事了。

struct AbstractFunctor
{
  AbstractFunctor( Functor * in_f ): f(in_f) {}
  // TODO: Copy constructor etc.
  Functor * f;
  bool operator()(int a, int b) const { return (*f)(a,b); }
};
int main()
{
  vector<int> v;
  Functor * mf = new MyFunctor();
  sort(v.begin(), v.end(), AbstractFunctor(mf) );
}

正如Kerrek和Michael Anderson所说,你不能直接这么做。正如Michael所展示的,您可以编写一个包装器类。但std::中也有一个:

sort(v.begin(),
     v.end(), 
     std::bind(&Functor::operator(),
               mf,
               std::placeholders::_1,
               std::placeholders::_2) );