简体   繁体   中英

polymorphism with sub-classes of different template type

I've got class with template and some classes inherit from him. We want to create instance of the father class without declaring its template type, and call a function returning the template type.

Example:

class FatherWrap {
    virtual ~FatherWrap() = default;
};

template<typename T>
class FatherClass : public FatherWrap
{
    virtual T getValue();
};

class SonClass1 : public FatherClass<int>
{
    int getValue() override;
};

class SonClass2 : public FatherClass<string>
{
    string getValue() override;
};  

int main()
{
    FatherWrap* ch = new SonClass1();
    T a = ch->getValue; // What to do instead of T.
}

Let's say you have:

  • An unparameterized (no template) base class B
  • An intermediate template class I<T> inheriting from B
  • Some derived classes D1 , D2 , etc. each of which inherits from a specialization of I

You want to write some code in terms of B . You can do that--but you have to limit yourself to using the API that B defines. The methods of B can be virtual, and the implementations/overrides of those methods in I<T> and Dx can use the type T , but those types can't be exposed to a component the only knows about B .

If you want to write some logic that uses T , then that logic needs to be either in a method of I<T> , or in a template function that is itself parameterized with a class type:

template<class U>
U someTypeSpecificLogic(I<U> intermediate) {
    // can call methods that accept/return U here
}

You can't write logic in terms of B that depends on the type T because that type is only defined for the subclass I<T> . Consider that the B you have be a different subclass of B and not an I<T> at all.

You could skip the FatherWrap entirely and make the base class return a variant:

struct FatherClass : FatherWrap {
    virtual std::variant<int, std::string> getValue();
};

struct SonClass1 : FatherClass {
    std::variant<int, std::string> getValue() override {
        return "some text";
    }
};

struct SonClass2 : FatherClass {
    std::variant<int, std::string> getValue() override {
        return 95;
    }
};   

Alternatively, you could template any code that uses SonClass :

struct SonClass1 { // no parent.
    std::string getValue() {
        return "some text";
    }
};

struct SonClass2 { // no parent.
    int getValue() {
        return 95;
    }
};

template<typename T>
void useSonClass(T& son) {
    // value may be int or string.
    auto value = son.getValue();
}

int main() {
    SonClass1 sc1;
    SonClass2 sc2;

    useSonClass(sc1);
    useSonClass(sc2);
}

If you want to contain it, just use a variant:

int main() {
    std::variant<SonClass1, SonClass2> sc = SonClass2{};

    std::visit(
        [](auto& sc) { useSonClass(sc); },
        sc
    );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM