我对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的容器,通过而不是使用模板来处理各种类型。