Информация об изменениях

Сообщение Re[3]: template (синтаксис) больше не нужен? от 04.11.2023 4:26

Изменено 04.11.2023 4:31 watchmaker

Re[3]: template (синтаксис) больше не нужен?
Здравствуйте, johny5, Вы писали:

J>Здравствуйте, reversecode, Вы писали:


J>Вот накидал пример с перегрузкой (оставим за скобками целесообразность кода, только синтаксис).


J>Было: ...

J>Стало: ...

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

J>requires (!HasFind<decltype(container), decltype(key)>)


Это при SFINAE нужно было следить, чтобы условия под всякими std::enable_if были взаимоисключающими, чтобы не было неоднозначности при нескольких подходящих альтернативах. А ограничения, заданные через requires уже имеют (частичный) порядок.
То есть не нужно писать
auto find(const auto& container, const auto& key) requires (!HasFind<decltype(container), decltype(key)>) {...}
, а достаточно просто
auto find(const auto& container, const auto& key) {...}

Компилятор и так обязан выбрать первую перегрузку (через метод find) в случае, когда обе они подходят, потому что она более ограниченная/специфичная.

Заодно это убирает и момент удивления при чтении кода: "зачем в нём проверяется что-то про наличие метода, если в теле функции ничего такого не используется?". Потому что это было техническое ограничение при реализации через шаблоны. А в концептах почти всегда стоит проверять только те требования к типу, которые действительно нужны.


J>auto find(const auto& container, const auto& key) requires HasFind<decltype(container), decltype(key)>


Во-первых, такое сложно читать — убирание template не упрощает чтение кода.

Во-вторых, в концептах же специально сделана поддержка простой записи ограничений на типы: вместо const auto& container достаточно написать const HasFind<KeyType> auto& container и убрать ручные проверки. Компилятор сам подставит тип контейнера в концепт HasFind нужным параметром.

Опять же, в зависимости от сложности выражения это можно использовать как совместно с placeholder'ом auto (как выше), так и внутри template.
template <class KeyType, HasFind<KeyType> ContainerType>
auto find(const ContainerType& container, const KeyType& key) {
    ...
}

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


J> concept сводят на нет необходимость старого синтаксиса шаблонов через template<typename .. >

Не совсем.
Есть достаточно много простых случаев, когда достаточно использовать placeholder auto c концептом. Например, для независимых ограничений типов аргументов функции:
void foo(const std::integral auto value);

Но для более сложных случаев предполагается использовать концепты и template совместно. И такое совместное использование оказывается гораздо более выразительным, но ключевое слово template в нём остаётся, а не убирается любой ценой, как в твоих примерах.
Re[3]: template (синтаксис) больше не нужен?
Здравствуйте, johny5, Вы писали:

J>Вот накидал пример с перегрузкой (оставим за скобками целесообразность кода, только синтаксис).


J>Было: ...

J>Стало: ...

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

J>requires (!HasFind<decltype(container), decltype(key)>)


Это при SFINAE нужно было следить, чтобы условия под всякими std::enable_if были взаимоисключающими, чтобы не было неоднозначности при нескольких подходящих альтернативах. А ограничения, заданные через requires уже имеют (частичный) порядок.
То есть не нужно писать
auto find(const auto& container, const auto& key) requires (!HasFind<decltype(container), decltype(key)>) {...}
, а достаточно просто
auto find(const auto& container, const auto& key) {...}

Компилятор и так обязан выбрать первую перегрузку (через метод find) в случае, когда обе они подходят, потому что она более ограниченная/специфичная.

Заодно это убирает и момент удивления при чтении кода: "зачем в нём проверяется что-то про наличие метода, если в теле функции ничего такого не используется?". Потому что это было техническое ограничение при реализации через шаблоны. А в концептах почти всегда стоит проверять только те требования к типу, которые действительно нужны.


J>auto find(const auto& container, const auto& key) requires HasFind<decltype(container), decltype(key)>


Во-первых, такое сложно читать — убирание template не упрощает чтение кода.

Во-вторых, в концептах же специально сделана поддержка простой записи ограничений на типы: вместо const auto& container достаточно написать const HasFind<KeyType> auto& container и убрать ручные проверки. Компилятор сам подставит тип контейнера в концепт HasFind нужным параметром.

Опять же, в зависимости от сложности выражения это можно использовать как совместно с placeholder'ом auto (как выше), так и внутри template.
template <class KeyType, HasFind<KeyType> ContainerType>
auto find(const ContainerType& container, const KeyType& key) {
    ...
}

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


J> concept сводят на нет необходимость старого синтаксиса шаблонов через template<typename .. >

Не совсем.
Есть достаточно много простых случаев, когда достаточно использовать placeholder auto c концептом. Например, для независимых ограничений типов аргументов функции:
void foo(const std::integral auto value);

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