Customization Through Template Specialization
Lasted edited: 2026-07
C++ Libraries unfortunately do not provide a lot of customization points due to the language design. I can think of two main ways of providing customizations, both with downsides: macros & template parameters.
One use-case of library customization is changing print/debug statements:
namespace lib {
void helloWorld() {
customPrint("Hello World!");
}
}
With printing, I/O streams can be changed through the OS through redirections, but this isn't standard and within the language. Another more complex use-case is having custom allocators or even object tracking.
Macros
#ifndef LIB_PRINT
# define LIB_PRINT(M) std::println(M)
#endif
namespace lib {
void helloWorld() {
MY_LIB_PRINT("Hello World!");
}
}
#define MY_LIB_PRINT(M) std::println(std::cerr, M)
#include <lib.h>
int main() {
lib::helloWorld();
}
This is the most common way I've seen implementations handle customization points where you define a macro prior to including the header.
The biggest downside is this does not work with C++ modules as macros are encapsulated within their files.
Template Function Objects
namespace lib {
struct Printer {
void operator()(auto&&... args) {
std::println(std::forward<decltype(args)>(args)...);
}
}
template <class Print = Printer>
void helloWorld(Print printer = {}) { // default construct the printer if no dynamic object is passed in.
std::invoke(printer, "Hello World!");
}
}
#include <lib.h>
struct MyPrinter {
void operator()(auto&&... args) {
std::println(std::cerr, std::forward<decltype(args)>(args)...);
}
}
int main() {
lib::helloWorld<MyPrinter>();
}
This is the most STL-way to do it and allows for multiple instances of the function with different underlying printers (which can be an advantage). You can move some of the boilerplate by allowing normal functions to be passed into the template and/or only allowing for static operator(), but you do lose the customizability by allowing dynamic objects.
In the case where you need the entire library to change, it is tedious to write the same template argument the entire time. For class we can use using declarations, but for functions we can use P2826: Expression Aliases (this avoids needing to write perfect-forwarding).
Hidden Template Specialization
namespace lib {
template <bool Override = true>
struct Printer {
static void operator()(auto&&... args) { std::println(std::forward<decltype(args)>(args)...); };
}
void helloWorld() {
Printer<>("Hello World!");
}
}
#include <lib.h>
template <>
struct Printer<true> {
static void operator()(auto&&... args) { std::println(std::forward<decltype(args)>(args)...); };
}
int main() {
helloWorld();
}