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

Why can't we trivially copy std::function

本文关键字:std function 复制 我们 不能 简单 为什么      更新时间:2023-10-16

我要求这样做的原因是我需要将 std::function存储在向量中,如果需要更多内存,我们在公司中拥有的内部向量基本上是在进行realloc。(基本上只是memcpy,无需复制/移动操作员)

这意味着我们可以放入容器中的所有元素都需要琐碎的拷贝。

这是一些代码,以演示我有问题的副本:

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);

我知道我们应该支持元素的复制/移动,以便安全存储std::function。但是我仍然非常好奇上面std::function复制的直接原因是什么。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Updateline ----------------------------------------------------------------------------------------------------

更新了代码样本。

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

琐碎复制的 std::function对原始对象内存有一定的依赖性,删除原始内存将丢弃不复制的 std::function,甚至没有,而没有的破坏原始对象。

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

问题是必须如何实现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处的

X对象已经被破坏,并且持有的内存已被释放。无论X是否为TriviallyCopyable,我们都没有X。我们的艺术家以前称为X

由于std::function负责管理自己的对象,因此它也不能是TriviallyCopyable ,即使,我们还添加了要求其管理的所有可销售的要求均为TriviallyCopyable


要在realloc_vector中工作,您需要需要function_ref(或std::function<>*)之类的东西(即,一种根本不拥有任何资源的类型),或者您需要实现自己的function版本(a)保留自己作为成员的存储,以避免分配内存(b)仅使用TriviallyCopyable可呼叫物构造,以便它本身变得琐碎地复制。无论哪种解决方案更好,都取决于您的程序实际在做什么。

,但我仍然非常好奇什么是无效的直接原因 std ::函数副本上方。

std::function不能为TriviallyCopyable(或有条件的TriviallyCopyable),因为作为通用可呼叫对象包装器,它不能假设存储的可callable是TriviallyCopyable

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

要琐碎地复制是与给定类型的固有相关的东西,而不是与对象有关。
考虑以下示例:

#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的给定专业化做出决定。因为它是可呼叫对象的通用容器,您可以将其分配给它的可复制对象以及不容易复制的对象,其余的不用说。

std ::函数可能为捕获的变量分配内存。与分配内存的任何其他类一样,它也不是可复制的。