Здравствуйте, Hard_Club, Вы писали:
H_C>В STL есть несколько типов итераторов http://www.cplusplus.com/reference/iterator/ H_C>Только не могу найти соответствующих под-типов. Или разница только в iterator tag
В STL используется утиная типизация — не наследование, а контракты.
Если некий итератор является bidirectional, то он является также forward. То есть, принцип подстановки Лисков действует, но на уровне метапрограмм (шаблонов и, не дай боже, макросов).
Чтобы протащить его на уровень указателей/ссылок, придётся использовать, например, boost::any_iterator.
Да, а чтобы можно было различать разновидности (категории) итераторов, выбирая наиболее подходящую специализацию алгоритма (или просто чтоб сделать проверку) — используются тэги и трейтсы.
std::iterator_traits<IteratorType> извлекает из типа информацию
— категория
— тип значения
— тип указателя
и т.п.
Для примитивных типов — т.е. для указателей — эта информация полностью внешняя
Чтобы не колбасить ни специализации iterator_traits, ни руками вбивать все определения зависимых имён, используется миксин std::iterator
template<class Category, class Value, class Distance = ptrdiff_t, class Pointer = Value*, class Reference = Value& > struct iterator {
typedef Category iterator_category;
typedef Distance difference_type;
.....
};
У буста иерархия категорий более подробная, чем у STL, но суть такая же.
Проверять категорию нужно через typename iterator_traits<Iterator>::iterator_category, а не непосредственно Iterator::iterator_category, потому что Iterator может быть голым указателем.
Кстати, для простоты проверок и для автоматизации выбора наилучшей специализации алгоритма иерархия категорий сделана на наследовании классов.
Здравствуйте, Hard_Club, Вы писали:
H_C>В STL есть несколько типов итераторов http://www.cplusplus.com/reference/iterator/
H_C>Только не могу найти соответствующих под-типов. Или разница только в iterator tag
да.
в бусте есть хелперы для создания своих, но суть от этого не меняется.
К>В STL используется утиная типизация — не наследование, а контракты. К>Если некий итератор является bidirectional, то он является также forward. То есть, принцип подстановки Лисков действует, но на уровне метапрограмм (шаблонов и, не дай боже, макросов). К>Чтобы протащить его на уровень указателей/ссылок, придётся использовать, например, boost::any_iterator.
Что-то не понял этого утверждения. Он делает стирание типов
то MyIterator должен приводиться к ForwardIteratorInterface<int>.
И в обычных ООП-языках именно так и делается.
Либо напрямую, чтоб MyIterator наследовался от ForwardIteratorInterface<int>, либо через прокси-объект, реализующий интерфейс и знающий про MyIterator.
Да, происходит стирание типа, поскольку fun() ничего не знает про MyIterator и оперирует интерфейсом.
Для шаблонов этих сложностей не нужно.
Главное, чтобы MyIterator реализовывал контракт, необходимый для fun: например, был CopyConstructible, имел оператор ++ и при разыменовании отдавал int.
Этот контракт можно проверять с помощью SFINAE и static assert'ов. Причём — или каждую фичу контракта, или клятву. Наличие тэга той или иной категории — это клятва.
То, что тэги категорий образуют ООП-иерархию, это просто для удобства.
К>Для шаблонов этих сложностей не нужно. К>Главное, чтобы MyIterator реализовывал контракт, необходимый для fun: например, был CopyConstructible, имел оператор ++ и при разыменовании отдавал int. К>Этот контракт можно проверять с помощью SFINAE и static assert'ов. Причём — или каждую фичу контракта, или клятву. Наличие тэга той или иной категории — это клятва. К>То, что тэги категорий образуют ООП-иерархию, это просто для удобства.
Т.е. шаблонный тег разве не может определять имплементацию?