簡體   English   中英

如果不知道其參數類型,如何默認一個特殊成員 function?

[英]How can one default a special member function if one doesn't know its parameter types?

考慮這種情況:

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

我顯式聲明了一個移動構造函數,所以如果我想要一個未刪除的復制構造函數,我需要顯式聲明一個復制構造函數。 如果我想default它,我怎樣才能找到正確的參數類型?

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

我也對您是否遇到過在實際程序中實際出現這種情況的情況感興趣。 規范說

明確默認的 function 應...

  • 具有相同聲明的 function 類型(除了可能不同的 ref 限定符以及在復制構造函數或復制賦值運算符的情況下,參數類型可能是“對非常量 T 的引用”,其中 T 是成員函數的類)就好像它已被隱式聲明,

如果隱式聲明的復制構造函數的類型為A & ,我希望我的復制構造函數顯式默認為參數類型A & 但是,如果隱式聲明的復制構造函數具有參數類型A const& ,我希望我的顯式默認復制構造函數具有參數類型A & ,因為這將禁止從 const 左值進行復制。

我不能同時聲明這兩個版本,因為當隱式聲明的 function 具有參數類型A &而我的顯式默認聲明具有參數類型A const&時,這將違反上述規則。 據我所知,僅當隱式聲明為A const&且顯式聲明為A &時,才允許存在差異。

編輯:事實上,規范甚至說

如果 function 在其第一個聲明中明確默認,...

  • 在復制構造函數、移動構造函數、復制賦值運算符或移動賦值運算符的情況下,它應具有與已隱式聲明的參數類型相同的參數類型。

所以我需要定義這些out-of-class(我認為這不會受到傷害,因為據我所知,唯一的區別是 function 將變得不平凡,在這些情況下很可能無論如何)

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;

好吧,我發現如果顯式聲明的 function 是A const&是無效的,而隱式聲明是A &

用戶提供的顯式默認 function(即,在其第一次聲明后顯式默認)在顯式默認的位置定義; 如果這樣的 function 被隱式定義為已刪除,則程序格式錯誤。

這符合 GCC 正在做的事情。 現在,我怎樣才能實現匹配隱式聲明的構造函數類型的最初目標?

我想我沒有看到問題......這與實現復制構造函數的常見情況有何不同?

如果您的復制構造函數不會修改參數(並且隱式定義的復制構造函數不會這樣做),則應將參數作為常量引用傳遞。 對於不通過常量引用獲取參數的復制構造函數,我所知道的唯一用例是在 C++03 中,當您想要實現移動la std::auto_ptr時,這通常是一個壞主意。 在 C++0x 中,移動將像使用移動構造函數一樣實現。

在我看來,您需要一些類型推導(概念?)。

編譯器通常會使用A(A const&)版本,除非其中一個成員要求它寫成A(A&) 因此,我們可以包裝一些小模板hackery 來檢查每個成員擁有哪個版本的復制構造函數。

最新的

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() {}
      ^

我們可以看到 CopyByConstRef 被正確地從重載集中驅逐,希望感謝 SFINAE。

我從未見過隱式復制構造函數是A&的情況 - 在任何情況下你都應該使用const A&

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM