Variadic templates are a powerful feature in C++ that allow you to write functions, classes, or other templates that can accept an arbitrary number of template arguments. Introduced in C++11, variadic templates are a key component of modern C++ metaprogramming, enabling more flexible and reusable code.
The basic syntax for variadic templates involves using an ellipsis (...) to declare a template parameter pack and to unpack it.
template<typename... Args>
void functionName(Args... args) {
// Function body
}
Here's a simple example of a variadic template function that prints all arguments passed to it:
#include <iostream>
template<typename... Args>
void printAll(Args... args) {
(std::cout << ... << args) << std::endl; // Fold expression in C++17
}
int main() {
printAll(1, 2.5, "Hello", 'A'); // Outputs: 1 2.5 Hello A
return 0;
}
template<typename... Args>
defines a variadic template with a parameter pack Args.(std::cout << ... << args) << std::endl;
is a fold expression (introduced in C++17)
that expands the parameter pack and prints each argument.printAll
function can accept any number of arguments of any type.Before C++17, variadic templates often used recursion to process each argument individually. Here’s an example of a function that prints each argument on a new line:
#include <iostream>
// Base case: No arguments left
void print() {
std::cout << "End of arguments" << std::endl;
}
// Recursive case: Process one argument and recurse
template<typename T, typename... Args>
void print(T first, Args... args) {
std::cout << first << std::endl; // Print the first argument
print(args...); // Recursively call print with the remaining arguments
}
int main() {
print(1, 2.5, "Hello", 'A');
return 0;
}
print()
is the base case that handles the scenario when no arguments are left.template<typename T, typename... Args> void print(T first, Args... args)
is the recursive case
that processes the first argument and then recursively calls print with the remaining arguments.print
is called recursively until all arguments have been processed.Variadic templates can also be used in class templates. Here's an example of a simple tuple-like class that can hold an arbitrary number of elements:
#include <iostream>
#include <tuple>
template<typename... Args>
class SimpleTuple {
public:
SimpleTuple(Args... args) : data(args...) {}
void print() const {
printTuple(data);
}
private:
std::tuple<Args...> data;
template<std::size_t I = 0>
void printTuple(const std::tuple<Args...>& t) const {
if constexpr (I < sizeof...(Args)) {
std::cout << std::get<I>(t) << " ";
printTuple<I + 1>(t);
} else {
std::cout << std::endl;
}
}
};
int main() {
SimpleTuple<int, double, std::string> tuple(1, 2.5, "Hello");
tuple.print(); // Outputs: 1 2.5 Hello
return 0;
}
template<typename... Args>
declares a variadic template class SimpleTuple.SimpleTuple(Args... args)
initializes the std::tuple member data with the passed arguments.print()
method uses a helper function printTuple to recursively print each element in the tuple.constexpr
is used to perform compile-time recursion over the tuple elements (this requires C++17 or later).#include <iostream>
class Base1 {
public:
void show() const { std::cout << "Base1\n"; }
};
class Base2 {
public:
void display() const { std::cout << "Base2\n"; }
};
template<typename... Bases>
class Derived : public Bases... {
public:
void showAll() const {
(Bases::show(), ...);
}
void displayAll() const {
(Bases::display(), ...);
}
};
int main() {
Derived<Base1, Base2> obj;
obj.showAll(); // Calls Base1::show()
obj.displayAll(); // Calls Base2::display()
return 0;
}
template<typename... Bases>
declares a variadic template Derived class that inherits from all types in the Bases parameter pack.Derived<Base1, Base2>
creates a class that inherits from both Base1 and Base2.showAll()
and displayAll()
use fold expressions to call the respective methods from the base classes.typename...
(or class...
) and can be expanded using ...
.Variadic templates are a powerful tool in modern C++ programming, enabling more flexible and reusable code. They are particularly useful in metaprogramming and in scenarios where you need to handle an unknown number of arguments or types.
Previous Page | Course Schedule | Course Content