Home » C++ » How std::function works

How std::function works

Posted by: admin November 30, 2017 Leave a comment


You know, we can wrap or store a lambda function to a std::function:

#include <iostream>
#include <functional>
int main()
    std::function<float (float, float)> add = [](float a, float b)
    //            ^^^^^^^^^^^^^^^^^^^^
        return a + b;

    std::cout << add(1, 2) << std::endl;

My question is around std::function, as you can see it is a template class but it can accept any kind of function signature.

For example float (float, float) in this form return_value (first_arg, second_arg).

What’s the structure of std::function and how does it accept a function signature like x(y,z) and how it works with it? Is float (float, float) a new valid expression in C++?


It uses some type erasure technique.

One possibility is to use mix subtype polymorphism with templates. Here’s a simplified version, just to give a feel for the overall structure:

template <typename T>
struct function;

template <typename Result, typename... Args>
struct function<Result(Args...)> {
    // this is the bit that will erase the actual type
    struct concept {
        virtual Result operator()(Args...) const = 0;

    // this template provides us derived classes from `concept`
    // that can store and invoke op() for any type
    template <typename T>
    struct model : concept {
        template <typename U>
        model(U&& u) : t(std::forward<U>(u)) {}

        Result operator()(Args... a) const override {

        T t;

    // this is the actual storage
    // note how the `model<?>` type is not used here    
    std::unique_ptr<concept> fn;

    // construct a `model<T>`, but store it as a pointer to `concept`
    // this is where the erasure "happens"
    template <typename T,
        typename=typename std::enable_if<
                decltype( t(std::declval<Args>()...) ),
    function(T&& t)
    : fn(new model<typename std::decay<T>::type>(std::forward<T>(t))) {}

    // do the virtual call    
    Result operator()(Args... args) const {
        return (*fn)(std::forward<Args>(args)...);

(Note that I overlooked several things for the sake of simplicity: it cannot be copied, and maybe other problems; don’t use this code in real code)