Концепции C ++ составные требования и требования типа возврата - PullRequest
5 голосов
/ 24 апреля 2020

В последний раз, когда я использовал концепции C ++ с G CC и флаг fconcepts, следующий фрагмент кода работал

template <typename T, typename U>
concept equality_comparable = requires(T a, U b) {
  { a == b } -> bool;
  { a != b } -> bool;
};

Очевидно, что это уже не так, и return-type- type- требование после составного требования теперь может содержать только ограничения типа. Если я не ошибаюсь, это в основном означает использование другой концепции, чтобы удовлетворить требование типа возвращаемого значения .

Так что отлично читаемый и (для стандартов C ++) короткий фрагмент становится

template <typename From, typename To>
concept convertible_to = std::is_convertible_v<From, To>;

template <typename T, typename U>
concept equality_comparable = requires(T a, U b) {
  { a == b } -> convertible_to<bool>;
  { a != b } -> convertible_to<bool>;
};

Конечно, это даже не полная реализация, но давайте пока проигнорируем это. Может кто-нибудь объяснить мне, почему комитет решил изменить это? Лично я нахожу, что «неявно используемый параметр шаблона» в концепции convertible_to крайне раздражает и сбивает с толку.

1 Ответ

7 голосов
/ 24 апреля 2020

Ну, что это на самом деле означает:

template <typename T, typename U>
concept equality_comparable = requires(T a, U b) {
  { a == b } -> bool;
  { a != b } -> bool;
};

Означает ли это, что a == b должен иметь тип точно bool, или это означает, что если вы распадаете тип, который вы get bool (то есть const bool или bool& в порядке), или это означает, что преобразуется в bool (т.е. std::true_type в порядке)? Я не думаю, что это вообще ясно из синтаксиса - и любой из этих трех может быть осмысленно желаемым для конкретной концепции (как P1452 указывает, в то время, отношение Same<T> к ConvertibleTo<T> в понятиях было 40-14).

Далее в статье указывается, что в Концепции TS, где существовал -> Type, мы также имели возможность написать что-то вроде vector<Concept> ... или -> vector<Concept> в качестве требования. Это тип, но он будет очень трудно вести себя с семантикой decltype(()), принятой нами в P1084 .

По сути, я не думаю, что на самом деле «отлично читаемый» фрагмент - это несколько возможных значений для этого синтаксиса, каждый из которых может иметь желаемое значение в зависимости от контекста. И наиболее часто используемый в то время (same_as<bool>) даже не тот, который нам нужен (convertible_to<bool>).

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

Это новшество в C ++, но лично я нахожу, что в этих случаях он довольно хорошо читается , Видя:

{ a == b } -> convertible_to<bool>;

Просто читается в точности как требование: a == b должно быть допустимым выражением, которое можно преобразовать в bool. Для унарных понятий это делает использование довольно приятным, поскольку вы можете использовать их вместо несколько бессмысленного ключевого слова typename / class:

template <range R>
void algo(R&& r);

, которое ничем не отличается от других языков. Например, в Rust, например:

fn algo<I: Iterator>(i: I)

Там "неявно используемый параметр шаблона" настолько неявен, что даже не является частью объявления черты , он там тоже неявный:

pub trait Iterator { ... }

Таким образом, даже с синтаксисом более длинной формы вы написали бы where I: Iterator, тогда как в C ++ вы все равно написали бы requires range<R>.

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

...