为什么我们不能简单地复制 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
的给定特化做出决定。因为它是可调用对象的通用容器,您可以为它分配普通可复制对象以及不可普通复制的对象,其余的不言而喻。
我问这个的原因是我需要将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
的给定特化做出决定。因为它是可调用对象的通用容器,您可以为它分配普通可复制对象以及不可普通复制的对象,其余的不言而喻。