将模板传递给函数而不指定具体类型

Posted

技术标签:

【中文标题】将模板传递给函数而不指定具体类型【英文标题】:Passing template to a function without specifying concrete type 【发布时间】:2019-11-04 18:06:06 【问题描述】:

是否可以在不指定具体类型的情况下将模板传递给函数?

我有一个基类,它是一个模板和它的两个不同的特化。下面是一个基类的例子:

template<typename T1, typename T2> class Dataset 

protected:
   std::vector<std::pair<T1, T2> > _data_buffer;
public:
   virtual void doSomething(MatrixRm& data) = 0;

这是扩展基类的第一个类:

class InMemoryDataset : public Dataset<MatrixRm, MatrixRm>

public:
    void doSomething(MatrixRm& data) override
    
     ...
    

这是扩展基类的第二个类:

class OnlineDataset : public Dataset<std::string, std::string>

public:
    void doSomething(MatrixRm& data) override
    
     ...
    

现在,我想将这些类中的任何一个传递给不同类的函数或构造函数。但如果不指定具体类型,我目前无法弄清楚如何做到这一点。

这是我的想象:

void someFunction(Dataset* dataset)

    //do something with the specialization

在 Visual Studio 中,我收到以下错误消息:

类模板“数据集”的参数列表丢失

这对我来说是有道理的,为什么这是不允许的,但有什么办法可以解决这个问题吗?

【问题讨论】:

为什么不能使用template &lt;class T1, class T2&gt; void someFunction(Dataset&lt;T1, T2&gt;* dataset) 【参考方案1】:

你也可以将函数声明为模板:

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> *)

【讨论】:

谢谢! someFunction 将对这两种类型执行相同的操作。我使用模板只是为了区分数据在数据集中的存储方式。这样,我必须实现两个功能做同样的事情,对吧? @Bastian 抱歉回答晚了,是的,你只实现了一次someFunction【参考方案2】:

在这种特定情况下,您可以只为您的界面使用非模板基类:

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;
;

【讨论】:

为什么有人反对这个?我认为这很聪明:) @Frank: ideone.com/w7bjsz 这样他就无法将 Dataset 类型传递给 foo 函数。 @nomanpouigt OP 的数据集已经很抽象了。所以他们一开始就不能这样做【参考方案3】:

一个习惯用法是假设someFunction 只会被Dataset 对象调用,并且只将Dataset 视为模板参数,如下所示:

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

当然,这个解决方案会为someFunction 的每个特化生成不同的代码,并为传递给它的每个唯一类型生成不同的代码。

这个习语可以使用一些模板元编程样板以更安全的方式实现,如下所示:

#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)
    // ...

这段代码定义了元函数is_dataset 以有效地执行编译时检查,传递的Dataset 模板实际上是一个实际的Dataset 对象。

您似乎知道,Dataset* 不是真正的类型(甚至不是不完整的类型)。它只是为构造一个真实类型(例如Dataset&lt;std::string, std::string&gt;)提供了一个接口。

【讨论】:

谢谢!您的解决方案有您所说的一个缺点……元编程会降低性能,对吗?在我的情况下,这还不够。 您应该不会注意到任何运行时损失。唯一的缺点是(可能)过多的代码翻译(即代码膨胀)。对于大量 Dataset 规范,您可能注意到编译时间增加。

以上是关于将模板传递给函数而不指定具体类型的主要内容,如果未能解决你的问题,请参考以下文章

问题将模板类型传递给函数并用于局部变量赋值 c++

为啥将 lambda 传递给受约束的类型模板参数会导致“不完整类型”编译器错误?

如何将变量结构类型传递给 C 中的函数

深入理解函数模板

给定传递给它的参数类型,如何确定函数参数的类型?

将模板类型传递给宏[重复]