4

このケースを考えてみましょう:

template<typename T>
struct A {
  A(A ???&) = default;
  A(A&&) { /* ... */ }
  T t;
};

ムーブ コンストラクターを明示的に宣言したので、削除されていないコピー コンストラクターが必要な場合は、コピー コンストラクターを明示的に宣言する必要があります。必要に応じdefaultて、正しいパラメーターの型を見つけるにはどうすればよいですか?

A(A const&) = default; // or
A(A &) = default; // ?

そのようなシナリオが実際のプログラムで実際に発生したケースに遭遇したかどうかにも興味があります。スペックは言う

明示的にデフォルト設定された関数は...

  • 同じ宣言された関数型を持っている (参照修飾子が異なる可能性があること、およびコピー コンストラクターまたはコピー代入演算子の場合を除き、パラメーターの型が "非 const T への参照" である可能性があることを除きます。ここで、T は関数の名前です)。メンバー関数のクラス) が暗黙的に宣言されたかのように、

暗黙的に宣言されたコピー コンストラクターが typeA &を持つ場合、コピー コンストラクターをパラメーター type で明示的にデフォルト設定する必要がありますA &。しかし、暗黙的に宣言されたコピー コンストラクターがパラメーター typeを持つ場合、明示的にデフォルト設定されたコピー コンストラクターにパラメーター type を持たせたくA const&ありませんA &

A &暗黙的に宣言された関数がパラメーターの型を持ち、明示的にデフォルト設定された宣言がパラメーターの型を持っている場合、上記の規則に違反するため、両方のバージョンを宣言することはできませんA const&。私が見ることができることから、暗黙の宣言がA const&であり、明示的な宣言が である場合にのみ違いが許されますA &

編集:実際、仕様はさえ言っています

関数が最初の宣言で明示的にデフォルト設定されている場合、...

  • コピー コンストラクター、ムーブ コンストラクター、コピー代入演算子、またはムーブ代入演算子の場合は、暗黙的に宣言されている場合と同じパラメーター型を持ちます。

したがって、これらのクラス外を定義する必要があります(私が見ることができる限り、唯一の違いは、関数が非自明になることであり、これらの場合にとにかくそうである可能性があるため、害はないと思います)

template<typename T>
struct A {
  A(A &);
  A(A const&);
  A(A&&) { /* ... */ }
  T t;
};

// valid!?
template<typename T> A<T>::A(A const&) = default;
template<typename T> A<T>::A(A &) = default;

明示的に宣言された関数がA const&であり、暗黙の宣言が である場合、無効であることがわかりましたA &

ユーザー提供の明示的にデフォルト設定された関数 (つまり、最初の宣言後に明示的にデフォルト設定された関数) は、明示的にデフォルト設定された時点で定義されます。そのような関数が削除済みとして暗黙的に定義されている場合、プログラムは不適切な形式です。

これは、GCC が行っていることと一致します。さて、暗黙的に宣言されたコンストラクターの型を一致させるという当初の目標をどのように達成できますか?

4

3 に答える 3

4

私は問題を見落としていると思います...これは、コピーコンストラクターを実装する一般的なケースとどう違うのですか?

コピー コンストラクターが引数を変更しない場合 (および暗黙的に定義されたコピー コンストラクターが変更しない場合)、引数は定数参照として渡されます。定数参照によって引数を取らないコピー コンストラクターについて私が知っている唯一の使用例は、C++03で la の移動std::auto_ptrを実装したい場合ですが、これは通常は悪い考えです。C++0x では、move コンストラクターを使用した場合と同様に、move が実装されます。

于 2011-05-29T13:06:13.230 に答える
3

何らかの型推論 (概念?) が必要になるように思えます。

コンパイラはA(A const&)、それが書かれているメンバーのいずれかによって必要とされない限り、通常、バージョンを使用しA(A&)ます。したがって、いくつかの小さなテンプレート ハッカーをラップして、各メンバーが持つコピー コンストラクターのバージョンを確認できます。

最新

ideoneで参照するか、コード スニペットの後に Clang によるエラーを読んでください。

#include <memory>
#include <type_traits>

template <bool Value, typename C>
struct CopyConstructorImpl { typedef C const& type; };

template <typename C>
struct CopyConstructorImpl<false,C> { typedef C& type; };

