The Closure Outlives The Frame

This queue runs each job later, from a worker thread.

#include <functional>
#include <string>
#include <vector>

void log(const std::string& tag, int n);
void run(std::vector<std::function<void()>>& jobs);

std::vector<std::function<void()>> jobs;

void enqueue(const std::string& name, int retries) {
    std::string tag = "job:" + name;
    jobs.push_back([&tag, retries] {
        log(tag, retries);
    });
}

int main() {
    enqueue("index", 3);
    enqueue("compact", 1);
    run(jobs);   // jobs invoked here
}

It works in a debug build and prints garbage tags in release. What is wrong?

A

name dangles: enqueue("index", 3) binds a temporary std::string to a const& parameter, and the closure holds on to it.

B

tag is a local of enqueue and is captured by reference; it is destroyed when enqueue returns, so invoking the closure inside run reads a dead object.

C

Nothing dangles: std::function copies the closure, and copying the closure copies everything it refers to.

D

retries dangles: it is a parameter, so its storage is gone once enqueue returns.