避免未定义的行为:将临时变量传递给具有 const ref 成员变量的“std::function”

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

以下示例是生产代码中的简化版本

#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();
}

在生产代码中,访问字符串 A(即通过 vector 调用函数,进而访问 A 中的 myString)会导致崩溃。编译器资源管理器似乎没有问题,但是如果未定义此行为,则输出可能不可信:https://godbolt.org/z/cPPKeK9zd

假设这是未定义的行为,我只能将 const & 存储到 A,我可以在 Store(...) 函数中做什么来发出编译器错误并尝试捕获编译时的未定义行为。

这个例子没有未定义的行为。

调用 Store 从类型 B 的临时对象复制初始化类型 std::function<void()> 的参数。在这样做时,std::function 使用完美转发来初始化它自己的 B 类型的内部对象,因此它是从原始临时对象移动构造的。

emplace_back 的调用复制构造函数,因此复制构造类型 B 的内部对象。

初始 B 对象是一个临时对象是无关紧要的,因为向量中函数内的副本不是。此 B 副本中对 A 的引用仍指向同一个 A 对象,该对象仍在其生命周期内。