Home > database >  Function that returns templated object with any template argument type
Function that returns templated object with any template argument type

Time:12-02

I have a templated class, say:

template <class C, int I>
class A {
    A() {};
    someMethod(){...};
};

Now, let's say I have several objects of this class used in my code with different template parameters (classes B,C, and D are some defined classes):

A<B, 1> object1;
A<C, 2> object2;
A<D, 3> object3;

What I would like to have is a function that returns one of these three objects based on some logic:

A getObject(){
    if(condition1)
        return object1;
    else if(condition2)
        return object2;
    else
        return object3;
}

However, when I try to compile this, I get error: invalid use of template-name ‘A’ without an argument list, which makes sense, but I don't know which arguments should I provide? For instance, if I put

A<B, 1> getObject(){
    return object1;       
}

then I can only return object1, but not object2 or object3.

Ultimately, my goal is to have something like:

A obj = getObject();
...
lot's of code where obj.someMethod() get's called a lot
...

CodePudding user response:

This is simpler to reason about if you think of A<B,1>, A<B,2> and A<B,3> as three completely unrelated classes Foo, Bar and Moo, which they bascially are. Now try to find a way to return those from a single method: You need a common base or any or variant ... runtime polymorphism: The same method returns an object and you do not care about the type as long as it implements an interface.

class A_base {
    virtual void someMethod() = 0;
    virtual ~A() {}
};

template <class C, int I>
class A : public A_base {
    A() {}
    void someMethod() override {/*...*/}
};

std::unique_ptr<A_base> getObject() {
      //...
}

I assumed that condition is only known at runtime. Otherwise the whole quesiton is moot, because if it is know at compile time then you could simply make getObject a template that returns only the one type determined by the condition.

CodePudding user response:

Your return statements on the getObject function each have a different type (Templates instanced with different classes have different types). Functions can only return a single type so returning the templates as they are is not a possibility. So the solution needs all the templates to share a common type. To accomplish this you would need to create a base class with a virtual someMethod call, make every template inherit from this base class implementing someMethod and return a pointer to base class from the getObject function (Being careful not to narrow the derived class returned). Then you can leverage virtual dispatch to call the appropriate someMethod.

CodePudding user response:

A is not a type and therefore you cannot declare it as a return value. And A<B, 1>, A<C, 2> and A<D, 3> are all different types. What you can do, however, that doesn't require changing A is to call a function object with the object instead of returning it. Your function would then become

template <typename F>
void getObject(int i, F f) {
  if (i == 1) {
    f(object1);
  } else if (i == 2) {
    f(object2);
  } else if (i == 3) {
    f(object3);
  }
}

and you would use it like

  getObject(3, [](auto x) {
    std::cout << "Call someMethod: " << x.someMethod() << std::endl;
  });

Having auto x in the function argument list is only available in more recent C versions. If your C version does not support that, you would have to declare a class and implement the operator() for the types.

  • Related