0

I have the following situation(simplified from my actual problem):

// In Tree.hpp
template <class T>
class Tree : public Plant {
    ...
    
    // Returns true if valid for purposes
    bool valid() const override;

}

The template parameter can be either a Deciduous or Coniferous, or a child of those types such as Pine or Oak.

I would like to differentiate valid() based on whether the templated parameter is Deciduous or a child, or Coniferous or a child, meaning two versions.

// In Tree.cpp
template<>
bool Tree<Coniferous>::valid() const {
    //Coniferous valid logic
}

template<>
bool Tree<Deciduous>::valid() const {
    //Deciduous valid logic
}

This does not work, as I get errors such as:

libForest.so: undefined reference to `Tree<Pine>::valid() const'
libForest.so: undefined reference to `Tree<Oak>::valid() const'

What is the correct way to do this?

Dorito Johnson
  • 217
  • 1
  • 11
  • Reopened, because there are explicit specializations here for which the duplicate suggestion that templates must be implemented in the header does not apply. – user17732522 Sep 29 '22 at 17:26

1 Answers1

1

Your explicit specializations for the Deciduous and Coniferous are correct. But they are specializations explicitly only for when the template argument is one of exactly these types.

If you want to include derived classes, then you want a partial specialization which specializes on a category of types based on some condition.

Unfortunately you can't partially specialize a member of a class template (as a partial specialization on the class template's template arguments). Instead you must partially specialize the whole class for the two categories of types.

Since that is probably not what you want to do, I would suggest some approach other than specialization. For example since C++17 you can write:

// In Tree.hpp
template <class T>
class Tree : public Plant {
    ...
    
    // Returns true if valid for purposes
    bool valid() const override {
        if constexpr(std::is_base_of_v<Coniferous, T>) {
            // Coniferous valid logic
        } else /*if constexpr(std::is_base_of_v<Deciduous, T>)*/ {
            // Deciduous valid logic
        } /* else { ... } */
    }
};

However, with the limited explanation you gave of your code it seems weird that you have inheritance with a virtual function interface on the one hand for Plant and Tree, but what seem to be more specific subclass like Oak then suddenly become a template parameter instead of a subclass? Maybe reconsider whether this is really what you want.

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • Thank you, unfortunately I don't have the option of using the C++17 solution. In C++11 the only solution then would be to define a valid() function for each possible derived class of Deciduous/Coniferous? – Dorito Johnson Sep 29 '22 at 17:54