Mechanism for this is well explained here: Template "copy constructor" does not prevent compiler-generated move constructor, but I would like to better undestand why it is made this way. I understand that move constructor is not generated even if any other constructor is written by the programmer, because it is an indication that construction of object is non trivial and auto generated constructor will probably be wrong. Then why templated constructors that have the same signature as copy constructors are not simply named copy constructors?
Example:
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;
};
and then:
Person p("asd");        // OK!
//Person p4(p);         // error as Person(T&&) is a better match
if I make p const:
const Person p("asd");
Person p4(p);           // thats ok, generator constructor is a better match
but if I explicitly delete even a move constructor with:
Person(Person&&) = delete;
then auto generation of constructors is inhibited.
You understand wrong.
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 has generated move/copy construct/assignment.
Live example.
A copy/move construct/assignment written by a programmer results in the other ones being suppressed.
Now, writing a constructor suppresses the zero-argument constructor. That may be why you are confused.
Templated constructors with the same signature as copy constructors are not copy constructors because the standard says so.
As it happens, templated code is rarely the correct code for a copy or move constructor/assignment.
The fact that forwarding references often grab self& and self const&& copy/move over the actual copy/move operations is a problem.  C++ isn't perfect.
Usually the way to avoid this is:
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";
}
or !std::is_base_of<Person, std::decay_t<T>>::value which covers some other situations (like inheriting constructors).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With