nabulke
nabulke

Reputation: 11255

How to deduce template type if I only got pointer to base class

How could I implement function Foo to dispatch to the correct function?

The following code reproduces the problem, and got some additional information in comments.

#include <string>

class Base  
{
public:
virtual ~Base(){};
};

template<typename T>
class Derived : public Base 
{};

void DoSomething(const Derived<std::string> &){};
void DoSomething(const Derived<int> &){};

void Foo(Base *test)
{
    // How to call correct DoSomething method?
    // (I can't change class Base, but I know that test points to some instance of class Derived)?
    // if(test points to an instance of Derived<int>)
    //     call void DoSomething(const Derived<int> &){};
    // else if(test points to an instance of Derived<std::string>)
    //     call void DoSomething(const Derived<std::string> &){};
}

int main(int argc, char* argv[])
{
    Base *test = new Derived<int>;

    Foo(test);

    delete test;
    return 0;
}

Upvotes: 2

Views: 356

Answers (4)

shuiyu
shuiyu

Reputation: 181

As to the OO, only the derived class itself knows who it is, and what to do.

#include <string>
class Base {}
class Mid : public Base
{
public:
    virtual void DoSomething() = 0;
};

template<typename T>
class Derived : public Mid 
{
public:
    virtual void DoSomething() {}
};

template<> void Derived<std::string>::DoSomething(){}
template<> void Derived<int>::DoSomething(){}

void Foo(Base *test)
{
    dynamic_cast<Mid*>(test)->DoSomething(); //TODO check the return of dynamic_cast
}

int main(int argc, char* argv[])
{
    Base *test = new Derived<int>;

    Foo(test);

    delete test;
    return 0;
}

Upvotes: 3

Seb
Seb

Reputation: 2715

#include <string>

class Base  
{};

class DerivedBase : public Base 
{
public:
    virtual void DoSomething() = 0;
};

template<typename T>
class Derived : public DerivedBase
{};

void Foo(Base *test)
{
   if (auto* p = dynamic_cast<DerivedBase*>(test))
       p->DoSomething();
   else
       throw runtime_error("oops");
}

int main(int argc, char* argv[])
{
   Base *test = new Derived<int>;

   Foo(test);

   delete test;
   return 0;
}

If you declare an intermediate class, you can use polymorphism without changing Base. You only have to do one dynamic_cast<> but you won't have to change Foo() each time you want to use a new type as a template argument, thus having your code respect the Open/Closed Principle.

Upvotes: 3

David Hammen
David Hammen

Reputation: 33106

How to call correct DoSomething method?

It's much better is take advantage of how polymorphism works in C++ rather than to write some dispatch function based on casting. A couple of alternatives:

  • Declare DoSomething as a public pure virtual method in class Base. The template will provide the implementation.
  • Define DoSomething as a public non-virtual method in class Base. The implementation invokes some private pure virtual method. The template once again provides the implementation of this virtual method.

Upvotes: 1

John Zwinck
John Zwinck

Reputation: 249093

void Foo(Base *test)
{
  if (auto* p = dynamic_cast<Derived<int>*>(test))
    DoSomething(*p);
  else if (auto* p = dynamic_cast<Derived<string>*>(test))
    DoSomething(*p);
  else
    throw runtime_error("oops");
}

This works because dynamic_cast returns nullptr if the type is not correct. The code above is C++11, but it would be C++98 if you replace the "auto" types with the obvious ones (I'd use a macro if there are lots of cases).

Upvotes: 3

Related Questions