多态性中的通用STL迭代器



我对C++有点陌生,我有一个抽象类(纯虚拟),它有两个类继承自它。每个派生类只在不同的STL容器中保存相似的数据(数组与映射)我重载基类的运算符,以便执行例如STL元素的添加。我希望能够迭代STL的元素,无论它是什么类型

我已经在通用迭代器和类似的东西上搜索了几天,但什么都找不到。

我没有太多的代码可以共享,因为我什么都做不了。我想也许让我的基类保存一个表示STL容器的模板变量,然后在运算符中获取它的迭代器,但同样,不确定如何。

protected:
    template<class T>
    T gStlContainer;

然后访问

gStlContainer::iterator<double> it;

显然没用。

有什么建议吗?谢谢

编辑:我正在编辑,试图用一个例子更好地解释。我在基类(抽象)中重载+运算符,我希望它对STL容器的每个元素进行迭代,并将其添加到另一个元素中,例如,假设我有这个数组作为STL

arr = [0,1,2,3] // Say it's stored in Derived2 class
arr = [4,5,6,7] // Say it's stored in Derived3 class

这些数组中的每一个都存储在一个派生类中。当我做时

Derived1 = Derived2 + Derived3;

则Derived1将持有

arr = [4,6,8,10]

希望现在更清楚一点。问题是,它并不总是一个数组,例如,它可以组合数组和映射。这就是为什么我需要通用迭代器,或者某种解决方案。

谢谢!

C++没有通用迭代器,但它有模板函数,可以编写这些函数来操作任何类型的迭代器。要在容器中添加所有元素,已经有了适合您的算法。阅读std::accumulate

您从错误的角度看待问题。你可以这样做,但你为什么要这样做?如果基类的行为应该不同,那么它不应该实现该方法,而是应该将逻辑委托给派生类。

class Base
{
public:
    virtual ~Base() {}
    virtual void iterate() = 0;
};
class Derived : Base
{
public:
    virtual void iterate() { /*iterate through vector or map or whatever*/ }
};

动态多态性对对象来说是合理的,但对算法来说却非常糟糕。对于算法,使用静态多态性要好得多。如果你觉得你想在它们之间有一个支持operator+()的容器系统,只需确保它们以某种方式引用了一个命名空间,并在这个命名空间中定义了一个合适的operator+()。例如,您可以将std::vector<T, A>与分配器A一起使用,该分配器继承自具有运算符的命名空间中的某个对象。下面是这种方法的一个例子。

这个想法基本上是让名称空间实现合适的运算符。例如,addable实现operator+()printable实现operator<<()。除非某个类型以某种方式引用了它们,否则不会查看这两个命名空间,例如,通过从此命名空间中的类型继承,或者通过使用继承其中一个类型的类型的模板参数。这种查找机制被称为依赖于参数的查找。因此,这两个名称空间都提供了一个空的struct tag {};,当从继承时,它将不需要任何开销。

为了获得以某种方式引用这些名称空间的容器,下面的代码只创建了一个分配器类模板,该模板派生自这两种类型。然后,这个分配器与std::vector<T, A>std::list<T, A>一起使用,以创建一个类型别名,从而轻松地创建相应的容器。为了显示一切都很好地工作,main()只是演示了运算符的使用。

下面的代码在几个地方利用了C++2011,使符号更短。该原理也适用于C++2003。主要是使用类型别名和初始值设定项列表是行不通的。

#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
#include <vector>
namespace addable
{
    struct tag {};
    template <typename T0, typename T1>
    T0 operator+ (T0 const& c0, T1 const& c1)
    {
        T0 rc;
        std::transform(c0.begin(), c0.end(),
                       c1.begin(),
                       std::back_inserter(rc),
                       std::plus<typename T0::value_type>());
        return rc;
    }
}
namespace printable
{
    struct tag {};
    template <typename T, typename = typename T::value_type>
    std::ostream&
    operator<< (std::ostream& out, T const& value)
    {
        out << "[";
        if (!value.empty()) {
            std::copy(value.begin(), value.end() - 1,
                      std::ostream_iterator<typename T::value_type>(out, ", "));
            out << value.back();
        }
        return out << "]";
    }
}
template <typename T>
struct my_allocator
    : addable::tag
    , printable::tag
    , std::allocator<T>
{
};
template <typename T>
using my_vector = std::vector<T, my_allocator<T>>;
template <typename T>
using my_list   = std::vector<T, my_allocator<T>>;
int main()
{
    my_vector<int> v({ 1, 2, 3, 4 });
    my_list<int>   l({ 2, 3, 4, 5 });
    my_vector<int> rc = v + l;
    std::cout << v << " + " << l << " = " << (v + l) << "n";
}

简单的答案是:你不能,至少STL的迭代器不能。

迭代程序通常是作为模板构建的,编译器为每个模板专门化创建一个新的类型。您可以将vector::迭代器视为VectorFloatIterator,将map::迭代器视为MapFloat迭代器。

它们是不相关的类型——甚至不是通过继承——只是碰巧有相似的接口,当然,C++不是鸭子类型的。当两种类型完全不相关时,不能将VectorFloatIterator传递给需要MapFloat迭代器的函数。

这甚至适用于使用标准算法——例如,for_each算法是模板化的,因此编译器将创建一个不能相互使用的for_each_vector_foat_iterator和for_each_map_float_iterator函数。

考虑编译时生成的类型是很有帮助的。将不同类型传递给函数的操作发生在运行时,但为时已晚。在编译时,您只能在C++中使用模板的"泛型化"(是的,我编造了这个词)功能。

话虽如此,@Luchian Grigore对这个问题有着最公认的答案:专门化operator+来处理可能出现在加法语句中的各种类型中的任何一种。疼痛吗?是的,有点。

话虽如此,boost::any就是你在用例中寻找的东西——它们是类似STL的容器,通过而不是使用模板来处理各种类型。

最新更新