Samaursa
Samaursa

Reputation: 17241

Avoiding undefined behaviour: passing a temporary to a `std::function` which has a const ref member variable

The following example is a simplified version found in production code

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

struct A
{
    std::string myString = "World";
};

struct B
{
    void operator()() 
    {
        std::cout << a.myString;
    }
    const A& a;
};

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

void Store(std::function<void()> myFunc)
{
    v.emplace_back(myFunc);
}

void Print()
{
    v[0]();
}

int main()
{
    A a; a.myString = "Hello";

    Store(B{a}); // temporary passed with a const-ref of on-the-stack `A`
    Print();
}

In production code, accessing the string A (i.e. invoking the function through the vector which in turn accesses myString in A) results in a crash. Compiler Explorer seems to be okay with it, however if this behaviour is undefined, the output probably cannot be trusted: https://godbolt.org/z/cPPKeK9zd

Assuming this is undefined behaviour and I can only store a const & to A, what can I do in the Store(...) function to issue a compiler error and try to catch the undefined behaviour at compile time.

Upvotes: 0

Views: 76

Answers (1)

Jeff Garrett
Jeff Garrett

Reputation: 7428

This example has no undefined behavior.

Calling Store copy-initializes the argument of type std::function<void()> from the temporary object of type B. In doing so, std::function uses perfect forwarding to initialize its own internal object of type B, which is therefore move-constructed from the original temporary.

The call to emplace_back copy-constructs the function which therefore copy-constructs the internal object of type B.

That the initial B object is a temporary is irrelevant, as the copy inside the function inside the vector is not. The reference to A inside this B copy still points to the same A object, which is still within its lifetime.

Upvotes: 2

Related Questions