85

I basically do not understand clang's -Wweak-vtables. Here is what I observed so far:

Case one: (triggers the warning)

class A {
    public:
    virtual ~A(){}        
};

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Case two: (Does not trigger warning)

class A {
    public:
    virtual ~A(){}        
};   

int main(){}

Case three: (Does not trigger warning)

class A {
    public:
    virtual ~A();

};

A::~A(){}

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Case four: (Triggers warning)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}        
};    

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Case five: (Does not trigger warning)

class A {
    public:
    virtual ~A(){}
    virtual void fun();      
};    

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Case six: (Does not trigger warning)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}
};    

class B : public A {};

int main(){}

Case seven: (Does not trigger warning)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}
};    

class B : public A {
    public:
    virtual void fun(){}
};

int main(){}

The exact warning is

warning: 'A' has no out-of-line virtual method definitions; its vtable 
will be emitted in every translation unit [-Wweak-vtables]

So apparently, if I do not declare a non-inline virtual function in a class, it causes some kind of problem if and only if I derive from it and the derived class has a virtual destructor.

Questions:

  1. Why is this a problem?
  2. Why does this get fixed by declaring a virtual function? (Warning speaks of definitions)
  3. Why does the warning not occur when I do not derive from the class?
  4. Why does the warning not occur when the derived class does not have a virtual destructor?
Filip Roséen - refp
  • 62,493
  • 20
  • 150
  • 196
Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182

1 Answers1

111

If all of a class's virtual methods are inline, the compiler has no way to select a translation unit in which to place a single shared copy of the vtable — instead, a copy of the vtable has to be placed in each object file that needs it. On many platforms the linker is able to unify these multiple copies, either by discarding duplicate definitions or by mapping all references to one copy, so this is only a warning.

Implementing a virtual function out-of-line enables the compiler to select the translation unit that implements that out-of-line method as a "home" for the class's implementation details, and places the single shared copy of the vtable in the same translation unit. If multiple methods are out-of-line, an arbitrary choice of method may be made by the compiler so long as that choice is determined only by the class's declaration; for example, GCC chooses the first non-inline method in declaration order.

If you don't override any method of a class, the virtual keyword has no observable effect, so there's no need for the compiler to emit a vtable for the class. If you do not derive from A, or if you fail to declare a derived class's destructor virtual, there are no overridden methods in A and thus A's vtable is omitted. If you declare an additional out-of-line virtual method to suppress the warning and also do something that overrides a method in A, the implementation of the non-inline virtual (and its accompanying copy of the vtable) needs to be supplied in a linked translation unit, otherwise linking will fail because the vtable is missing.

Jeffrey Hantin
  • 35,734
  • 7
  • 75
  • 94
  • 2
    What does it do when (different) out-of-line methods of the same class occur in several different TUs? – M.M Mar 01 '15 at 00:13
  • 2
    @Matt, one out-of-line method is chosen in some arbitrary but deterministic manner based on the class's declaration, and the vtable ends up in the same TU as the method implementation. All relevant TUs see the same declaration: bar inconsistent declaration shenanigans, they will agree on the chosen method and thus collectively emit exactly one vtable. – Jeffrey Hantin Mar 02 '15 at 11:46
  • This answer doesn't explain cases 6 and 7. – Leon Nov 10 '16 at 08:20
  • @Leon, is in cases 6 and 7 `B`'s destructor isn't virtual so `A`'s vtable isn't referenced; adding an unused empty inline virtual method doesn't change that situation. – Jeffrey Hantin Nov 11 '16 at 08:09
  • 2
    @JeffreyHantin What do you mean by *"B's destructor isn't virtual"*? The compiler generated (implicitly declared) destructor of `B` should be virtual (since the destructor of the base class has been declared `virtual`). – Leon Nov 11 '16 at 10:11
  • 2
    @Leon, quoth the spec: "A destructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (3.2) or when it is explicitly defaulted after its first declaration." Nothing in cases 6 or 7 uses or explicitly defaults `~B()`, therefore the default definition is not implicitly instantiated by the compiler. How can it be virtual if it doesn't exist? The triviality of the examples is causing some counterintuitive effects. – Jeffrey Hantin Nov 11 '16 at 22:10