https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78160
Andrew Pinski <pinskia at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|UNCONFIRMED |RESOLVED Keywords| |visibility Resolution|--- |INVALID --- Comment #1 from Andrew Pinski <pinskia at gcc dot gnu.org> --- This is the correct behavior for visibility. Since base_spec_uint8 is hidden, that means the type in the shared library and the main program are considered 2 different types. In the case of you explictly instantiate var_t<base_spec_uint8> (which instantiates var_t<base_spec_uint8>::alloc) but that has a hidden visibility as base_spec_uint8 has a hidden visbility. so var_t<base_spec_uint8>::alloc will be hidden in the shared object too and not exported. >From the .s generated from .cc file of the library (passed through the demangler): .hidden var_t<base_spec_uint8>::alloc() So if you create a share object, well that is going to be hidden inside that shared object and not exported. This behavior is described in the manual even: https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Common-Function-Attributes.html In C++, if a template argument has limited visibility, this restriction is implicitly propagated to the template instantiation. Otherwise, template instantiations and specializations default to the visibility of their template. If both the template and enclosing class have explicit visibility, the visibility from the template is used.