Search code examples
c++templatesinheritance

How to override a template function in Subclass?


I want to make GroupBase as a interface...

#include "string"
#include "unordered_map"

template<class Vty>
class BaseV {
public:
    BaseV() {
        //logic
    }

    virtual auto run() -> bool { return false; }
protected:
    //members
};

class GroupBase {
public:
    GroupBase() {
        //logic
    }

    template<class Vty>
    auto run(const std::string &name, const Vty &value) -> void {
        createValue<Vty>(name);
        ((BaseV *)_values.at(name))->run();
    }

    template<class Vty>
    auto createValue(const std::string &name) -> void {
        static_assert(false, "You must override createValue method to instead GroupBase::createValue!");
    }
protected:
    std::unordered_map<std::string, void*> _values{};
};

template<class Vty>
class ThirdPartyClass{};

template<class Vty>
class DerivedV : public BaseV<Vty> {
public:
    DerivedV() : BaseV<Vty>() {
        _data = new ThirdPartyClass<Vty>();
    }

    auto run() -> bool override { 
        //call function in _data;
    }
protected:
    ThirdPartyClass<Vty>* _data;
};

class GroupDerived : public GroupBase {
public:
    GroupDerived () : GroupBase() {
        //logic
    }

    template<class Vty>
    auto createValue(const std::string &name) -> void {
        _values.emplace(name, new DerivedV<Vty>());
    }
};

int main(int argc, char** argv) {
    auto group = GroupDerived();
    group.run("a", 1);
}

Of course this code cannot work.It will print the message in static_assert an compile will failed.

If remove this line:

static_assert(false, "You must override createValue method to instead GroupBase::createValue!");

It worked, but an warning will occur.

So how to make it more reasonable? (I have to add a new ThirdPartyClass 1, 2, 3...at any time...)


Solution

  • You may want to look into the CRTP pattern for this, if I understand your intent correctly. Also, your outline has no way to properly free dynamic memory, you better derive BaseV from a common interface with a virtual destructor, say IBaseValue.

    // Type MUST implement template method createValue(const std::string&)
    template<typename Type>
    struct Creator {
        template<typename ValueType>
        void run(const std::string& name, const ValueType& value){
            //to catch a case of stupid BaseV specialization
            static_assert(std::is_base_of_v<IBaseValue, BaseV<ValueType>>);
            //confused: we don't use 'value' argument at all?
            static_cast<Type*>(this)->template createValue<ValueType>(name);
        }
    protected:
        //s.t. the destructor properly frees memory
        std::unordered_map<std::string, std::unique_ptr<IBaseValue>> _values{};
        //To prevent accidental Creator object creation
        Creator(void) = default;
    };
    
    struct GroupDerived : public Creator<GroupDerived> {
        template<class Vty>
        auto createValue(const std::string &name) -> void {
            _values.emplace(name, new DerivedV<Vty>());
        }
    };
    
    int main(void){
        auto group = GroupDerived();
        group.run("a", 1); //Creator<GroupDerived>::run<int>();
    }