c++virtual-functionsc++-templates

explicit specialization of a class that inherits from a virtual class


I have a virtual class BASE, and an inherited class BOX_strwhich implements the virtual functions

class BASE {
public:
    virtual ~BASE() {};

    virtual std::vector<int> custom_int() const = 0;

    virtual std::vector<double> custom_double() const = 0;
};

struct BOX_str final : public BASE {

    template <typename ...Args>
    BOX_str(Args... args) : base(std::forward<Args>(args)...) {}

    std::string base;

    std::vector<int> custom_int() const {
        return custom_vec<int>(base);
    }

    std::vector<double> custom_double() const {
        return custom_vec<double>(base);
    }

    template <typename NUM>
    std::vector<NUM> custom_vec(const std::string& s) const {
        return {32, 62};
    }
};

But all of the BOX_str class content, except for the custom_vec() function, is generic amongst similar classes,

so I've tried to make a template class BOX

template <typename T>
struct BOX : public virtual BASE {

    template <typename ...Args>
    BOX(Args... args) : base(std::forward<Args>(args)...) {}

    T base;

    std::vector<int> custom_int() const {
        return custom_vec<int>(base);
    }

    std::vector<double> custom_double() const {
        return custom_vec<double>(base);
    }

    template <typename NUM>
    std::vector<NUM> custom_vec(const T&) const;
};

and leave custom_vec() to be implemented later for each explicit specialization of BOX

template <>
struct BOX<std::string> {
    template <typename NUM>
    std::vector<NUM> custom_vec(const std::string& s) const {
        return {42, 52};
    }
};

then I tried to test the classes

int main() {
    std::string mystr{ "abcd" };

    BASE* v1 = static_cast<BASE*>(new BOX<std::string>(mystr));
    BASE* v2 = static_cast<BASE*>(new BOX_str(mystr));
}

v2 didn't raise any errors, but v1 did: excess elements in struct initializer, which means the the explicit specialization of BOX cannot access its constructor, and all of BOX's contents.

I'm quite stuck and can't figure out how to correctly implement the BOX class so that it would work like BOX_str but in a generic manner. Would appreciate some assistance.


Solution

  • As always, any problem* can be solved by an additional abstraction layer:

    Define BOX::custom_vec to call a yet-to-be-defined ::custom_vec:

    template <typename T>
    struct BOX : public virtual BASE {
        // [...]
    
        template <typename NUM>
        std::vector<NUM> custom_vec(const T& v) const {
            return ::custom_vec<T, NUM>{}(v);
        }
    

    Then, provide a struct that can be specialized and fully redefined without useless duplication of code:

    template<class BoxT, class BoxN>
    struct custom_vec {};
    
    template<>
    struct custom_vec<std::string, int>
    { std::vector<int> operator()(std::string const&) const { return {42, 52}; } };
    
    template<>
    struct custom_vec<std::string, double>
    { std::vector<double> operator()(std::string const&) const { return {1.618, 3.14}; } };
    

    Demo on Compiler Explorer


    *except having too many abstraction layers