EN VI

C++ - How to override a template function in Subclass?

2024-03-12 16:30:04
C++ - 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<Derived> {
    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>();
}
Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login