クラスがデフォルトで構築できないのはなぜですか?


28

私はそれらのクラスを持っています:

#include <type_traits>

template <typename T>
class A {
public:
    static_assert(std::is_default_constructible_v<T>);

};

struct B {
   struct C {
      int i = 0;
   };

    A<C> a_m;
};

int main() {
    A<B::C> a;
}

コンパイル時、a_mデフォルトでは構築できませんが可能aです。

に変更Cする場合:

struct C {
      int i;
   };

すべて順調。

Clang 9.0.0でテスト済み。


3
GCC 8.3-OK、GCC 9.1 / 9.2-Fail。
19

2
それでもC() {}動作します。
19

3
これはバギーの匂いがします。Bugzillaにすぐに明らかな一致はありません。
オービットでの軽さのレース

2
興味深い:static_assertinはA失敗しますが、代わりにデフォルトでT内部を構築するA(たとえば、T t;そこにメンバーを置く)と、すべて正常に動作します。タイプ特性があなたに伝えていることと実際に可能なことの間の不一致...
sebrockm

2
@Nicolas True、しかしそれはいくつかのエッジケースが原因であり、どれもここでは適用されません(特に、cppreferenceの同じ文が言うようにconst int x;、イニシャライザなしでは無効です。これは、純粋constに組み込み型の初期化動作と歴史)
オービットのライトネスレース

回答:


9

これは、標準のテキストと、コメントに記載されているいくつかの主要な実装の両方で許可されていませんが、完全に無関係な理由のためです。

まず、「本による」理由:のインスタンス化のポイントはA<C>、標準によると、 の定義の直前Bであり、のインスタンス化のポイントstd::is_default_constructible<C>はその直前です。

クラステンプレートの特殊化の場合、[...]特殊化が別のテンプレート特殊化内から参照されるために暗黙的にインスタンス化される場合、特殊化の参照元のコンテキストがテンプレートパラメータに依存し、特殊化がインスタンス化されていない場合囲んでいるテンプレートのインスタンス化に対して、インスタンス化のポイントは、囲んでいるテンプレートのインスタンス化のポイントの直前です。それ以外の場合、そのような特殊化のインスタンス化のポイントは、その特殊化を参照する名前空間スコープ宣言または定義の直前になります。

Cその時点では明らかに不完全であるため、インスタンス化の動作std::is_default_constructible<C>は定義されていません。ただし、このルールを変更するコア問題287を参照してください。


実際には、これはNSDMIに関係しています。

  • NSDMIは、構文解析が遅れるので奇妙です。標準の用語では、「完全なクラスのコンテキスト」です。
  • したがって、それ= 0は原則としてBまだ宣言されていないものを参照する可能性があるため、実装はで終了するまで実際に解析を試みることはできませんB
  • クラスを完了するにCは、コンストラクターが宣言されていないため、特別なメンバー関数、特にデフォルトのコンストラクターの暗黙的な宣言が必要です。
  • その宣言の一部(constexpr-ness、noexcept-ness)は、NSDMIのプロパティに依存します。
  • したがって、コンパイラがNSDMIを解析できない場合は、クラスを完成できません。
  • その結果、インスタンス化した時点A<C>Cは不完全だと思います。

遅延解析された領域を処理するこの領域全体は、実装の相違を伴い、ひどく過小評価されています。クリーンアップされるまでに時間がかかる場合があります。


0

未定義の動作

上記のテンプレートのインスタンス化が不完全または不完全なタイプに直接または間接的に依存しており、そのインスタンス化が仮説的に完了した場合に別の結果が生じる可能性がある場合、動作は未定義です。


7
なぜCは不完全なのですか?
interjay

1
@interjay、C完全ですが、完全でBはありません。そしてB::C間接的にに依存しBます。
19

1
@Evg「直接的または間接的に依存する」というテキストは、cppreference.comにのみ表示されます。規格では、タイプTは完全である必要があると述べています。
interjay


2
@interjay私はこの表現のほとんどを書きました。私たちが言おうとしていることは、1)不完全なタイプが完了したときに、後でODR違反を引き起こす可能性がある方法で特性をインスタンス化した場合、それは未定義です。2)プログラムで実際に ODR違反を引き起こさなくても、定義されていないため、標準ライブラリの実装は、必要に応じてその特性が使用される時点でその診断を選択できます。場合C場合は答えを変更することができますいくつかの奇妙なSFINAEでデフォルトコンストラクタテンプレートがいるB異なっ完了し、その後、必ず、特性はそれに依存します。
TC
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.