4

考虑这种情况:

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

我显式声明了一个移动构造函数,因此如果我想要一个未删除的复制构造函数,我需要显式声明一个复制构造函数。如果我想要default它,我怎样才能找到正确的参数类型?

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

我也对您是否遇到过在实际程序中实际出现这种情况的情况感兴趣。规范说

显式默认的函数应...

  • 具有相同的声明函数类型(除了可能不同的引用限定符以及在复制构造函数或复制赋值运算符的情况下,参数类型可能是“对非常量 T 的引用”,其中 T 是成员函数的类)就好像它已被隐式声明,

如果隐式声明的复制构造函数具有 type A &,我希望我的复制构造函数显式默认为参数 type A &。但是如果隐式声明的复制构造函数有参数类型A const&,我不想让我的显式默认的复制构造函数有参数类型A &,因为这会禁止从 const 左值复制。

我不能声明这两个版本,因为当隐式声明的函数具有参数类型A &而我的显式默认声明具有参数类型时,这将违反上述规则A const&。据我所知,只有当隐式声明是A const&,而显式声明是 时,才允许有区别A &

编辑:事实上,规范甚至说

如果一个函数在它的第一个声明中被显式默认,...

  • 在复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符的情况下,它应具有与已隐式声明的参数类型相同的参数类型。

所以我需要定义这些out-of-class(我认为这不会受到伤害,因为据我所知,唯一的区别是该函数将变得不平凡,在这些情况下很可能无论如何)

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 中,移动将像使用移动构造函数一样实现。

于 2011-05-29T13:06:13.230 回答
3

在我看来,你需要一些类型推导(概念?)。

编译器通常会使用该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。

于 2011-05-29T15:22:44.820 回答
1

我从未见过隐式复制构造函数会出现的情况A&- 在任何情况下你都应该对const A&.

于 2011-05-29T13:05:22.557 回答