我在this之类的SO上找到了一些很好的仿函数示例,所有令人信服的示例似乎都在定义operator()
的类中使用了状态。
我在一本书中找到了一个例子,它定义了函数调用操作符而没有状态,我无法帮助但感觉这是一个尴尬的用法,并且一个普通的样式函数指针,会比在这里以各种方式使用operator()
- 更少的代码,更少的变量(你必须实例化比较器),它可能由于实例化而更有效,并且没有意义或封装的损失(因为它只是一个功能)。
我知道std::sort
允许您在operator()
类和函数之间进行选择,但由于上述逻辑,我总是只使用这些函数。
为什么选择课程是什么原因?
以下是例子(释义):
class Point2D {
//.. accessors, constructors
int x,y;
};
class HorizComp {
public:
bool operator()(const Point2D& p, const Point2D& q) const
{ return p.getX() < q.getX(); }
};
class VertComp {
public:
bool operator()(const Point2D& p, const Point2D& q) const
{ return p.getY() < q.getY(); }
};
template <typename E, typename C>
void printSmaller(const E& p, const E& q, const C& isLess) {
cout << (isLess(p, q) ? p : q) << endl; // print the smaller of p and q
}
//...
// usage in some function:
Point2D p(1.2, 3.2), q(1.5, 9.2);
HorizComp horizComp;
VertComp vorizComp;
printSmaller(p, q, horizComp);
printSmaller(p, q, vorizComp);
答案 0 :(得分:8)
典型的原因是当你这样做时:
bool less_than(const Point&, const Point&);
// ...
std::sort(..., &less_than);
谓词的模板参数如下:
bool(const Point&,const Point&)
由于sort函数接收函数指针,因此编译器更难以内联std::sort()
内的谓词用法。这是因为你可以有另一个功能
bool greater_than(const Point&, const Point&);
具有完全相同的类型,这意味着std::sort()
instatiation将在两个谓词之间共享。 (记住,我说它使内联更难,并非不可能)。
相反,当你这样做时:
struct less_than {
bool operator()(const Point&, const Point&) const;
};
// ...
std::sort(..., less_than());
struct greater_than {
bool operator()(const Point&, const Point&) const;
};
// ...
std::sort(..., greater_than());
编译器为每个谓词生成std::sort()
的唯一模板实例化,从而更容易内联谓词的定义。
答案 1 :(得分:6)
一个原因是运行时效率。如果将指针传递给函数,编译器必须非常聪明才能为内联函数生成代码。传递定义operator()
的对象使得 更容易让编译器生成内联代码。特别是对于像分类这样的东西,这可以大大提高速度。
在C ++ 11中,使用类的另一个原因是为了方便起见 - 您可以使用lambda表达式来定义类。
答案 2 :(得分:0)
其他人对编译器内联函子的能力提出了很好的观点。函子对象与函数指针的另一个可能的优点是灵活性。仿函数可能是一个模板,可能是派生类,也许它有运行时配置(即使在调用operator()时无状态也是如此。
答案 3 :(得分:0)
另一个原因是有时一个比较功能是不够的。假设我们有一个指针向量:
struct X { string name; };
vector<shared_ptr<X>> v;
现在,如果我们想要按name
对矢量进行排序,我们必须为sort
函数定义我们自己的谓词:
struct Cmp1
{
bool operator()(const shared_ptr<X>& left, const shared_ptr<X>& right) const
{ return left->name < right->name; }
};
这很酷,但是当我们需要找到具有特定名称的对象时,我们该怎么做?要使用equal_range
,谓词需要有两个不同的比较函数:
struct Cmp2
{
bool operator()(const shared_ptr<X>& left, const string& right) const
{ return left->name < right; }
bool operator()(const string& left, const shared_ptr<X>& right) const
{ return left < right->name; }
};
这允许我们使用equal_range
名称对象调用string
:
equal_range(v.begin(), v.end(), name, Cmp2())
答案 4 :(得分:0)
在定义时未知仿函数参数状态的模板库中,类类型的参数提供了比非实例函数指针更通用的接口。 STL是一个很好的例子,其中statefull或无状态的perdicates和functor可以用作类和函数的参数。 如果模板编程不是计划的一部分,则函数指针优于无状态仿函数类;单个函数实例可以接受特定签名的所有函数指针,并且代码大小最小化。但是,如果未来库扩展的概率很小,仿函数会使它更通用。