Note that a class template is not a class, it's something you use to generate classes.
Similarly, a function template is not a function, it's something you use to generate functions.
Each function parameter always have one exact type.
If you declare a function parameter using a class template where you specify a type inside angle brackets then that's no longer a template. It's a type (a class type).
1 2 3 4 5 6 7 8 9
|
struct ExampleInt
{
int x;
};
void foo(ExampleInt e) // ExampleInt is a class type
{
std::cout << e.x << "\n";
}
| |
1 2 3 4 5 6 7 8 9 10
|
template <typename T>
struct Example
{
T x;
};
void foo(Example<int> e) // Example<int> is a class type
{
std::cout << e.x << "\n";
}
| |
foo is a function in both of the above examples. The only difference is that in the first example the type of the parameter is named
ExampleInt
and in the second example it's named
Example<int>
.
If you change foo to use
template <typename T>
or
auto
then foo is no longer a function. It's a
function template.
1 2 3 4 5
|
template <typename T>
void foo(Example<T> e)
{
std::cout << e.x << "\n";
}
| |
T is a
template argument and it must be known when you later call foo.
You can either specify it explicitly inside
<>
|
foo<int>({5}); // T=int; calls foo<int>
| |
or you could let it deduce the type from the function arguments
|
foo(Example<int>{5}); // T=int; calls foo<int>
| |
Template argument deduction for function templates has existed in C++ from the beginning (AFAIK). It doesn't mean you can always rely on the template arguments to be deduced. The template argument might not be used in the function parameter list or it might be used multiple times and therefore be ambiguous if you supply different types. E.g.
std::max(5, 7.0);
will fail to compile because you are passing an int and a double and it's not clear which type to use.
Class template argument deduction (CTAD) is a new thing that was added in C++17 to allow similar deduction of template arguments for class templates. Just like with function templates it cannot work in all cases.
Examples when declaring a local variable:
|
Example e{5}; // T=int; e is an instance of type Example<int>
| |
|
Example e{}; // error: T cannot be deduced
| |
With user-defined constructors it can be even more tricky for the compiler to deduce the correct types. I'm no expert on this but I know you can write "deduction guides" to help CTAD work correctly in those cases.