Undefined references to base template class' m

2019-09-06 04:21发布

问题:

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?