[英]Empty class in std::tuple
即使類型是空類類型[...],任何對象或成員子對象的大小必須至少為1,以便能夠保證相同類型的不同對象的地址始終是不同的。
我知道這個。 我剛剛發現的是,某些庫類型(如std::tuple
不會對包含的空類使用任何大小。 這是真的? 如果是的話,那怎么樣?
編輯:在閱讀@ bolov關於他的回答的最后一個注釋后,我仍然有一個問題:因為Empty
是POD
所以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)
我們從一開始就能看出來
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.