So I am updating some C++11 code to use gcc-11, and have run into a issue...
Namely, it appears that in gcc-11 the constructor symbol, for a class, which is explicitly instantiated, does not exist if the constructor uses a type from a template class, defined in an anonymous namespace.
A simplified example that produces the issue can be seen below.
Clang-12 and gcc-8 do not exhibit this behavior and export the symbols (as I would have expected).
template.h:
#pragma once
namespace {
template <typename T>
struct MyAnonTempStruct
{
typedef float BaseType;
};
}
template <typename T>
class MyTemplateClass
{
public:
typedef typename MyAnonTempStruct<T>::BaseType BaseType;
public:
MyTemplateClass(const BaseType* array);
};
template.cpp:
#include "template.h"
template <typename T>
MyTemplateClass<T>::MyTemplateClass(const BaseType* array)
{
}
template class MyTemplateClass<float>;
Using the compilation command
gcc -c -o template.o template.cpp
using nm I get the following symbol output for Clang-12 and gcc-8:
0000000000000000 W MyTemplateClass<float>::MyTemplateClass(float const*)
0000000000000000 W MyTemplateClass<float>::MyTemplateClass(float const*)
0000000000000000 n MyTemplateClass<float>::MyTemplateClass(float const*)
For gcc-11 I only get a text symbol:
0000000000000000 t MyTemplateClass<float>::MyTemplateClass(float const*)
If I mark the explicit specialization as extern in the header things work again in gcc-11. ie, adding:
extern template class MyTemplateClass<float>;
So I guess my question is: Is this expected behavior, and the fact that it previously worked was just because it was undefined, or is this some form of a miscompilation?
Anonymous namespaces generally should not be used in header files. There are very few exceptions to this, and your use case is not one. You can use a namespace detail
to suggest to people that the code inside is not meant for them to use.
GCC 11 is doing nothing wrong here, your code is simply not portable.