如果一个不知道其参数类型的人怎么能默认一个特殊的成员函数?

问题描述

| 考虑这种情况:
template<typename T>
struct A {
  A(A ???&) = default;
  A(A&&) { /* ... */ }
  T t;
};
我显式声明了一个move构造函数,因此,如果我想拥有一个删除的复制构造函数,则需要显式声明一个复制构造函数。如果要对它进行“ 1”设置,如何找到正确的参数类型?
A(A const&) = default; // or
A(A &) = default; // ?
我也对您是否遇到过这样的情况真正在实际程序中弹出的情况感兴趣。规格说   明确认的功能应...         具有相同的声明函数类型(除了可能使引用限定符不同以及在   如果是复制构造函数或复制赋值运算符,则参数类型可以是\“对非常量T的引用”,其中T是成员函数的类的名称),就好像它是隐式声明的一样,    如果隐式声明的副本构造函数的类型为
A &
,我想让我的副本构造函数的参数类型为
A &
显式认。但是,如果隐式声明的副本构造函数的参数类型为
A const&
,则我不想让我的认副本构造函数的参数类型为
A &
,因为这将禁止从const左值进行复制。 我不能同时声明两个版本,因为在隐式声明的函数具有参数类型“ 3”而我的显式认声明具有参数类型“ 5”的情况下,这将违反上述规则。据我所知,仅当隐式声明为
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 &
:   由用户提供的显式函数(即在其首次声明后显式认)在显式认的位置定义;如果将此类函数隐式定义为Delete,则程序格式错误。 这与GCC所做的匹配。现在,我如何才能实现与隐式声明的构造函数的类型匹配的最初目标?     

解决方法

        在我看来,您将需要某种类型的演绎(概念?)。 除非成员之一要求将其写为“ 15”,否则编译器通常将使用“ 14”版本。因此,我们可以包装一些小模板黑客,以检查每个成员具有哪个版本的副本构造函数。 最新 请在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。     ,        我想我看不到问题了……与实现复制构造函数的常见情况有什么不同? 如果您的副本构造函数不会修改参数(并且隐式定义的副本构造函数不会这样做),则应将参数作为常量引用传递。我所知道的唯一一个不使用常量引用作为参数的副本构造函数的用例是,在C ++ 03中,当您想要实现移动la
std::auto_ptr
时,这通常是个坏主意。在C ++ 0x中,移动将像使用move构造函数一样实现。     ,        我从未见过隐式副本构造函数为
A&
的情况-在任何情况下都应以you24ѭ做为好。