簡體   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