0

I have a Textf class that can store formatted text and its properties (which is only color right now). I then have a Header class that derives from it and adds a dashed underline, has some extra attributes, etc.

Additionally, I have a separate println class (used a class for partial specialization support). Among other types, I have added support for Textf objects. Naturally, I assumed (as with functions) the compiler would match a Header object with the Textf template. Instead, I got MSVC error C2679 (using VS 2019), which is because the compiler defaulted to the original template definition (because the Textf pattern was not matched since the object is a Header object).

If I were to define another template specialization for every child class, both specializations would have the exact same behavior, making the code extremely redundant (especially if there were many child objects).

For clarity, here is some sample code:

template<class LnTy>
class println  // Default template def
{
public:
    println(LnTy line)
    {
        std::cout << line;
    }
};

class Textf
{
protected:
    std::string_view m_text;
    Color m_text_color;  // Color is a scoped enum
    Color m_background_color;

public:
    // Constructors, etc...

    virtual void display() const
    {
        // Change color
        // Diplay text
        // Restore color
    }
};

class Header : public Textf
{
private:
    // 'U_COORD' is struct { unsigned X, Y; }
    U_COORD m_text_start_location;

public:
   // Constructors, etc...

    void display() const override
    {
        // Change color
        // Change cursor location
        // Display text
        // Display dashed underline (next line)
        // Restore cursor location (next line)
        // Restore color
    }
};

template<>
class println<Textf>  // Specialization for 'Textf'
{
    println(Textf line)
    {
        line.display();
    }
};

How do I take advantage of polymorphism in templates for a situation like this?

I've come across other questions and done some research, but it seems I was only able to find things about a child class is inheriting from a template parent class, which isn't my point of confusion here.

In case it helps, I'm using the Windows console API to change the text's colors.

Drake Johnson
  • 640
  • 3
  • 19
  • Does this answer your question? [Passing a derived class to a template function specialized with base class](https://stackoverflow.com/questions/27988024/passing-a-derived-class-to-a-template-function-specialized-with-base-class) – Daniel Jan 12 '20 at 23:49

2 Answers2

3

You can use SFINAE to make sure the right template-version gets used.

template<class LnTy, class = void> // second parameter is used in the specialization for SFINAE
class println  // Default template def
{
public:
    println(const LnTy& line)
    {
        std::cout << line;
    }
};

template<class LnTy>
class println<LnTy, std::enable_if_t<std::is_base_of_v<Textf, LnTy>>>
{
    println(const Textf& line)
    {
        line.display();
    }
};

If the only reason for using the class is only this function, you can do the same thing with a template function.

template<class LnTy, std::enable_if_t<!std::is_base_of_v<Textf, LnTy>, int> = 0>
void println(const LnTy& line)
{
    std::cout << line;
}

template<class LnTy, std::enable_if_t<std::is_base_of_v<Textf, LnTy>, int> = 0>
void println(const Textf& line)
{
    line.display();
}
super
  • 12,335
  • 2
  • 19
  • 29
  • But you're using SFINAE, so why does the second function need to have an enable_if/ Surely the compiler just won't instantiate it for Ts without a display method? – Allan Cameron Jan 13 '20 at 00:13
  • 1
    The `enable_if` is what prevents it from being instatiated. It's not until after instatiation that the compiler will look at the function body, and then it will produce compile-time error. – super Jan 13 '20 at 00:16
0

You could try excluding the derived classes from matching the default template:

template<class LnTy>
typename std::enable_if< !std::is_base_of<Textf, LnTy>::value >::type
class println  // Default template def
{
public:
    println(LnTy line)
    {
        std::cout << line;
    }
}

and make the "specialised" version a non-template overload.

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
  • 1
    This will not help `Header` match the partial specialization though. – super Jan 12 '20 at 23:51
  • Yes, you're right @super. You would also need to remove the template specialisation from the second println and have it as a non-template overload. – Allan Cameron Jan 12 '20 at 23:54