Ready to practice?
Sign up to access interactive coding exercises and track your progress.
Advanced Function Techniques and Templates Summary
Review and test your understanding of all advanced function and template concepts covered in this chapter.
Chapter Summary: Advanced Function Techniques and Templates
Congratulations on completing this chapter! You've learned powerful techniques that make C++ code more flexible and reusable. Let's review the key concepts with examples.
Defining Overloaded Functions
Function overloading lets you define multiple functions with the same name, differentiated by their parameter lists:
#include <iostream>
#include <string_view>
void print(int value) {
std::cout << "Integer: " << value << '\n';
}
void print(double value) {
std::cout << "Double: " << value << '\n';
}
void print(std::string_view value) {
std::cout << "String: " << value << '\n';
}
int main() {
print(42); // Calls print(int)
print(3.14); // Calls print(double)
print("Hello"); // Calls print(std::string_view)
return 0;
}
The compiler selects the appropriate version based on the arguments you provide. Return types alone cannot differentiate overloaded functions.
Distinguishing Between Overloads
Function overloads are differentiated by their parameter lists. The number, types, and order of parameters all matter:
void process(int x, double y); // Different from below
void process(double x, int y); // Order matters
void calculate(int x); // Different from below
void calculate(int x, int y); // Number of parameters matters
Resolving Ambiguous Overloads
When the compiler resolves function calls, it follows a specific order:
- Exact matches (including trivial conversions)
- Matches via numeric promotions
- Matches via numeric conversions
- Matches via user-defined conversions
An ambiguous match occurs when multiple functions match equally well:
void foo(int x);
void foo(double x);
int main() {
foo(5L); // Error: ambiguous - long converts equally to int and double
return 0;
}
Explicitly Deleted Functions
The = delete specifier prevents specific function calls from compiling:
void processValue(int x) {
std::cout << "Processing: " << x << '\n';
}
void processValue(double) = delete; // Prevent double calls
int main() {
processValue(5); // OK
// processValue(3.14); // Error: use of deleted function
return 0;
}
Deleted functions participate in overload resolution—if chosen as the best match, compilation fails with an error.
Optional Parameter Values
Default arguments provide fallback values for function parameters:
#include <iostream>
#include <string_view>
void greet(std::string_view name, std::string_view greeting = "Hello") {
std::cout << greeting << ", " << name << "!\n";
}
int main() {
greet("Alice"); // Uses default: "Hello, Alice!"
greet("Bob", "Good morning"); // "Good morning, Bob!"
return 0;
}
Parameters with defaults must appear rightmost in the parameter list. Default arguments aren't part of a function's signature and don't help differentiate overloaded functions.
Writing Generic Functions
Function templates are blueprints for generating functions. They use type template parameters (placeholders for types) that get replaced with actual types when instantiated:
#include <iostream>
template <typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << max(3, 7) << '\n'; // Uses max<int>
std::cout << max(3.5, 2.1) << '\n'; // Uses max<double>
return 0;
}
Generating Functions from Templates
The process of creating actual functions from templates is called instantiation. When instantiation occurs due to a function call, it's implicit instantiation:
template <typename T>
T square(T value) {
return value * value;
}
int main() {
int a{ square(5) }; // Implicitly instantiates square<int>
double b{ square(2.5) }; // Implicitly instantiates square<double>
// Explicit template argument
auto c = square<long>(10); // Explicitly instantiates square<long>
return 0;
}
Multi-Parameter Templates
Templates can use multiple type parameters, allowing functions to accept arguments of different types:
#include <iostream>
template <typename T, typename U>
auto add(T a, U b) {
return a + b; // Return type deduced by compiler
}
int main() {
auto result1 = add(5, 3.14); // int + double = double
auto result2 = add(2.5f, 10); // float + int = float
std::cout << result1 << '\n'; // 8.14
std::cout << result2 << '\n'; // 12.5
return 0;
}
The auto return type lets the compiler deduce the return type from the function body.
Compile-Time Value Parameters
Templates can also use value parameters (like integers) known at compile time:
#include <iostream>
template <typename T, int size>
T sumArray(const T (&arr)[size]) {
T total{};
for (int i = 0; i < size; ++i) {
total += arr[i];
}
return total;
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
std::cout << sumArray(numbers) << '\n'; // 15
double values[] = {1.5, 2.5, 3.0};
std::cout << sumArray(values) << '\n'; // 7.0
return 0;
}
Non-type template parameters are useful for array sizes, configuration constants, and other compile-time values.
Organizing Templates Across Files
Unlike regular functions, template definitions must be visible wherever they're used. Typically, entire template definitions go in header files:
// math_utils.h
#pragma once
template <typename T>
T clamp(T value, T min, T max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
// main.cpp
#include "math_utils.h"
#include <iostream>
int main() {
std::cout << clamp(15, 0, 10) << '\n'; // 10
std::cout << clamp(-5, 0, 10) << '\n'; // 0
return 0;
}
For organization, implementations can go in separate .tpp or .inl files that are included by the header.
Key Terminology
| Term | Definition |
|---|---|
| Overloaded function | Multiple functions sharing the same name, differentiated by parameters |
| Overload resolution | Process of selecting which overloaded function to call |
| Ambiguous match | When multiple functions match equally well |
| Default argument | Pre-specified value for a parameter |
| Function template | Pattern for generating related functions |
| Type template parameter | Placeholder for a type in a template |
| Template parameter declaration | Syntax defining template parameters |
| Function template instantiation | Creating concrete functions from templates |
| Implicit instantiation | Automatic instantiation triggered by function calls |
| Function instance | Concrete function generated from a template |
| Template argument deduction | Automatic determination of template types from arguments |
| Generic programming | Programming style using templates |
| Abbreviated function template | Function using auto parameters (C++20) |
| Non-type template parameter | Template parameter representing a compile-time value |
Looking Ahead
Templates are fundamental to modern C++ and power much of the standard library. In later chapters, you'll encounter:
- Class templates for creating generic data structures
- Template specialization for handling specific types differently
- Variadic templates for functions accepting any number of arguments
- Advanced template techniques like SFINAE and concepts
Understanding function templates provides the foundation for these more advanced topics. You'll find templates everywhere in professional C++ code—they're essential tools for writing flexible, reusable software.
Advanced Function Techniques and Templates Summary - Quiz
Test your understanding of the lesson.
Practice Exercises
Generic Math Library
Build a math library combining templates, overloading, and default arguments.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!