25

明示的なコピー コンストラクターは、 のようなものを許可せずFoo foo = bar;、コピーの使用を として強制しますFoo foo(bar);。さらに、明示的なコピー コンストラクターでは、関数から値でオブジェクトを返すこともできません。ただし、コピーの初期化を中かっこに置き換えてみました。

struct Foo
{
    Foo() = default;
    explicit Foo(const Foo&) = default;
};

int main()
{
    Foo bar;
    Foo foo{bar}; // error here
}

エラーが発生しています(g ++ 5.2)

エラー: 'Foo::Foo(Foo&)' の呼び出しに一致する関数がありません

または (clang++)

エラー: 構造体初期化子に余分な要素があります

を削除explicitするとコードが g++ でコンパイル可能になりますが、clang++ は同じエラーで失敗します (@Steephen に感謝)。何が起きてる?均一な初期化は、初期化子リスト コンストラクターと見なされますか (他のすべてのコンストラクターより優先されます)。しかし、その場合、コピー コンストラクターが明示的でない場合にプログラムがコンパイルされるのはなぜでしょうか。

4

1 に答える 1

24

C++14 がファイナライズされた直後にCore issue 1467の解決によって対処されたケースに遭遇しました。

fooまず、classは集合体であることに注意してください。あなたのコードはの直接リスト初期化を行っていfooます。リスト初期化のルールは [8.5.4p3] にあります。

C++14 (公開された標準に最も近い作業草案である N4140 から引用) では、上記の段落は次のように始まります。

型のオブジェクトまたは参照のリスト初期化は、T次のように定義されます。

  • が集合体の場合T、集合体の初期化が実行されます (8.5.1)。

[...]

そのため、クラスが集約の場合、コンパイラは集約の初期化を試みますが、失敗します。

これは問題として認識され、ワー​​キング ドラフトで修正されました。現在のバージョン N4527 から引用すると、上記の段落は次のように始まります。

型のオブジェクトまたは参照のリスト初期化は、T次のように定義されます。

  • Tがクラス型であり、イニシャライザ リストに型cv の要素が 1 つある場合U(ここで、UTまたは から派生したクラスT)、オブジェクトはその要素から初期化されます (コピー リスト初期化の場合はコピー初期化、または直接初期化による場合)。直接リスト初期化)。
  • それ以外の場合、Tが文字配列であり、初期化子リストに適切に型指定された文字列リテラル (8.5.2) である単一の要素がある場合、そのセクションで説明されているように初期化が実行されます。
  • それ以外の場合、Tが集合体の場合、集合体の初期化が実行されます (8.5.1)。

[...]

あなたの例は、最初の箇条書きで説明されているケースに該当し、デフォルトのコピーコンストラクターを使用して直接リスト初期化されます(直接初期化であるため、それが であるかどうかに関係fooなく)explicit

つまり、コンパイラが欠陥レポートで解決策を実装する場合です。

  • GCC 5.2.0 (および 6.0.0 トランク) はそうしているようですが、それに関連するバグがあるようですexplicit
  • Clang 3.6.0 はそうではありませんが、3.8.0 トランクはそうであり、正しく実行します (問題でexplicitはありません)。
  • MSVC 14 にはありますが、IDE の IntelliSense にはありません (下の波線bar- IntelliSense で使用される EDG コンパイラも更新されていないようです)。

更新: この回答が書かれて以来、作業草案は、質問の例と上記の説明に関連するいくつかの方法でさらに修正されました。

  • CWG 2137は、上で引用した段落の最初の箇条書きが、その例外をすべてのクラス タイプに適用することで少し行き過ぎたことを示しています (問題のメモには関連する例が含まれています)。箇条書きの冒頭は次のようになります。
    • T集約クラスであり、[...]
  • 論文P0398R0に含まれるCWG 1518の決議は、コンストラクターを宣言するクラス(たとえデフォルトであっても) が集合体ではなくなったことを示しています。explicit

explicitこれは、すべての変更が実装された後、質問の例が;の有無にかかわらず機能することを意図しているという事実を変更しません。それを機能させる基本的なメカニズムがわずかに変更されたことを知っておく価値があります。

これらの変更はすべて不具合レポートの解決策であるため、コンパイラが C++14 および C++11 モードの場合にも適用されるはずです。

于 2015-08-02T21:48:47.580 に答える