无状态类仿函数什么时候可以代替 ac 风格的函数?

发布于 2025-01-05 16:55:24 字数 1181 浏览 5 评论 0原文

我在 SO 上找到了一些很好的函子示例,例如这个,以及所有令人信服的例子似乎是在定义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);

I've found some good examples of functors on SO like this one, and all the convincing examples seem to use state in the class that defines operator().

I came across an example in a book that defines the function call operator without having state, and I can't help but feel like this is an awkward usage, and that a normal style function pointer, would be better than using operator() in every way here - less code, less variables (you have to instantiate the comparators), its probably more efficient due to the instantiation, and no loss of meaning or encapsulation (since it's just one function).

I know std::sort lets you pick between operator() classes and functions, but I've always just used the functions because of the above logic.

What are the reasons why a class might be preferred?

Here's the example (paraphrased):

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

梦魇绽荼蘼 2025-01-12 16:55:24

典型的原因是,当您执行此操作时:

bool less_than(const Point&, const Point&);
// ...
std::sort(..., &less_than);

谓词的模板参数如下:

bool(const Point&,const Point&)

由于排序函数接收函数指针,因此编译器更难以内联在 std::sort( )。 函数

bool greater_than(const Point&, const Point&);

发生这种情况是因为您可能有另一个具有完全相同类型的 ,这意味着 std::sort() 实例将在两个谓词之间共享。 (请记住,我说过它会使内联变得更加困难,而不是不可能)。

相反,当您执行以下操作时:

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() 的唯一模板实例化,从而更容易内联谓词的定义。

The typical reason is that when you do this:

bool less_than(const Point&, const Point&);
// ...
std::sort(..., &less_than);

The template argument for the predicate is the following:

bool(const Point&,const Point&)

Since the sort function receives a function pointer, it is more difficult for the compiler to inline the predicate use inside std::sort(). This happens because you could have another function

bool greater_than(const Point&, const Point&);

which has the exact same type, meaning the std::sort() instatiation would be shared between the two predicates. (remember that I said that it makes inlining more difficult, not impossible).

In contrast, when you do this:

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

The compiler generates a unique template instantiation for std::sort() for each predicate, making it easier to inline the predicate's definition.

逆光飞翔i 2025-01-12 16:55:24

原因之一是运行时效率。如果将指针传递给函数,则编译器必须非常聪明才能为该函数内联生成代码。传递定义 operator() 的对象使编译器更容易生成内联代码。特别是对于排序之类的事情,这可以大大提高速度。

在 C++11 中,使用类的另一个原因是为了方便——您可以使用 lambda 表达式来定义类。

One reason is run-time efficiency. If you pass a pointer to a function, the compiler has to be unusually clever to produce code for that function inline. Passing an object that defines operator() makes it much easier for the compiler to produce the code inline. Especially for something like sorting, this can increase speed quite substantially.

In C++11, another reason to use a class is for convenience -- you can use a lambda expression to define the class.

羞稚 2025-01-12 16:55:24

其他人对编译器内联函子的能力提出了很好的观点。函子对象与函数指针相比​​的另一个可能的优点是灵活性。函子可能是一个模板,可能是一个派生类,也许它具有运行时配置(即使在调用operator()时是无状态的等等。

Others have made good points about the ability for the compiler to inline the functor. One other possibile advantage of functor objects vs. function pointers is flexibility. The functor might be a template, maybe a derived class, perhaps it has run time configuration (even if stateless at the time operator() is called etc.

云朵有点甜 2025-01-12 16:55:24

另一个原因是有时一个比较功能是不够的。假设我们有一个指针向量:

struct X { string name; };
vector<shared_ptr<X>> v;

现在,如果我们想按名称对向量进行排序,我们必须为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; }
};

这允许我们使用 string 名称对象调用 equal_range

equal_range(v.begin(), v.end(), name, Cmp2())

Another reason is that sometimes one comparison function is not enough. Let's say we have a vector of pointers:

struct X { string name; };
vector<shared_ptr<X>> v;

Now if we want to sort the vector by name, we have to define our own predicate for the sort function:

struct Cmp1
{
    bool operator()(const shared_ptr<X>& left, const shared_ptr<X>& right) const
    { return left->name < right->name; }
};

That's cool, but what do we do when we need to find the objects with specific name? To work with equal_range, the predicate needs to have two different comparison functions:

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

This allows us to call equal_range with a string name object:

equal_range(v.begin(), v.end(), name, Cmp2())
七度光 2025-01-12 16:55:24

在模板库中,函子参数的状态在定义时未知,类类型的参数提供了比非实例函数指针更通用的接口。
STL 是一个很好的例子,其中有状态或无状态谓词和函子可以用作类和函数的参数。
如果模板编程不是计划的一部分,函数指针优于无状态函子类;函数的单个实例可以接受特定签名的所有函数指针,并且代码大小被最小化。但如果未来库扩展的可能性很小,函子会使它更加通用。

In template libraries where the state of functor argument is not known at definition time, argument of class type provides a more generic interface than none-instance function pointers.
STL is a great example where the statefull or stateless perdicates and functors can be used as parameters to classes and functions.
If template programming is not part of the plan, function pointer is superior to stateless functor class; a single instance of function can accept all function pointers of a specific signature and code size is minimized. But in case there is a minimal probability of future library extension, functor makes it more generic.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文