Debashish
Debashish

Reputation: 1185

Derived class member functions are involved

I got a piece of code from the C++ Templates the Complete Guide book: In the following program, we create a Person class with perfect forwarded template constructor and two other member function (const copy constructor , move constructor)

#include <utility>
#include <string>
#include <iostream>

class Person
{
  private:
    std::string name;
  public:
    // generic constructor for passed initial name:
    template<typename STR>
    explicit Person(STR&& n) : name(std::forward<STR>(n)) {
        std::cout << "TMPL-CONSTR for '" << name << "'\n";
    }

    // copy and move constructor:
    Person (Person& p) : name(p.name) {
        std::cout << "COPY-CONSTR Person '" << name << "'\n";
    }
    Person (Person const& p) : name(p.name) {
        std::cout << "COPY-CONSTR Person '" << name << "'\n";
    }
    Person (Person&& p) : name(std::move(p.name)) {
        std::cout << "MOVE-CONSTR Person '" << name << "'\n";
    }
};


int main()
{
  std::string s = "sname";
  Person p1(s);
  Person p2("tmp");
  Person p3(p1);            // Error
  Person p4(std::move(p1));
}

Regarding the error at line Person p3(p1); Here the book says "According to overload resolution rules"

template<typename STR>
Person(STR&& n)

"is better match than the copy constructor"

Person(const Person& p)

So, if I add one more constructor inside Person as follows:

Person (Person& p) : name(p.name) {
    std::cout << "COPY-CONSTR Person '" << name << "'\n";
}

The error should go away. Indeed the error went away and here is the output:

TMPL-CONSTR for 'sname'
TMPL-CONSTR for 'tmp'
COPY-CONSTR Person 'sname'              : this is due to `Person (Person& p)`
MOVE-CONSTR Person 'sname'

But the book also says, this is a partial solution only, because template constructor of Derived class of Person is still a better match by the overload resolution rules.

So I tried to check that also:

#include <utility>
#include <string>
#include <iostream>

class Person
{
  private:
    std::string name;
  public:
    // generic constructor for passed initial name:
    template<typename STR>
    explicit Person(STR&& n) : name(std::forward<STR>(n)) {
        std::cout << "TMPL-CONSTR for '" << name << "'\n";
    }

    // copy and move constructor:
    Person (Person& p) : name(p.name) {
        std::cout << "COPY-CONSTR Person '" << name << "'\n";
    }
    Person (Person const& p) : name(p.name) {
        std::cout << "COPY-CONSTR Person '" << name << "'\n";
    }
    Person (Person&& p) : name(std::move(p.name)) {
        std::cout << "MOVE-CONSTR Person '" << name << "'\n";
    }
};

#if 1 // or 0
class newPerson : public Person
{
  private:
    std::string name;
  public:
    // generic constructor for passed initial name:
    template<typename STR>
    explicit newPerson(STR&& n) : name(std::forward<STR>(n)) {
        std::cout << "TMPL-CONSTR for '" << name << "'\n";
    }

    // copy and move constructor:
    newPerson (newPerson const& p) : name(p.name) {
        std::cout << "COPY-CONSTR Person '" << name << "'\n";
    }
    newPerson (newPerson&& p) : name(std::move(p.name)) {
        std::cout << "MOVE-CONSTR Person '" << name << "'\n";
    }
};
#endif

int main()
{
  std::string s = "sname";
  Person p1(s);
  Person p2("tmp");
  Person p3(p1);             // Error again
  Person p4(std::move(p1));
}

I am pasting the clang error in this case (gcc error list were too big)

specialmemtmplMain.cpp:41:5: error: constructor for 'newPerson' must explicitly initialize the base class 'Person' which does not have a default constructor
    newPerson (newPerson const& p) : name(p.name) {
    ^
specialmemtmplMain.cpp:5:7: note: 'Person' declared here
class Person
      ^
specialmemtmplMain.cpp:44:5: error: constructor for 'newPerson' must explicitly initialize the base class 'Person' which does not have a default constructor
    newPerson (newPerson&& p) : name(std::move(p.name)) {
    ^
specialmemtmplMain.cpp:5:7: note: 'Person' declared here
class Person
      ^
2 errors generated.

So, my question is, I have not instantiated anything of newPerson class. Then why is the error refers to newPerson constructors ? What is really going on here ?

Upvotes: 0

Views: 53

Answers (1)

Dmitry Kuzminov
Dmitry Kuzminov

Reputation: 6584

The problem is that the derived class shall call one of the constructors of the base class. You don't call the constructor explicitly, so the comiler tries to call the default constructor of Person::Person() implicitly, but there is no default constructor defined.

Upvotes: 1

Related Questions