The answer to your first question,
What causes the unresolved external in
the linker when the .dtor is virtual
or when the .ctor isn't inlined?
...is, quite simply, that you don't have a definition for the destructor.
Now your second question is somewhat more interesting:
why do I not get an unresolved
external if I make the destructor
non-virtual, or if I inline the
constructor?
And the reason is because your compiler didn't need X
's destructor since you never instantiated X
, so it threw your whole class away. If you try to compile this program, you will get an unresolved external:
class X
{
public:
X();
~X();
};
X::X() {};
int main()
{
X x;
return 0;
}
But if you comment out X x;
it will compile just fine, as you have observed.
Now let's come back around to why it won't compile if the destructor if virtual
. I'm speculating here, but I believe the reason is because, since you have a virtual destructor, X
is now a polymorphic class. In order to lay-out polymorphic classes in memory, compilers that implement polymorphism using a vtable need the adresses to every virtual function. You haven't implemented X::~X
, so an unresolved external results.
Why doesn't the compiler just throw X
away as it did when X
was not a polymorphic class? More speculation here. But I expect the reason is because even if you haven't directly instantiated X
, it can't be sure that nowhere in your code does an X
live, masqerading as something else. For an example, consider an abstract base class. In this case, you'll never instantiate Base
directly and the code for Derived
might be in a totally seperate translation unit. So when the compiler gets to this polymorphic class, it can't discard it even if it doesn't know you instantiated it.