为什么我们不能简单地复制 std::function

Why can't we trivially copy std::function

我问这个的原因是我需要将std::function存储在一个vector中,而我们公司内部的vector基本上是在需要更多内存的情况下进行realloc。 (基本上只有memcpy,没有copy/move运算符)

这意味着我们可以放入容器中的所有元素都需要可简单复制。

这里有一些代码来演示我有问题的副本:

void* func1Buffer = malloc(sizeof(std::function<void(int)>));
std::function<void(int)>* func1p = new (func1Buffer) std::function<void(int)>();
std::function<void(int)>* func2p = nullptr;
*func1p = [](int) {};
char func2Buffer[sizeof(*func1p)];
memcpy(&func2Buffer, func1p, sizeof(*func1p));
func2p = (std::function<void(int)>*)(func2Buffer);
// func2p is still valid here
(*func2p)(10);
free(func1Buffer);
// func2p is now invalid, even without std::function<void(int)> desctructor get triggered
(*func2p)(10);

我知道我们应该支持元素的 copy/move 以便安全地存储 std::function。 但是我还是很好奇上面std::function复制无效的直接原因是什么

---------------------------------------- ----------UpdateLine------------------------------------ --------------

更新了代码示例。

我通过更多调试我们的内部向量,找到了导致此失败的直接原因。

简单复制的std::function对原始对象内存有一定的依赖,删除原始内存会垃圾复制的std::function即使没有的破坏原始对象。

感谢大家对此的回答post。这些都是有价值的输入。 :)

But I am still very curious about what is the direct cause of invalid std::function copy above.

std::function 不能是 TriviallyCopyable(或有条件地 TriviallyCopyable),因为作为通用的可调用对象包装器,它不能假定存储的可调用对象是 TriviallyCopyable.

考虑实施您自己的 std::function 版本,该版本仅支持 TriviallyCopyable 可调用对象(使用固定缓冲区进行存储),或者如果适用于您的情况,则使用函数指针向量。

A std::function 可能会为捕获的变量分配内存。与分配内存的任何其他 class 一样,它不可简单复制。

问题是 std::function 必须如何实现:它必须管理它持有的任何对象的生命周期。所以当你写:

{
    std::function<Sig> f = X{};
} 

f 超出范围时,我们必须调用 X 的析构函数。此外,std::function 将 [可能] 分配内存来保存 X 因此 f 的析构函数也必须 [可能] 释放该内存。

现在考虑当我们尝试这样做时会发生什么:

char buffer[100000]; // something big
{
    std::function<void()> f = X{};
    memcpy(buffer, &f, sizeof(f));
}
(*reinterpret_cast<std::function<void()>*>(buffer))();

当我们在 buffer 调用函数 "stored" 时,X 对象已经被销毁并且持有它的内存已经 [可能] 被释放。不管 X 是否是 TriviallyCopyable,我们都不再有 X。我们有一位以前被称为 X 的艺术家。

因为它有责任 std::function 管理它自己的对象,所以它不能 TriviallyCopyable 即使 我们添加了它管理的所有可调用对象的要求TriviallyCopyable.


要在 realloc_vector 中工作,您需要 function_ref(或 std::function<>*)之类的东西(即不拥有任何资源的类型),或者你需要实现你自己的 function 版本,它 (a) 保留自己的存储作为成员以避免分配内存 (b) 只能用 [=21 构造=] callables 这样它本身就可以轻松复制。哪种解决方案更好取决于您的程序实际在做什么。

可简单复制是与给定类型而不是对象固有相关的东西。
考虑以下示例:

#include<type_traits>
#include<functional>

int main() {
    auto l = [](){};
    static_assert(not std::is_trivially_copyable<decltype(l)>::value, "!");

    std::function<void(void)> f;
    bool copyable = std::is_trivially_copyable<decltype(f)>::value;
    f = l;

    // do something based on the
    // fact that f is trivially copyable
}

一旦您将 lambda 分配给函数,您如何执行 属性,这不是平凡可复制的?

您正在寻找的是一种运行时机制,它根据分配给函数的实际对象做出决定。
这不是 std::is_trivially_copyable 的工作方式。
因此,编译器必须在编译时就 std::function 的给定特化做出决定。因为它是可调用对象的通用容器,您可以为它分配普通可复制对象以及不可普通复制的对象,其余的不言而喻。