0

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

  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) 的丑陋,即使没有执行路径会调用这个函数。希望有一种更简单的方法,我只是没有看到更直接的解决方案。

4

3 回答 3

0

通用方法是func1采用输出迭代器:

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

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

std::list<int> tmp;
obj->func1(std::back_inserter(tmp));
于 2013-11-07T17:15:49.010 回答
0

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

如何编写一个以通用方式获取迭代器或集合的函数?

泛型迭代器

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";
    }
于 2013-11-08T14:30:41.957 回答
0

C++ 方式是使用迭代器。你可以用标准算法做几乎任何事情。该库在 Containers <--> Iterators <--> Algorithms 上有意分离

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

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

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

希望有帮助。

于 2013-11-07T16:25:25.917 回答