是否可以将虚函数和函数对象混合使用std算法?

时间:2014-01-18 05:41:42

标签: c++ algorithm stl polymorphism functor

例如,有一个行李系统:

class BagBase
{
public:
    typedef std::vector<BagObj*> ObjVec;
    virtual ObjVec FindObjs(const FinderBase& finder);
protected:
    ObjVec Objs;
}

class Bag1 : public BagBase
{
public:
    typedef std::vector<MyBagObj*> MyObjVec;
    virtual ObjVec FindObjs(const FinderBase& finder);
// ignore ObjVec Objs in BagBase, 
// store objects with another type (with more information)
// but you can still extract BagObj* from MyObjs through FindObjs
private:
    MyObjVec MyObjs;
}

class Bag2 : public BagBase
{
// just use FindObjs defined in BagBase
}

我想使用FindObjs实施std::find_if, 例如:

struct FinderBase
{
    virtual bool operator()(BagObj* o);
};

struct Finder1 : public FinderBase
{
    virtual bool operator()(BagObj* o);
};

// and Finder2, Finder3, so on, each target to find by different conditions

实施virtual ObjVec BagBase::FindObjs(const FinderBase& finder);之类的:

virtual ObjVec BagBase::FindObjs(const FinderBase& finder)
{
    ObjVec results;
    // a while loop execute std::find_if each time
    // below is just a concept
    while (...)
    {
        ObjVec::iterator it = std::find_if(Objs.begin(), Objs.end(), finder);
        if (it != Objs.end())
        {
            results.push_back(*it);
        }
        ++it;
    }
}

因为Bag1使用其他类型的向量,它可能看起来像:

virtual ObjVec Bag1::FindObjs(const FinderBase& finder)
{
    ObjVec results;
    while (...)
    {
        // the difference: MyObjVec
        MyObjVec::iterator it = std::find_if(Objs.begin(), Objs.end(), finder);
        if (it != Objs.end())
        {
            // extract Obj* from MyObj*
            MyObj* my_obj = (*it);
            Obj* obj = my_obj->obj;
            results.push_back(obj);
        }
        ++it;
    }
}

问题是,std::find_if复制finder, 所以它确实知道它的实际类型(它可能是Finder1Finder2), 它只是复制,好像它是一个FinderBase对象。

virtual FindObjs是必须的,因为vector种类不同,所以我无法通过finder使用template,但是这与std::find_if的需要直接相矛盾,后者无法推断出finder的实际类型。

那么这个问题的更好解决方案是什么? 可以混合使用virtualtemplate这个东西吗? 谢谢!

P.S。需要C ++ 03的解决方案,谢谢!

2 个答案:

答案 0 :(得分:3)

finder对象包裹在reference_wrapper中。 refcref可用于创建一个。

MyObjVec::iterator it = std::find_if(Objs.begine(), Objs.end(), std::ref(finder));

如果你使用的是前C ++ 11编译器,那就是Boost.Ref

编辑:
正如评论中指出的那样,只需将std::ref替换为boost::ref即可。这是因为与boost::reference_wrapper不同,std::reference_wrapper缺少operator()。仅当std::reference_wrapper包装可调用对象时,此运算符才会参与重载解析,从而允许它以可调用的方式透明地运行。

要从Boost获得相同的功能,我们需要使用boost::bind来允许调用boost::reference_wrapper中包含的可调用对象。

MyObjVec::iterator it = std::find_if(Objs.begine(), Objs.end(), 
                                     boost::bind<bool>(boost::ref(finder), _1));

Live demo显示std::refboost::ref的使用情况。

答案 1 :(得分:0)

我不确定我是否完全理解这个问题,但这是我从阅读中得到的...我不确定为什么std::refstd::cref也需要.. std::find_if似乎与多态对象和虚函数一起正常工作。

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

struct Object
{
    void print() {std::cout<<"Object\n";}
    virtual ~Object(){};
};

struct Integer : public Object
{
    void print() {std::cout<<"Integer\n";}
    virtual ~Integer(){}
};

struct Float : public Object
{
    void print() {std::cout<<"Float\n";}
    virtual ~Float(){}
};

struct FinderBase
{
    virtual bool operator()(Object* obj){return obj;}
};

struct Finder : public FinderBase
{
    virtual bool operator()(Object* obj){return obj;}
};

class Parent
{
    protected:
        std::vector<Object*> Objs;

    public:
        Parent() {Objs.push_back(new Object());}
        virtual ~Parent() {delete Objs.back();}

        virtual void find(FinderBase* finder);
};

void Parent::find(FinderBase* finder)
{
    auto it = std::find_if(Objs.begin(), Objs.end(), *finder);
    if (it != Objs.end())
    {
        typename decltype(Objs)::value_type obj = *it;
        obj->print();
    }
}


class Brother : public Parent
{
    protected:
        std::vector<Integer*> Objs;

    public:
        Brother() {Objs.push_back(new Integer());}
        virtual ~Brother() {delete Objs.back();}

        virtual void find(FinderBase* finder);
};

void Brother::find(FinderBase* finder)
{
    auto it = std::find_if(Objs.begin(), Objs.end(), *finder);
    if (it != Objs.end())
    {
        typename decltype(Objs)::value_type b = *it;
        b->print();
    }
}


class Sister : public Parent
{
    protected:
        std::vector<Float*> Objs;

    public:
        Sister() {Objs.push_back(new Float());}
        virtual ~Sister() {delete Objs.back();}

        virtual void find(FinderBase* finder);
};

void Sister::find(FinderBase* finder)
{
    auto it = std::find_if(Objs.begin(), Objs.end(), *finder);
    if (it != Objs.end())
    {
        typename decltype(Objs)::value_type s = *it;
        s->print();
    }
}

int main()
{
    Parent P;
    Brother B;
    Sister S;

    Finder finder;
    P.find(&finder);
    B.find(&finder);
    S.find(&finder);

    std::cout<<"\n\n";

    Parent* ptr = new Parent();
    ptr->find(&finder);
    delete ptr;

    Parent* Bptr = new Brother();
    Bptr->find(&finder);
    delete Bptr;

    Parent* Sptr = new Sister();
    Sptr->find(&finder);
    delete Sptr;

    return 0;
}

打印:

Object
Integer
Float


Object
Integer
Float

您可以尝试一下:http://ideone.com/2zQmT0