Bastian
Bastian

Reputation: 1593

Passing template to a function without specifying concrete type

Is it possible to pass a template to a function without specifying the concrete type?

I have a base class which is a template and two different specializations of it. Here is an example of the base class:

template<typename T1, typename T2> class Dataset 
{
protected:
   std::vector<std::pair<T1, T2> > _data_buffer;
public:
   virtual void doSomething(MatrixRm& data) = 0;
}

Here is the first class which extends the base class:

class InMemoryDataset : public Dataset<MatrixRm, MatrixRm>
{
public:
    void doSomething(MatrixRm& data) override
    {
     ...
    }
}

And here is the second class which extends the base class:

class OnlineDataset : public Dataset<std::string, std::string>
{
public:
    void doSomething(MatrixRm& data) override
    {
     ...
    }
}

Now, I'd like to pass any of this classes to a function or a constructor of a different class. But I currently can't figure out how to do this without specifying the concrete type.

Here's how I imagine it:

void someFunction(Dataset* dataset)
{
    //do something with the specialization
}

In Visual Studio I get the following error message:

argument list for class template "Dataset" is missing

It makes sense to me why this is not allowed but is there any way around this?

Upvotes: 1

Views: 111

Answers (3)

Lukas-T
Lukas-T

Reputation: 11340

You can declare the function as template too:

template<class T1, class T2>
void someFunction(Dataset<T1, T2>* dataset)
{
    //do something with the specialization
}

void foo() {
    InMemoryDataset inMemory;
    someFunction(&inMemory); 
    // Will call someFunction<MatrixRm, MatrixRm>(Dataset<MatrixRm, MatrixRm> *)

    OnlineDataset online;
    someFunction(&online); 
    // will call someFunction<std::string, std::string>(Dataset<std::string, std::string> *)
}

Upvotes: 1

Wil
Wil

Reputation: 115

One idiom is to assume that someFunction will only every be called with a Dataset object and just treat Dataset as a template parameter as in:

template<class Dataset>
void someFunction(Dataset dataset){
    // ...
}

Of course, this solution would generate different code for each specialization of someFunction for each unique type passed to it.

This idiom can implemented in a safer fashion with some template metaprogramming boilerplate as in:

#include <type_traits>

template<class T>
struct is_dataset: std::false_type{};

template<class T1, class T2>
struct is_dataset<Dataset<T1, T2>>: std::true_type{};

template<class Dataset>
std::enable_if_t<is_dataset<Dataset>::value, void>
someFunction(Dataset dataset){
    // ...
}

This code defines the metafunction is_dataset to effectively perform a compile time check is the passed Dataset template is, in fact, an actual Dataset object.

As you seem to be aware, Dataset* is not a real type (or even an incomplete type). It simply provides an interface for construction of a real type (e.g. Dataset<std::string, std::string>).

Upvotes: 1

user4442671
user4442671

Reputation:

In this specific case, you can just use a non-templated base class for your interface:

class Dataset {
public:
   virtual void doSomething(MatrixRm& data) = 0;
};

template<typename T1, typename T2> class Dataset_impl : public Dataset
{
protected:
   std::vector<std::pair<T1, T2> > _data_buffer;
};

Upvotes: 1

Related Questions