【问题标题】:Why compilers generate a copy/move constructors when there is a templated constructor?当有模板化构造函数时,为什么编译器会生成复制/移动构造函数?
【发布时间】:2017-07-20 23:34:53
【问题描述】:

这里很好地解释了这个机制:Template "copy constructor" does not prevent compiler-generated move constructor,但我想更好地理解为什么会这样。我知道即使程序员编写了任何其他构造函数,也不会生成移动构造函数,因为这表明对象的构造不是微不足道的,并且自动生成的构造函数可能是错误的。那为什么和拷贝构造函数有相同签名的模板化构造函数不是简单命名的拷贝构造函数呢?

例子:

class Person {
 public:
  template<typename T>
    Person(T&& t) : s(std::forward<T>(t)) {
      std::cout << __PRETTY_FUNCTION__ << "\n";
    }

  Person(int n) {
    std::cout << __PRETTY_FUNCTION__ << "\n";
  }

  // No need to declare copy/move constructors as compiler will do this implicitly
  // Templated constructor does not inhibit it.
  //Person(const Person&) = default;
  //Person(Person&&) = default;

 private:
  std::string s;
};

然后:

Person p("asd");        // OK!
//Person p4(p);         // error as Person(T&&) is a better match

如果我让p const:

const Person p("asd");
Person p4(p);           // thats ok, generator constructor is a better match

但如果我使用以下命令显式删除移动构造函数:

Person(Person&&) = delete;

然后禁止自动生成构造函数。

【问题讨论】:

  • "我知道即使程序员编写了任何其他构造函数,也不会生成移动构造函数" 这不是真的。只有存在复制构造函数/赋值才能阻止移动构造函数的生成。
  • @NicolBolas 谢谢,我在 Effective Modern C++ 中查找了第 17 项,并且移动构造函数/赋值运算符和析构函数阻止生成移动构造函数或赋值运算符。

标签: c++ c++11


【解决方案1】:

你理解错了。

struct noisy {
  noisy() { std::cout << "ctor()\n"; }
  noisy(noisy&&) { std::cout << "ctor(&&)\n"; }
  noisy(noisy const&) { std::cout << "ctor(const&)\n"; }
  noisy& operator=(noisy&&) { std::cout << "asgn(&&)\n"; return *this; }
  noisy& operator=(noisy const&) { std::cout << "asgn(const&)\n"; return *this; }
};

struct test {
  noisy n;
  test(int x) { (void)x; }
};

test 已生成移动/复制构造/赋值。

Live example.

程序员编写的复制/移动构造/赋值会导致其他的被抑制。

现在,编写构造函数会抑制零参数构造函数。这可能就是你感到困惑的原因。


与复制构造函数具有相同签名的模板构造函数不是复制构造函数,因为标准是这样规定的。

碰巧的是,模板化代码很少是复制或移动构造函数/赋值的正确代码。

转发引用经常抢占self&amp;self const&amp;&amp; 复制/移动而不是实际的复制/移动操作是一个问题。 C++ 并不完美。

通常避免这种情况的方法是:

template<class T,
  class=std::enable_if_t<
    !std::is_same<std::decay_t<T>, Person>::value
  >
>
Person(T&& t) : s(std::forward<T>(t)) {
  std::cout << __PRETTY_FUNCTION__ << "\n";
}

!std::is_base_of&lt;Person, std::decay_t&lt;T&gt;&gt;::value 涵盖其他一些情况(如继承构造函数)。

【讨论】:

  • 谢谢,我现在正在阅读 Effective Modern C++ 中的第 27 项,其中涉及如何避免通用(或现在转发)引用构造函数出现问题的方法。太糟糕了 Scott Meyers 没有深入 SFINAE。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-16
  • 2011-05-24
  • 2020-01-14
  • 1970-01-01
  • 2013-04-09
  • 1970-01-01
  • 2016-10-16
相关资源
最近更新 更多