7

もちろん、答えは「いいえ」です。なぜなら、それを書いた人々はそれについて真剣に考えたからです。しかし、私はその理由を知りたいです。

(テンプレートのない) クラスはヘッダー ファイルで宣言されることが多く、それらは個別にコンパイルされるいくつかのファイルに含まれていることを考慮して、これら 2 つのファイルを再検討してください。

file1.c

#include <cstddef>

struct Foo {
public:
   int pub;
private:
   int priv;
};

size_t getsize1(Foo const &foo) {
  return sizeof(foo);
}

file2.c

#include <cstddef>

struct Foo {
public:
   int pub;
private:
   int priv;
};

size_t getsize2(Foo const &foo) {
  return sizeof(foo);
}

通常、Foo はヘッダー ファイルで宣言され、両方に含まれますが、効果は上記のとおりです。(つまり、ヘッダーを含めることは魔法ではありません。ヘッダーのコンテンツをその行に配置するだけです。)両方をコンパイルして、次のようにリンクできます。

main.cc

#include <iostream>
struct Foo {
public:
   int pub;
private:
   int priv;
};

size_t getsize1(Foo const &);
size_t getsize2(Foo const &);

int main() {
    Foo foo;
    std::cout << getsize1(foo) << ", " << getsize2(foo) << ", " << sizeof(foo) << '\n';
}

これを行う 1 つの方法は、g++ を使用することです。

g++ -std=c++11 -c -Wall file1.cc 
g++ -std=c++11 -c -Wall file2.cc 
g++ -std=c++11 -c -Wall main.cc 
g++ -std=c++11 -Wall *.o -o main

そして(私のアーキテクチャと環境では)、これは次のように表示されます:8、8、8。sizeofは、file1.cc、file2.cc、およびmain.ccの各コンパイルで同じです

しかし、c++11 標準はこれを保証していますか? 3 つすべての Foo とレイアウトの互換性があると期待しても本当に問題ないのでしょうか? Foo にはプライベート フィールドとパブリック フィールドの両方が含まれているため、c++11 標準 (ワーキング ドラフト) の第 9 節の 7 で定義されている標準レイアウトの構造体ではありません。

標準レイアウト クラスは、次のようなクラスです。

  • タイプ非標準レイアウト クラス (またはそのようなタイプの配列) または参照の非静的データ メンバーを持たない、
  • 仮想関数 (10.3) も仮想基本クラス (10.1) もありません。
  • すべての非静的データメンバーに対して同じアクセス制御 (条項 11) を持ち
  • 非標準レイアウトの基本クラスはありません。
  • 最も派生したクラスに非静的データ メンバーがなく、非静的データ メンバーを持つ基本クラスが最大 1 つあるか、または非静的データ メンバーを持つ基本クラスがない。
  • 最初の非静的データ メンバーと同じ型の基底クラスはありません。

構造体を使用しているため、徹底的にするために、次のパーは次のように述べています。

標準レイアウト構造体は、class-key 構造体または class-key クラスで定義された標準レイアウト クラスです。標準レイアウト ユニオンは、クラス キー ユニオンで定義された標準レイアウト クラスです。

私の知る限りでは、標準は標準レイアウトの構造体間のレイアウト互換性のみを定義しています (条項 9.2、par 18)。

2 つの標準レイアウト構造体 (条項 9) 型は、同じ数の非静的データ メンバーを持ち、対応する非静的データ メンバー (宣言順) がレイアウト互換型 (3.9) を持っている場合、レイアウト互換性があります。

では、3 つの Foo がすべてレイアウト互換であることが保証されているのでしょうか? さらに重要なことに、その理由は?

コンパイル中に Foo のさまざまなレイアウトを作成する (非決定論的) コンパイラが c++11 コンパイラではないのはなぜですか?

4

1 に答える 1

14

3 つの は同じ型, でFooあるため、レイアウト互換性があります。struct ::Foo

[基本型]

11 - 2 つの型 T1 と T2 が同じ型の場合、T1 と T2 はレイアウト互換型です。

これらのクラスは、同じ (完全修飾された) 名前を持ち、外部リンケージを持つため、同じタイプです。

【基本】

9 - 複数の翻訳単位で使用される名前は、各翻訳単位で指定された名前のリンケージ (3.5) に応じて、これらの翻訳単位で同じエンティティを参照する可能性があります。

名前のない名前空間内で (再帰的に) 宣言されていない名前空間スコープで宣言されたクラス名には、外部リンケージがあります。

[基本リンク]

2 - 別のスコープの宣言によって導入された名前と同じ [...] タイプ [...] を表す可能性がある場合、その名前はリンケージを持つと言われます
。他の翻訳単位のスコープまたは同じ翻訳単位の他のスコープからの名前で参照されます。[...]
4 - 名前のない名前空間、または名前のない名前空間内で直接的または間接的に宣言された名前空間には、内部リンケージがあります。他のすべての名前空間には外部リンケージがあります。上記の内部リンケージが与えられていない名前空間スコープを持つ名前は、[...]
— 名前付きクラス (第 9 節)、または typedef 宣言で定義された名前のないクラスの名前である場合、囲んでいる名前空間と同じリンケージを持ちます。クラスには、リンケージの目的で typedef 名がある (7.1.3) [...]

定義が同じトークン シーケンスで構成されている限り、異なる翻訳単位に現れるクラス型の複数の定義を持つことができることに注意してください。

[basic.def.odr]

6 - 各定義が異なる翻訳単位に現れ、[...]各定義が提供されるという条件で、プログラム内にクラスタイプの複数の定義(条項9)[...]が存在する可能性があります[...]同じ一連のトークンで構成されます [...]

したがって、Fooの名前が異なる場合、それらは同じタイプではありません。それらが匿名の名前空間内または関数定義内に出現した場合 (インライン関数を除く。 [dcl.fct.spec] /4 を参照)、それらは外部リンケージを持たないため、同じ型にはなりません。どちらの場合も、標準レイアウトである場合にのみレイアウト互換性があります。


いくつかの例:

// tu1.cpp
struct Foo { private: int i; public: int j; };

// tu2.cpp
struct Foo { private: int i; public: int j; };

2 つFooの は同じタイプです。

// tu1.cpp
struct Foo { private: int i; public: int j; };

// tu2.cpp
struct Foo { private: int i; public: int k; };

ODR 違反; 未定義の動作。

// tu1.cpp
struct Foo { private: int i; public: int j; };

// tu2.cpp
struct Bar { private: int i; public: int j; };

呼び方が違うので、種類が違います。レイアウト非対応。

// tu1.cpp
struct Foo { int i; int j; };

// tu2.cpp
struct Bar { int i; int j; };

異なる名前、異なるタイプですが、レイアウト互換性があります (標準レイアウト以降)。

// tu1.cpp
namespace { struct Foo { private: int i; public: int j; }; }

// tu2.cpp
namespace { struct Foo { private: int i; public: int j; }; }

内部リンケージ; 他の種類。

// tu1.cpp
static void f() { struct Foo { private: int i; public: int j; }; }

// tu2.cpp
static void f() { struct Foo { private: int i; public: int j; }; }

リンケージなし; 他の種類。

// tu1.cpp
inline void f() { struct Foo { private: int i; public: int j; }; }

// tu2.cpp
inline void f() { struct Foo { private: int i; public: int j; }; }

[dcl.fct.spec] /4による同型。

于 2014-10-23T14:28:40.107 に答える