I have a bunch of things mixed, and they all lead to undefined references. Please, excuse me for simply dropping the code, but, since I don't know which are the individual problems, I can't search for them.
c.hpp
// A public class
class C {
private:
// This class is private because it is used just for convenience;
// it is not part of C's interface. Its methods are not defined because
// the types for which it will be instantiated are known.
template<typename T>
class Base {
private:
T* variable;
public:
// Note that this method should be available to C users, i.e., it
// is part of the interface. Should Base then be in the public
// section of C?
T* get() const noexcept;
};
public:
// Users of this class do not care how Derived1 and Derived2 are
// implemented. Since they were very similar in nature (think of
// them as the various iterator versions), I decided that it would
// be better to let the shared code be in a common template which
// was then instantiated for a set of known parameters.
class Derived1 : public Base<char> {
public:
int function() noexcept;
};
class Derived2 : public Base<int> {
/* ... */
};
};
// Should the following be included? Would it avoid unneeded implicit instantiations,
// or are those already avoided because C::Base's methods are declared but not defined?
extern template C::Base<char>;
extern template C::Base<int>;
c.cpp
template<typename T>
T* C::Base<T>::get() const noexcept {
return this->variable;
}
// This approach serves to hide the implementation and to shorten build times,
// while maintaining the flexibility of templates. Or so I believe.
template class C::Base<char>;
template class C::Base<int>;
// This should be another question, but... is this the correct place for
// class method attributes? There isn't much information about attributes.
[[gnu::visibility("default")]]
int C::Derived1::function() noexcept {
return 7;
}
When the code is compiled under default
visibility, it all compiles, links and runs fine. However, if visibility is switched to hidden
, which is what I want to achieve, there are linking errors:
undefined reference to
C::Base<char>::get() const
undefined reference to
C::Base<int>::get() const
The rest of the code links fine, because all functions are marked with the appropiate visibility attribute, as shown. Since the public functions of C::Base
are intended to be part of the exposed interface, they should be marked with default
visibility too, but I don't know how. For instance, all of the following seems to be forbidden:
// This is not possible because templates are not "real" entities
[[gnu::visibility("default")]]
template<typename T>
T* C::Base<T>::get() const noexcept {
return this->variable;
}
// But it isn't possible to specify it here either!
[[ /*error*/ ]] template [[ /*error*/ ]] class [[ /*error*/ ]]
C::Base<int> [[ /*error*/ ]];
I have also tried to specify the attribute in the declaration of the methods:
class C {
private:
template<typename T>
class Base {
private:
/* ... */
public:
[[gnu::visibility("default")]]
T* get() const noexcept;
};
/* ... */
};
but it didn't change anything.
Could you please explain what is happening, covering the concerns expressed in the comments? Specifically, how and where should the visibility attribute be specified?