3

我想要一个标题中描述的功能。

我注意到与包含任何类型(int、double)元素的任何类型(列表、向量等)的容器一起使用的 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);

如果此迭代器具有在派生类中实现的纯虚拟 operator++ 和 operator*,我们可以将这些对象传递给processContainer. 但是有一个问题:如果 IIterator 是抽象类,我们不能在 的实现中实例化它processContainer,如果我们传递一个指向 IIterator 的指针,这个函数就可以修改它。

有人知道其他黑客可以做到这一点吗?还是比上述方法更好的另一种方法?提前致谢。

4

4 回答 4

5

更简单的方法是忽略限制,只需将您的函数实现为任何迭代器的模板。如果迭代器没有引用类型,那么用户将在“类型 X 没有doSomethingWithElement成员函数”行中收到可怕的错误消息。

接下来是提供一个static_assert,该函数仍将采用任何迭代器(这意味着它将参与任何类型的重载决议),但错误消息将提供更多信息。

此外,您可以决定使用 SFINAE 从候选集中消除过载。但是,虽然 SFINAE 是一把漂亮的金锤,但我不确定您手头是否有合适的钉子。

如果您真的想走得更远,您可以查看any_iteratorAdob​​e 库中的示例,了解如何对迭代器类型执行类型擦除以避免模板。这种方法的复杂性比以前的任何方法都高几个数量级,并且运行时成本也会更高,唯一的优点是 ABI 更清晰,代码量更小(不同的迭代器可以传递给单个函数)。

于 2013-06-24T17:18:08.590 回答
0

你不能完全做你想做的事。您可以使用enable_if来限制函数的可用性:

template < typename Container >
typename enable_if<is_same<typename Container::value_type, MyElement>,void>::type processContainer(Container c)...
于 2013-06-24T17:32:53.613 回答
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);
}
于 2013-06-24T17:21:55.493 回答
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());
}
于 2013-06-25T11:30:36.820 回答