Function pointers and function templates can interact in interesting and powerful ways, but they also present some challenges. Let's explore this relationship in detail.
First, let's remind ourselves what a function template looks like:
#include <iostream>
template<typename T>
T Add(T a, T b) {
return a + b;
}
int main() {
std::cout << Add(5, 3) << "\n";
std::cout << Add(5.5, 3.2);
}
8
8.7
The compiler generates specific functions based on the types used when calling the template function.
When using function pointers with template functions, we need to be explicit about which instantiation of the template we're referring to:
#include <iostream>
template <typename T>
T Multiply(T a, T b) { return a * b; }
int main() {
int (*intMultiply)(int, int) = Multiply<int>;
double (*doubleMultiply)(double, double) =
Multiply<double>;
std::cout << intMultiply(5, 3) << "\n";
std::cout << doubleMultiply(5.5, 3.2);
return 0;
}
15
17.6
Here, we're creating function pointers to specific instantiations of the Multiply
template function.
One challenge is that template argument deduction doesn't work with function pointers. This won't compile:
template<typename T>
T Square(T x) {
return x * x;
}
int main() {
auto funcPtr = Square;
}
error: unable to deduce 'auto' from 'Square'
The compiler can't deduce which instantiation of Square
to use because there's no context for type deduction.
We can solve this in a few ways. We can specify the template arguments explicitly:
#include <iostream>
template<typename T>
T Square(T x) {
return x * x;
}
int main() {
auto funcPtr = Square<int>;
std::cout << funcPtr(5);
}
25
Alternatively, use a type alias with a specific type:
#include <iostream>
template<typename T>
T Square(T x) {
return x * x;
}
using IntSquare = int(*)(int);
int main() {
IntSquare funcPtr = Square;
std::cout << funcPtr(5);
}
25
Function templates can also have non-type parameters, which can be used with function pointers:
#include <array>
#include <iostream>
template <typename T, size_t Size>
void PrintArray(
const std::array<T, Size>& arr) {
for (const auto& item : arr) {
std::cout << item << " ";
}
std::cout << "\n";
}
int main() {
std::array<int, 3> intArr = {1, 2, 3};
std::array<double, 2> doubleArr = {1.1, 2.2};
void (*intPrinter)(const std::array<int, 3>&)
= PrintArray;
void (*doublePrinter)(
const std::array<double, 2>&) = PrintArray;
intPrinter(intArr);
doublePrinter(doubleArr);
}
1 2 3
1.1 2.2
In this case, both the type and the non-type template parameters are part of the function pointer type.
Understanding these interactions is crucial when working with both function templates and function pointers in C++. While they provide a lot of flexibility, they also require careful handling to ensure type safety and avoid compilation errors.
Answers to questions are automatically generated and may not have been reviewed.
Learn to create flexible and modular code with function pointers