Parameter pack expansion in C++ is a feature used in template programming, particularly with variadic templates, where you can work with an arbitrary number of template parameters. It allows functions and classes to accept and process any number of template arguments, making your code more flexible and general.
Here's a basic rundown of how parameter pack expansion works:
-
Variadic Templates: Introduced in C++11, these templates allow you to write functions and classes that can take a variable number of template arguments. This is achieved using an ellipsis (
...
) syntax. -
Parameter Pack: A template parameter pack is a set of zero or more template parameters. Similarly, a function parameter pack is a set of zero or more function parameters.
-
Expansion: Parameter pack expansion occurs when you use the pack in a context where multiple elements are expected, with the ellipsis (
...
) indicating where and how the expansion should take place.
For example, consider a simple function template that prints all its arguments:
template<typename... Args>
void print(Args... args) {
(std::cout << ... << args) << '\n';
}
In this function, Args
is a template parameter pack representing a variable number of arguments, and args
is a function parameter pack. The line (std::cout << ... << args)
is where the expansion occurs. It expands to a series of insertions into std::cout
for each element in the args
pack.
If you call print(1, "string", 3.14)
, the expansion results in something equivalent to std::cout << 1 << "string" << 3.14
.
Let's explore some practical, real-world scenarios where parameter pack expansion in C++ variadic templates can be highly useful:
Imagine you're building a software application and need a flexible logger that can accept various types of data:
template<typename... Args>
void logMessage(Args... args) {
(std::cout << ... << args) << std::endl;
}
// Usage
logMessage("User ", userId, " logged in at ", timestamp);
This function can take any number of arguments of different types and log them to the console.
A function that can sum an arbitrary number of numbers, regardless of their type (int, double, etc.), can be quite useful:
template<typename... Args>
auto sum(Args... args) -> decltype((args + ...)) {
return (args + ...);
}
// Usage
auto total = sum(1, 2, 3, 4.5); // Can mix integers and floating-point numbers
A function to print the contents of a tuple, regardless of the number and types of its elements:
#include <tuple>
template<typename Tuple, std::size_t... Is>
void printTupleImpl(const Tuple &t, std::index_sequence<Is...>) {
((std::cout << (Is == 0 ? "" : ", ") << std::get<Is>(t)), ...);
}
template<typename... Args>
void printTuple(const std::tuple<Args...>& t) {
printTupleImpl(t, std::index_sequence_for<Args...>{});
std::cout << std::endl;
}
// Usage
auto t = std::make_tuple(10, "Hello", 3.14);
printTuple(t);
Creating a type-safe version of printf
that accepts any number of arguments of various types:
template<typename... Args>
void printFormatted(const std::string& format, Args... args) {
// Implementation would involve parsing the format string and matching
// placeholders to the types of the arguments in 'args...'
// For simplicity, let's assume it just prints the arguments.
(std::cout << ... << args) << std::endl;
}
// Usage
printFormatted("Name: %s, Age: %d", "Alice", 30);
A function that applies another function to multiple arguments:
template<typename Func, typename... Args>
void applyToAll(Func f, Args... args) {
(f(args), ...);
}
// Usage
applyToAll([](int x){ std::cout << x*x << " "; }, 1, 2, 3, 4);
Building objects from a variable number of arguments, like in a factory pattern:
template<typename T, typename... Args>
T* createObject(Args... args) {
return new T(args...);
}
// Usage
class MyClass {
public:
MyClass(int, std::string) {}
// ...
};
auto obj = createObject<MyClass>(42, "Example");
In all these scenarios, variadic templates and parameter pack expansion provide a flexible, type-safe way to handle multiple arguments of varying types and numbers, which is a cornerstone of modern C++ template metaprogramming.
Refs: 1