【问题标题】:Define an iterator type in a stl container wrapper在 stl 容器包装器中定义迭代器类型
【发布时间】:2017-08-11 21:43:30
【问题描述】:

围绕 STL 容器编写包装类的正确方法是什么,它也是一个模板(可以接受泛型类型 T 作为元素)并允许我像直接使用 STL 容器一样使用迭代器?

我想做以下类型的事情

#include <list>
#include <iostream>

class MyClass{};

template<class T>
class Wrapper
{
    public:
        typename std::list<T>::iterator iterator;

        std::list<T> elements;
        iterator begin(){ return elements.begin(); };
        iterator end(){ return elements.end(); };
};

int main()
{
    Wrapper<MyClass> wrapper;

    for (Wrapper::iterator it = wrapper.begin(); it != wrapper.end(); ++it)
        std::cout<<"Hi"<<std::endl;
}

但是编译器说:

 error: ‘iterator’ in ‘class Wrapper<T>’ does not name a type

【问题讨论】:

  • typedef typename std::list&lt;T&gt;::iterator iterator;using iterator = typename std::list&lt;T&gt;::iterator; 。正如所写,iterator 是数据成员,而不是成员类型。

标签: c++ stl containers wrapper


【解决方案1】:

你有两个错误。

就像 Igor Tandetnik 在评论中所说,您的 iterator 是数据成员,而不是您案例的嵌套类型。

你必须在课堂上这样做:

typedef typename std::list<T>::iterator iterator;

或者,在 C++11 中:

using iterator = typename std::list<T>::iterator;

另外,您在main() 代码中使用了错误的iterator。应该是这样的:

Wrapper<MyClass>::iterator it = wrapper.begin()

或者,在 C++ 11 或更高版本中,您可以这样做:

for(const auto &element : wrapper) {
    ...
}

就个人而言,我更喜欢使用私有继承而不是封装。在我看来,公共继承意味着“IS A”关系,而私有继承意味着“在期限内实现”关系。

你可以这样做:

template<typename T>
class WrapperList : private List<T> {
    ... Your code that belongs to your wrapper
}

【讨论】:

  • 从标准容器继承通常是一个(非常)糟糕的主意。
【解决方案2】:

如果您想包装 std::list 并通过一些功能来丰富它,并且本质上还要在包装器中维护一些(或大部分)std::list 接口(作为迭代功能),那么有(在至少)两种可能性。

一个(组合)是在包装器中定义 begin()、end() 方法和迭代器类型,以便将它们传播到包含的结构,正如 Andrea Araldo 所建议的那样。

其中一个(私有继承)是利用私有继承,这在某些情况下非常方便。所需步骤:

例如,在下面对这个意义上的提议的 sn-p (WrapperPrivInh) 进行了轻微修改。在这种情况下,std::list 的所有标准构造函数都可以在包装接口中免费使用。 此外,为了完整性,还提出了 WrapperCompos 变体,其中列表包含在包装器中,它公开了支持基于迭代器的循环所需的内容。在这种情况下,只有“默认元素数量”构造函数是从头开始重新定义的。

#include <list>
#include <iostream>

class MyClass
{ 
public:
    MyClass() { std::cout<<"(DEBUG)MyClass::default constructor\n"; }
};

template<class T>
class WrapperPrivInh : private std::list<T>
{
    public:
        using std::list<T>::iterator;  //for the iterator type
        using std::list<T>::begin;     // for the begin()
        using std::list<T>::end;       //  and end() methods
        using std::list<T>::list;      // for the constructors, if needed

        //std::list<T> elements;
        //iterator begin(){ return elements.begin(); };
        //iterator end(){ return elements.end(); };
};

template<class T>
class WrapperCompos 
{
    std::list<T> m_list;

    public:
        using iterator= typename std::list<T>::iterator;  //for the iterator type

        WrapperCompos(int const n) : m_list(n) { }

        iterator begin(){ return m_list.begin(); };
        iterator end(){ return m_list.end(); };
};


int main()
{
    {
        std::cout<<"Experiment with private inheritance"<<'\n';
        WrapperPrivInh<MyClass> wrapper(3);  // constructor witch builds N (3 here) MyClass default elements (derived "for free" from std::list)

        for(WrapperPrivInh<MyClass>::iterator it = wrapper.begin(); it != wrapper.end(); ++it)
            std::cout<<"Hi "<<&(*it)<<std::endl;
    }

    {
        std::cout<<"\nExperiment with private inheritance"<<'\n';
        WrapperCompos<MyClass> wrapper(3);  // constructor witch builds N (3 here) MyClass default elements (derived "for free" from std::list)

        for(WrapperCompos<MyClass>::iterator it = wrapper.begin(); it != wrapper.end(); ++it)
            std::cout<<"Hi "<<&(*it)<<std::endl;
        for( auto const& x : wrapper )  //range-for-loop syntax (from c++11)
            std::cout<<"Hi2 "<<&x<<std::endl;
    }
}

私有继承方法很有趣,可以轻松、正确地将包装结构的特征传播到包装器。例如,如果包装的结构与它们兼容,那么包装器可以很容易地与 std::algorithms 兼容,就像 std::list 一样。 希望这可以帮助。 最好的

【讨论】:

  • 从标准容器派生——无论是否私下——也被认为是不好的做法。有大量信息可以解释原因。
  • 在这种特定情况下,私有继承的主要缺点是什么,主要目标似乎是包装 std::list 以添加一些功能并至少重用 std::list 的一部分公共接口?
  • stackoverflow.com/questions/6806173/…阅读ex0du5(它有50分赏金)的答案的后半部分。
  • 这样的帖子似乎更专注于公共继承,我非常同意。它从未提及私有继承,并且大多数建议更适合公共继承。当然,私有继承有其自身的缺陷,但在这样的答案中强调的主要关键点不会显着影响私有继承。对于那里提出的大多数问题,私有继承的行为类似于组合(用 Scott Meyers 的话来说,“是根据”来实现的),这是在这篇文章中提出的。
  • 该答案后半部分的问题适用于继承,无论(在 C++ 中)是公共的、私有的还是受保护的。您正在考虑在您的班级中重用标准容器的难易程度,而我正在考虑其他人重用您班级中的功能的能力。从一个不打算用作基类的类继承会阻碍对类功能的重用,因为它背离了一些支持重用的原则,例如开闭、liskov 替换等。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-16
  • 2013-02-25
  • 2013-03-07
  • 2011-03-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多