Зачем нужен iterator traits
Перейти к содержимому

Зачем нужен iterator traits

  • автор:

Трейты

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

Объявление трейта

Минимальное объявление трейта — это просто ключевое слово trait и его имя:

  • Scala 2 и 3
trait HairColor 

Трейты наиболее полезны в качестве обобщенного типа с абстрактными методами.

  • Scala 2
  • Scala 3
trait Iterator[A]  def hasNext: Boolean def next(): A > 
trait Iterator[A]: def hasNext: Boolean def next(): A 

При наследовании от трейта Iterator[A] требует указание типа A а также реализация методов hasNext и next .

Использование трейтов

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

  • Scala 2
  • Scala 3
trait Iterator[A]  def hasNext: Boolean def next(): A > class IntIterator(to: Int) extends Iterator[Int]  private var current = 0 override def hasNext: Boolean = current  to override def next(): Int =  if (hasNext)  val t = current current += 1 t > else 0 > > val iterator = new IntIterator(10) iterator.next() // вернет 0 iterator.next() // вернет 1 
trait Iterator[A]: def hasNext: Boolean def next(): A class IntIterator(to: Int) extends Iterator[Int]: private var current = 0 override def hasNext: Boolean = current  to override def next(): Int = if hasNext then val t = current current += 1 t else 0 end IntIterator val iterator = new IntIterator(10) iterator.next() // вернет 0 iterator.next() // вернет 1 

Этот класс IntIterator использует параметр to в качестве верхней границы. Он наследуется от Iterator[Int] , что означает, что метод next должен возвращать Int.

Подтипы

Туда, где требуется определенный тип трейта, мы можем передавать любой наследованный от требуемого трейта класс

  • Scala 2
  • Scala 3
import scala.collection.mutable.ArrayBuffer trait Pet  val name: String > class Cat(val name: String) extends Pet class Dog(val name: String) extends Pet val dog = new Dog("Harry") val cat = new Cat("Sally") val animals = ArrayBuffer.empty[Pet] animals.append(dog) animals.append(cat) animals.foreach(pet => println(pet.name)) // выведет "Harry" и "Sally" 
import scala.collection.mutable.ArrayBuffer trait Pet: val name: String class Cat(val name: String) extends Pet class Dog(val name: String) extends Pet val dog = Dog("Harry") val cat = Cat("Sally") val animals = ArrayBuffer.empty[Pet] animals.append(dog) animals.append(cat) animals.foreach(pet => println(pet.name)) // выведет "Harry" и "Sally" 

У трейта Pet есть абстрактное поле name , которое реализовано в классах Cat and Dog . В последней строке мы вызываем pet.name , который должен быть реализован в любом подтипе, унаследованном от трейта Pet .

Дополнительные ресурсы

  • Узнайте больше о трейтах в Scala Book
  • Использование трейтов для определения Enum

Объясните пожалуйста зачем нужна структура класса iterator_traits.

Author24 — интернет-сервис помощи студентам

Объясните, пожалуйста, зачем в C++ нужны указатели
Никак что-то не пойму, зачем в C++ указатели? Ведь можно спокойно программировать без них.

