zoukankan      html  css  js  c++  java
  • [转]boost.bind 如何 bind

    Boost.bind 好用么?当然好用,而且它也确定进入下一代的 C++ 标准了,也早就进了 TR1 了。回顾一下,它允许我们干这个:

    #include <algorithm>
    #include <iostream>
    #include <string>
    #include <vector>
    #include <boost/bind.hpp>

    using namespace std;
    using namespace boost;

    struct Person
    {
        Person(const string& name)
            : name_(name)
        {}
        
        string Name()
        {
            return name_;
        }
        
        string name_;
    };

    int main()
    {
        typedef vector<Person> PersonList;
        
        PersonList personList;
        personList.push_back(Person("Ralph"));
        personList.push_back(Person("Joy"));
        personList.push_back(Person("Martin"));
        
        PersonList::iterator iter =
            find_if(personList.begin(), personList.end(),
                bind(&Person::Name, _1) == "Ralph");
                
        cout << (iter == personList.end() ? "Not found."
                                          : iter->Name().append(" found."))
             << endl;
    }


    如果没有它我们怎么办呢?恕我鲁钝,我还没办法用 bind1st, bind2nd 之类办到同样的事,恐怕你也只有写一个完全没营养的 predicate 来达到目的了。

    当然,用不了几次,你就会问(或者可能多数人是在第一次看到的时候就会问):它是怎么办到的?其实还不如问,如果换了你会怎么做?我们降低条件,想象自己 bind 的作者,在开始的时候舍弃一切细节,只要实现类似 bind1st 或者 mem_fun 的功能,该怎么做?我们把这个 bind 叫做 simple_bind

    STL bind1st 能给一点启示:它接受两个参数,返回一个 functor ,毫无疑问 simple_bind 也得这样干,我们把它返回的 functor 类型叫做 simple_binder

    template <typename R, typename T>
    class simple_binder
    {
    public:
        explicit simple_binder(R (T::*pfn)())
            : pfn_(pfn)
        {}
       
        R operator()(T& t)
        {
            return t.*pfn_();
        }
    private:
        R (T::*pfn_)();
    };


    这个东西跟 mem_fun_ref_t 可以相比,因为毕竟,算法是不变的,无论我们在外面玩什么花样,提供给算法的 functor 类型都必须长成这样。中间的 R (T::*pfn)() 是函数指针定义,的确,C++ 里面的函数指针定义从来就不可爱,所以才有了 boost.function ... 扯远了。

    我们知道了 simple_bind 的返回类型,那么参数呢,第一个参数简单,跟上面的 pfn 是一样的,第二个是什么呢?我们先空起来:

    template <typename R, typename T>
    simple_binder<R, T>
    simple_bind( R (T::*pfn)(), ??? )
    {
        return simple_binder<R, T>(pfn);
    }


    有没有发现什么?在这里我们压根没用到第二个参数,这就给了我们一个极其简单的定义:

    class placeholder{};
    placeholder _1;


    那么现在程序好像是完整了,我们可以这么用:

    #include <iostream>
    #include <string>

    using namespace std;

    struct Person
    {
        Person(const string& name)
            : name_(name)
        {}
       
        string Name()
        {
            return name_;
        }
       
        string name_;
    };

    template <typename R, typename T>
    class simple_binder
    {
    public:
        explicit simple_binder(R (T::*pfn)())
            : pfn_(pfn)
        {}
       
        R operator()(T& t)
        {
            return (t.*pfn_)();
        }
    private:
        R (T::*pfn_)();
    };

    class placeholder{};
    placeholder _1;


    template <typename R, typename T>
    simple_binder<R, T>
    simple_bind( R (T::*pfn)(), placeholder&)
    {
        return simple_binder<R, T>(pfn);
    }

    int main()
    {
        Person person("Ralph");
        cout << simple_bind(&Person::Name, _1)(person) << endl;
    }


    输出是 Ralph ,跟直接调用一样!我们的 simple_bind 起作用了!

    好了,现在让我们再向前一步,如果 Person 加了一个函数 SetName:

        void SetName(string name)
        {
            name_ = name;
        }


    它有一个参数,而我们希望调用的形式类似于 simple_bind(&Person::SetName, string("Joy"), _1) 我们怎么修改 simple_bind 呢?

    首先还是从 simple_binder 入手,由于调用方法不变,我们所要做的只是在 simple_binder 里面保存参数,没错,就跟 binder1st 一样:

    template <typename R, typename T, typename Arg>
    class simple_binder
    {
    public:
        explicit simple_binder(R (T::*pfn)(Arg), const Arg& arg)
            : pfn_(pfn)
            , arg_(arg)
        {}
       
        R operator()(T& t)
        {
            return (t.*pfn_)(arg_);
        }
    private:
        R (T::*pfn_)(Arg);
        Arg arg_;
    };


    接下来的就顺理成章了,simple_bind arg 传递给 simple_binder ,而 placeholder 甚至不需要修改,我们甚至可以把这个 simple_bind 直接喂给 STL 算法:

    #include <algorithm>
    #include <iostream>
    #include <string>
    #include <vector>

    using namespace std;

    struct Person
    {
        Person(const string& name)
            : name_(name)
        {}
       
        string Name()
        {
            return name_;
        }
       
        void SetName(string name)
        {
            name_ = name;
        }
       
        string name_;
    };

    template <typename R, typename T, typename Arg>
    class simple_binder
    {
    public:
        explicit simple_binder(R (T::*pfn)(Arg), const Arg& arg)
            : pfn_(pfn)
            , arg_(arg)
        {}
       
        R operator()(T& t)
        {
            return (t.*pfn_)(arg_);
        }
    private:
        R (T::*pfn_)(Arg);
        Arg arg_;
    };

    class placeholder{};
    placeholder _1;

    template <typename R, typename T, typename Arg>
    simple_binder<R, T, Arg>
    simple_bind( R (T::*pfn)(Arg), const Arg& arg, placeholder&)
    {
        return simple_binder<R, T, Arg>(pfn, arg);
    }

    int main()
    {
        typedef vector<Person> PersonList;
       
        PersonList personList;
        personList.push_back(Person("Ralph"));
        personList.push_back(Person("Joy"));
        personList.push_back(Person("Martin"));
       
        for_each(personList.begin(), personList.end(),
            simple_bind(&Person::SetName, string("Joy"), _1));
       
        cout << personList[0].Name() << endl
             << personList[1].Name() << endl
             << personList[2].Name() << endl;
    }


    输出是三个 Joy

    现在看起来,我们的 simple_binder 稍微有点样子了,但是 boost.bind 允许 placeholder 被放在任何一个位置,而 simple_binder 不允许,但是这个简单,我们有重载:

    template <typename R, typename T, typename Arg>
    simple_binder<R, T, Arg>
    simple_bind( R (T::*pfn)(Arg), const placeholder&, const Arg& arg)
    {
        return simple_binder<R, T, Arg>(pfn, arg);
    }

    加上这个,_1 就可以被随便放在第一个或者第二个位置,例如:

    int main()
    {
        typedef vector<Person> PersonList;
       
        PersonList personList;
        personList.push_back(Person("Ralph"));
        personList.push_back(Person("Joy"));
        personList.push_back(Person("Martin"));
       
        for_each(personList.begin(), personList.end(),
            simple_bind(&Person::SetName, string("Joy"), _1));
           
        for_each(personList.begin(), personList.end(),
            simple_bind(&Person::SetName, _1, string("Ralph")));
       
        cout << personList[0].Name() << endl
             << personList[1].Name() << endl
             << personList[2].Name() << endl;
    }

    输出是三个 Ralph 。提醒一下,boost.bind 当然不是用这个办法,要不然还不用等到参数数量到10个,到了3个我们就要累死。simple_bind 的目的只是在于探索,用最容易理解的方式达成目的,如果说 boost 是一座拆除了脚手架、精雕细琢的圣殿,simple_bind 就是尝试把脚手架再搭起来。

    回顾一下,现在我们已经能比较好地解决一个参数的成员函数调用了,现在看看两个参数。我们从目的入手,希望达到的效果是这样:

        Person person("Ralph");
        simple_bind(&Person::SetName, _1, _2)(person, string("Martin"));

    首先,这里有了一个新的符号 _2 ,这很简单

    placeholder _2;

    然后 simple_bind 也顺利成章的又增加了一个重载:

    template <typename R, typename T, typename Arg>
    simple_binder<R, T, Arg>
    simple_bind( R (T::*pfn)(Arg), const placeholder&, const placeholder&)
    {
        return simple_binder<R, T, Arg>(pfn);
    }

    注意,这个 simple_binder 构造函数只有一个参数,而它还没写出来,我们加上一个:

        // new simple_binder ctor: 1 argument
        explicit simple_binder(R (T::*pfn)(Arg))
            : pfn_(pfn)
        {} 

    在这种情况下,两个参数都是由调用者直接提供给 simple_binder 的,这就意味着我们还要重载一次 simple_binder::operator()

        R operator()(T& t, const Arg& arg)
        {
            return (t.*pfn_)(arg);
        }   

    好了,这下子都解决了,完整程序如下:

    #include <algorithm>
    #include <iostream>
    #include <string>
    #include <vector>

    using namespace std;

    struct Person
    {
        Person(const string& name)
            : name_(name)
        {}
       
        string Name()
        {
            return name_;
        }
       
        void SetName(string name)
        {
            name_ = name;
        }
       
        string name_;
    };

    template <typename R, typename T, typename Arg>
    class simple_binder
    {
    public:
        explicit simple_binder(R (T::*pfn)(Arg))
            : pfn_(pfn)
        {}
       
        explicit simple_binder(R (T::*pfn)(Arg), const Arg& arg)
            : pfn_(pfn)
            , arg_(arg)
        {}
       
        R operator()(T& t)
        {
            return (t.*pfn_)(arg_);
        }

        R operator()(T& t, const Arg& arg)
        {
            return (t.*pfn_)(arg);
        }   
    private:
        R (T::*pfn_)(Arg);
        Arg arg_;
    };

    class placeholder{};
    placeholder _1, _2;

    template <typename R, typename T, typename Arg>
    simple_binder<R, T, Arg>
    simple_bind( R (T::*pfn)(Arg), const Arg& arg, const placeholder&)
    {
        return simple_binder<R, T, Arg>(pfn, arg);
    }

    template <typename R, typename T, typename Arg>
    simple_binder<R, T, Arg>
    simple_bind( R (T::*pfn)(Arg), const placeholder&, const Arg& arg)
    {
        return simple_binder<R, T, Arg>(pfn, arg);
    }

    template <typename R, typename T, typename Arg>
    simple_binder<R, T, Arg>
    simple_bind( R (T::*pfn)(Arg), const placeholder&, const placeholder&)
    {
        return simple_binder<R, T, Arg>(pfn);
    }

    int main()
    {
        typedef vector<Person> PersonList;
       
        PersonList personList;
        personList.push_back(Person("Ralph"));
        personList.push_back(Person("Joy"));
        personList.push_back(Person("Martin"));
       
        for_each(personList.begin(), personList.end(),
            simple_bind(&Person::SetName, string("Joy"), _1));
           
        for_each(personList.begin(), personList.end(),
            simple_bind(&Person::SetName, _1, string("Ralph")));
       
        Person person("Ralph");
        simple_bind(&Person::SetName, _1, _2)(person, string("Martin"));
       
        cout << personList[0].Name() << endl
             << personList[1].Name() << endl
             << personList[2].Name() << endl;
        cout << person.Name() << endl;
    }

    输出是:

    Ralph
    Ralph
    Ralph
    Martin

    虽然实现丑陋了点,但是达到目的了,不是么?好了,现在新的问题来了,我们还希望可以这样:

        simple_bind(&Person::SetName, _2, _1)(string("Joy"), person));

    其实从这里可以看出来,在前面的实现中,placeholder 没有发挥任何作用,是真正的 placeholder ,然而在这里不同,我们至少希望 placeholder 能够携带某些编译期信息,以便让我们能在调用决策中选择相应的参数,换句话说,我们希望 placeholder _1 _2 除了名字不同,还能让编译器看起来有所区别,当然最简单的想法是:

    class placeholder1{};
    class placeholder2{};
    placeholder1 _1;
    placeholder2 _2;

    然而模仿 MCD IntToType 技术,这样是不是更优雅呢:

    template <int I>
    class placeholder{};
    placeholder<1> _1;
    placeholder<2> _2;

    boost.bind 里面就是这么干的。有了这个,就可以依照 placeholder 顺序来重载:

    template <typename R, typename T, typename Arg>
    simple_binder<R, T, Arg>
    simple_bind( R (T::*pfn)(Arg), const placeholder<1>&, const placeholder<2>&)
    {
        return simple_binder<R, T, Arg>(pfn);
    }

    template <typename R, typename T, typename Arg>
    simple_binder<R, T, Arg>
    simple_bind( R (T::*pfn)(Arg), const placeholder<2>&, const placeholder<1>&)
    {
        //
    返回什么呢?
    }

    返回什么呢?我们可以定义一个 simple_binder2 ,它在 operator() 里面会交换其参数的位置,也可以修改 simple_binder,让它根据某些标志来干这件事……

    我们还是就此打住吧!

    不用我说你也看得出来,这种暴力法很 快就会让代码规模和程序复杂度膨胀到无法收拾的地步,是时候检讨我们的设计了。我们原先的设计其实是从最简单的情况出发,在需求增多的时候,我们用蛮力加 以扩展,然而当规模进一步扩大,设计本身的局限就开始显现出来,现在应该从原先的设计中提取抽象,考虑更加灵活的设计了。

    (待续...
  • 相关阅读:
    超分网络一般不需要BN
    常用的损失函数loss
    VS Code导入torch后出现红色下划线警告
    光流算法:Horn–Schunck光流讲解
    网络训练loss不下降的问题
    l1,l2loss的比较
    Nvidia DALI加速数据增强
    pytorch将部分参数进行加载
    模型训练技巧
    node 特性及优缺点
  • 原文地址:https://www.cnblogs.com/adylee/p/2067758.html
Copyright © 2011-2022 走看看