17

次のクラスを検討してください。

struct with_copy {
    with_copy() = default;
    with_copy(with_copy const&) {}
    with_copy& operator=(with_copy const&) { return *this; }
};

struct foo {
    with_copy c;
    std::unique_ptr<int> p;
};
  • with_copyコピーコンストラクターはありますか? はい。明示的に定義されていました。
  • with_copy移動コンストラクターはありますか? いいえ。明示的なコピー コンストラクターは、それが生成されないようにします。
  • with_copy削除された移動コンストラクターはありますか? いいえ。移動コンストラクターがないことは、削除されたコンストラクターがあることと同じではありません。削除された移動コンストラクターは、コピーに退化するのではなく、不適切な形式で移動しようとします。
  • with_copyコピー可能ですか?はい。そのコピー コンストラクターはコピーに使用されます。
  • with_copy可動ですか?はい。そのコピー コンストラクターは移動に使用されます。

...そして今、トリッキーなもの。

  • fooコピーコンストラクターはありますか? はい。unique_ptrの削除されたコピーコンストラクターを呼び出すため、デフォルトの定義が不適切な形式になるため、削除されたものがあります。
  • foo移動コンストラクターはありますか? GCC は「はい」と答え、clang は「いいえ」と答えます。
  • foo削除された移動コンストラクターはありますか? GCCとclangの両方がノーと言います。
  • fooコピー可能ですか?いいえ。そのコピー コンストラクターは削除されます。
  • foo可動ですか?GCC は「はい」と答え、clang は「いいえ」と答えます。

(構築ではなく代入を考慮する場合、動作は似ています。)

私が見る限り、GCC は正しいです。fooには、各メンバーに対して移動を実行する移動コンストラクターが必要です。このwith_copy場合、これはコピーに退化します。Clang の動作は非常にばかげているように見えます。2 つの可動メンバーを持つ集合体がありますが、集合体は不動のレンガです。

誰が正しいですか?

4

2 に答える 2

8

C++11、または n3485、[class.copy]/9:

クラスの定義でXムーブ コンストラクターが明示的に宣言されていない場合は、次の場合にのみ、暗黙的にデフォルトとして宣言されます。

  • Xユーザー宣言のコピー コンストラクターがありません。
  • Xユーザー宣言のコピー代入演算子がありません。
  • Xユーザー宣言の移動代入演算子がない
  • Xユーザー宣言のデストラクタがなく、かつ
  • 移動コンストラクターは、暗黙的に削除済みとして定義されません。

および /11:

暗黙的に宣言されたコピー/移動コンストラクターは、inline publicそのクラスのメンバーです。クラスのデフォルトのコピー/移動コンストラクターは、次のX場合に削除済み (8.4.3) として定義されXます。

  • [...]
  • コピー コンストラクターの場合、右辺値参照型の非静的データ メンバー、または
  • 移動コンストラクターの場合、非静的データ メンバー、または移動コンストラクターを持たず、自明にコピーできない型を持つ直接または仮想基本クラス。

自明にコピー可能でwith_copyないため、move-constructorfooはありません(削除済みとして定義されるため、暗黙的に宣言されません)。


C++1y、または github repo commit e31867c0 from 2013-11-12; DR1402 の組み込み:

/9:

クラスの定義でXムーブ コンストラクターが明示的に宣言されていない場合は、次の場合にのみ、暗黙的にデフォルトとして宣言されます。

  • Xユーザー宣言のコピー コンストラクターがありません。
  • Xユーザー宣言のコピー代入演算子がありません。
  • Xユーザー宣言の移動代入演算子を持たず、かつ
  • Xユーザー宣言のデストラクタがありません。

および /11:

暗黙的に宣言されたコピー/移動コンストラクターは、inline public そのクラスのメンバーです。クラスのデフォルトのコピー/移動コンストラクターは、次のX 場合に削除済み (8.4.3) として定義されXます。

  • [...]
  • コピー コンストラクターの場合、右辺値参照型の非静的データ メンバー。

削除済みとして定義されているデフォルトのムーブ コンストラクターは、オーバーロードの解決によって無視されます (13.3、13.4)。

ここでfooは、move-constructor を使用します。

于 2013-12-16T11:02:42.567 に答える
7

あなたが何をテストしたかはよくわかりませんが、foo確実に移動割り当て可能で移動構築可能です。確かに、これはムーブ コンストラクターやムーブ代入がアクセス可能であることについて何も述べていません。右辺値からの構築または代入が機能するだけです。clang (clang バージョン 3.5 (トランク 196718)) とgcc (gcc バージョン 4.9.0 20131031 (実験的) (GCC)) の両方がこの評価に同意します。これは私が試した完全なソースです:

#include <iostream>
#include <type_traits>
#include <memory>

struct with_copy {
    with_copy() = default;
    with_copy(with_copy const&) {}
    with_copy& operator=(with_copy const&) { return *this; }
};

struct foo {
    with_copy c;
    std::unique_ptr<int> p;
};

int main()
{
    std::cout << "move constructible: "
              << std::is_move_constructible<foo>::value << '\n';
    std::cout << "move assignable: "
              << std::is_move_assignable<foo>::value << '\n';
    foo f0;
    foo f1 = std::move(f0);
    f0 = std::move(f1);
}
于 2013-12-16T10:51:30.660 に答える