当存在模板化构造函数时,为什么编译器会生成复制/移动构造函数?

时间:2017-03-01 15:03:21

标签: c++ c++11

这方面的机制很好地解释了: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;

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

1 个答案:

答案 0 :(得分:5)

你理解错了。

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&self const&&复制/移动实际的复制/移动操作这一事实是一个问题。 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<Person, std::decay_t<T>>::value涵盖了其他一些情况(比如继承构造函数)。