簡體   English   中英

std :: tuple中的空類

[英]Empty class in std::tuple

即使類型是空類類型[...],任何對象或成員子對象的大小必須至少為1,以便能夠保證相同類型的不同對象的地址始終是不同的。

cppreference引用

我知道這個。 我剛剛發現的是,某些庫類型(如std::tuple不會對包含的空類使用任何大小。 這是真的? 如果是的話,那怎么樣?


編輯:在閱讀@ bolov關於他的回答的最后一個注釋后,我仍然有一個問題:因為EmptyPOD所以memcpy是安全的。 但是如果你想記住一個“幽靈”地址(參見@ bolov的答案),你會有效地寫入一個int元素( sizoef(Empty)是1)。 這似乎不太好。

對象的大小必須大於零。 子對象的大小沒有該約束。 這導致空基優化(EBO),其中空基類不占用空間(編譯器在近20年前開始實現)。 反過來,這導致std::tuple通常實現為繼承鏈,其中空基類不占用空間。

tl,dr這進一步增強了我對圖書館實施者的尊重。 他們必須導航以實現std::tuple這種優化的規則,一旦你開始考慮如何實現它,就會令人鼓舞。


當然,我繼續玩了一下,看看這是怎么回事。

設置:

struct Empty {};

template <class T> auto print_mem(const T& obj)
{
    cout << &obj << " - " << (&obj + 1) << " (" << sizeof(obj) << ")" << endl;
}

考試:

int main() {
    std::tuple<int> t_i;
    std::tuple<Empty> t_e;
    std::tuple<int, Empty, Empty> t_iee;
    std::tuple<Empty, Empty, int> t_eei;
    std::tuple<int, Empty, Empty, int> t_ieei;

    cout << "std::tuple<int>" << endl;
    print_mem(t_i);
    cout << endl;

    cout << "std::tuple<Empty>" << endl;
    print_mem(t_e);
    cout << endl;

    cout << "std::tuple<int, Empty, Empty" << endl;
    print_mem(t_iee);
    print_mem(std::get<0>(t_iee));
    print_mem(std::get<1>(t_iee));
    print_mem(std::get<2>(t_iee));
    cout << endl;

    cout << "std::tuple<Empty, Empty, int>" << endl;
    print_mem(t_eei);
    print_mem(std::get<0>(t_eei));
    print_mem(std::get<1>(t_eei));
    print_mem(std::get<2>(t_eei));
    cout << endl;

    print_mem(t_ieei);
    print_mem(std::get<0>(t_ieei));
    print_mem(std::get<1>(t_ieei));
    print_mem(std::get<2>(t_ieei));
    print_mem(std::get<3>(t_ieei));
    cout << endl;
}

結果:

std::tuple<int>
0xff83ce64 - 0xff83ce68 (4)

std::tuple<Empty>
0xff83ce63 - 0xff83ce64 (1)

std::tuple<int, Empty, Empty
0xff83ce68 - 0xff83ce6c (4)
0xff83ce68 - 0xff83ce6c (4)
0xff83ce69 - 0xff83ce6a (1)
0xff83ce68 - 0xff83ce69 (1)

std::tuple<Empty, Empty, int>
0xff83ce6c - 0xff83ce74 (8)
0xff83ce70 - 0xff83ce71 (1)
0xff83ce6c - 0xff83ce6d (1)
0xff83ce6c - 0xff83ce70 (4)

std::tuple<int, Empty, Empty, int>
0xff83ce74 - 0xff83ce80 (12)
0xff83ce7c - 0xff83ce80 (4)
0xff83ce78 - 0xff83ce79 (1)
0xff83ce74 - 0xff83ce75 (1)
0xff83ce74 - 0xff83ce78 (4)

Ideone鏈接

我們從一開始就能看出來

sizeof(std:tuple<Empty>)                   == 1 (because the tuple cannot be empty)
sizeof(std:tuple<int>)                     == 4
sizeof(std::tuple<int, Empty, Empty)       == 4
sizeof(std::tuple<Empty, Empty, int)       == 8
sizeof(std::tuple<int, int>)               == 8
sizeof(std::tuple<int, Empty, Empty, int>) == 12

我們可以看到有時確實沒有為Empty保留空間,但在某些情況下,為Empty分配了1 byte (其余是填充)。 Empty元素是最后一個元素時,它看起來像是0

仔細檢查通過獲得的地址get我們可以看到,無法在兩個Empty的元組元素具有相同的地址(這是與上面的一行規則),即使(這些地址Empty )似乎是內部 int元素。 此外, Empty元素的地址不在容器元組的內存位置之外。

這讓我想到:如果我們有比sizeof(int)更多的尾隨Empty 這會增加元組的大小嗎? 確實如此:

sizeof(std::tuple<int>)                                         // 4
sizeof(std::tuple<int, Empty>)                                  // 4
sizeof(std::tuple<int, Empty, Empty>)                           // 4
sizeof(std::tuple<int, Empty, Empty, Empty>)                    // 4
sizeof(std::tuple<int, Empty, Empty, Empty, Empty>)             // 4
sizeof(std::tuple<int, Empty, Empty, Empty, Empty, Empty>)      // 8 yep. Magic

最后一點注意:可以為Empty元素設置“幻像”地址(它們似乎與int元素“共享”內存)。 由於Empty是一個...... well ...空類,它沒有非靜態數據成員,這意味着無法訪問為它們獲取的內存。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM