美文网首页
The C++ standard library(侯捷/孟岩 译

The C++ standard library(侯捷/孟岩 译

作者: Ewitter | 来源:发表于2019-06-01 16:19 被阅读0次

    8 functors/functor objects

    8.1 conception of functor

    functor,是定义了operator()的对象。

    eg:
    FunctionObjectType fo;
    // ...
    fo(...)
    
    其中fo()调用仿函数fo的operator()而非函数fo()。
    
    可将functor视为一般函数,但更复杂,eg:
    class FunctonObjectType
    {
        public:
            void operator() (){  // statements }
    };
    

    functor定义形式复杂,但优点:

    1. functor较一般函数更灵巧。因为functor可有两个状态不同的实体。
    2.functor有其类型,故可将其类型作为template参数。
    3. functor执行速度更快。
    
    8.1.1 functor作sort criteria
    /* 需将某些class objects以已序形式置于容器中。
     * 但或不想或不能,总之无法使用一般的operator<来排序。
     * 要以某种特定规则(常基于某些成员函数)来排序,此时可用functor。
     */
    // fo/sort1.cpp
    
    #include <iostream>
    #include <string>
    #include <set>
    #include <algorithm>
    using namespace std;
    
    class Person
    {
        public:
            string firstname() const;
            string lastname() const;
            // ...
    };
    
    /* class fot function predicate
     * -operator() returns whether a person is less than another person
     */
    class PersonSortCriterion
    {
        public:
            bool operator() (const Person& p1, const Person& p2) const
            {
                /* a person is less than another person
                 * - if the last name is less
                 * - if the last name is equal and the first name is less
                 */
                return p1.lastname() < p2.lastname() ||
                    (!(p2.lastname() < p1.lastname()) &&
                     p1.firstname() < p2.firstname() );
            }
    };
    
    int main()
    {
        // declare set type with special sorting criterion
        typedef set<Person, PersonSortCriterion> PersonSet;
    
        // create such a collection
        PersonSet col1;
        //...
        
        // do something with the elements
        PersonSet::iterator pos;
        for(pos = col1.begin(); pos != col1.end(); ++pos)
        {
            //...
        }
        // ...
    }
    

    前面有相关代码,可参考p199。

    8.1.2 拥有internal state的functor
    // 展示functor 模拟函数在同一时刻下拥有多个状态
    // fo/genera1.cpp
    
    #include <iostream>
    #include <list>
    #include <algorithm>
    #include "../stl/print.hpp"
    using namespace std;
    
    class IntSequence
    {
        private:
            int value;
        public:
            // constructor
            IntSequence (int initialValue) : value(initialValue){}
    
            // "function call"
            int operator() ()
            {
                return value++;
            }
    };
    
    int main()
    {
        list<int> col1;
    
        // insert value from 1 to 9
        generate_n (back_inserter(col1),    // start
                9,  // number of elements
                IntSequence(1) );   // generate values
    
        PRINT_ELEMENTS(col1);
    
        // replace second to last element but one with values
        // starting at 42
        generate (++col1.begin(),   // start
                --col1.end(),   // end
                IntSequence(42) );  // generate values
        PRINT_ELEMENTS(col1);
    }
    
    output: general.png

    functor是passed by value,而不是passed by reference: passed by value优点是可传递常量或临时表达式,缺点不能改变随参数而来最终的functor state。
    有两种办法从“运行了functor”的算法中获得结果:

      1. by reference方式传递functor。
      1. 使用for_each()。(见下一节)
    // fo/genera2.cpp
    
    #include <iostream>
    #include <list>
    #include <algorithm>
    #include "../stl/print.hpp"
    using namespace std;
    
    class IntSequence
    {
        private:
            int value;
        public:
            // constructor
            IntSequence(int initialValue) : value(initialValue){}
    
            // "function call"
            int operator() ()
            {
                return value++;
            }
    };
    
    int main()
    {
        list<int> col1;
        IntSequence seq(1); // integral sequence starting with 1
    
        // insert value from 1 to 4
        // - pass function object by reference
        // so that it will continue with 5
        generate_n<back_insert_iterator<list<int> >, 
            int, IntSequence& >(back_inserter(col1) ,   // start
                    4,  // number of elements
                    seq);   // generate values
        PRINT_ELEMENTS(col1);
    
        // insert values from 42 to 45
        generate_n(back_inserter(col1), // start
                4,  // number of elements
                IntSequence(42));   // generates values
        PRINT_ELEMENTS(col1);
    
        // continue with first sequence
        // - pass function object by value
        // so that it will continue with 5 again
        generate_n(back_inserter(col1), 4,seq);
        PRINT_ELEMENTS(col1);
    
        // continue with first sequence again
        generate_n(back_inserter(col1), 4, seq);
        PRINT_ELEMENTS(col1);
    }
    
    output: genera2.png
    8.1.3 for_each()返回值

    for_each()可以返回其functor,其它算法都无该特性。

    // 使用for_each()返回值,处理一个序列的平均值
    
    // fo/foreach3.cpp
    
    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    // function object to process the mean value
    class MeanValue
    {
        private:
            long num;   // num of elements
            long sum;   // sum of all element values
        public:
            // constructor
            MeanValue() : num(0), sum(0){}
    
            // "function call"
            // - process one more elements of the sequence
            void operator() (int elem)
            {
                num++;  // increment
                sum += elem;    // add value
            }
    
            // return mean value
            double value() 
            {
                return static_cast<double>(sum)/static_cast<double>(num);
            }
    };
    
    int main()
    {
        vector<int> col1;
    
        // insert elements from 1 to 8
        for(int i = 1; i <= 8; ++i)
        {
            col1.push_back(i);
        }
    
        // process and print mean value
        MeanValue mv = for_each(col1.begin(), col1.end(), MeanValue());
        cout << "mean value: " << mv.value() << endl;
    }
    
    程序说明:返回的functor被赋值给mv,故可调用mv.value()查询其状态。
    
    output: foreach3.png
    8.1.4 predicates && functors

    predicate,即返回bool值的一个函数或functor,但对STL而言并非所有返回bool的函数都是合法的predicate

    // fo/removeif.cpp
    
    #include <iostream>
    #include <list>
    #include <algorithm>
    #include "../stl/print.hpp"
    using namespace std;
    
    class Nth
    {   // function object that returns true for the nth call
        private:
            int nth;    // call for which to return true
            int count;
        public:
            Nth(int n) : nth(n), count(0){}
            bool operator() (int)
            {
                return ++count == nth;
            }
    };
    
    int main()
    {
        list<int> col1;
    
        // insert elements from 1 to 9
        for (int i = 1; i <= 9; ++i)
        {
            col1.push_back(i);
        }
        PRINT_ELEMENTS(col1, "col1: ");
    
        // remove third element
        list<int>::iterator pos;
        pos = remove_if(col1.begin(), col1.end(), Nth(3));
        col1.erase(pos, col1.end());
        PRINT_ELEMENTS(col1, "nth removed: ");
    }
    
    output: removeif.png

    结果及说明:

    结果删除了第3和第6个元素,
      原因在于remove_if()标准实现内部保留predicate的一份副本:
    template <class ForwIter, class Predicate>
    ForwIter std::remove_if(ForwIter beg, ForwIter end, Predicate op)
    {
        beg = find_if(beg, end, op);
        if (beg == end)
        {
            return beg;
        }
        else
        {
            ForwIter next = beg;
            return remove_copy_if(++next, end, beg, op);
        }
    }
    
    remove_if()使用find_if()来查找应被移除的第一个元素,
      然后使用predicate op的副本处里剩余的元素,
        故原始状态的Nth再次被调用,即会移除剩余元素的第三个元素。
    

    由上,predicate不应该因为被调用而改变自身状态
    当然,避免上述问题可改变下remove_if()的标准实现:

    beg = find_if(beg, end, op); 语句替换为
    while(beg != end && !op(*beg)){  ++beg;}
    

    8.2 预定义的functor

    使用这些functors,须#include <functional>


    t8-1.png

    bind1st(op, para)指将para绑定到op的第一个参数,bind2nd(op,para)指将para绑定到op的第二个参数。(可见p311的fopow1.cpp)

    8.2.1 function adapter(函数配接器)

    function adapter指能 将functor和另一个functor(或某个值或一般函数)结合起来的functor。function adapter也声明于<functional>。

    t8-2.png

    functional compositon(功能复合、函数复合):把多个functor结合起来形成强大的表达式。

    8.2.2 对成员函数设计的函数配接器

    C++标准库提供了一些额外的函数配接器,通过它们,可对群集内每个元素调用其成员函数。


    t8-3.png

    eg:

    // 利用mem_fun_ref,对vector内每个对象调用其成员函数
    // fo/memfun1a.cpp
    
    class Person
    {
        private:
            std::string name;
        public:
            // ...
            void print() const
            {
                std::cout << name << std::endl;
            }
            void printWithPrefix (std::string prefix) const
            {
                std::cout << prefix << name << std::endl;
            }
    };
    
    void foo(const std::vector<Person>& col1)
    {
        using std::for_each;
        using std::bind2nd;
        using std::mem_fun_ref;
    
        // call member function print() for each element
        for_each(col1.begin(), col1.end(),
                mem_fun_ref(&Person::print));
    
        // call member function printWithPrefix() for each element
        // - "person: " is passed as an argument to the member function
        for_each(col1.begin(), col1.end(),
                bind2nd(mem_fun_ref(&Person::printWithPrefix), "person: ") );
    }
    

    不能直接把一个成员函数传给算法,for_each()会针对第三参数传来的指针调用operator() 而非调用该指针所指的函数(可见p126的for_each()实现)。用配接器会将operator()调用操作做适当转换。

    eg:
    for_each(col1.begin, col1.end(), &Person::print);  
    // error:can't call operator() for a member function potiner
    
    8.2.3 对一般函数设计的函数配接器
    t8-4.png
    bool check(int elem);
    pos = find_if(col1.begin(), col1.end(), 
                  not1(ptr_fun(check) ) );  // 查找第一个令检验失败的元素
    
    pos = find_if (col1.begin(), col1.end(),   
                   bind2nd(ptr_fun(strcmp), "") ) ;
    // 采用strcmp()将每个元素与空的C-string比较。
    
    8.2.4 user-defined functor使用function adapter

    希望user-defined functor能和function adapter搭配使用,须满足一些条件:提供类型成员反映其参数和返回值类型
    C++标准库提供了一些结构:

    template <class Arg, class Result>
    struct unary_function
    {
        typedef Arg argument_type;
        typedef Result result_type;
    };
    
    template <class Arg1, class Arg2, class Result>
    struct binary_function
    {
        typedef Arg1 first_argument_type;
        typedef Arg2 second_argument_type;
        typedef Result result_type;
    };
    

    如此只要继承上述某种形式,即可满足adaptable条件。
    eg:

    // fo/fopow.hpp
    
    #include <funcitonal>
    #include <cmath>
    
    template <class T1, class T2>
    struct fopow : public std::binary_function<T1, T2, T1>
    {
        T1 operator() (T1 base, T2 exp) const
        {
            return std::pow(base, exp);
        }
    };
    
    
    // fo/fopow1.cpp
    
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <iterator>
    using namespace std;
    
    #include "fopow.hpp"
    
    int main()
    {
        vector<int> col1;
    
        for (int i = 1; i <= 9; ++i)
        {
            col1.push_back(i);
        }
    
        // print 3 raised to the power of all elements
        transform(col1.begin(), col1.end(),
                ostream_iterator<int>(cout, " "),
                bind1st(fopow<float,int>(), 3));
        cout << endl;
    
        // print all elements raised to the power of 3
        transform(col1.begin(), col1.end(),
                ostream_iterator<int>(cout, " "),
                bind2nd(fopow<float,int>(), 3));
        cout << endl;
    }
    
    output: fopow1.png

    8.3 组合型functor

    一般而言,所有函数行为都可由functor组合实现。但C++标准库未提供足够的配接器来实现。
    理论上,下面的compose adapter(组合型配接器)很有用。

    f( g(elem) )
    一元判别式g()结果作为一元判别式f()参数。
    
    f( g(elem1, elem2) )
    二元判别式g()结果作为一元判别式f()参数。
    
    f( g(elem1), h(elem2) )
    一元判别式g()和h()的结果作为二元判别式f()的参数。
    

    但上述compose adapter未被纳入标准。书中作者自定义名称:

    t8-5.png
    8.3.1 unary compose function object adapter
    • 以compose_f_gx进行nested(嵌套)计算
      compose_f_gx在SGI STL实现版本称为compose1,compose_f_gx的可实现如下:
    // fo/compose11.hpp
    
    #include <functional>
    
    /* class for the compose_f_gx adapter */
    template <class OP1, class OP2>
    class compose_f_gx_t :
        public std::unary_function<typename OP2::argument_type, typename OP1::result_type>
    {
        private:
            OP1 op1;    // process: op1(op2(X))
            OP2 op2;
        public:
            // constructor
            compose_f_gx_t(const OP1& o1, const OP2& o2) : op1(o1), op2(o2){}
    
            // function call
            typename OP1::result_type
                operator() (const typename OP2::argument_type& x) const
                {
                    return op1(op2(X));
                }
    
    };
    /* conveniences functions for the compose_f_gx adapter */
    template <class OP1, class OP2>
        inline compose_f_gx_t<OP1, OP2>
        compose_f_gx (const OP1& o1, const OP2& o2)
    {
        return compose_f_gx_t<OP1, OP2>(o1, o2);
    }
    
    // 对一个序列的每个元素先 加10 再乘5
    // fo/compose1.cpp
    
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <functional>
    #include <iterator>
    #include "../stl/print.hpp"
    #include "compose11.hpp"
    using namespace std;
    
    int main()
    {
        vector<int> col1;
    
        // insert elements from 1 to 9
        for (int i = 1; i <= 9; ++i)
        {
            col1.push_back(i);
        }
        PRINT_ELEMENTS(col1);
    
        // for each element add 10 and multiply by 5
        transform (col1.begin(), col1.end(),
                ostream_iterator<int>(cout, " "),
                compose_f_gx(bind2nd(multiplies<int>(), 5),
                            bind2nd(plus<int>(), 10) ) );
        cout << endl;
    }
    
    output: compose1.png
    • 以compose_f_gx_hx组合两个准则
      compose_f_gx_hx在SGI STL实现版本称为compose2,compose_f_gx_hx可实现如下:
    // fo/compose21.hpp
    
    #include <functional>
    
    /* class for the compose_f_gx_hx adapter */
    
    template <class OP1, class OP2, class OP3>
    class compose_f_gx_hx_t :
        public std::unary_function<typename OP2::argument_type, typename OP1::result_type>
    {
        private:
            OP1 op1;    // process: op1(op2(x), op3(x))
            OP2 op2;
            OP3 op3;
        public:
            // constructor
            compose_f_gx_gx_t (const OP1& o1, const OP2& o2, const OP3& o3) :
                op1(o1), op2(o2), op3(o3){}
    
            // function call
            typename OP1::result_type
                operator() (const typename OP2::argument& x) const
                {
                    return op1(op2(x), op3(x));
                }
    };
    
    /* convenience functions for the compose_f_gx_hx adapter */
    template <class OP1, class OP2, class OP3>
    inline compose_f_gx_hx_t<OP1, OP2, OP3>
    compose_f_gx_hx (const OP1& o1, const OP2& o2, const OP3& o3)
    {
        return compose_f_gx_hx_t<OP1, OP2, OP3>(o1, o2, o3);
    }
    
    // 删除序列中大于4且小于7 的元素
    // fo/compose2.cpp
    
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <functional>
    #include "../stl/print.hpp"
    #include "compose21.hpp"
    using namespace std;
    
    int main()
    {
        vector<int> col1;
    
        // insert elements from 1 to 9
        for (int i = 1; i <= 9; ++i)
        {
            col1.push_back(i);
        }
        PRINT_ELEMENTS(col1);
    
        // remove all elements that are greater than four and less than seven
        // - retain new end
        vector<int>::iterator pos;
        pos = remove_if(col1.begin(), col1.end(),
                compose_f_gx_hx(logical_and<bool>(),
                                bind2nd(greater<int>(), 4),
                                bind2nd(less<int>(),7) ) );
        // remove "removed" elements in col1
        col1.erase(pos, col1.end());
        PRINT_ELEMENTS(col1);
    }
    
    output: compose2.png
    8.3.2 binary compose function object adapters

    二元组合函数配接器,可将两个一元运算(分别接受不同参数)的结果加以处理,称之为compose_f_gx_hy,可能的实现如下:

    // fo/compose22.hpp
    
    #include <functional>
    
    /* class for the compose_f_gx_hy adapter */
    template <class OP1, class OP2, class OP3>
    class compose_f_gx_hy_t :
        public std::binary_function<typename OP2::argument_type, 
                                    typename OP3::argument_type,
                                    typename OP1::result_type>
    {
        private:
            OP1 op1;    // process: op1(op2(x), op3(y))
            OP2 op2;
            OP3 op3;
        public:
            // constructor
            compose_f_gx_hy_t(const OP1& o1, const OP2& o2, const OP3& o3) :
                op1(o1), op2(o2), op3(o3){}
    
            // function call
            typename OP1::result_type
                operator() (const typename OP2::argument_type& x,
                        const typename OP3::argument_type& y) const
                {
                    return op1(op2(x), op3(y));
                }
    };
    
    /* convenience function for the compose_f_gx_hy adapter */
    template <class OP1, class OP2, class OP3>
    inline compose_f_gx_hy_t<OP1, OP2, OP3>
    compose_f_gx_hy(const OP1& o1, const OP2& o2, const OP3& o3)
    {
        return compose_f_gx_hy_t<OP1,OP2, OP3>(o1, o2, o3);
    }
    
    // fo/compose22.hpp
    
    #include <functional>
    
    /* class for the compose_f_gx_hy adapter */
    template <class OP1, class OP2, class OP3>
    class compose_f_gx_hy_t :
        public std::binary_function<typename OP2::argument_type, 
                                    typename OP3::argument_type,
                                    typename OP1::result_type>
    {
        private:
            OP1 op1;    // process: op1(op2(x), op3(y))
            OP2 op2;
            OP3 op3;
        public:
            // constructor
            compose_f_gx_hy_t(const OP1& o1, const OP2& o2, const OP3& o3) :
                op1(o1), op2(o2), op3(o3){}
    
            // function call
            typename OP1::result_type
                operator() (const typename OP2::argument_type& x,
                        const typename OP3::argument_type& y) const
                {
                    return op1(op2(x), op3(y));
                }
    };
    
    /* convenience function for the compose_f_gx_hy adapter */
    template <class OP1, class OP2, class OP3>
    inline compose_f_gx_hy_t<OP1, OP2, OP3>
    compose_f_gx_hy(const OP1& o1, const OP2& o2, const OP3& o3)
    {
        return compose_f_gx_hy_t<OP1,OP2, OP3>(o1, o2, o3);
    }
    
    output: compose3.png

    除p319,p499有“大小写无关”子串查找但未用compose_f_gx_hy。

    相关文章

      网友评论

          本文标题:The C++ standard library(侯捷/孟岩 译

          本文链接:https://www.haomeiwen.com/subject/jqvotctx.html