如果不知道参数类型,如何默认特殊成员函数?

时间:2011-05-29 12:35:34

标签: c++ c++11 defaulted-functions

考虑这种情况:

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

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

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

我也对你是否遇到这样一个场景实际出现在真实节目中的情况感兴趣。规范说

  

明确默认的功能应该......

     
      
  • 具有相同的声明函数类型(除了可能不同的ref-quali firs和除了in   在复制构造函数或复制赋值运算符的情况下,参数类型可以是“对非const T的引用”,其中T是成员函数类的名称),就像它已被隐式声明一样,
  •   

如果隐式声明的复制构造函数具有类型A &,我希望使用参数类型A &显式默认我的复制构造函数。但是如果隐式声明的复制构造函数具有参数类型A const&,那么希望我的显式默认复制构造函数具有参数类型A &,因为那样会禁止复制来自const lvalues。

我无法声明这两个版本,因为当隐式声明的函数具有参数类型A &并且我的显式默认声明具有参数类型A const&时,这会违反上述规则。从我所看到的情况来看,仅当隐式声明为A const&时才允许存在差异,并且显式声明将为A &

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

  

如果某个函数在其第一个dec-上明确默认   哀伤,......

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

所以我需要定义这些课外(我认为这并没有伤害,因为据我所知,唯一的区别是该功能将变得非常重要,无论如何在这些情况下很可能)

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 &

  

用户提供的显式默认函数(即,在第一次声明后显式默认)是在明确默认的位置定义的;如果将这样的函数隐式定义为已删除,则该程序格式错误。

这符合海湾合作委员会正在做的事情。现在,我如何实现我的原始目标,即匹配隐式声明的构造函数的类型?

3 个答案:

答案 0 :(得分:4)

我想我没有看到问题......这与实现复制构造函数的常见情况有何不同?

如果你的拷贝构造函数不会修改参数(并且隐式定义的拷贝构造函数不会这样做),那么参数应作为常量引用传递。我所知道的唯一一个没有通过常量引用接受参数的复制构造函数的用例是在C ++ 03中你想要实现移动 la std::auto_ptr,这通常是一个反正坏主意。在C ++ 0x中,移动将与移动构造函数一样实现。

答案 1 :(得分: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。

答案 2 :(得分:1)

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