C++20 Abbreviated Function Templates
Table of Contents
- What is an Abbreviated Function Template?
- Traditional Template vs Abbreviated Template
- Key Differences
- Constrained Auto Abbreviated Functions
- Important Limitation: No Abbreviated Class Templates
- Advantages of Abbreviated Function Templates
- When to Use
- Compilation
What is an Abbreviated Function Template?
An abbreviated function template is a C++20 feature that allows you to write template functions using auto as a parameter type instead of explicitly declaring template parameters. This provides a more concise and readable syntax for function templates.
In C++20, when you use auto (or a constrained auto with concepts) as a function parameter type, the compiler automatically treats it as a template parameter. Each auto parameter introduces an independent template type parameter.
Syntax:
// Unconstrained auto
auto functionName(auto param1, auto param2) {
// function body
}
// Constrained auto with concepts
auto functionName(ConceptName auto param1, ConceptName auto param2) {
// function body
}
This is equivalent to:
// Unconstrained equivalent
template<typename T1, typename T2>
auto functionName(T1 param1, T2 param2) {
// function body
}
// Constrained equivalent
template<ConceptName T1, ConceptName T2>
auto functionName(T1 param1, T2 param2) {
// function body
}
Traditional Template vs Abbreviated Template
Traditional Template Function
#include <iostream>
#include <typeinfo>
template <typename T>
T min(const T& a, const T& b) {
std::cout << "Type of a: " << typeid(a).name()
<< " Type of b: " << typeid(b).name() << " Min: ";
return a < b ? a : b;
}
int main() {
std::cout << min(1, 2) << std::endl;
std::cout << min(2.7, 2.5) << std::endl;
std::cout << min('a', 'b') << std::endl;
return 0;
}
Output:
Type of a: i Type of b: i Min: 1
Type of a: d Type of b: d Min: 2.5
Type of a: c Type of b: c Min: a
Template Expansion (using clang++ -std=c++20 -Xclang -ast-print -fsyntax-only):
template <typename T> T min(const T &a, const T &b) {
std::cout << "Type of a: " << typeid(a).name()
<< " Type of b: " << typeid(b).name() << " Min: ";
return a < b ? a : b;
}
template<> int min<int>(const int &a, const int &b) { /* ... */ }
template<> double min<double>(const double &a, const double &b) { /* ... */ }
template<> char min<char>(const char &a, const char &b) { /* ... */ }
C++20 Abbreviated Template Function
#include <iostream>
#include <typeinfo>
auto min(const auto& a, const auto& b) {
std::cout << "Type of a: " << typeid(a).name()
<< " Type of b: " << typeid(b).name() << " Min: ";
return a < b ? a : b;
}
int main() {
std::cout << min(1, 2) << std::endl;
std::cout << min(2.7, 2.5) << std::endl;
std::cout << min('a', 'b') << std::endl;
return 0;
}
Output:
Type of a: i Type of b: i Min: 1
Type of a: d Type of b: d Min: 2.5
Type of a: c Type of b: c Min: a
Template Expansion (using clang++ -std=c++20 -Xclang -ast-print -fsyntax-only):
auto min(const auto &a, const auto &b) {
std::cout << "Type of a: " << typeid(a).name()
<< " Type of b: " << typeid(b).name() << " Min: ";
return a < b ? a : b;
}
template<> int min<int, int>(const int &a, const int &b) { /* ... */ }
template<> double min<double, double>(const double &a, const double &b) { /* ... */ }
template<> char min<char, char>(const char &a, const char &b) { /* ... */ }
Key Differences
- Syntax: The abbreviated form uses
autoinstead of explicittemplate<typename T>declaration - Each
autois independent: Notice in the expansion that the abbreviated version createsmin<int, int>,min<double, double>, etc., meaning eachautoparameter is a separate template parameter - Readability: The abbreviated syntax is more concise and resembles regular function syntax
Equivalent Template Syntax
The abbreviated function:
auto min(const auto& a, const auto& b) {
std::cout << "Type of a: " << typeid(a).name()
<< " Type of b: " << typeid(b).name() << " Min: ";
return a < b ? a : b;
}
Is exactly equivalent to:
template<typename T1, typename T2>
auto min(const T1& a, const T2& b) {
std::cout << "Type of a: " << typeid(a).name()
<< " Type of b: " << typeid(b).name() << " Min: ";
return a < b ? a : b;
}
Important: Each auto parameter becomes an independent template parameter (T1, T2). This means the function can accept two different types, such as min(5, 3.14) where a is int and b is double.
Constrained Auto Abbreviated Functions
C++20 also allows you to add constraints to abbreviated function templates using concepts. This ensures that the template parameters meet certain requirements.
The Problem with Unconstrained Auto
Consider this example with a custom type:
#include <iostream>
#include <typeinfo>
#include <string>
struct StudentId {
std::string name;
std::string id;
};
auto min(const auto& a, const auto& b) {
std::cout << "Type of a: " << typeid(a).name()
<< " Type of b: " << typeid(b).name() << " Min: ";
return a < b ? a : b; // Error! StudentId doesn't have operator<
}
int main() {
StudentId s1{"Alice", "001"};
StudentId s2{"Bob", "002"};
std::cout << min(1, 2) << std::endl; // Works
std::cout << min(s1, s2) << std::endl; // Compilation Error!
return 0;
}
Error: StudentId doesn’t have operator< defined, so the comparison a < b fails.
Solution: Using Concepts with Constrained Auto
We can create a custom concept to constrain our function:
#include <iostream>
#include <typeinfo>
#include <string>
#include <concepts>
struct StudentId {
std::string name;
std::string id;
// Define comparison operator
bool operator<(const StudentId& other) const {
return name < other.name;
}
};
// Custom concept for types that support < operator
template<typename T>
concept Comparable = requires(T a, T b) {
{ a < b } -> std::convertible_to<bool>;
};
auto min(const Comparable auto& a, const Comparable auto& b) {
std::cout << "Type of a: " << typeid(a).name()
<< " Type of b: " << typeid(b).name() << " Min: ";
return a < b ? a : b;
}
int main() {
StudentId s1{"Alice", "001"};
StudentId s2{"Bob", "002"};
std::cout << min(1, 2) << std::endl;
std::cout << min(2.7, 2.5) << std::endl;
std::cout << min('a', 'b') << std::endl;
auto result = min(s1, s2);
std::cout << result.name << " (ID: " << result.id << ")" << std::endl;
return 0;
}
Output:
Type of a: i Type of b: i Min: 1
Type of a: d Type of b: d Min: 2.5
Type of a: c Type of b: c Min: a
Type of a: 9StudentId Type of b: 9StudentId Min: Alice (ID: 001)
The Comparable concept checks if a type supports the < operator:
template<typename T>
concept Comparable = requires(T a, T b) {
{ a < b } -> std::convertible_to<bool>;
};
This requires that for type T, the expression a < b must be valid and convertible to bool.
Equivalent Traditional Syntax
The constrained abbreviated function is equivalent to:
template<Comparable T1, Comparable T2>
auto min(const T1& a, const T2& b) {
std::cout << "Type of a: " << typeid(a).name()
<< " Type of b: " << typeid(b).name() << " Min: ";
return a < b ? a : b;
}
Benefits of Constrained Auto
- Compile-time error checking: Catch type errors early with clear error messages
- Self-documenting code: The constraint explains what types are acceptable
- Better IDE support: IDEs can provide better autocomplete and hints
- Type safety: Prevents misuse of generic functions
Important Limitation: No Abbreviated Class Templates
C++20 does NOT support abbreviated class templates. You cannot write:
// This is NOT valid C++20
class MyClass<auto T> { // Error!
T value;
};
Reason: Abbreviated function templates work because the compiler can deduce template parameters from function arguments at the call site. Class templates require explicit instantiation (e.g., MyClass<int>), so there’s no argument deduction context for auto to work with.
You must still use traditional template syntax for classes:
// Correct way for class templates
template<typename T>
class MyClass {
T value;
public:
MyClass(T v) : value(v) {}
// But member functions CAN use abbreviated templates!
auto add(auto other) {
return value + other;
}
auto compare(const auto& other) const {
return value < other;
}
};
Example Usage:
int main() {
MyClass<int> obj(10); // Class needs explicit type
std::cout << obj.add(5) << std::endl; // Member function: auto deduces int
std::cout << obj.add(3.14) << std::endl; // Member function: auto deduces double
std::cout << obj.compare(20) << std::endl; // Member function: auto deduces int
return 0;
}
Output:
15
13.14
1
This demonstrates that while class templates must use traditional syntax, their member functions can freely use abbreviated function template syntax.
Advantages of Abbreviated Function Templates
- Conciseness: Less boilerplate code
- Readability: Easier to read and understand at a glance
- Flexibility: Each
autocan deduce to a different type - Modern: Aligns with modern C++ practices
When to Use
Abbreviated function templates are ideal for:
- Simple generic functions
- Lambda expressions
- Functions where the template nature is obvious from context
- Reducing syntactic noise in template-heavy code
For complex templates with constraints, explicit template syntax or C++20 concepts may be more appropriate for clarity.
Compilation
To compile code using abbreviated function templates:
g++ -std=c++20 program.cpp -o program
clang++ -std=c++20 program.cpp -o program