C ++不鼓励收集基类 - 无论如何都要伪造它?

时间:2017-03-09 07:32:37

标签: c++ c++11 templates containers

(Java)Collection in C++没有类似的概念。

我能理解其中的原因,但我想知道是否有任何方法可以优雅地

实施例

我已经实施了许多自定义Collection 它们都Iterator正常工作,类似于std::vectorstd::unordered_set等。

他们是MyArray<T>MyBinaryTree<T>MySet<T>

在这里,我将展示一个工作代码,显示我想要伪造它的位置。

我们说我有2个级别的程序:库和用户 只做一件事 - User命令Library来吃掉桶中的所有Orange*

Library.h

class Library{
    public: static void eatAll(const MyArray<Orange*>& bucket);
};

Library.cpp

#include "Orange.h"
void Library::eatAll(const MyArray<Orange*>& bucket){
    for(auto orange:bucket){
        orange->eaten();
    }
}

User.h

MyArray<Orange*> bucket;
Library::eatAll(bucket);    

没关系。

现在,我希望Library::eatAll也支持MyBinaryTree<Orange*>,我有一些不太理想的方法,如下所示。

我的解决方案

1。 Java方式

  • MyBinaryTree<T>MyArray<Orange*>(及其迭代器)继承自新的类Collection<T>(和CollectionIterator<T>)。
  • 将签名更改为Library::eatAll(const Collection<T>&)

缺点:来自&#34;虚拟&#34;的性能损失Collection<T>中的一些函数。

2。模板v1

//Library.h
template<class T> void eatAll(const T&t ){
    for(auto orange : t){
        orange->eaten();
    }
}
  • 使eatAll成为模板功能

缺点: eatAll的实施必须在标题中 我必须在#include orange.hLibrary.h 有时,我真的想转发声明。

3。模板v2

//Library.h
template<class T> void eatAll(const T&t ){
    for(auto orange : t){
        eatAnOrange(orange)
    }
}
private: void eatAnOrange(Orange* orange){
    //below implementation is inside Library.cpp
    orange->eaten();
}
  • 创建一个中间人函数eatAnOrange

缺点:

  • 代码不太可读,不简洁,导致一点可维护性问题。
  • 如果有很多其他功能,例如squeezeAll(),我可能需要创建很多中间函数,例如squeezeAnOrange()

4。运算符=()

通过隐式构造函数在3个集合类中创建转换器 缺点:创建新的集合实例会带来性能。

//Here is what it will do, internally (roughly speaking)
MyBinaryTree<Orange*> bucket;
Library::eatAll(MyArray<Orange*>(bucket)); 

我相信我的解决方案不够优雅 是否有任何解决方案不会遭受上述劣势?

修改
目前的答案都比我的方法更优雅(谢谢!),但仍有缺点: - - Oliv在#include "orange.h"中需要User.h - 理查德·霍奇斯的虚拟函数调用。

2 个答案:

答案 0 :(得分:7)

在C ++中,使用迭代器设计模式遍历集合。整个STL是围绕这个概念设计的。它可能符合您的需求:

您可以将eatAll定义为接受两个迭代器的函数:

template<class Iterator,class Sentinel>
void eatAll(Iterator it, Sentinel s){
    for (;it!=s;++it)
      it->eaten();
}

范围,如算法界面:

template<class Range>
void eatAll(Range& r){
    for (auto& v:r)
      v.eaten();
}

您必须将二叉树定义为范围(它必须实现begin()end())。希望树木是一种可以线性化的图形。然后,所有智能工作都将进入迭代器实现!

答案 1 :(得分:2)

