A template generates type-specific code from one definition. Correct generic code depends on the operations it actually uses, not assumptions about one example type. Modern constraints make those requirements visible and produce errors closer to the call site.
std::totally_ordered documents the operations needed by clamp_value and rejects unsuitable types during overload resolution.
#include <concepts>
#include <iostream>
template<std::totally_ordered T>
T clamp_value(T value, T low, T high) {
if (value < low) return low;
if (high < value) return high;
return value;
}
int main() {
std::cout << clamp_value(15, 0, 10) << '\n';
std::cout << clamp_value(2.5, 1.0, 4.0) << '\n';
}
10
2.5
Function arguments often allow type deduction; class template argument deduction works only where constructors or deduction guides provide enough information. Specialize behavior sparingly because overloads and policy objects are often easier to compose.
Function templates are instantiated when used with concrete arguments. Definitions usually remain visible in headers unless explicit instantiation is designed deliberately. This can increase compile time, so avoid placing unrelated implementation or heavy includes in a widely used template header.
Use concepts or requires expressions to state semantic requirements such as sortable or contiguous input. Perfect forwarding and specialization are advanced tools with sharp edges; begin with value, reference, and const-reference parameters that express ownership plainly. Test multiple types, including one that should be rejected.
A template is checked again when it is instantiated with a concrete type. If the body uses ordering, addition, iteration, or copying, that operation is part of the real interface even when the function signature does not say so. C++20 concepts let the declaration state those requirements and produce diagnostics near the call.
Keep the requirement no stronger than the implementation needs. Requiring an exact concrete type or unnecessary operations makes a reusable template reject valid types.
#include <concepts>
#include <iostream>
template <typename T>
requires std::totally_ordered<T>
const T& maximum(const T& left, const T& right) {
return left < right ? right : left;
}
int main() {
std::cout << maximum(7, 11) << '\n';
}
11
The concept documents and checks the ordering contract before the function body is instantiated.
Try this next
0 of 2 completed
The compiler needs the full definition when it instantiates a template for a particular type.
It fails when the arguments do not provide enough type information or imply conflicting types.
Concepts state type requirements near the template and usually produce clearer errors than a failed instantiation deep inside its body.
Explore 500+ free tutorials across 20+ languages and frameworks.