Why does explicit template instantiation result in weak-template-vtables warning when there are out-of-line virtuals?

EDIT: I do not think this is a bug in Clang, but instead a consequence of a requirement of the Itanium C++ ABI: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vague-itemplate This section is referenced in the Clang source in RecordLayoutBuilder.cpp in computeKeyFunction:

Template instantiations don't have key functions per Itanium C++ ABI 5.2.6. Same behaviour as GCC.

The Itanium specification says that class template instantiations will be stored in a COMDAT section in the object file. COMDAT sections are used to store multiple definitions of the same object, which can then be unified at link-time. If the template was compiled the way I expected in my answer, with a key function anchoring it to a specific translation unit, then that wouldn't be compliant with this ABI.

I do think the warning is unhelpful, but as it's not part of -Wall or -Wextra I don't mind so much.

(Original post below)

I'm inclined to believe that this is due to a bug in Clang, reported here: https://bugs.llvm.org/show_bug.cgi?id=18733

Reposting content here in case the link breaks:

Rafael Ávila de Espíndola 2014-02-05 00:00:19 PST

Given

template<typename T>
class foo {
  virtual ~foo() {}
};

extern template class foo<int>;
template class foo<int>;

clang warns:

test.cpp:6:23: warning: explicit template instantiation 'foo<int>' will emit a vtable in every translation unit [-Wweak-template-vtables]
extern template class foo<int>;
                      ^
1 warning generated.

note that the warning points to the explicit template instantiation declaration, but is triggered by the definition. This should probably be checking if the definition is in a header. In a .cpp file there is only one translation unit that sees it.

Comment 1 David Faure 2016-02-13 12:21:27 PST

Yes, this false positive is indeed annoying. Thanks for reporting it. Clang developers: thanks for fixing it :-)

I'd be grateful for anyone else's opinion, but I agree with the bug reporter that this warning seems bogus in this case.

Although the last comment on the bug report refers to it as fixed, the bug is still listed with status "new", so I do not believe it is fixed.


Does the line template class Derived<int>; exist in a header-file, which again is included in multiple source-files?

In that case the, vtable and methods of class Derived<int> will exist in multiple object-files. And the linker has to figure out what to do with those multiple copies.

How the compiler and linker is supposed resolve this according to the c++ standard, i am not sure of. But typically I don't care since the copies should normally look the same.

But to avoid this issue, you should put extern template class Derived<int>; in the header file, and template class Derived<int>; in exactly 1 compile unit (aka. source-file)

EDIT (to reflect your split of the code into "file.hpp" and "file.cpp"):

I have played a little bit with clang-6 (I that is the latest version I have)

To me the warning is of the type "If you do X, Y will happen". But it doesn't mean y has happened.

In this case Y is multiple vtables and that will only happen if you put template class Derived<int>; in multiple source files, which you dont't do.

The warning gets triggered for each template class Derived<int>; in your sources, so if you only see one warning, there will be only one vtable.

But there is a way to get rid of the warning: Do not have explicit instantiation, and rely on the compiler to instantiate the class implicitly.

For that you have to put all of your template definition in the header file. So move the definitions:

template<typename T>
bool Derived<T>::func(void) {return true;}

template<typename T>
Derived<T>::Derived(void) {}

into the header file, and remove extern template class Derived<int>; and template class Derived<int>;