0

I have a base class that uses template, and it has a few methods that are not dependent on the template type, but when I use the pointer Base* a instead of the derived class the compiler complains because there is no type specified. I know in java this is possible but not sure if it is possible in C++. Here a simple example:

template <typename T>
class Base {
public:
    Base(const T& t) : _t(t) {}
    virtual ~Base() { }

    void doSomething() { std::cout << "Hello world/n"; }

    virtual T getVal() const { return _t; }

private:
    T _t;
};

class DerivedA : public virtual Base<std::string>
{
public:
    DerivedA(const std::string& path) : Base<std::string>(path) {}
    virtual ~DerivedA() {}
};

class DerivedB : public virtual Base<int>
{
public:
    DerivedB(int value) : Base<int>(value) {}
    virtual ~DerivedB() {}
};

int main(int argc, const char * argv[]) {

    DerivedA d("hello world\n");
    Base* basePtr = &d; // ERROR: Use of class template 'Base' requires template arguments
    basePtr->doSomething();

Thanks in advance

Joan P.S
  • 1,553
  • 1
  • 16
  • 42
  • 1
    If you have functions which do not depend on any template arguments in their signature, like the `doSomething` function, then you could create a non-template "real" base-class that the templated `Base` class inherits from. – Some programmer dude Sep 26 '18 at 11:38
  • 2
    You can't do this. `Base` and `Base` are different specializations, and so they are distinct types. you cannot cast an apple into a car... – user1810087 Sep 26 '18 at 11:38
  • 1
    dont confuse java generics with c++ templates, those are two very different concepts – 463035818_is_not_an_ai Sep 26 '18 at 11:39
  • 2
    The suggestions to use a concrete (non-template) base class are generally correct, but it's worth checking - _why_ do you want a base class pointer? What are you using it for? – Useless Sep 26 '18 at 11:55

3 Answers3

3

Simply create another base class which is not a template:

class ReallyBase {
public:
    virtual ~ReallyBase() = default;
    void doSomething() { std::cout << "Hello world\n"; }
};

template <typename T>
class Base : public ReallyBase {
public:
    Base(const T& t) : _t(t) {}
    virtual const T& getVal() const { return _t; }    
private:
    T _t;
};
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
0

Generics in java are something completely different than templates in C++. Generics are based on type-erasure while with templates each instantiation is a seperate type completely unrelated to a different instantiation.

Ie in Java an ArrayList<Integer> is basically the same thing as a ArrayList<Boolean>. With templates on the other hand, Base<int> has no relation to Base<std::string> (other than being two types resulting from the same template) and hence your derived classes actually do not share a common base class.

To solve your problem you could write a (non-template) base class that declares the common interface of all your derived classes.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
0

Let's 1st talk about a simple solution to your problem, writing a derivation function:

template<typename T>
Base<T>* make_base(Base<T>* param) {
    return param;
}

You can use this as follows:

DerivedA d("hello world\n");
auto basePtr = make_base(&d);

basePtr->doSomething();

This solution can be further improved as in this example but since I don't think it's the best solution I'll not clutter the answer with it but leave such optimization for your perusal.

But 2nd let's talk about improving your design. You'll notice that any of the standard containers create a using value_type =... thereby storing the template types that they are passed. This is important for this exact situation! If you publicly add using value_type = T to your Base template class you'll circumvent the problem you're having all together, without the need for a make_base you'll be able to do:

Base<decltype(d)::value_type>* basePtr = &d;

An important follow up concept here is that DerivedA and DerivedB do not have the same base class. Thus you can never do something like this

auto basePtr = make_base(&d);
basePtr = new DerivedB({});

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288