Skip to content

Prefer callables over inheritance for simple behavioral patterns

Use free functions, lambdas, or other callable objects instead of inheritance hierarchies for simple behavioral customization.

Callable objects offer:

  • Value Semantics - avoids complexities and risks from Reference Semantics
  • Zero-overhead abstractions - lambdas can be inlined completely
  • Composability - can be stored, passed, and combined easily
  • Leaner - less boilerplate (no base class, virtual destructor)
  • Loose coupling - no dependency on a specific base class; easy to extend or substitute behavior
#include <algorithm>
class TextFormatter {
public:
virtual std::string format(const std::string& text) const = 0;
virtual ~TextFormatter() = default;
};
std::string format_text(const std::string& text, const TextFormatter& formatter)
{
return formatter.format(text);
}
class UpperCaseFormatter : public TextFormatter {
public:
std::string format(const std::string& text) const override {
std::string result = text;
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
return result;
}
};
// Usage
auto formatted = format_text("Hello world", UpperCaseFormatter{});
#include <algorithm>
using TextFormatter = std::function<std::string(const std::string&)>;
std::string format_text(const std::string& text, const TextFormatter& formatter)
{
return formatter(text);
}
TextFormatter toUppercase = [](const std::string& text) {
std::string result = text;
std::transform(text.begin(), text.end(), result.begin(), ::toupper);
return result;
};
// Usage
auto formatted = format_text("Hello world", toUppercase);
#include <algorithm>
template<typename Formatter>
auto format_text(const std::string& text, Formatter formatter)
{
return formatter(text);
}
auto toUppercase = [](const std::string& text) {
std::string result = text;
std::transform(text.begin(), text.end(), result.begin(), ::toupper);
return result;
};
// Usage
auto formatted = format_text("Hello world", toUppercase);
#include <algorithm>
#include <concepts>
#include <ranges>
auto format_text(const std::string& text,
std::invocable<const std::string&> auto formatter) {
return formatter(text);
}
auto toUppercase = [](const std::string& text) {
std::string result = text;
std::ranges::transform(text, result.begin(), ::toupper);
return result;
};
// Usage
auto formatted = format_text("Hello world", toUppercase);

This guideline works best for:

  • Simple behavioral variations
  • Stateless or minimal state operations
  • Cases where the “strategy” is just an algorithm

For complex strategies with significant state or multiple methods, inheritance may still be appropriate.