繁体   English   中英

这是未定义的,因为我 memcpy'ed 一个 non_trivially_copyable 类型?

[英]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, &copyable, 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM