【问题标题】:Generic STL iterator in polymorphism多态性中的通用 STL 迭代器
【发布时间】:2012-09-03 17:05:33
【问题描述】:

我对 C++ 有点陌生, 我有一个抽象类(纯虚拟),它继承了 2 个类。 每个派生类都拥有相似的数据,只是在不同的 STL 容器中(数组 vs 映射) 我正在重载基类的运算符,以便执行例如添加 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++03/ C++11) 以及是否可以使用 boost::functionstd::function 之类的库。如果您不需要提供算法的单个实现并且每个派生类型都进行迭代,那么它可能会简单得多。

标签: c++ generics stl


【解决方案1】:

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

【讨论】:

  • 感谢您的回答,但很抱歉,我不明白这对我有什么帮助?另外,我编辑了我的问题,试图更好地解释我的目标
【解决方案2】:

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

class Base
{
public:
    virtual ~Base() {}
    virtual void iterate() = 0;
};

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

【讨论】:

  • 感谢您的回答,我需要迭代的函数是运算符重载,我不想为每种情况重载(即,将数组添加到数组,将数组添加到映射等) 我已经编辑了我的问题以包含更具体的示例
  • @Lablabla 逻辑错误。 map[1] 应该返回哪个元素?地图不是连续的……
  • map[i] 其中映射 int->double 应添加到 array[i],如果 map[i] 不存在,则将其视为零。但那是以后的事了。练习描述说我们应该以这种方式实现它,而不需要关心 STL 容器。那里特别提到了地图。
【解决方案3】:

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

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

为了获取以某种方式引用这些命名空间的容器,下面的代码只是创建了一个派生自这两种类型的分配器类模板。然后将此分配器与std::vector&lt;T, A&gt;std::list&lt;T, A&gt; 一起使用以创建类型别名以轻松创建相应的容器。为了展示一切都很好,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";
}

【讨论】:

    【解决方案4】:

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

    迭代器通常构建为模板,编译器会为每个模板特化创建一个新类型。您可以将 vector::iterator 视为 VectorFloatIterator,将 map::iterator 视为 MapFloatIterator。

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

    这甚至适用于使用标准算法 - 例如 for_each 算法是模板化的,因此编译器将创建类似 for_each_vector_float_iterator 和 for_each_map_float_iterator 函数的东西,它们不能相互使用。

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

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

    话虽如此,boost::any 是您正在寻找的用例 - 它们是类似 STL 的容器,可以通过使用模板来处理各种类型来处理任何类型。 p>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-18
      • 1970-01-01
      • 2018-05-31
      • 2011-08-02
      • 1970-01-01
      • 2012-03-16
      相关资源
      最近更新 更多