template <typename C, typename T>
struct CopyConstructor {
  typedef typename CopyConstructorImpl<std::is_constructible<T, T const&>::value, C>::type type;
};

// Usage
template <typename T>
struct Copyable {
  typedef typename CopyConstructor<Copyable<T>, T>::type CopyType;

  Copyable(): t() {}

  Copyable(CopyType) = default;

  T t;
};

int main() {
  {
    typedef Copyable<std::auto_ptr<int>> C;
    C a; C const b;
    C c(a); (void)c;
    C d(b); (void)d;  // 32
  }
  {
    typedef Copyable<int> C;
    C a; C const b;
    C c(a); (void)c;
    C d(b); (void)d;
  }
}

これにより、次のことが得られます。

6167745.cpp:32:11: error: no matching constructor for initialization of 'C' (aka 'Copyable<std::auto_ptr<int> >')
        C d(b); (void)d;
          ^ ~
6167745.cpp:22:7: note: candidate constructor not viable: 1st argument ('const C' (aka 'const Copyable<std::auto_ptr<int> >')) would lose const qualifier
      Copyable(CopyType) = default;
      ^
6167745.cpp:20:7: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
      Copyable(): t() {}
      ^
1 error generated.

編集前

これが私が思いつくことができる最高のものです:

#include <memory>
#include <type_traits>

// Usage
template <typename T>
struct Copyable
{
  static bool constexpr CopyByConstRef = std::is_constructible<T, T const&>::value;
  static bool constexpr CopyByRef = !CopyByConstRef && std::is_constructible<T, T&>::value;

  Copyable(): t() {}

  Copyable(Copyable& rhs, typename std::enable_if<CopyByRef>::type* = 0): t(rhs.t) {}
  Copyable(Copyable const& rhs, typename std::enable_if<CopyByConstRef>::type* = 0): t(rhs.t) {}

  T t;
};

int main() {
  {
    typedef Copyable<std::auto_ptr<int>> C; // 21
    C a; C const b;                         // 22
    C c(a); (void)c;                        // 23
    C d(b); (void)d;                        // 24
  }
  {
    typedef Copyable<int> C;                // 27
    C a; C const b;                         // 28
    C c(a); (void)c;                        // 29
    C d(b); (void)d;                        // 30
  }
}

これはほとんど機能します...「a」を作成するときにエラーが発生したことを除いて。

6167745.cpp:14:78: error: no type named 'type' in 'std::enable_if<false, void>'
      Copyable(Copyable const& rhs, typename std::enable_if<CopyByConstRef>::type* = 0): t(rhs.t) {}
                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
6167745.cpp:22:11: note: in instantiation of template class 'Copyable<std::auto_ptr<int> >' requested here
        C a; C const b;
          ^

と:

6167745.cpp:13:67: error: no type named 'type' in 'std::enable_if<false, void>'
      Copyable(Copyable& rhs, typename std::enable_if<CopyByRef>::type* = 0): t(rhs.t) {}
                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
6167745.cpp:28:11: note: in instantiation of template class 'Copyable<int>' requested here
        C a; C const b;
          ^

どちらも同じ理由で発生しますが、その理由はわかりません。デフォルトのコンストラクターがあっても、コンパイラーはすべてのコンストラクターを実装しようとしているようです。SFINAE が適用されると思っていたのですが、そうではないようです。

ただし、エラー行 24 は正しく検出されます。

6167745.cpp:24:11: error: no matching constructor for initialization of 'C' (aka 'Copyable<std::auto_ptr<int> >')
        C d(b); (void)d;
          ^ ~
6167745.cpp:13:7: note: candidate constructor not viable: 1st argument ('const C' (aka 'const Copyable<std::auto_ptr<int> >')) would lose const qualifier
      Copyable(Copyable& rhs, typename std::enable_if<CopyByRef>::type* = 0): t(rhs.t) {}
      ^
6167745.cpp:11:7: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
      Copyable(): t() {}
      ^

うまくいけば SFINAE のおかげで、CopyByConstRef がオー​​バーロード セットから正しく削除されたことがわかります。

于 2011-05-29T15:22:44.820 に答える
1

暗黙のコピー コンストラクターが存在するケースは見たことがありませA&const A&

于 2011-05-29T13:05:22.557 に答える