1

I am trying to implement the following code:

template<class ManagerBase>
class ManagerA : public ManagerBase {};

template<class ManagerBase>
class ManagerB : public ManagerBase {};

class Base {
public:
    template<typename ManagerBase>
    virtual ManagerBase* CreateManager() const = 0;
};

class A : public Base{
public:
    template<typename ManagerBase>
    virtual ManagerBase* CreateManager() const { return new ManagerA<ManagerBase>; }
};

class B : public Base{
public:
    template<typename ManagerBase>
    virtual ManagerBase* CreateManager() const { return new ManagerB<ManagerBase>; }
};

class SomeManagerBase {};

int main() {
    Base* pPolymorphic = new A;
    SomeManagerBase* pBase = pPolymorphic->CreateManager<SomeManagerBase>();
}

But since C++ doesn't seem to allow mixing virtual functions with templates, how can I achieve that? I've already thought about having a switch on the base class instead of a pure function so it would call the right function from the base itself, but this doesn't seem a good approach because each time I create a new class that derives from Base I'll have to change this function, so I was expecting someone would have a better idea of what can be done here.

  • 1
    Does ManagerBase necessarily have to be a template?! You are right about the limitation, a virtual template would result in a possibly infinite number of virtual member functions, and that doesn't fit the standard implementation of virtual functions (using a fixed size table). – BoP Jun 26 '22 at 16:32
  • @BoP I am inclined to make it to derive from a template, because I want to create a interface where the user have the opportunity to implement something besides the default implementation it already has, so that would be the purpose of the base class here. – Rafael Ferreira Jun 26 '22 at 16:38
  • Do you expect the user to explicitly instantiate for each used type, or use some sort of "reflection" to get the type info (for the latter, C++ doesn't have that)? What you want can't be done for the same reason as [c++ - Why can templates only be implemented in the header file? - Stack Overflow](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – user202729 Jun 26 '22 at 16:43
  • @user202729 The user can decide to instantiate either class `A` or class `B`, but once he call `CreateManager`, he would only be able to decide what would be the base class, the class that derives from it would come from the implementation. I know this can't be done the way I've shown in the code, this is why I was expecting someone to have a good solution for this, because all the ones I can't think of are bad for the architecture of the code, and makes it less maintanable. If I can't find one I suppose I'll just give up on giving the user that layer of control over those classes. – Rafael Ferreira Jun 26 '22 at 16:48
  • There's an option of passing the `ManagerBase` as an object instead of a template parameter, then use e.g. virtual `clone()` method https://stackoverflow.com/questions/2032881/creating-a-new-object-from-dynamic-type-info to construct the new object. Basically you have to drop the template part and make it all dynamic. – user202729 Jun 26 '22 at 16:58
  • @user202729 Hey, I didn't understand very well how this clone method works, so I'm supposed to copy the data from the derived class to the base class object? I'm almost giving up on this :( – Rafael Ferreira Jun 26 '22 at 18:02
  • I don't really understand what you're trying to do here, but try templating the classes and not the functions, maybe that will get you somewhere. – Paul Sanders Jun 26 '22 at 23:19
  • 1
    As shown, ManagerA and ManagerB do not depend on their template parameters. Why do you want them to be templates? Understanding that might allow some advance towards a solution. – n. m. could be an AI Jun 27 '22 at 05:06

1 Answers1

0

It's not clear what you want to do, because if you want to duplicate an object given only its base class, the valid return type will have to depend on runtime values. One possibility would be to create a non-template virtual function, and invoke it from a template wrapper. For example:

#include <cassert>
#include <typeinfo>

template<class ManagerBase>
class ManagerA : public ManagerBase {};

template<class ManagerBase>
class ManagerB : public ManagerBase {};

class Base {
    virtual Base* CreateManagerImpl() const = 0;
public:
    virtual ~Base() {}
    template<typename T> T* CreateManager() const {
        if (!dynamic_cast<const T*>(this))
            return nullptr;
        return static_cast<T*>(CreateManagerImpl());
    }
};

class A : public Base {
public:
    Base* CreateManagerImpl() const override { return new ManagerA<A>; }
};

class B : public Base {
public:
    Base* CreateManagerImpl() const override { return new ManagerA<B>; }
};

int
main()
{
    Base *pa = new A;
    Base *pb = pa->CreateManager<A>();
    Base *pc = pa->CreateManager<B>();
    assert(pb);
    assert(!pc);
}

But note how this function will fail and return NULL if you ask for the wrong type.

Now note that what you are asking for is to be able to swap in an arbitrary base type, which you can't do if you don't have the type information at compile time. Maybe you can re-write your code to use only templates, in which case you will have one version of your functions for each type combination. But at the point where you are writing polymorphic code, the point is that the exact same instructions in memory will be handling all types, so there's no way to create a new type that depends on a type that you don't have at compile time (namely the fully derived type of a Base *).

user3188445
  • 4,062
  • 16
  • 26
  • 1
  • 1
    I mean you have to cast it to what you want, but I agree my template method is not very useful. The bottom line is that you can pass type information around via `typeid` or implicitly with `dynamic_cast`, but once you have runtime type information in the form of a `std::type_info`, you can't really do much to it, like extract template arguments or allocate a new object of that type. The OP seems to want to do compile-type things at runtime, which can't work. In particular, you can't even know the set of all possible base types in a single translation unit. – user3188445 Jun 27 '22 at 05:09
  • @user3188445 Thanks for the efforts on this answer, I appreciate it. But unfortunately, as "n. 1.8e9-where's-my-share m." mentioned, this doesn't let the user decide what the base class is. The only solutions I can think about this is either passing the base object in the constructor and using it internally on each Manager class(simulating a virtual class), or to use a switch like I've mentioned in the question. This would be very troublesome to manage so in the lack of a solution I will give up on it, the Manager class will always derive from the same base class. – Rafael Ferreira Jun 27 '22 at 18:01