2014-11-05 1 views
1

У меня есть шаблон для функции, которая принимает аргумент итератора вывода. Как я могу использовать static_assert, чтобы проверить, что экземпляр использует соответствующий итератор? (Т.е., так что он является выходным итератора, и что он присваивает элементы правильного типа.)Как static_assert можно использовать для проверки типа элемента аргумента итератора на шаблонную функцию?

#include <iostream> 
#include <list> 
#include <set> 

template <class OutputIter> 
void add_ints (OutputIter iter) 
{ 
    static_assert (something_goes_here, 
        "Arg must be an output iterator over ints"); 

    *iter++ = 1; 
    *iter++ = 2; 
    *iter++ = 3; 
} 

main() 
{ 
    // Insert iterator will add three elements. 
    std::set<int> my_set; 
    add_ints (std::inserter (my_set, my_set.end())); 
    for (int i : my_set) std::cout << i << "\n"; 

    // Non-insert iterator will overwrite three elements. 
    std::list<int> my_list ({ 0, 0, 0 }); 
    add_ints (my_list.begin())); 
    for (int i : my_list) std::cout << i << "\n"; 

#if 0 
    // Want nice compile error that container elements are not ints. 
    std::set<std::string> bad_set; 
    add_ints (std::inserter (bad_set, bad_set.end())); 
#endif 
#if 0 
    // Want nice compile error that argument is not an iterator. 
    class Foo {} foo; 
    add_ints (foo); 
#endif 
} 
+1

Использование 'iterator_traits :: value_type'. Вы должны использовать 'iterator_traits' для вашей текущей проверки для размещения типа без вложенного типа' iterator_category' (не уверен, что это возможно для OutputIterator). Я также утверждаю, что вы должны использовать 'is_base_of' вместо' is_same' для проверки категории итератора. Ваш код должен по-прежнему работать, если кто-то передал вам более утонченную категорию итераторов, которая по-прежнему соответствует всем требованиям 'OutputIterator' (хотя сегодня такого типа в stdlib нет). – Praetorian

+1

'OutputIterator' должны поддерживать только' * r = o', а не 'r = o'. Тот факт, что для некоторых стандартных выходных итераторов библиотеки операция разыменования является no-op, является детальностью реализации этих итераторов, и вы не должны полагаться на это в общем коде. Проверка на 'std :: output_iterator_tag' также не является отличной идеей в целом; многие измененные итераторы будут иметь другой тег. –

+3

@Praetorian Это не делает то, что вы хотите. Для многих 'OutputIterator', 'iterator_traits :: value_type' является' void'. –

ответ

5

OutputIterator ы не должны иметь типы значений; их value_type вполне может быть void, и на самом деле это void для чисто выходных итераторов из стандартной библиотеки.

В исходном вопросе вы проверили output_iterator_tag, но не должны. Существует множество совершенно изменчивых итераторов, которые имеют другую категорию. std::vector<int>::iterator Категория: random_access_iterator_tag, например.

Вместо этого непосредственно проверьте правильность выражений применимых выражений. Все Iterator s должны поддерживать *r и ++r, а кроме того OutputIterator s должен поддерживать *r = o, r++ и *r++ = o, так:

template<class...> 
struct make_void { using type = void; }; 

template<class... Ts> 
using void_t = typename make_void<Ts...>::type; 

template<class Iter, class U, class = void> 
struct is_output_iterator_for : std::false_type {}; 

template<class Iter, class U> 
struct is_output_iterator_for< 
    Iter, U, 
    void_t<decltype(++std::declval<Iter&>()), 
      decltype(*std::declval<Iter&>() = std::declval<U>()), 
      decltype(*std::declval<Iter&>()++ = std::declval<U>())>> : std::true_type {}; 
+0

@ T.C. Если я понимаю, это улучшение моего первоначального примера. Так вот, как «static_assert» в моем примере следует заменить? 'static_assert (is_output_iterator_for :: значение," msg ");' –

+0

@JohnH. Я считаю, что это будет 'static_assert (is_output_iterator_for :: value," msg ");'. – 0x499602D2

+2

@ 0x49 Все дело в том, что выходные итераторы не имеют типов значений. 'static_assert (is_output_iterator_for :: значение," msg ");' –