There are two programs below. When I run the first program, I encounter an error stating undefined reference to base class constructor and destructor. However, when I run the second one, it successfully compiles. Am I right to assume that the base class constructor and destructor are synthesized in the second program, but not in the first one? Why is that so?
class Base
{
public:
Base();
virtual ~Base();
};
class Derived : public Base
{
public:
Derived();
virtual ~Derived();
};
Derived::Derived(){};
Derived::~Derived(){};
int main
(
int argc,
char** argv
)
{
return 0;
}
class Base
{
public:
Base();
virtual ~Base();
};
class Derived : public Base
{
public:
Derived(){};
virtual ~Derived(){};
};
int main
(
int argc,
char** argv
)
{
return 0;
}
I think I understand the linker error, what I don't understand is how come the second example does not cause a linker error, as it does not provide implementation for base class constructor or destructor, either.
the second example ... does not provide implementation for base class constructor or destructor, either.
Sorry, I completely missed the actual point of your question.
I assume it has something to do with the functions defined within the class body being implicitly inline, but I can't find a good source to confirm exactly why that works. https://en.cppreference.com/w/cpp/language/inline
A function defined entirely inside a class/struct/union definition, whether it's a member function or a non-member friend function, is implicitly an inline function
class Base
{
public:
Base();
virtual ~Base();
};
class Derived : public Base
{
public:
inline Derived();
inlinevirtual ~Derived();
};
Derived::Derived(){};
Derived::~Derived(){};
int main
(
int argc,
char** argv
)
{
return 0;
}
Someone more in tune with the specifics of the standard can probably explain this better, but essentially, this is all because of the One Definition Rule in C++.
(1) One and only one definition of every non-inline function or variable that is odr-used is required
(2) For an inline function, a definition is required in every translation unit where it is odr-used.
(3) Informally, an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken, or a reference is bound to it
In your second example's (non-inline) Derived constructor, the Base class constructor is implicitly called before the body of the Derived constructor, so its definition needs to be known (item 1), because the Derived constructor is not inline itself.
But once you declare Derived as inline, it is now inline just like the Base constructor. Your program never calls the Derived constructor from anywhere, therefore the compiler is OK with a definition for it not existing, because the Derived constructor is not odr-used (item 2, item 3).
I think what you say makes sence, I tried calling Derived d in the main function, which I assume constitutes an "odr-use" of the default constructor. The constructor call throws a linker error and confirms what you said. However, if I replace Derived d with Derived d(), the error goes away! I think I understand the difference between the two: Derived d calls the default constructor; Derived d() value-initializes d, a process which should have also called the default constructor (besides value-initializing class members). My question is: I expected both calls to end in a linker error, but Derived d() doesn't. Why?
class Base
{
public:
Base();
virtual ~Base();
};
class Derived : public Base
{
public:
Derived(){};
virtual ~Derived(){};
};
int main
(
int argc,
char** argv
)
{
Derived d1(); //no error
Derived d2; //error!
return 0;
}
Derived d(); actually just declares a function called d1 that takes no arguments and returns a Derived class! So that's the only reason there is no error. To get rid of the linnker errors you need to define Base() and ~Base().
> I tried calling Derived d in the main function, which I assume constitutes an "odr-use" of the default constructor.
In the original example, there is odr-use of the destructor whether we call it or not (it is virtual, but not pure virtual). However, for odr violations ,"no diagnostic is required"; the implementation may not encounter any use of the vtables for these classes in the program, and may not try to instantiate them.