簡體   English   中英

為什么 C++11 的 POD“標准布局”定義是這樣的?

[英]Why is C++11's POD “standard layout” definition the way it is?

我正在研究C++11 中新的、輕松的 POD 定義(第 9.7 節)

標准布局類是這樣一個類:

  • 沒有非標准布局類(或此類類型的數組)或引用類型的非靜態數據成員,
  • 沒有虛函數(10.3)和虛基類(10.1),
  • 對所有非靜態數據成員具有相同的訪問控制(第 11 條),
  • 沒有非標准布局的基類,
  • 要么在最派生的類中沒有非靜態數據成員,並且最多有一個具有非靜態數據成員的基類,要么沒有具有非靜態數據成員的基類,以及
  • 沒有與第一個非靜態數據成員相同類型的基類。

我強調了讓我感到驚訝的部分。

如果我們容忍具有不同訪問控制的數據成員,會出現什么問題?

如果第一個數據成員也是基類會出現什么問題? IE

struct Foo {};
struct Good : Foo {int x; Foo y;};
struct Bad  : Foo {Foo y; int x;};

我承認這是一個奇怪的結構,但為什么要禁止Bad而不是Good

最后,如果一個以上的組成類具有數據成員,會出現什么問題?

它基本上是關於與 C++03 和 C 的兼容性:

  • 相同的訪問控制 - C++03 實現允許使用訪問控制說明符作為重新排序類的(組)成員的機會,例如為了更好地打包它。
  • 具有非靜態數據成員的層次結構中不止一個類——C++03 沒有說明基類的位置,或者是否在相同類型的完整對象中存在的基類子對象中省略了填充。
  • 基類和相同類型的第一個成員 - 由於第二條規則,如果基類類型用於數據成員,那么它必須是一個空類。 許多編譯器確實實現了空基類優化,因此 Andreas 關於具有相同地址的子對象的說法是正確的。 我不確定標准布局類是什么意思,這意味着基類子對象與相同類型的第一個數據成員具有相同的地址是不好的,但是基類子對象何時具有相同的地址並不重要與不同類型的第一個數據成員相同的地址。 [編輯:這是因為相同類型的不同對象具有不同的地址,即使它們是空的子對象。 感謝約翰內斯]

的C ++ 0x大概可以這樣定義,這些東西都是標准布局類型也是如此,在這種情況下,它也將確定他們是如何布局,以它為標准布局類型相同的程度。 約翰內斯的回答更進一步,看看他的例子,這些東西會干擾標准布局類的一個很好的屬性。

但是如果這樣做,那么某些實現將被迫更改它們如何布置類以匹配新要求,這對於 C++0x 之前和之后的編譯器的不同版本之間的結構兼容性來說是一個麻煩。 它基本上打破了 C++ ABI。

我對如何定義標准布局的理解是,他們研究了在不破壞現有實現的情況下可以放寬哪些 POD 要求。 所以我假設沒有檢查,上面是一些現有的 C++03 實現確實使用類的非 POD 性質來做一些與標准布局不兼容的事情的例子。

您可以將標准布局類對象地址強制轉換為指向其第一個成員的指針,然后由后面的段落之一返回,這也通常在 C 中完成:

struct A { int x; };
A a;

// "px" is guaranteed to point to a.x
int *px = (int*) &a;

// guaranteed to point to a
A *pa = (A*)px; 

為此,第一個成員和完整對象必須具有相同的地址(編譯器無法通過任何字節調整 int 指針,因為它無法知道它是否是A的成員)。

最后,如果一個以上的組成類具有數據成員,會出現什么問題?

在一個類中,成員按照聲明順序按遞增地址分配。 然而,C++ 並沒有規定跨類的數據成員的分配順序。 如果派生類和基類都有數據成員,則標准不會故意定義它們的地址順序,以便在布局內存方面給予實現充分的靈活性。 但是要使上述演員表起作用,您需要知道分配順序中的“第一個”成員是什么!

如果第一個數據成員也是基類會出現什么問題?

如果基類與第一個數據成員具有相同的類型,則將基類放在內存中派生類對象之前的實現將需要在內存中派生類對象數據成員之前有一個填充字節(基類的大小為 1 ),以避免基類和第一個數據成員具有相同的地址(在 C++ 中,相同類型的兩個不同的對象總是具有不同的地址)。 但這又會使派生類對象的地址無法轉換為其第一個數據成員的類型。

如果我們容忍具有不同訪問控制的數據成員,會出現什么問題?

當前語言說編譯器不能在相同的訪問控制下對成員重新排序。 像:

struct x
{
public:
    int x;
    int y;
private:
    int z;
};

這里 x 必須在 y 之前分配,但相對於 x 和 y,z 沒有限制。

struct y
{
public:
    int x;
public:
    int y;
};

新的措辭表示盡管有兩個public s,但y仍然是一個 POD。 這實際上是對規則的放松。

至於為什么不允許Bad讓我引用我發現的一篇文章:

這確保了具有相同類類型並且屬於同一個最派生對象的兩個子對象不會分配在相同的地址。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2172.html

從第 5 條來看,似乎兩者都是非 pod,因為最派生的類具有非靜態數據成員(int),它不能具有具有非靜態數據成員的基類。

我理解為:“只有一個“基”類(即類本身或它繼承的類之一)可以具有非靜態數據成員”

struct Good也不是標准布局,因為 Foo 和 Good 具有非靜態數據成員。

這樣,Good 應該是:

struct Foo {int foo;};
struct Good : public Foo {Foo y;};

未能滿足第 6 項。 因此是第 6 顆子彈?

暫無
暫無

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

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