[英]Is this undefined because I memcpy'ed a non_trivially_copyable type?
我很难理解为什么不允许使用 memcpy'ing non_copyable 类型,或者即使我的以下代码是不允许的:
struct trivially_copyable_type
{
int member;
};
struct non_trivially_copyable_type
{
int member;
non_trivially_copyable_type() { }
non_trivially_copyable_type(const non_trivially_copyable_type& other) { }
};
int main()
{
bool result = std::is_trivially_copyable_v<trivially_copyable_type>; // True
result = std::is_trivially_copyable_v<non_trivially_copyable_type>; // False
trivially_copyable_type copyable;
void* memory = malloc(128);
memcpy(memory, ©able, sizeof(copyable));
trivially_copyable_type* p1 = (trivially_copyable_type*)memory;
p1->member = 7; // This is allowed
non_trivially_copyable_type noncopyable;
memcpy(memory, &noncopyable, sizeof(noncopyable));
non_trivially_copyable_type* p2 = (non_trivially_copyable_type*) memory;
p2->member = 7; // This is undefined or illegal?
}
如果我们 malloc memory 并通过指向 int 的指针访问 memory,如下所示:
int * ptr = (int*) malloc(128);
*ptr = 7; // We didn't create an int, but can treat that memory as int
以同样的方式:
trivially_copyable_type* ptr = (trivially_copyable_t*) malloc(128);
*ptr = 7; // Same, we didn't create a 'trivially_copyable_type object at memory, but can read and write to it
但:
non_trivially_copyable_type* ptr = (non_trivially_copyable_t*) malloc(128);
*ptr = 7; // Is whether this is legal dependent on whether the type is trivially_constructible or not?
我不明白为什么或者如果在前面的例子中我 memcpy 进入缓冲区是非法的。
据我了解,普通可复制类型是可以安全处理的类型,例如标准库,作为“C 风格”类型,在复制过程中没有副作用。 因此没有(用户定义的)构造函数、析构函数、赋值运算符。 它们是我们过去所知的 POD 类型,但已根据现代 C++ 的要求和需求进行了更新。
假设您要计算某些标准算法调用的operator=
的数量。 说, std::copy
。 您为 class 重载operator=
,它会进行一些计数。 该库可以通过使用memcpy
来优化std::copy
的实现吗? 嗯,无条件? 那么你所有的计数都会丢失,这通常是一场灾难。 这不是我们在 C++ 中所期望的。 C++ 中的类通常有一些不变量,构造函数可以强制它们。 复制和内存移动不是一回事。
当这样的优化是 C++ 安全的? 仅当对象在循环中逐个元素移动(复制)或 memcpy-ed 无关紧要。
这是 cppreference 中std::copy
的部分描述:
将由 [first, last) 定义的范围中的元素复制到从 d_first 开始的另一个范围。
...
在实践中,如果值类型是 TriviallyCopyable 并且迭代器类型满足 LegacyContiguousIterator,则 std::copy 的实现会避免多次赋值并使用大容量复制函数,例如 std::memmove。
所以让我重复一遍:对于可简单复制的类型,如果在while
循环或memcpy
中使用逐元素复制,则没有区别。
它是否保证复制平凡可复制的对象总是安全的? 当然 - 不是:以一个简单的链表结构为例:
struct Node
{
int value;
Node* next;
};
这是一种可简单复制的类型。 您可以将这些结构放在一个向量中,以便它们形成一个完美的链表。 但是,如果您将此向量复制到不同的位置或由于某些push_back
自动重新分配数据,那么您注定要失败。 但是,如果您在一个简单的while
循环中复制此类对象,这是您可以并且应该期待的!
问题很困惑。 简单可复制保证当您来回复制 object 的位时,您会得到相同的 object。 但是您必须已经拥有 object。 在所有情况下,您只是将位填充到您希望 object 所在的位置,这是 object 寿命的问题。 您何时可以假设存在这样的 object?
如果我们 malloc memory 并通过指向 int 的指针访问 memory,如下所示:
int * ptr = (int*) malloc(128);
*ptr = 7; // We didn't create an int, but can treat that memory as int
这在P0593之前是错误的,后者被用作 C++17 的缺陷解决方案。 int
的生命周期必须开始。 在 P0593 之前,这必须通过放置 new来完成。 现在,它隐式启动,因为int
是隐式生命周期类型,而malloc
启动隐式生命周期。
以同样的方式:
trivially_copyable_type* ptr = (trivially_copyable_t*) malloc(128);
*ptr = 7; // Same, we didn't create a 'trivially_copyable_type object at memory, but can read and write to it
这实际上与可复制无关,因为没有复制。 这没关系,因为trivially_copyable_type
是一个聚合,它是一个隐式生命周期类型,包含一个int
也是。
但:
non_trivially_copyable_type* ptr = (non_trivially_copyable_t*) malloc(128);
*ptr = 7; // Is whether this is legal dependent on whether the type is trivially_constructible or not?
这与微不足道的可复制性无关。 没有 object,因为non_trivially_copyable_type
不是隐式生命周期类型。
考虑:
struct non_trivially_copyable_type
{
int member;
non_trivially_copyable_type *self;
non_trivially_copyable_type() { self = this; }
non_trivially_copyable_type(const non_trivially_copyable_type& other) { }
int get_member() { return self->member; }
};
现在你看到问题了吗? 如果你memcpy
这个类型,然后销毁你复制的 object,下一次调用get_member
会做错事。
class 有一个构造函数。 这意味着它的实例在构造之前不存在。 您不得访问不存在的 object 的成员。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.