非模板函数,用于处理特定类型元素的任何容器

时间:2013-06-24 16:56:17

标签: c++ templates iterator containers

我希望有一个标题中描述的功能。

我注意到,使用包含任何类型元素(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的指针,这个函数将能够修改它。

有人知道其他任何黑客这样做吗?或者是比上述方法更好的另一种方法?提前谢谢。

4 个答案:

答案 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());
}