Callables in Modern C++

Overview

A Callable is any entity that can be invoked with parentheses (). C++ provides multiple mechanisms for representing and using callables, from raw function pointers to high-level polymorphic wrappers like std::function.

Callables unify functions, objects, and expressions that perform actions. Understanding their differences is crucial for writing generic and flexible code, especially when working with the STL, callbacks, or modern functional-style programming.

Note

In generic programming, callables allow code to treat functions, lambdas, and functors uniformly through template type deduction or std::invoke.

Types of Callables

C++ supports several categories of callables:

  1. Function pointers — addresses of free functions.

  2. Functors (function objects) — structs or classes that overload operator().

  3. Lambdas — unnamed function objects defined inline.

  4. std::function — a type-erased wrapper for any callable with a compatible signature.

Callables overview diagram

Quick Comparison

Table 2 Quick Comparison

Callable Type

Captures State?

Overhead

Typical Use

Function pointer

No

None

Callbacks; C APIs

Functor

Yes

None

STL algorithms

Lambda

Yes

Minimal

Inline logic

std::function

Yes (erased)

Possible heap

Generic storage

Detailed Topics

The following sections explore each callable category in detail:

Interoperability

All callable types can be used with std::invoke and std::is_invocable to determine at compile time whether they can be called with specific arguments.

Example

#include <functional>
#include <iostream>

int add(int a, int b) { return a + b; }

struct Multiplier {
  int factor;
  int operator()(int x) const { return x * factor; }
};

int main() {
  auto lambda = [](int x) { return x * 3; };
  std::function<int(int)> generic;

  generic = add;                 // Function pointer
  std::cout << generic(2, 3) << "\\n"; // 5

  generic = Multiplier{4};       // Functor
  std::cout << generic(5) << "\\n";    // 20

  generic = lambda;              // Lambda
  std::cout << generic(6) << "\\n";    // 18
}

Tip

Prefer lambdas for inline, short-lived operations. Use std::function when the callable must be stored, passed around, or reassigned dynamically.

Summary

  • Function pointers are the most lightweight but least flexible.

  • Functors offer state and are ideal for STL algorithms.

  • Lambdas provide concise syntax for local or temporary callables.

  • ``std::function`` generalizes callables through runtime type erasure.

Understanding these options helps you design APIs that are both efficient and expressive.