如果你想让它真正具有多态性,那么我们必须处理两件事:

  1. 容器的实际类型

  2. 取消引用地图的结果是一对包含键和值引用的事实。

  3. 我的观点是,对此的答案不是从容器派生,这是限制,而是创建一个多态“值迭代器”,它模拟所有迭代器并正确提取它们的值。

    然后我们可以编写这样的代码:

    vector:
    Orange
    Orange
    
    map:
    Orange
    Orange
    Orange
    

    要得到这个:

    #include <typeinfo>
    #include <memory>
    #include <iostream>
    #include <vector>
    #include <map>
    #include <iterator>
    
    // define an orange
    struct Orange {
    };
    
    // a meta-function to get the type of the value of some iterated value_type    
    template<class ValueType> struct type_of_value
    {
        using type = ValueType;
    };
    
    // specialise it for maps and unordered maps
    template<class K, class V> struct type_of_value<std::pair<K, V>>
    {
        using type = V;
    };
    
    template<class ValueType> using type_of_value_t = typename type_of_value<ValueType>::type;
    
    // function to extract a value from an instance of a value_type    
    template<class ValueType> struct value_extractor
    {
        template<class V>
        auto& operator()(V&& v) const {
            return v;
        }
    };
    
    // specialised for maps    
    template<class K, class V> struct value_extractor<std::pair<K, V>>
    {
        template<class Arg>
        auto& operator()(Arg&& v) const {
            return std::get<1>(v);
        }
    };
    
    template<class Iter>
    auto extract_value(Iter const& iter) ->  auto&
    {
        using value_type = typename std::iterator_traits<Iter>::value_type;
        auto e = value_extractor<value_type> {};
        return e(*iter);
    }
    
    // a polymorphic (forward only at the moment) iterator
    // which delivers the value (in the case of maps) or the element (every other container)
    template<class ValueType>
    struct PolymorphicValueIterator {
    
        using value_type = type_of_value_t<ValueType>;
    
    private:
        struct iterator_details {
            std::type_info const &type;
            void *address;
        };
    
        struct concept {
    
            virtual std::unique_ptr<concept> clone() const = 0;
    
            virtual value_type& invoke_deref() const = 0;
    
            virtual void invoke_next(std::size_t distance = 1) = 0;
    
            virtual iterator_details get_details() = 0;
    
            virtual bool is_equal(const iterator_details &other) const = 0;
    
            virtual ~concept() = default;
    
        };
    
        template<class Iter>
        struct model final : concept {
    
            model(Iter iter)
                    : iter_(iter)
            {}
    
            std::unique_ptr<concept> clone() const override
            {
                return std::make_unique<model>(iter_);
            }
    
    
            virtual value_type& invoke_deref() const override {
                return extract_value(iter_);
            }
    
            void invoke_next(std::size_t distance = 1) override
            {
                iter_ = std::next(iter_, distance);
            }
    
            iterator_details get_details() override {
                return {
                        typeid(Iter),
                        std::addressof(iter_)
                };
            }
    
            bool is_equal(const iterator_details &other) const override {
                if (typeid(Iter) != other.type) {
                    return false;
                }
                auto pother = reinterpret_cast<Iter const*>(other.address);
                Iter const& iother = *pother;
                return iter_ == iother;
            }
    
            Iter iter_;
        };
    
    
        std::unique_ptr<concept> concept_ptr_;
    
    public:
        bool operator==(PolymorphicValueIterator const &r) const {
            return concept_ptr_->is_equal(r.concept_ptr_->get_details());
        }
    
        bool operator!=(PolymorphicValueIterator const &r) const {
            return not concept_ptr_->is_equal(r.concept_ptr_->get_details());
        }
    
        PolymorphicValueIterator &operator++() {
            concept_ptr_->invoke_next(1);
            return *this;
        }
    
        value_type& operator*() const {
            return concept_ptr_->invoke_deref();
        }
    
        template<class Iter>
        PolymorphicValueIterator(Iter iter)
        {
            concept_ptr_ = std::make_unique<model<Iter>>(iter);
        }
    
        PolymorphicValueIterator(PolymorphicValueIterator const& r)
                : concept_ptr_(r.concept_ptr_->clone())
        {}
    
        PolymorphicValueIterator& operator=(PolymorphicValueIterator const& r)
        {
            concept_ptr_ = r.concept_ptr_->clone();
            return *this;
        }
    
    };
    
    template<class Iter>
    auto makePolymorphicValueIterator(Iter iter)
    {
        using iter_value_type = typename std::iterator_traits<Iter>::value_type;
        using value_type = type_of_value_t<iter_value_type>;
        return PolymorphicValueIterator<value_type>(iter);
    }
    
    // a test
    void do_orange_things(PolymorphicValueIterator<Orange> first, PolymorphicValueIterator<Orange> last)
    {
        while(first != last) {
            std::cout << "Orange\n";
            ++first;
        }
    }
    
    int main()
    {
    
        std::vector<Orange> vo {
                Orange(), Orange()
        };
    
        std::map<int, Orange> mio {
                { 1, Orange() },
                { 2, Orange() },
                { 3, Orange() }
        };
    
        std::cout << "vector:\n";
        auto first = makePolymorphicValueIterator(vo.begin());
        auto last = makePolymorphicValueIterator(vo.end());
        do_orange_things(first, last);
    
        std::cout << "\nmap:\n";
        first = makePolymorphicValueIterator(mio.begin());
        last = makePolymorphicValueIterator(mio.end());
        do_orange_things(first, last);
    }
    

    这是一个最小的,完整的实现:

    {{1}}