Reputation: 1593
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
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
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
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