Как ограничить итератор тем, что он является прямым итератором? - PullRequest
6 голосов
/ 06 января 2012

У меня есть функция, которая должна перечислять итератор несколько раз, но в соответствии с MSDN , "Как только вы увеличиваете любую копию входного итератора, ни одна из других копий не может быть безопасно сравнена, разыменованным или увеличенным в дальнейшем. "

Чтобы упростить задачу, вместо создания отдельной реализации для не-прямых итераторов, которые копируют данные и перечисляют копию, я хочу ограничить свой методчтобы принимать только прямые итераторы и отклонять входные итераторы статически.

Сейчас у меня есть что-то вроде:

template<typename It, typename TCallback /*signature: bool(value_type)*/>
bool EnumerateTwice(const It &begin, const It &end, TCallback callback)
{
    for (It it = begin; it != end; ++it)
        if (!callback(*it))
            return false;
    for (It it = begin; it != end; ++it)
        if (!callback(*it))
            return false;
    return true;
}

, но ничто не ограничивает It быть прямым итератором.

Как наложить это ограничение на шаблонную функцию?(C ++ 03)

Ответы [ 4 ]

5 голосов
/ 06 января 2012

Вы можете использовать SFINAE и заменить bool на:

typename enable_if<
   is_same<typename std::iterator_traits<It>::iterator_category,
           std::forward_iterator_tag>::value,
   bool>::type

Вам может потребоваться определить is_same и enable_if самостоятельно, если вы не хотите извлекать их из Boost или TR1:

template <typename A, typename B>
struct is_same { static const bool value = false; };

template <typename T>
struct is_same<T, T> { static const bool value = true; };

template <bool, typename> struct enable_if { };
template <typename T> struct enable_if<true, T> { typedef T type; };
4 голосов
/ 06 января 2012

Не проверено, но вы можете попробовать что-то вроде:

template<typename It, typename TCallback /*signature: bool(value_type)*/>
bool EnumerateTwice_Interal(const It &begin, const It &end, TCallback callback, 
      std::forward_iterator_tag)
{
     //do your things
}

template<typename It, typename TCallback /*signature: bool(value_type)*/>
bool EnumerateTwice(const It &begin, const It &end, TCallback callback)
{
    EnumerateTwice_Internal(begin, end, callback,
        typename std::iterator_traits<It>::iterator_category());
}
3 голосов
/ 06 января 2012

Вы можете сделать это, используя std::enable_if:

#include <iterator>
#include <type_traits>
#include <utility>

template <typename It, typename TCallback>
typename std::enable_if<std::is_base_of<std::forward_iterator_tag,
                        typename std::iterator_traits<It>::iterator_category>::value,
                    bool>::type
EnumerateTwice(It begin, It end, TCallback) {
    ...
}

Это использует класс из C ++ 11, но все это можно сделать и в C ++ 03.

0 голосов
/ 15 августа 2012

Чтобы расширить ответ Родриго - я нашел это решение и подумал, что стоит упомянуть:

struct True { unsigned char _[2]; operator bool() const { return true; } };
char is_forward_iterator(std::input_iterator_tag const *) { return 0; }
True is_forward_iterator(std::forward_iterator_tag const *) { return True(); }

Теперь, если вы хотите проверить это внутри какой-то функции, вы можете сказать:

if (is_forward_iterator(static_cast<iterator_traits<It>::iterator_category*>(0)))
{
    ...
}

и если вы хотите проверить это в шаблонах, вы можете проверить:

sizeof(
  is_forward_iterator(static_cast<iterator_traits<It>::iterator_category*>(0))
) > 1

с основным преимуществом этого метода в том, что он избегает объявления шаблонов (например, для лучшей скорости компиляции).

...