我希望有一个标题中描述的功能。
我注意到,使用包含任何类型元素(int,double)的任何类型(list,vector等)容器的STL算法通过使用迭代器类型作为模板参数来提供通用性,例如
template<typename _II, typename _OI>
inline _OI
copy(_II __first, _II __last, _OI __result)
这是一种很好的方法,直到算法适用于任何类型的元素。元素类型的唯一要求是它必须具有复制构造函数。
但假设我们有一个具体的类型
class MyElement
{
public:
void doSomethingWithElement();
};
我们希望通过调用函数doSomethingWithElement()
来实现一个处理此类元素的函数。
编写一个接收特定类型容器的函数不是很方便,因为许多容器都以相同的方式处理(例如迭代器),如果需要处理不同类型的容器,我们将被迫复制码。编写模板工作正常,但它看起来很难看,因为我们必须在声明它的位置(在头文件中)实现函数。此外,当我们想要处理只有一种类型的元素时,参数化这种类型并不是实现目标的正确方法。
我一直在考虑可以像
一样使用的迭代器接口void processContainer(IIterator<MyElement> begin, IIterator<MyElement> end);
如果这个迭代器具有在派生类中实现的纯虚拟运算符++和运算符*,我们可以将这些对象传递给processContainer
。但是有一个问题:如果IIterator是抽象类,我们无法在processContainer
的实现中实例化它,如果我们传递一个指向IIterator的指针,这个函数将能够修改它。
有人知道其他任何黑客这样做吗?或者是比上述方法更好的另一种方法?提前谢谢。
答案 0 :(得分:4)
更简单的方法是忽略限制,只需将您的函数实现为任何迭代器的模板。如果迭代器没有引用类型,那么用户将在“类型X没有doSomethingWithElement
成员函数”的行中收到可怕的错误消息。
接下来的事情是提供一个static_assert
,该函数仍将采用任何迭代器(意味着它将参与任何类型的重载解析)但错误消息将提供更多信息。
此外,您可以决定使用SFINAE从候选集中删除重载。虽然SFINAE是一把漂亮的金色锤子,但我不确定你手边有合适的钉子。
如果您真的想要进一步发展,可以查看Adobe库中的any_iterator
作为如何在迭代器类型上执行类型擦除以避免模板的示例。这种方法的复杂性比以前的任何一个都要高几个数量级,并且运行时成本也会更高,这提供了更清晰的ABI和更少代码大小的唯一优势(不同的迭代器可以传递给单个函数)。 / p>
答案 1 :(得分:0)
您可以使用std :: for_each():http://www.cplusplus.com/reference/algorithm/for_each/
完整代码:
void callDoSomething(MyElement &elem)
{
elem.doSomething();
}
int main()
{
std::vector<MyElement> vec(100);
std::for_each(vec.begin(), vec.end(), callDoSomething);
}
答案 2 :(得分:0)
你无法完全按照自己的意愿去做。您可以使用enable_if
来限制函数的可用性:
template < typename Container >
typename enable_if<is_same<typename Container::value_type, MyElement>,void>::type processContainer(Container c)...
答案 3 :(得分:0)
不可能创建一个具有完整迭代器功能的抽象迭代器 - 包括复制自身的能力 - 而无需更改迭代器接口。但是,您可以使用抽象基类实现迭代器功能的子集:
#include <iterator>
#include <vector>
#include <list>
template<typename T>
struct AbstractIterator
{
virtual bool operator!=(const AbstractIterator<T>& other) const = 0;
virtual void operator++() = 0;
virtual T& operator*() = 0;
};
template<typename Iterator>
struct ConcreteIterator : AbstractIterator<typename std::iterator_traits<Iterator>::value_type>
{
typedef typename std::iterator_traits<Iterator>::value_type value_type;
Iterator i;
ConcreteIterator(Iterator i) : i(i)
{
}
virtual bool operator!=(const AbstractIterator<value_type>& other) const
{
return i != static_cast<const ConcreteIterator*>(&other)->i;
}
virtual void operator++()
{
++i;
}
virtual value_type& operator*()
{
return *i;
}
};
template<typename Iterator>
ConcreteIterator<Iterator> wrapIterator(Iterator i)
{
return ConcreteIterator<Iterator>(i);
}
class MyElement
{
public:
void doSomethingWithElement();
};
void processContainerImpl(AbstractIterator<MyElement>& first, AbstractIterator<MyElement>& last)
{
for(; first != last; ++first)
{
(*first).doSomethingWithElement();
}
}
template<typename Iterator>
void processContainer(Iterator first, Iterator last)
{
ConcreteIterator<Iterator> wrapFirst = wrapIterator(first);
ConcreteIterator<Iterator> wrapLast = wrapIterator(last);
return processContainerImpl(wrapFirst, wrapLast);
}
int main()
{
std::vector<MyElement> v;
processContainer(v.begin(), v.end());
std::list<MyElement> l;
processContainer(l.begin(), l.end());
}