C++20

requires можно писать как до сигнатуры функции, так и сразу после, но перед { после requires должно быть написано bool constexpr выражение нельзя вешать requires не на шаблонные функции нельзя, потому что если было бы можно, но на стадии линковки было бы много проблем

область применения requirement’ов:

  1. разделять перегрузки функций (развитие идей std::enable_if (sfinae), if constexpr)
  2. накладывать требования на тип

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

здесь requires - это спецификатор, элемент синтаксиса при объявлении шаблонной сущности

не то, что позволяет выбрать версию перегрузки и не то, что определяет инстанцировать или нет, а происходит на стадии набора кандидатов

поэтому тут функции с одинаковыми сигнатурами, но для компилятора они, можно сказать, с разными названиями

requires влияет на перегрузку тем, что наличие requires лучше, чем отсутствие

будет 1, 2

но если будет 2 функции с requires, пусть даже с несоответствующим requires, то будет CE. но не со словами о redefinition, а с ambigous call перегрузка по require не умеет вычислять requirement и определять строгость

если requirement будут в точности одинаковые, то тогда будет redefinition (только если стоит перед сигнатурой функции)

requires expression

здесь requires - это оператор вычисляется в compile-time, возвращает true/false в зависимости от того, компилируется ли {}

template <typename T>
requires requires(T a, T b) { a + b; }
T add (const T& a, const T& b) {
	return a + b;
}

если внутри {} шаблонная функция, то он не будет её инстанцировать. он лишь проверяет, что определена такая функция над данными типами. проверяется только на компилируемость, не на истинность! внутри requires можно писать ещё один requires. это и будет проверка на истинность

внутри {} requirement’ов :

  1. simple requirements. простой экспрешен. проверяется на компилируемость
  2. type requirements. что начинается с typename, наример typename T::value_type. проверяется существует ли тип
  3. nested requirements. что начинается с requires проверяется на истинность (третий requires :)) (тоже самое, что первый requires, разница в том, что до этого может ещё чето проверить на компилируемость, прежде чем проверять на истинность)
  4. compound requirements. экспрешен -> концепт/метафункция (это не оператор ->, а специальная ->)

nested requirement + type requirement

concepts

концепты - наборы requirement’ов, объединённые общим названием

compound requirement

теперь можно сокращать template <typename It> requires InputIterator<It> до template <InputIterator It>

можно писать ещё короче:

это почти эквивалентно, за исключением того, что здесь beg и end могут быть разными типами, тк auto друг от друга не зависят. если добавить is_same, то будет эквивалентно

перегрузка по концептам

компилятор не умеет делать перегрузку по requires-экспрешенам (не умеет понимать, что частный случай, а что общий), но умеет по концептам. она работает, только если компилятор сумел доказать, что любой тип удовлетворяющий концепту B удовлетворяет концепту A (тогда концепт B - частный случай концепта A). если компилятор видит, что конъюнкция атомарных requiremen’ов концепта A совпадает с концептом B, но в концепте A requiremen’ов больше, то B - частный случай A компилятор конвертирует один набор концептов в ДНФ, а другой в КНФ. а потом проверяет, чтобы из каждого дизъюнкта следовал конъюнкт

если концепты одинаковы, то один поглощает другой

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

все коммутативные концепты должны через отдельный концепт проверяться

рекурсия на концептах запрещена и запрещено накладывать requires на концепт

концепты можно навешивать на auto template <std::invocable<int> auto F>

киллерфича концептов - писать интерфейсы без виртуальных методов ограничение на категорию типов и есть интерфейс:

вместо того, чтоб писать концепт -> if constexpr requires вызывает для вектора variant лямбду, которая вызывает метод, если он есть

концепты нужны для а) интерфейсов б) понятных compile-time ошибок в) чтобы ввести отношение поглощения

hasMethod implementation

второе struct hasMeow обязательно должно быть частичной специализацией с ограничениями, иначе будет redefinition