Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The C++17 overload pattern combines several lambdas into one callable object, so each std::variant alternative can have its own visitor lambda beside the std::visit call. The helper is only two lines; its compactness comes from three language features: pack expansion in a using-declaration, class-template argument deduction, and aggregate initialization.

The two-line overload helper

template<class... Ts> struct overload : Ts... { using Ts::operator()...; };
template<class... Ts> overload(Ts...) -> overload<Ts...>;

Each Ts is a type supplied to the helper—typically the unique closure type of a lambda. The class inherits from all of those types, then makes each base type’s operator() visible in the derived class. The second line is a deduction guide: it tells the compiler how to infer the template arguments from the types passed in braces.

Use it as a visitor for std::variant

A std::variant can hold one active value from a specified set of alternative types. std::visit calls a visitor with that active value. With the overload helper, write one lambda per alternative at the call site:

std::variant<int, float, std::string> intFloatString { "Hello" };

std::visit(overload{
    [](const int& i) { std::cout << "int: " << i; },
    [](const float& f) { std::cout << "float: " << f; },
    [](const std::string& s) { std::cout << "string: " << s; }
}, intFloatString);

This prints string: Hello. The braced expression constructs one object from the three lambda closures. Because that object exposes all three call operators, ordinary overload resolution selects the one matching the active value’s type. See the C++ reference for std::visit.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How the three C++17 features make it work

1. Pack expansion in a using-declaration

using Ts::operator()...; expands across the Ts parameter pack, bringing each base lambda’s call operator into scope. Without this form, older approaches commonly used recursive helper templates to expose a variable number of call operators. C++17 lets the helper express the operation directly.

2. Class-template argument deduction and a deduction guide

Every lambda expression has its own closure type, and that type has no convenient name to write in source code. The deduction guide maps constructor-like arguments to the matching specialization: for overload{lambda1, lambda2}, it deduces Ts... as the two closure types. This avoids spelling those types or using a separate factory function such as a pre-C++17 make_overloader.

3. Aggregate initialization of base subobjects

The helper declares no constructor. In C++17, aggregate initialization allows the braced list to initialize its lambda base subobjects directly. That is why overload{lambda1, lambda2} can build the combined visitor without a custom forwarding constructor.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Practical choices and limitations

  • Concrete versus generic lambdas: Concrete lambdas make the handled alternatives explicit and keep different behavior easy to read. A generic lambda can cover cases that share behavior, but can also make the selected overload less obvious.
  • Multiple variants: When visiting more than one variant, the visitor must be callable for every possible combination of active alternatives. Add overloads for combinations that need distinct behavior; a generic lambda can act as a fallback. Do not assume that covering each type individually is sufficient for a multi-variant visit.
  • Captured state: Captures remain in their lambda closure objects, which are the helper’s base subobjects. Stateful lambdas can therefore be combined just like non-capturing lambdas.
  • Callable types: This two-line form is intended for lambda closure types. It does not directly support regular function pointers; broader callable support requires a different or more advanced helper.

Compared with a hand-written visitor struct, the helper removes the named class and its repeated operator() declarations, keeping visitor logic close to the std::visit call. A named visitor can still be clearer when the logic is large, reused, or needs callable forms beyond lambdas.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.