Clang: No Out-Of-Line Virtual Method Definitions (Pure Abstract C++ Class)

clang: no out-of-line virtual method definitions (pure abstract C++ class)

We don't want to place the vtable in each translation unit. So there must be some ordering of translation units, such that we can say then, that we place the vtable in the "first" translation unit. If this ordering is undefined, we emit the warning.

You find the answer in the Itanium CXX ABI. In the section about virtual tables (5.2.3) you find:

The virtual table for a class is emitted in the same object containing the definition of its key function, i.e. the first non-pure virtual function that is not inline at the point of class definition. If there is no key function, it is emitted everywhere used. The emitted virtual table includes the full virtual table group for the class, any new construction virtual tables required for subobjects, and the VTT for the class. They are emitted in a COMDAT group, with the virtual table mangled name as the identifying symbol. Note that if the key function is not declared inline in the class definition, but its definition later is always declared inline, it will be emitted in every object containing the definition.

NOTE: In the abstract, a pure virtual destructor could be used as the key function, as it must be defined even though it is pure. However, the ABI committee did not realize this fact until after the specification of key function was complete; therefore a pure virtual destructor cannot be the key function.

The second section is the answer to your question. A pure virtual destructor is no key function. Therefore, it is unclear where to place the vtable and it is placed everywhere. As a consequence we get the warning.

You will even find this explanation in the Clang source documentation.

So specifically to the warning: You will get the warning when all of your virtual functions belong to one of the following categories:

  1. inline is specified for A::x() in the class definition.

    struct A {
    inline virtual void x();
    virtual ~A() {
    }
    };
    void A::x() {
    }
  2. B::x() is inline in the class definition.

    struct B {
    virtual void x() {
    }
    virtual ~B() {
    }
    };
  3. C::x() is pure virtual

    struct C {
    virtual void x() = 0;
    virtual ~C() {
    }
    };
  4. (Belongs to 3.) You have a pure virtual destructor

    struct D {
    virtual ~D() = 0;
    };
    D::~D() {
    }

    In this case, the ordering could be defined, because the destructor must be defined, nevertheless, by definition, there is still no "first" translation unit.

For all other cases, the key function is the first virtual function that does not fit to one of these categories, and the vtable will be placed in the translation unit where the key function is defined.

Declaring an interface in C++ and not emmiting its vtable to every translation unit

You already have a non-pure virtual function: the destructor! Just define it in its own translation unit.

// IDemo.h

class IDemo
{
public:
virtual ~IDemo();
virtual void OverrideMe() = 0;
};

// IDemo.cpp

IDemo::~IDemo() = default;

What is the meaning of clang's -Wweak-vtables?

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.

Declaring abstract class (pure virtual method) increase binary size substantially

It's almost certainly because of unexpected inclusion of exception handling, which libc++ has built into it, regardless of whether or not you compile your code with --noexception or whatever the proper gnu-ism is.

The exception in question is probably 'pure virtual function call' or something like that (a fairly obscure runtime error to get, but possible if you call virtual functions in the base class constructor).

The answer is to provide your own empty implementation of this, atexit(), and whatever random callout that you don't really need. Once you do that, the linker won't drag in the other stuff (which drags in other stuff, which drags in other stuff, etc).

void __cxa_pure_virtual(void) 
{
BKPT();
}

Is what I have on our project, though things may have changed in your version of libc++

Virtual destructor for pure abstract base classes

Whereas, clang generates warning for:

class A {
public:
virtual ~A() {}
virtual void foo() = 0;
};

using = default doesn't trigger it.

class A {
public:
virtual ~A() = default;
virtual void foo() = 0;
};

Even if both are valid.

Demo

Determine if a member call is virtual in the Clang AST

The distinguishing factor is hasQualifier on the MemberExpr of the callee object. If hasQualifier is true, then the function call is non-virtual.

Avoid weak-vtable warnings for classes only defined in a source file

It turns out that standalone clang does the right thing by default, only the clang code model in Qt creator shows the useless warning. It's already reported as QTCREATORBUG-19741, so there's nothing more to do than wait for an updated Qt creator version.



Related Topics



Leave a reply



Submit