理解 STL 中的函子

发布于 2024-09-06 22:30:34 字数 1425 浏览 9 评论 0原文

引用 NM Jousttis 的“C++ 标准库”第 5.9 节


#include < iostream>
#include < list>
#include < algorithm>

using namespace std;

//function object that adds the value with which it is initialized
class AddValue {
    private:
       int the Value; //the value to add
    public:
       //constructor initializes the value to add
       AddValue(int v) : theValue(v) {    }
       //the "function call" for the element adds the value
       void operator() (int& elem) const {  elem += theValue; }
 };

int main()
{
      list<int> coll;
      for (int i=1; i<=9; ++i) 
         coll.push_back(i); 

      //The first call of for_each() adds 10 to each value:
      for_each (coll.begin(), coll.end(), AddValue(10)) ; 

这里,表达式 AddValue(10) 创建一个 AddValue 类型的对象,该对象初始化为 值 10。AddValue 的构造函数将此值存储为成员 theValue。里面 for_each(),对coll的每个元素调用“()”。同样,这是对operator()的调用 传递的 AddValue 类型的临时函数对象。实际元素作为 争论。函数对象将其值 10 添加到每个元素。那么元素有 以下值: 添加 10 后:

11 12 13 14 15 16 17 18 19

第二次调用 for_each() 使用相同的功能来添加第一个元素的值 到每个元素。它使用第一个元素初始化 AddValue 类型的临时函数对象 集合的:

for_each (coll.begin(), coll.end(), AddValue (*coll. begin()) ) ; 

添加第一个元素后,输出如下:

22 23 24 25 26 27 28 29 30

我不明白的是在第二种情况下,为什么输出不意味着

22 34 35 36 37 38 39 40 41

是为每个调用创建一个新的函子,还是为每个调用使用的函子?

quoting from "The C++ Standard Library" by N M Jousttis, Section 5.9


#include < iostream>
#include < list>
#include < algorithm>

using namespace std;

//function object that adds the value with which it is initialized
class AddValue {
    private:
       int the Value; //the value to add
    public:
       //constructor initializes the value to add
       AddValue(int v) : theValue(v) {    }
       //the "function call" for the element adds the value
       void operator() (int& elem) const {  elem += theValue; }
 };

int main()
{
      list<int> coll;
      for (int i=1; i<=9; ++i) 
         coll.push_back(i); 

      //The first call of for_each() adds 10 to each value:
      for_each (coll.begin(), coll.end(), AddValue(10)) ; 

Here, the expression AddValue(10) creates an object of type AddValue that is initialized with
the value 10. The constructor of AddValue stores this value as the member theValue. Inside
for_each(), "()" is called for each element of coll. Again, this is a call of operator () for
the passed temporary function object of type AddValue. The actual element is passed as an
argument. The function object adds its value 10 to each element. The elements then have the
following values:
after adding 10:

11 12 13 14 15 16 17 18 19

The second call of for_each() uses the same functionality to add the value of the first element
to each element. It initializes a temporary function object of type AddValue with the first element
of the collection:

for_each (coll.begin(), coll.end(), AddValue (*coll. begin()) ) ; 

The output is then as follows after adding first element:

22 23 24 25 26 27 28 29 30

what I don't understand is in the second case why is the output is not

22 34 35 36 37 38 39 40 41

meaning is a new functor being created for each call or is the functor used for each call ?

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

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

发布评论

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

评论(6

画骨成沙 2024-09-13 22:30:34

表达式AddValue(*coll.begin()) 创建一个AddValue 类的临时对象。然后将该临时值传递给 for_each 函数。然后,for_each 调用对象的函数调用运算符 - 即 operator() - 对从 coll.begin()的每个元素调用一次>coll.end()

从技术上讲,for_each 按值(而不是引用)获取函子参数,因此它实际上对临时对象的副本进行操作,而不是临时对象本身。

The expression AddValue(*coll. begin()) creates one temporary object of class AddValue. That temporary is then passed to the for_each function. for_each then calls the object's function call operator - that is, operator() - once for each element from coll.begin() to coll.end().

Technically, for_each takes the functor parameter by value (not reference), so a it actually operates on a copy of the temporary, not the temporary itself.

逆光飞翔i 2024-09-13 22:30:34

[编辑]我原本误解了作者的初衷。我纠正了错误。

for_each(coll.begin(), coll.end(),
AddValue (*coll.begin()) ) ;

之后的输出如下
添加第一个元素:

22 23 24 25 26 27 28 29 30

我不明白的是
第二种情况为什么输出不是

22 34 35 36 37 38 39 40 41

含义是正在创建一个新函子
对于每个调用或使用的函子
每次通话?

首先,无论for_each算法是否复制你传入的函子都是无关紧要的。相关的是,您应该存储指向第一个元素而不是指向对象的迭代器或指针。如果你这样做并每次都取消引用它,它应该可以解决问题。

struct AddValue 
{
    const int* ptr;
    explicit AddValue(const int* iptr): ptr(iptr) {}
    void operator() (int& elem) const {elem += *ptr; }
};

int main()
{
    vector<int> v;
    for (int j=1; j <= 9; ++j)
        v.push_back(j + 10);
    for_each(v.begin(), v.end(), AddValue(&v[0]) );
    // v will be [22 34 35 36 37 38 39 40 41]
}

然而,我将不得不谈谈我对函数式编程的两点看法。通过函数式编程,您可以获得真正简洁、巧妙紧凑的代码。然而,调试起来也很痛苦,并且具有分散代码的效果。除非您的代码可以从中受益匪浅,否则请尽可能考虑使用简单的基于迭代器的 for 循环,因为它会让您的程序员同事更轻松地调试和阅读您的代码。

我过去犯了一个错误,那就是对函数式编程过于严厉,这让我的团队讨厌我。我痴迷于用组合谓词逻辑编写极其严格的代码,并构建了巨大的函数对象库,这些函数对象可以反复组合重用。事实上,我所做的只是花了很多时间编写函子,而我本可以编写简单的、同样可重用的函数,而不是可以从一个简单的基于迭代器的 for 循环调用(并且同样容易内联)(甚至更容易使用C++0x 基于范围的 for 循环和 BOOST_FOR_EACH)。我仍然使用 C++ 中的函数式编程,但很少使用。当你经历了很多麻烦并不断​​增加构建时间以将两行或三行代码合并为一行时,你真的必须问自己并深入思考这是否值得,不仅为了你自己,而且为了每个工作的人用你的代码。

[Edit] I originally misunderstood the author's original intent. I corrected the mistake.

for_each (coll.begin(), coll.end(),
AddValue (*coll. begin()) ) ;

The output is then as follows after
adding first element:

22 23 24 25 26 27 28 29 30

what I don't understand is in the
second case why is the output is not

22 34 35 36 37 38 39 40 41

meaning is a new functor being created
for each call or is the functor used
for each call ?

First of all, regardless of whether the for_each algorithm copies the functor you pass in is irrelevant. What's relevant is that you should be storing the iterator or pointer to the first element rather than the pointee. If you do that and dereference it each time, it should do the trick.

struct AddValue 
{
    const int* ptr;
    explicit AddValue(const int* iptr): ptr(iptr) {}
    void operator() (int& elem) const {elem += *ptr; }
};

int main()
{
    vector<int> v;
    for (int j=1; j <= 9; ++j)
        v.push_back(j + 10);
    for_each(v.begin(), v.end(), AddValue(&v[0]) );
    // v will be [22 34 35 36 37 38 39 40 41]
}

However, I'm going to have to throw in my two cents about functional programming. You can get really concise, cleverly-tight code through functional programming. However, it's also a big pain to debug and has the effect of decentralizing code. Unless your code can significantly benefit from it, consider simple iterator-based for loops when you can as it'll give your fellow programmers a much easier time debugging and reading through your code.

I made the mistake of getting super heavy-handed with functional programming in the past and it made my team hate me. I was obsessed with writing devilish tight code with combinatorial predicate logic and built huge libraries of function objects that could be reused over and over and in combinations. Really all I did was spend a lot of time writing functors when I could have been writing simple, equally reusable functions instead that could have been called (and inlined just as easily) from a simple iterator-based for loop (even easier to write with C++0x's range-based for loop and BOOST_FOR_EACH). I still use functional programming in C++, but sparingly. When you're going through a lot of trouble and piling on to build times to make two or three lines of code into one, you really have to ask yourself and think deeply if it's worth it and not just for yourself, but for everyone working with your code.

余生共白头 2024-09-13 22:30:34

是的,就是你说的。函子默认按值传递,因此它们在 std::for_each 的代码中复制。但是,您可以编写自己的 std::for_each 版本,明确声明您希望通过引用传递函子。

Yes, it is what you said. Functors are passed by value by default and thus they are copied in code of std::for_each. However you could write your own version of std::for_each explicitly stating that you want to pass functor by reference.

稍尽春風 2024-09-13 22:30:34

是的。函子的新副本将通过 for_each 传递。你正在读的书解释了这一点。

Yes. A new copy of your functor is passed allong through for_each. The book you're reading explains this.

冰雪梦之恋 2024-09-13 22:30:34

我相信像这样复制函子的原因是为了使 for_each 更通用。

想象一个通过引用获取仿函数的实现。如果函子是右值(例如,如果它是从另一个函数返回的),那就会破坏:

std::for_each(first, last, get_functor(...))

当然,在这些情况下,算法可以通过 const 引用获取函子(因为它们可以绑定到右值),但随后函子必须是 const。

唯一真正通用的解决方案是按值传递函子。然后它可以与 const、非常量、右值和左值函子一起使用。

I believe the reason why the functor is copied around like this is to make for_each more general.

Imagine an implementation that took the functor by reference. That would break if the functor is an rvalue (say, if it is returned from another function):

std::for_each(first, last, get_functor(...))

Of course, in those cases, the algorithm could take the functor by const reference (since they can be bound to rvalues), but then the functor has to be const.

The only really general solution is to pass the functor by value. Then it works with const, non-const, rvalue and lvalue functors.

孤独陪着我 2024-09-13 22:30:34

您的 AddValue 构造函数采用 int,因此当您从 *coll.begin() 构造它时,将使用集合中第一个成员的值初始化成员变量theValue

现在此问题已修复(没有其他任何内容会修改 theValue),因此每次在此 AddValue 对象或此 AddValue< 的任何副本中使用 theValue 时/code> 对象它仍将具有与初始化时相同的值。

这是构造第一个 AddValue 对象时 *coll.begin() 的值,而不是值 * coll.begin() 可能已修改为。

Your AddValue constructor takes an int so when you construct it from *coll.begin() then value of the first member of your collection is used to initialize the member variable theValue.

This is now fixed (nothing else modifies theValue) so everytime theValue is used from this AddValue object or any copies of this AddValue object it will still have the same value with which it was initialized.

This is the value that *coll.begin() had at the time the first AddValue object was constructed, not the value *coll.begin() might have been modified to.

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