繁体   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