Объясните зачем нужна данная функция
Довольно односложный вопрос. Когда и зачем используется данная функция (соответственно, что будет.

Зачем присваивать переменной disc ноль? Объясните пожалуйста
Зачем присваивать disc = 0 (см. 9 строчку кода: double oplata, disc=0; Препод говорит что нужен.

Объясните пожалуйста зачем при авторизации создавать новую сессию
Всем доброго! Подскажите пож-та в чем разница между 2-мя подходами и какой правильнее: 1. При.

Ушел с форума

Эксперт С++

16473 / 7436 / 1187
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1

Представьте, что итератор для какого-то контейнера на самом деле
является обычным указателем. Но для алгоритмов из STL нужно больше
сведений, чем этот указатель может предоставить, а поместить «внутрь»
него соответствующие typedef-ы нельзя. Вот для этого, в частности, и
нужны traits.

Заколхозить итератор

Привет. Нужен итератор для своего класса (как писать по старинке в целом представляю — сделать нужные тайпдефы), смотрю в справочник, куча всего стала deprecated, включая старые требования к итераторам (внимание на LegacyIterator).

std::iterator_traits is the type trait class that provides uniform interface to the properties of LegacyIterator types. 

Во всем этом замешаны и новомодные концепты. Не хочется писать какое-то легаси г, как сейчас станет правильным? Сделать член-тип таг (random_access_iterator_tag, например), а весь этот iterator_traits интерфейс не нужен будет? Ну а чего они его легаси обзывают.

pavlick ★★
27.10.20 23:19:35 MSK

Я с концептами не разбирался еще, может там нужно унаследоваться от него?

pavlick ★★
( 27.10.20 23:27:11 MSK ) автор топика
Ответ на: комментарий от pavlick 27.10.20 23:27:11 MSK

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

anonymous
( 28.10.20 00:02:22 MSK )

Ты перепутал legacy с deprecated?

anonymous
( 28.10.20 00:33:48 MSK )

Зависит от требований.

struct iterator <> с операторами ++, -> и *.

И в довесок begin(iterator) и end(iterator)

invy ★★★★★
( 28.10.20 00:34:48 MSK )
Ответ на: комментарий от invy 28.10.20 00:34:48 MSK

Ну это естественно, но без тайпдефов не будут работать стд алгоритмы. Кажется, я начиная подозревать в чем тут дело — нам всем хотят рекомендовать пользоваться стд::ranges вместо стд::алгоритмов, а для них все это хозяйство (iterator_traits и смежное не нужно). Проверить пока затруднительно — в стд::ranges не все завезли, а проверять с версией из буста лень.

pavlick ★★
( 28.10.20 07:26:06 MSK ) автор топика

Что-то цпп стал слишком часто и радикально меняться.

pavlick ★★
( 28.10.20 07:29:07 MSK ) автор топика
Ответ на: комментарий от pavlick 28.10.20 07:29:07 MSK

Что-то цпп стал слишком часто и радикально меняться.

После окончания университетов многие 2 + 2 рассчитывают с использованием тройного интеграла.

Это и есть современный C++ 

anonymous
( 28.10.20 07:52:45 MSK )

Пиши враппер внутрь можешь депрекейтед кода засунуть если будет острая нужда поменяешь реализацию внутри оставив свой интерфейс неизменным, профит.

anonymous
( 28.10.20 07:56:00 MSK )
Ответ на: комментарий от pavlick 28.10.20 07:26:06 MSK

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

anonymous
( 28.10.20 08:05:02 MSK )
Ответ на: комментарий от pavlick 28.10.20 07:26:06 MSK

Вот тут пример как реализовывать итератор самостоятельно, и насколько меньше писать используя boost.stl_interfaces

Можешь выбрать любой вариант(я бы использовал boost)

(в требованиях С++14 компилятор)

fsb4000 ★★★★★
( 28.10.20 08:14:54 MSK )
Последнее исправление: fsb4000 28.10.20 08:43:48 MSK (всего исправлений: 1)

Ответ на: комментарий от pavlick 28.10.20 07:26:06 MSK

Рейнжи и итераторы связаны довольно мало. Концепты тут вообще ни при чем (хотя можешь их применить для упрощения тестирования, воткнув несколько static_assert с requires < some_iterator_concept >в качестве условия).

Пример итератора можешь найти здесь, не забудь учесть замечания из комментов.

Сделать член-тип таг (random_access_iterator_tag, например),

Да. Нужно сделать необходимые тайпдефы, нужно реализовать необходимые операции, вставить тег. Это нужно будет для работы iterator_traits и прочих вещей.

а весь этот iterator_traits интерфейс не нужен будет?

iterator_traits – нужен удобного извлечения информации из произвольного итератора, и, особенно, единого способа работать как с итераторами, так и с указателями. В своей реализации итератора ты никак с ним не взаимодействуешь в общем случае, хотя (начиная с С++20) и можешь специализировать тег напрямую в iterator_traits .

Ну а чего они его легаси обзывают.

std::iterator_traits – никак не легаси. Возможно, вы путаете с std::iterator , который объявлен deprecated, так как количество бойлерплейта он уменьшает только для примитивных юзкейсов, при этом чреват труднонаходимыми ошибками.

Siborgium ★★★★★
( 28.10.20 08:30:46 MSK )
Последнее исправление: Siborgium 28.10.20 08:43:29 MSK (всего исправлений: 3)

Ответ на: комментарий от anonymous 28.10.20 07:52:45 MSK

Владимир, после всего вами написанного, не вам осуждать других. Вы еще за свои «lexsem»’ы не ответили.

Siborgium ★★★★★
( 28.10.20 08:35:26 MSK )
Ответ на: комментарий от Siborgium 28.10.20 08:30:46 MSK

iterator_traits – нужен удобного извлечения информации из произвольного итератора, и, особенно, единого способа работать как с итераторами, так и с указателями. В своей реализации итератора ты никак с ним не взаимодействуешь в общем случае, хотя (начиная с С++20) и можешь специализировать тег напрямую в iterator_traits.

Да начиная с цпп11 он как бы нах не нужен, все типы можно вывести через всякие decltype и auto, просто костыль из прошлого.

pavlick ★★
( 28.10.20 08:50:55 MSK ) автор топика
Ответ на: комментарий от pavlick 28.10.20 08:50:55 MSK

Можно, но зачем? Из указателей вам типы придется доставать иначе, так как это примитивный тип. Для единообразной обработки указателей (которые суть random-access итераторы) и итераторов вам придется писать свой iterator_traits на коленке. Вот только iterator_traits уже написан.

Siborgium ★★★★★
( 28.10.20 08:57:37 MSK )
Последнее исправление: Siborgium 28.10.20 08:58:49 MSK (всего исправлений: 1)

Ответ на: комментарий от Siborgium 28.10.20 08:57:37 MSK

Зачем? iterator_traits это просто соглашение об интерфейсе, все требования собраны в одном месте, их спользуют стд алгоритмы. Но после цпп11 стд алгоритмы можно написать так, что они сами будут выводить необходимые типы, не надо им больше лезть в контейнер и брать оттуда тип. А мне, как автору пользовательского контейнера, ничего писать не надо, лишь операторы необходимые ([], ++, …).

pavlick ★★
( 28.10.20 09:11:14 MSK ) автор топика
Ответ на: комментарий от pavlick 28.10.20 09:11:14 MSK

можно написать так, что они сами будут выводить необходимые типы, не надо им больше лезть в контейнер и брать оттуда тип.

Нет, нельзя. Как только вам понадобится тег, вам придется воспользоваться iterator_traits (вернее, пришлось бы до С++20).

не надо им больше лезть в контейнер

В контейнер и так никто не лезет, типы берутся из итератора.

автору пользовательского контейнера, ничего писать не надо, лишь операторы необходимые ([], ++, …).

Серым по темно-серому пишу

Нужно сделать необходимые тайпдефы, нужно реализовать необходимые операции, вставить тег.

В своей реализации итератора ты никак с ним [с iterator_traits] не взаимодействуешь

Тайпдефы и операторы, все. Тайпдефы можно из операторов и вывести.

Siborgium ★★★★★
( 28.10.20 09:21:53 MSK )
Последнее исправление: Siborgium 28.10.20 09:24:30 MSK (всего исправлений: 1)

Ответ на: комментарий от Siborgium 28.10.20 09:21:53 MSK

Так сейчас концепты на каждый чих и тип итератора (output, random, …). Да и раньше можно было закостылить через SFINAE. Нафиг таги? Но вместо причесывания алгоритмов они продвигают ranges, пока мне так видится это все.

pavlick ★★
( 28.10.20 09:31:24 MSK ) автор топика
Ответ на: комментарий от pavlick 28.10.20 09:31:24 MSK

Так сейчас концепты на каждый чих […] Нафиг таги?

Как же вы меня одолели. Специально для вас процитирую свое сообщение, на которое вы отвечаете:

Как только вам понадобится тег, вам придется воспользоваться iterator_traits (вернее, пришлось бы до С++20).

Но вместо причесывания алгоритмов они продвигают ranges

И правильно делают. На чем, по вашему, работают ranges? На уровне железа угадывают, какая у вас структура данных и как по ней итерироваться, или что?

Siborgium ★★★★★
( 28.10.20 09:35:37 MSK )
Последнее исправление: Siborgium 28.10.20 09:35:58 MSK (всего исправлений: 1)

Ответ на: комментарий от pavlick 28.10.20 09:31:24 MSK

Да и раньше можно было закостылить через SFINAE. Нафиг таги?

SFINAE придумали гораздо позже. Можно почитать историю, когда какие патерны придумали.

А дальше обратная совместимость…

Если бы писали сейчас, то многое было бы сделано по-другому. Тот же полиморфизм был бы как в Rust…

fsb4000 ★★★★★
( 28.10.20 09:37:33 MSK )
Ответ на: комментарий от Siborgium 28.10.20 09:35:37 MSK

На чем, по вашему, работают ranges? На уровне железа угадывают, какая у вас структура данных и как по ней итерироваться, или что?

Я пока не готов на эту тему спорить, концепты еще не смотрел плотно. Но чисто теоретически — если я вижу член-функцию operator[], то логично, что перед нами random access iterator, не?

Структура iterator_traits

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

Синтаксис

struct iterator_traits < typedef typename Iterator::iterator_category iterator_category; typedef typename Iterator::value_type value_type; typedef typename Iterator::difference_type difference_type; typedef difference_type distance_type; typedef typename Iterator::pointer pointer; typedef typename Iterator::reference reference; >; 

Замечания

Структура-шаблон определяет типы элементов

  • iterator_category : синоним для Iterator::iterator_category .
  • value_type : синоним для Iterator::value_type .
  • difference_type : синоним для Iterator::difference_type .
  • distance_type : синоним для Iterator::difference_type .
  • pointer : синоним для Iterator::pointer .
  • reference : синоним для Iterator::reference .

Частичные специализации определяют критические типы, связанные с указателем на объект типа тип * или const тип *.

В этой реализации вы также можете применить несколько шаблонов функций, которые не используют частичную специализацию:

template C _Iter_cat(const iterator&); template random_access_iterator_tag _Iter_cat(const Ty *); template Ty *val_type(const iterator&); template Ty *val_type(const Ty *); template Diff *_Dist_type(const iterator&); template ptrdiff_t *_Dist_type(const Ty *); 

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

Пример

// iterator_traits.cpp // compile with: /EHsc #include #include #include #include using namespace std; template < class it >void function( it i1, it i2 ) < iterator_traits::iterator_category cat; cout << typeid( cat ).name( ) << endl; while ( i1 != i2 ) < iterator_traits::value_type x; x = *i1; cout ; cout ; int main( ) < vectorvc( 10,'a' ); list li( 10 ); function( vc.begin( ), vc.end( ) ); function( li.begin( ), li.end( ) ); > /* Output: struct std::random_access_iterator_tag a a a a a a a a a a struct std::bidirectional_iterator_tag 0 0 0 0 0 0 0 0 0 0 */ 

Требования

Заголовок:

Пространство имен: std

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *