Каковы сходства и различия между концепциями C++ и чертами ржавчины?

В Rust основным инструментом для абстракции являются черты. В C++ есть два инструмента для абстракций: абстрактные классы и шаблоны. Чтобы избавиться от некоторых недостатков использования шаблонов (например, трудно читаемых сообщений об ошибках), C++ ввел концепции, которые являются "именованными наборами требований".

Обе функции кажутся довольно похожими:

  • Определение черты/концепции осуществляется путем перечисления требований.
  • Оба могут быть использованы для привязки/ограничения параметров универсального/шаблонного типа.
  • Черты ржавчины и шаблоны C++ с концепциями являются мономорфизированными (я знаю, что черты ржавчины также можно использовать с динамической диспетчеризацией, но это другая история).

Но из того, что я понимаю, есть и заметные отличия. Например, концепции C++, по-видимому, определяют набор выражений, которые должны быть действительными, вместо перечисления сигнатур функций. Но существует много разной и запутанной информации (может быть, потому что концепции появляются только в C++ 20?). Вот почему я хотел бы знать: в чем именно заключаются различия и сходства концепций C++ и признаков Rust?

Есть ли функции, которые предлагаются только концепциями или чертами? Например, как насчет типов и констант, связанных с Rust? Или ограничивает тип несколькими чертами/понятиями?

Ответ 1

Отказ от ответственности: я еще не использовал понятия, все, что я знаю о них, было взято из различных предложений и ссылок, так что ответьте на этот вопрос с недоверием.

Полиморфизм времени выполнения

Черты ржавчины используются как для полиморфизма времени компиляции, так и иногда для полиморфизма времени исполнения; Понятия только о полиморфизме времени компиляции.

Структурный против номинального.

Наибольшее различие между Концепциями и Чертами заключается в том, что Концепции используют структурную типизацию, тогда как Черты используют номинальную типизацию:

  • В C++ тип никогда явно не удовлетворяет Концепции; оно может "случайно" удовлетворить его, если оно удовлетворяет всем требованиям.
  • В Rust используется специальная синтаксическая конструкция impl Trait for Type чтобы явно указать, что тип реализует Trait.

Есть ряд последствий; в целом, номинальная типизация лучше с точки зрения удобства сопровождения - добавление требования к черте - в то время как структурная типизация лучше для библиотек сторонних производителей - тип из библиотеки A может удовлетворить концепцию из библиотеки B без их ведома друг друга.

Ограничения

Черты обязательны:

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

Концепции совершенно необязательны:

  • Метод может вызываться для переменной универсального типа, причем этот тип не требуется для удовлетворения какой-либо Концепции или каким-либо образом ограничен.
  • Метод можно вызывать для переменной универсального типа, удовлетворяющей Концепции (или нескольким), без указания этого метода какими-либо Концепциями или Ограничениями.
  • Ограничения (см. Примечание) могут быть полностью произвольными и определять требования без использования именованной Концепции; и еще раз, они совершенно необязательны.

Примечание: ограничение вводится requires положения и определяет либо требования Времнных или требования, основанные на концепции.

Требования

Набор выражаемых требований различен:

  • Концепции/Ограничения работают по замене, поэтому допускают всю широту языков; требования включают в себя: вложенные типы/константы/переменные, методы, поля, возможность использования в качестве аргумента другой функции/метода, возможность использования в качестве универсального аргумента другого типа и их комбинации.
  • Черты, напротив, допускают только небольшой набор требований: связанные типы/константы и методы.

Выбор перегрузки

Rust не имеет концепции перегрузки по принципу ad-hoc, перегрузка происходит только по чертам, а специализация пока невозможна.

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

Примечание: до этого либо SFINAE, либо диспетчеризация тегов будут использоваться в C++ для достижения выбора; Для работы с комплектами перегрузки открытого типа требовалась гимнастика.

дизъюнкция

Как использовать эту функцию мне пока не совсем понятно.

Механизмы требования в Rust являются чисто аддитивных (конъюнкции, иначе &&), в отличие от этого, в C++ requires оговорки могут содержать дизъюнкции ( так называемый ||).