Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can the type constraint `std::convertible_to` be used with only one template argument?

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?

like image 878
aradarbel10 Avatar asked Aug 23 '21 00:08

aradarbel10


People also ask

How will you restrict the template for a specific datatype?

There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.

What is a constraint in c++?

A constraint is a requirement that types used as type arguments must satisfy. For example, a constraint might be that the type argument must implement a certain interface or inherit from a specific class. Constraints are optional; not specifying a constraint on a parameter is equivalent to using a Object constraint.

What is template argument in C++?

In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

How do you declare a concept in C++?

Let's define a few concepts in this post. A concept can be defined by a function template or by a variable template. A variable template is new with C++14 and declares a family of variables. If you use a function template for your concept, it's called a function concept; in the second case a variable concept.


2 Answers

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.)

like image 167
Yakk - Adam Nevraumont Avatar answered Oct 14 '22 10:10

Yakk - Adam Nevraumont


There are several locations where a concept name can be used where the first argument to the template concept is not supplied in the template argument list. Constraining an auto deduced variable is one of them.

The first argument in these cases is provided by some expression, typically using template argument deduction rules. In the case of a constrained function parameter, the first argument is determined by the template function itself. That is, if you call foo(10), template argument deduction will deduce the auto template parameter as an int. Therefore, the full concept will be convertible_to<int, A>.

like image 40
Nicol Bolas Avatar answered Oct 14 '22 10:10

Nicol Bolas