Function Templates – More Details about Explicit Template Arguments and Concepts
In the last post, “Function Templates“, I wrote about the overloading of function templates and automatically deducing the return type of a function template. Today, I dive deeper and explicitly specify a function template’s arguments and bring concepts into the play.
Before I start this post, I have to make two general remarks. Today, I write about a don’t and a do.
- Don’t: In general, you should not explicitly specify the template arguments for function templates.
- Do: You should generally use restricted template parameters (concepts).
Let me start with the don’t.
Explicitly Specifying the Template Arguments
You can explicitly specify the template arguments. This is necessary if the compiler cannot deduce the type parameters of the function templates or if you use class templates. With C++17, the compiler can automatically deduce the type of the template arguments from the constructor arguments:
std::vector<int> myVec{1, 2, 3, 4, 5}; // (1) std::vector myVec{1, 2, 3, 4, 5}; // (2)
Instead of line (1), you can use line (2) in C++17. I will write more about this feature in an upcoming post.
Once more. In general, you should not specify the template arguments. But I intentionally did it.
Modernes C++ Mentoring
Do you want to stay informed: Subscribe.
// maxExplicitTypeParameter.cpp template <typename T> T max(const T& lhs,const T& rhs) { return (lhs > rhs)? lhs : rhs; } int main() { auto res1 = max<float>(5.5, 6.0); auto res2 = max<bool>(5.5, 6.0); auto res3 = max(5.5, 6.0); }
What is happening in lines (1) – (3)? C++ Insights helps me to analyze the code. These are the crucial output lines:
- The call
max<float>(5.5, 6.0)
in line (1) causes the instantiation of the function templatemax
fordouble
(line 10). Consequently, both doubles are converted toconst float
(line 40). - The call
max<bool>(5.5, 6.0)
in line (2) puts a lot of work on the compiler’s shoulder.- The invocation causes the compiler to convert the doubles to implicitly
bool.
- To compare both bools inside the function body (line 23), they must be promoted to
int
(line 23). - Finally, the return type
res2
is bool. Consequently, theint
must be converted tobool
.
- The invocation causes the compiler to convert the doubles to implicitly
- The call
max(5.5, 6.0)
in line (3) does precisely the job you want. No conversion or promotion is necessary.
Honestly, I would consider the max<bool>(5.5, 6.0)
as an error and not intentional. But this happens when you want to be brighter than the compiler.
There is a related syntax to the explicit specification of template arguments that you may sometimes see but puzzles you: max<>(5.5, 6.0);
When do I ask in my seminars: What could that mean? Based on the previous theory, half of my participants would guess it right
Imagine you have a function and a function template max
:
// maxCompilerDeduction.cpp double max(const double& lhs, const double& rhs) { return (lhs > rhs)? lhs : rhs; } template <typename T> T max(const T& lhs,const T& rhs) { return (lhs > rhs)? lhs : rhs; } int main() { auto res1 = max(5.5, 6.0); // (1) auto res2 = max<>(5.5, 6.0); // (2) }
As we learned in the previous post, “Function Templates“, the compiler prefers the function when the function and the function templates are ideal fits. Okay, this answers line (1). Line (2) expresses that the compiler should only consider the function template max
and ignore the function max
. Additionally, the compiler automatically deduces the template parameters for the function arguments. Consequently, C++ Insights shows that the compiler instantiated max
for double
.
So far, I have only considered function overloading with functions and function templates having unrestricted type parameters. Okay, I can do better and should. Now, I bring restricted type parameters (concepts) into the play. This means here is my do for this post: Use restricted type parameters if possible.
Overloading with Concepts
C++20 has the concept std::totally_ordered
. A type T
supports a total order if it supports partial order and any elements of T can be compared. Let me be more formal:
A type T supports partial order if the following relations for all elements a, b, and c of the type T hold:
- a <= a (reflexive)
- If a <= b and b <= c then a <= c (transitive)
- If a <=b and b <= a then a == b (antisymmetric)
A type T supports total order if it supports partial order and all elements of T can be compared.
- a <= b or b <= b (comparable)
The following program uses the concept std::totally_ordered
:
// maxUnconstrainedConstrained.cpp #include <iostream> #include <concepts> class Account { public: explicit Account(double b): balance(b) {} double getBalance() const { return balance; } private: double balance; }; Account max(const Account& lhs, const Account& rhs) { // (1) std::cout << "max function\n"; return (lhs.getBalance() > rhs.getBalance())? lhs : rhs; } template <std::totally_ordered T> // (2) T max(const T& lhs,const T& rhs) { std::cout << "max restricted function template\n"; return (lhs > rhs)? lhs : rhs; } template <typename T> // (3) T max(const T& lhs,const T& rhs) { std::cout << "max unrestriced function template\n"; return (lhs > rhs)? lhs : rhs; } int main() { Account account1(50.5); Account account2(60.5); Account maxAccount = max(account1, account2); // (4) int i1{50}; int i2(60); int maxI = max(i2, i2); // (5) }
The program defines a function max
taking two Accounts (line 1) and two function templates. The first function template max
in line (2) requires that the values support a total ordering. The second function template max
has no type constraints on its type parameters. As you might expect, the compiler chooses the best-fitting overload. A function template a fits better than another function template b if a is more specialized than b. This means it chooses the function for Accounts (line 4) and the function template max
with restricted type parameters for int
(line 5).
The comments in the various max
function make the decisions of the compiler transparent.
You can study the program on Compiler Explorer.
What’s next?
After the basics to function templates, I will present the basics of class templates in my next post. Additionally, I will write in this context about generic member functions, inheritance with templates, and alias templates.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery,and Matt Godbolt.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero | |
My special thanks to PVS-Studio | |
My special thanks to Tipi.build | |
My special thanks to Take Up Code | |
My special thanks to SHAVEDYAKS |
Seminars
I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
Standard Seminars (English/German)
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
- C++ – The Core Language
- C++ – The Standard Library
- C++ – Compact
- C++11 and C++14
- Concurrency with Modern C++
- Design Pattern and Architectural Pattern with C++
- Embedded Programming with Modern C++
- Generic Programming (Templates) with C++
- Clean Code with Modern C++
- C++20
Online Seminars (German)
- Embedded
Programmierung mit modernem C++ (24. Sep. 2024 bis 26.
Sep. 2024)
Contact Me
- Mobil: +49 176 5506 5086
- Mail: schulung@ModernesCpp.de
- German Seminar Page: www.ModernesCpp.de
- Mentoring Page: www.ModernesCpp.org
Modernes C++ Mentoring,
Leave a Reply
Want to join the discussion?Feel free to contribute!