【问题标题】:How to handle different C++ containers generically?如何一般处理不同的 C++ 容器?
【发布时间】:2013-11-07 16:05:39
【问题描述】:

场景(参考下面的代码):

  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";
    }
    

目标:

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

问题:

C++ 中是否有更好的面向对象设计来实现我正在做的事情?在从 func1() 返回之前,我有理由不想将列表转换为向量,反之亦然。具体来说,此时列表很大,我不希望产生额外的副本。我本可以将 func1() 设计为返回 opaque_iterator http://www.mr-edd.co.uk/code/opqit,但对引入未知的头文件犹豫不决。

无论如何,这个问题都有其自己的学术生命。这个问题在 Java 中很容易,因为集合实现了通用接口,但在 C++ 中似乎具有挑战性。尤其是为了让代码编译而不得不实现 Base::func1(std::vector& v) 的丑陋,即使没有执行路径会调用这个函数。希望有更简单的方法,我只是没有看到更直接的解决方案。

【问题讨论】:

    标签: c++ templates iterator polymorphism


    【解决方案1】:

    C++ 方式是使用迭代器。你可以用标准算法做几乎任何事情。图书馆是故意分开的 容器 迭代器 算法

    容器定义了迭代器(基本上是美化的指针),算法与迭代器一起工作。容器和算法彼此未知。

    通常你会传递几个迭代器(通常是container.begin()container.end()),算法将根据它们来实现。

    看看标准algorithms,看看你能不能想出你想做的解决方案。为此,您的函数应该在迭代器上而不是在容器上进行模板化。

    希望对您有所帮助。

    【讨论】:

      【解决方案2】:

      一般的方法是让func1 接受一个输出迭代器:

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

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

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

      【讨论】:

        【解决方案3】:

        我最终发现了很多类似的问题,其中一些是在 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/ 我不能声称理解这里发生的一切,但它确实有效。唯一的缺点是我必须将迭代器 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";
            }
        

        【讨论】:

          猜你喜欢
          • 2010-12-10
          • 2011-04-29
          • 1970-01-01
          • 2022-10-14
          • 2012-01-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-06-29
          相关资源
          最近更新 更多