【问题标题】:Too many template parameters in C++?C++中的模板参数太多?
【发布时间】:2012-09-27 11:48:00
【问题描述】:

我使用 C++ 并计划一个包含大约 100 个模板参数的类的库。当然,我担心有n个模板参数,如果用户需要每个组合,我们有2^n个不同的类,这是一种代码爆炸。但是,用户需要为此进行 2^n 次实例化。

我的问题是:这么多模板参数的主要技术问题是什么?

注意事项:

  • 对于技术,我对关于可读性、设计等的主观答案不感兴趣。我的意思是像这样的事实
    • 运行时
    • 代码大小
    • 允许的最大模板数

代码示例:

// here we have 2, but I have 100 template parameters
template<typename T1, typename T2>
class Class
{
    T1 x;
    T2 y;
    int add(T1 _x, T2 _y) { return _x+_y; } // 4 instanciations possible?
    Class<T2, T1>* swap() { return new Class<T2, T1>(); } // always 2 instanciations?
};

【问题讨论】:

  • 您不能仅仅放弃设计考虑。这糟糕的设计,部分是因为技术限制。
  • 您确实需要澄清“100 个模板参数”的含义。我脑海中的想象是一个有 100 个模板参数的模板。在德克萨斯州,我们有时间进行这种设计:拿一根绳子。
  • 我不想维护这段代码......
  • 如何计算2^n的组合数?仅当您的每个 n 参数只能采用两个不同的值(例如,如果它们是布尔值)时,这才是正确的。潜在实例化的正确数量是k^n,其中n 是参数的数量,k 是这些参数可以采用的值的数量。由于您声明 n ~ 100,您的用户将永远无法使用所有组合。你为什么不告诉我们你的库试图用这么多模板参数解决的实际问题?也许我们可以提出更可行的替代方案。

标签: c++ templates


【解决方案1】:

实际上模板化的类类型是在编译时确定的。这并不意味着您有 2^n 个不同的课程,而是您只有 n 个课程。在编译时会替换适当的类型,并且类成员/函数仅属于您使用它们的类型。

例如

template <class Type1, class Type2>
class A
{
private:
    Type1 member_x;
public:
    Type2 GetTypeValue(Type1, Type2);
};

当像这样实例化时:

A<int, string> *x = new A<int, string>();

仅作为具有整数类型成员和字符串类型成员的类编译。同样代表函数等。

更新: 使用下面的更新示例,您仍然只有一个函数实例,它将是一个 string 返回函数,带有参数 intstring

【讨论】:

  • 好的,但是如果我们有Type1 member_xType2 member_x,并且有一个同时使用两者的成员函数,我们会不会得到4 个成员函数?
  • @Johannes 我已经编辑了帖子,并为您的问题提供了额外的解释。同样,一切都在编译时执行。
  • @KonstantinD-Infragistics 我看到了你的例子。但是想象一些棘手的事情,比如GetTypeValue() 会返回一个带有不同模板参数的class A 对象......例如,它可以只交换int 和字符串,所以它会返回A&lt;string, int&gt;()(上面添加的示例)。你确定不会发生代码爆炸吗?
  • 是的,我认为这样,我们将得到具有 2n 个成员函数且只有一个实例化的整个对称群 S_n,对吗?
  • @Johannes 再次取决于具体的实现。如果不使用模板类,编译器将完全忽略它。如果您的代码利用了模板类型的全部可能性,那么您可以获得许多不同的成员函数实例。
【解决方案2】:

主要考虑因素是代码大小,这可能会影响性能。每个不同的模板实例化都需要生成所有使用的成员函数(或全部,如果用户进行手动模板实例化)。不同的模板实例之间不会有代码重用。

除此之外,您提供大量参数的任何元素都很难处理。从维护的角度来看,带有 10 个参数的声明已经很难阅读。它要么延伸多行,要么在行中延伸得非常宽,并且很难通过检查来确定所有参数都在正确的位置。 X 是第 7 个参数还是第 8 个参数? 是的,你可以数一数,但它会变得很痛苦。如果参数数量为 100,则问题只会更加严重。

为什么你希望所有这些都是模板参数?如果没有更多信息,您将不会得到其他建议,但很可能有针对同一问题的其他设计不需要这种复杂程度。也许在编译时不需要知道参数(它们可以以数组/向量的形式传递给函数/构造函数,用于非类型参数),或者它们可以被分组(一个类型模板参数包含一组相关的 typedef )...

【讨论】:

  • 是的,分组也是我的想法。当你传递std::string 时,你已经隐藏了char 参数——就像那样。但是从你写的内容来看,你是否认为代码爆炸可能会呈指数级增长(即使是线性数量的实例和成员)?
  • @Johannes:这取决于您的代码。如果只有一个专业,就不会有代码爆炸。如果程序中的每个对象都有一组稍微不同的模板参数,那么将为每个对象生成所使用的函数,并且会出现代码爆炸。程序中唯一模板实例化的数量并不直接取决于模板参数的数量,尽管存在更多参数意味着有更多的选项可以实现。
  • 谢谢,我需要有很多模板参数的环境,但是它们自然地分组为切片,所以摸索就可以了。再次感谢。
【解决方案3】:

这样做是有原因的。自动代码生成器 序列化、解析、文档、智能感知。所有标头都可以被解析,然后自动编写为第二个类,该类具有已解析类的每个成员函数的模板成员。由于自动代码系统将编写代码,因此可读性并不重要,但您可以访问任何类成员,无论大小或基于每个模板成员的类型的某些分支代码的间接性,这是每个自动生成的基类模板类。

然后,当使用该类时,您可以通过查看类型来为每个“类型”写出函数,或者您可以将每个模板类型的值显示为解析器或 IDE 显示器的输出。粗略地说,智能将被完全预编译以实现快速搜索。只需有一个并行项目,它可以根据头文件中的更改进行编译,然后它可以查找各种特征,而不管它是什么类型的类成员。

从技术上讲,它从解析、记录、输出或显示类中的项目的整个问题中删除类型,并在查看某些文件的某些程序的编译时执行此操作。因为每个成员都有一种查看类型的方式(类似于为什么编译器更喜欢使用 static_cast 而不是 c 样式转换)

代码大小将与每个类解析 1-1 的文件大小相同,优势将是任何程序内存空间,可以转换为匹配它的自动写入类型,然后使用可识别类型的函数访问以显示操作或输出值。想象一下,如果简单地在某个文件中找到一个“类”,就可以方便地访问该类中的所有信息,而无需进行任何解析,只需要以模板形式转换到您预先创建的类,其中自动生成类的基类有方法来操作现在可检测类型的数据。

可能他用 100 多个模板参数在做什么

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-07
    相关资源
    最近更新 更多