3

我对 C++ 有点陌生,我有一个抽象类(纯虚拟),它继承了 2 个类。每个派生类都保存类似的数据,只是在不同的 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]

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

谢谢!

4

4 回答 4

1

动态多态性对于对象来说是合理的,但对于算法来说却非常糟糕。对于算法,您最好使用静态多态性。如果你觉得你想要一个容器系统支持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";
}
于 2012-09-03T20:43:32.580 回答
1

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

于 2012-09-03T17:09:28.767 回答
1

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

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

class Derived : Base
{
public:
    virtual void iterate() { /*iterate through vector or map or whatever*/ }
};
于 2012-09-03T17:09:30.493 回答
0

简单的答案是:你不能,至少不能使用 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 的容器,通过使用模板来处理各种类型来处理任何类型。

于 2012-09-03T18:24:45.217 回答