如何一般地处理不同的C ++容器?

时间:2013-11-07 16:05:39

标签: c++ templates iterator polymorphism

场景(请参阅下面的代码以供参考):

  1. 原始(Base)实现必须有func1()返回一个列表。在内部,它会调用合并和拼接。
  2. 后续(派生)实现必须有func1()返回一个向量。它需要随机访问。
  3. func2()对于两个实现都是通用的,只需要一个前向迭代器。

    #include <iostream>
    #include <list>
    #include <vector>
    
    
    class Base {
    protected:
        virtual void func1(std::list<int>& l /* out parameter */) {
            // This must use list. Calls merge and splice.
            std::cout << "list version of func1 in base\n";
        }
    
        virtual void func1(std::vector<int>& v) {
            // This should never be called, but code won't compile without it.
            std::cout << "vector version of func1 in base\n";
        }
    
        template <class T> void func2(T container) {
            typename T::const_iterator it = container.cbegin();
            // Iterate and perform work. Common to both Base and Derived.
            std::cout << "func2 in base\n";
        }
    
        template <class T> void processHelper() {
            T container;
            func1(container);
            func2<T>(container);
        }
    
    public:
        virtual void process() {
            processHelper<std::list<int> >();
        }
    
    };
    
    class Derived : public Base {
    protected:
        virtual void func1(std::vector<int>& v /* out parameter */) {
            // This must use a random access container.
            std::cout << "Vector version of func1 in derived\n";
        }
    
    public:
        virtual void process() {
            processHelper<std::vector<int> >();
        }
    };
    
    int main(int argc, const char * argv[])
    {
        std::vector<int> var;
        Derived der;
        der.process();
    
        //std::list<int> var;
        //Base bs;
        //bs.process();
    
        std::cout << "done\n";
    }
    
  4. 目标:

    1. 没有(或极少)重复(剪切和粘贴)代码。
    2. 避免使用Boost进行编译。 (还没需要它。不想这样做。)这排除了几个any_iterator的实现。
    3. 问题:

      C ++中是否有更好的OO设计来实现我的目标?在从func1()返回之前,我有理由不想将列表转换为向量,反之亦然。具体来说,此时列表很大,我宁愿不招致额外的副本。我本可以设计func1()来返回opaque_iterator http://www.mr-edd.co.uk/code/opqit,但是却引入了一个未知的头文件犹豫不决。

      在任何情况下,这个问题都有自己的学术生活。这个问题在Java中非常容易,因为集合实现了通用接口,但在C ++中似乎具有挑战性。特别困扰的是必须实现Base :: func1(std :: vector&amp; v)才能获得编译代码,即使没有执行路径可以调用此函数。希望有一种更简单的方法,我只是没有看到更简单的解决方案。

3 个答案:

答案 0 :(得分:0)

C ++方法正在使用迭代器。你可以用标准算法做任何事情。图书馆故意分开 容器&lt; - &gt;迭代器&lt; - &gt;算法

容器定义迭代器(基本上是美化指针),算法与迭代器一起使用。容器和算法彼此之间并不相同。

通常你会传递几个迭代器(通常是container.begin()container.end()),算法将按照这些来实现。

查看标准algorithms,看看您是否可以针对自己想做的事情找到解决方案。要做到这一点,你的函数应该在迭代器而不是容器上进行模板化。

希望有所帮助。

答案 1 :(得分:0)

通用的方法是让func1获取输出迭代器:

template<class OutputIterator> void func1(OutputIterator &&out) {
    :

然后在要用于输出的容器上使用back_insert_iterator调用它:

std::list<int> tmp;
obj->func1(std::back_inserter(tmp));

答案 2 :(得分:0)

我最终在同一行找到了很多问题,其中一些问题在Stack Overflow上。所以也许这是重复的。如果是这样,道歉。以下是一些相关链接:

How to write a function that takes an iterator or collection in a generic way?

Generic iterator

http://www.artima.com/cppsource/type_erasure.html

我最后采用了一种简单的类型擦除方法,模仿了本文:http://www.cplusplus.com/articles/oz18T05o/我不能声称理解这里发生的一切,但确实有效。唯一的缺点是我必须在我的Container类中包装迭代器API并返回所有原语和众所周知的类,而不是直接暴露底层迭代器。所以我的Container包装器不是非常可重用的。

我已经发布了我在下面写的代码,希望它对其他人有用:

    #include <iostream>
    #include <list>
    #include <vector>

    // Type erasure for returning different std containers based off of: http://www.cplusplus.com/articles/oz18T05o/
    class Container {
    protected:
        class IContainer {
        public:
            virtual ~IContainer() {}

            virtual void setBegin() = 0;
            virtual bool isEnd() = 0;
            virtual int get() = 0;
            virtual void next() = 0;
        };

        template <typename T> class ContainerModel : public IContainer {
        public:
            ContainerModel(const T& container_) : m_container(container_) {}
            virtual ~ContainerModel() {}

            virtual void setBegin() {
                m_cit = m_container.cbegin();
            }
            virtual bool isEnd() {
                return (m_cit == m_container.cend());
            }
            virtual int get() {
                return *m_cit;
            }
            virtual void next() {
                ++m_cit;
            }

        protected:
            T m_container;
            typename T::const_iterator m_cit;
        };

        std::shared_ptr<IContainer> m_spContainer;

    public:
        template <typename T> Container(const T& t_) : m_spContainer(new ContainerModel<T>(t_)) {}
        virtual ~Container() {}

        virtual void setBegin() {
            m_spContainer->setBegin();
        }
        virtual bool isEnd() {
            return m_spContainer->isEnd();
        }
        virtual int get() {
            return m_spContainer->get();
        }
        virtual void next() {
            m_spContainer->next();
        }
    };

    class Base {
    protected:
        virtual Container func1() {
            std::cout << "list version of func1 in base\n";
            std::list<int> l;
            // Do lots of stuff with lists. merge(), splice(), etc.
            return Container(l);
        }

        virtual void func2(const Container& container) {
            // Iterate using setBegin(), get(), next() and isEnd() functions.
            std::cout << "func2 in base\n";
        }

    public:
        virtual void process() {
            Container container = func1();
            func2(container);
        }

    };

    class Derived : public Base {
    protected:
        virtual Container func1() {
            std::cout << "Vector version of func1 in derived\n";
            std::vector<int> v;
            // Do lots of stuff with vector's random access iterator.
            return Container(v);
        }
    };

    int main(int argc, const char * argv[])
    {
        Derived der;
        der.process();

        //Base bs;
        //bs.process();

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