Трейты
Трейты (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.
Объясните, пожалуйста, зачем в 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