首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如果一个人不知道它的参数类型,怎么能默认一个特殊的成员函数呢?

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

Stack Overflow用户
提问于 2011-05-29 12:35:34
回答 3查看 1K关注 0票数 4

考虑这一情况:

代码语言:javascript
复制
template<typename T>
struct A {
  A(A ???&) = default;
  A(A&&) { /* ... */ }
  T t;
};

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

代码语言:javascript
复制
A(A const&) = default; // or
A(A &) = default; // ?

我还感兴趣的是,您是否遇到了这样的场景在实际程序中出现的情况。说明书上写着

一个明确默认的函数将..。

  • 具有相同的已声明函数类型(除了可能的differing ref-fiers,以及在副本构造函数或副本赋值操作符的情况下,参数类型可以是“对非const T的引用”,其中T是成员函数的类的名称),就像它是隐式声明的那样,

如果隐式声明的复制构造函数将具有A &类型,则我希望使用参数类型A &显式地默认复制构造函数。但是,如果隐式声明的复制构造函数将具有参数类型A const&,则我不希望显式默认复制构造函数具有参数类型A &,因为这将禁止从const复制。

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

编辑:事实上,规范说

如果一个函数在其第一次dec- laration中被显式默认,.

  • 对于复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符,其参数类型应与隐式声明的参数类型相同。

因此,我需要定义这些类外(我认为这并不伤人,因为就我所能看到的唯一区别是,这个函数将变得非平凡,在这些情况下这可能是很可能的)。

代码语言:javascript
复制
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的做法相吻合。现在,我如何实现我原来的目标--匹配隐式声明的构造函数的类型?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-05-29 15:22:44

在我看来,你需要某种类型的推论(概念?)。

编译器通常会使用A(A const&)版本,除非其中一个成员需要它编写A(A&)。因此,我们可以包装一些小的模板黑客,以检查每个成员的复制构造函数的哪个版本。

最新

意为上查阅它,或者在代码片段之后由Clang读取错误。

代码语言:javascript
复制
#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;
  }
}

这意味着:

代码语言:javascript
复制
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.

前版

这是我能想到的最好的:

代码语言:javascript
复制
#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“时遇到了一些错误。

代码语言:javascript
复制
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;
          ^

和:

代码语言:javascript
复制
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行错误:

代码语言:javascript
复制
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。

票数 2
EN

Stack Overflow用户

发布于 2011-05-29 13:06:13

我想我看不出问题..。这与实现副本构造函数的常见情况有什么不同?

如果复制构造函数不会修改参数(并且隐式定义的复制构造函数不会修改),那么参数应该作为常量引用传递。对于不使用常量引用的复制构造函数,我知道的唯一用例是,在C++03中,您想要实现移动la std::auto_ptr,这通常是个坏主意。在C++0x中,移动将像使用移动构造函数一样实现。

票数 4
EN

Stack Overflow用户

发布于 2011-05-29 13:05:22

我从未见过隐式复制构造函数为A&的情况--在任何情况下,使用const A&都应该是好的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6167745

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档