#c #templates #constraints #c 20 #c -concepts
Вопрос:
Я прокручивал и просматривал стандарт и cppreference в течение нескольких часов безрезультатно, был бы очень признателен, если бы кто-нибудь объяснил мне это событие:
Я рассматриваю стандартную концепцию std::convertibe_to
. Вот простой пример, который я действительно понимаю
class A {};
class B : public A {};
std::convertible_to<A, B>; // false
std::convertible_to<B, A>; // true
Работает, как и ожидалось.
Теперь есть также еще один возможный способ его использования, который я не совсем понимаю
void foo(std::convertible_to<A> auto x) { /* ... */ }
, и эта функция может легко принимать любой тип, конвертируемый в A. Это, однако, странно, потому что первый параметр шаблона («От») по существу отбрасывается и выводится при вызове функции. Эта следующая функция также будет работать, и я вполне уверен, что она на самом деле эквивалентна предыдущей
template<typename T, std::convertible_to<T> S>
void foo(S x) { /* ... */ }
снова тип x
выводится, когда мы вызываем foo
.
Это работает, несмотря на то, что шаблон требует двух параметров. Я тоже пробовал std::derived_from
, и, похоже, это сработало. Эта форма указания концепции с использованием только одного параметра шаблона даже появляется в самом стандарте, поэтому должен быть какой-то синтаксис, который ее объясняет.
Обратите внимание, что на самом деле существует только одна версия std::convertible_to
, которая принимает два параметра шаблона.
Кто-нибудь может объяснить, почему это работает?
Комментарии:
Ответ №1:
void foo( constraint<P0, P1, P2> auto x );
это примерно переводится как
template<contraint<P0, P1, P2> X>
void foo( X x );
что примерно переводится как
template<class X> requires constraint<X, P0, P1, P2>
void foo( X x );
обратите внимание, как тип X
добавляется к аргументам шаблона ограничения.
Так что в вашем случае,
template<typename T, std::convertible_to<T> S>
void foo(S x) { /* ... */ }
это примерно
template<typename T, class S>
requires std::convertible_to<S, T>
void foo(S x) { /* ... */ }
(Я говорю грубо, потому что считаю, что они не совсем эквивалентны в тонких аспектах. Например, второй вводит имя X
, в то время как первый этого не делает. И, вероятно, есть и другие различия подобного масштаба; я имею в виду, что понимание перевода даст вам понимание того, что переводится. Это не похоже for(:)
for(;;)
на соответствие по циклу; стандарт определяет for(:)
циклы в терминах for(;;)
циклов, что не соответствует тому, что я утверждаю выше.)
Комментарии:
1. Последнее предложение:
s/not like/not unlike/
?2. @Quuxplusone Нет.
for(:)
циклы определяются как буквальный перевод написанного пользователем кода C в определенный фрагментfor(;;)
кода (с некоторыми переменными с секретными именами). цикл. Это причудливая часть стандарта. Насколько я знаю, приведенная выше эквивалентность не так прямолинейна.3. Ах, я вижу различие, которое вы проводите сейчас, хотя я думаю, что оно слишком тонкое для данного контекста. Поэтому лично я бы охарактеризовал это как «…не похоже, но и не похоже,…» 😉
4. @Quuxplusone почти, но не совсем, совсем не похоже …
Ответ №2:
Существует несколько мест, где concept
можно использовать имя, в которых первый аргумент концепции шаблона не указан в списке аргументов шаблона. Ограничение auto
выводимой переменной является одним из них.
Первый аргумент в этих случаях предоставляется некоторым выражением, обычно использующим правила вывода аргументов шаблона. В случае параметра ограниченной функции первый аргумент определяется самой функцией шаблона. То есть, если вы вызовете foo(10)
, вычет аргумента шаблона выведет параметр auto
шаблона как an int
. Поэтому полная концепция будет convertible_to<int, A>
.