I've scrolled and searched through the standard and cppreference for hours to no avail, would really appreciate if someone could explain this occurance for me:
I am looking at the standard concept std::convertibe_to
. Here's a simple example that I do understand
class A {};
class B : public A {};
std::convertible_to<A, B>; // false
std::convertible_to<B, A>; // true
Works as expected.
Now there is also another possible way to use it, that I don't quite understand
void foo(std::convertible_to<A> auto x) { /* ... */ }
, and this function can easily accept any type convertible to A. This is weird though, because the first template parameter ("From") is essencially dropped, and deduced on function call. This following function would also work, and I'm fairly certain it's actually equivalent to the previous one
template<typename T, std::convertible_to<T> S>
void foo(S x) { /* ... */ }
again the type of x
is deduced when we call foo
.
This works, despite the template requiring two parameters. I tried also with std::derived_from
and it seems to work. This form of specifying a concept with only one template parameter even appears in the standard itself, so there must be some piece of syntax that explains it.
Notice that the only version of std::convertible_to
that exists is in fact one that takes two template parameters.
Could anyone clarify why this works?
void foo( constraint<P0, P1, P2> auto x );
this translates roughly to
template<contraint<P0, P1, P2> X>
void foo( X x );
which translates roughly to
template<class X> requires constraint<X, P0, P1, P2>
void foo( X x );
notice how the type X
is prepended to the template arguments of the constraint.
So in your case,
template<typename T, std::convertible_to<T> S>
void foo(S x) { /* ... */ }
is roughly
template<typename T, class S>
requires std::convertible_to<S, T>
void foo(S x) { /* ... */ }
(I say roughly, because I believe they are not exactly equivalent in subtle ways. For example, the second one introduces the name X
, while the first does not. And there are probably other differences of similar scale; what I mean is that understanding the translation will give you an understanding of what is translated. This is unlike for(:)
loop-for(;;)
loop correspondence; the standard specifies for(:)
loops in terms of for(;;)
loops, which isn't what I'm claiming above.)