11

With regard to previous questions on this topic:

This a follow up of the question that I've asked recently: clang: no out-of-line virtual method definitions (pure abstract C++ class) and which was marked as duplicate of this question: What is the meaning of clang's -Wweak-vtables?. I don't think that that answered my question, so here I'm focusing on the very thing that puzzles me and that hasn't been answered yet.

My scenario:

I'm trying to compile the following simple C++ code using Clang-3.5:

test.h:

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

test.cc

#include "test.h"

A::A() {;}
A::~A() {;}

The command that I use for compiling this (Linux, uname -r: 3.16.0-4-amd64):

$clang-3.5 -Wweak-vtables -std=c++11 -c test.cc

And the error that I get:

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

The above code builds fine when class A is not pure abstract. The following code doesn't emit warnings, and the only change is that class A is no longer abstract:

test2.h:

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

test2.cc

#include "test2.h"

A::A() {;}
A::~A() {;}

My question

What's so special about pure abstract classes that the above code triggers warnings in Clang?

Community
  • 1
  • 1
banach-space
  • 1,781
  • 1
  • 12
  • 27
  • In your second example `A` is not abstract at all – M.M Feb 28 '15 at 23:55
  • What do you think would happen if you had no definition of `A::~A`? Then, if the compiler waited to see a definition of `A::~A` to emit the vtable, there would be no vtable! – user253751 Mar 01 '15 at 00:47
  • 1
    @immibis that's correct behaviour isn't it? (other TUs including test2.h will come to the same conclusion) – M.M Mar 01 '15 at 01:42

1 Answers1

8

A class with virtual methods always needs to emit a vtable. The compiler needs an indication of where to store the vtable - usually in the object that implements its first function.

What's so special about pure abstract classes? Since they have no methods, the compiler has to output a vtable in every translation unit so each translation unit can refer to the pure abstract base type. That's what the warning is telling you.

You might care, for example, if you want to avoid duplicating that memory in a very low memory environment, or if you go looking at the objects and wonder why there are multiple copies of the vtable around the place.

In any case, the fact that you can take a polymorphic pointer to an A object means that the compiler has to emit some information about that type — the vtable.

Option 1: implement a virtual method, such as the destructor

My preference when creating an abstract base class is to provide an out-of-line virtual destructor; ie. implement A::~A() in the .cpp file. The downside of a user-declared virtual destructor is that it implicitly deletes the automatically-generated copy- and move-constructors & operators, so you end up needing to redeclare them. According to the rule of five, this results in a base class like this:

A.h:

class A {
public:
    A() = default;

    A(const A&) = default;
    A(A&&) = default;
    A& operator=(const A&) = default;
    A& operator=(A&&) = default;
    virtual ~A();

    virtual void doSomething() = 0;
};

A.cpp:

A::~A()
{}

It's technically no longer a pure-abstract base class, but it is functionally identical. You get safe destruction by base pointer, it still allows inherited classes to be copy- and move-constructed, and you avoid duplicate vtables in your binary.

Option 2: disable the warning

You can disable the warning for that block with Clang's diagnostic pragmas if you like:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wweak-vtables"
class A {
public:
    virtual void doSomething() = 0;
    virtual ~A() = 0;
};
#pragma clang diagnostic pop

That's your dilemma: either make the class non-pure-abstract, or turn off the warning. Depending on your requirements you might prefer one or the other, but as with all warnings you should carefully consider it.

T Percival
  • 8,526
  • 3
  • 43
  • 43
  • Since when pure abstract classes have no methods? – banach-space Mar 27 '15 at 20:12
  • 2
    @Andrzej Abstract class = at least one pure virtual method. Pure abstract class == interface => all the method are pure virtual ? – Félix Cantournet Mar 31 '15 at 23:54
  • @Ted I'm still confused. In both my examples I do define A::~A() (and indeed A::A()). How is that equivalent to having _no_ methods? And why deleting the constructor also solves the problem: `class A { public: virtual ~A() = 0; };` (assume A::A() is also deleted in test.cc)? – banach-space Apr 12 '15 at 18:36
  • > How is that equivalent to having no methods? whatever you do these methods will still exist because C++ requires them ; if you don't have them explicitely they will just be generated automatically by the compiler. – Jean-Michaël Celerier Mar 05 '18 at 07:11
  • 1
    Your example has no out-of-line virtual method definitions. You have an out-of-line non-virtual method (`A()`), and inline virtual method (`~A()`). Non-virtual methods like your constructor are immaterial because they don't require a vtable. The virtual methods like `~A()` require a vtable, but none are implemented. What you need is an *out-of-line* *virtual method*. I originally answered regarding a *pure abstract class* because that's the title of the question is, but it's true that the example given is not pure abstract, because it has a user-defined constructor. – T Percival Nov 30 '18 at 00:39
  • The second one only disables the diagnostic, but it still results in the vtable being output to each translation unit, right? – Seth Johnson Mar 08 '21 at 02:12
  • 1
    Would an alternate solution be to add a private virtual nullop method with an implementation? That would keep you from having to rewrite the constructors/assignment operators. Although if I'm not mistaken it would also increase the vtable size (and thus the size of every class instance)... – Seth Johnson Mar 08 '21 at 02:15