Undefined references to base template class' m

2019-09-06 03:55发布

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?

0条回答
登录 后发表回答