关于C ++模板的最重要的事情......经验教训

时间:2009-02-28 23:47:19

标签: c++ templates

您对模板了解哪些最重要的事项:隐藏功能,常见错误,最佳和最有用的做法,提示...... 常见错误/疏忽/假设

我开始使用模板实现我的大多数库/ API,并希望收集在实践中发现的最常见的模式,提示等。

让我正式化问题:你对模板学到的最重要的事情是什么?

请尝试提供示例 - 与理解和过于干燥的描述相比,它更容易理解

由于

12 个答案:

答案 0 :(得分:18)

“Exceptional C ++ style”,第7项:函数重载解析在模板专门化之前发生。不要混合模板函数的重载函数和特化,或者你在实际调用函数时会感到讨厌。

template<class T> void f(T t) { ... }   // (a)
template<class T> void f(T *t) { ... }  // (b)
template<> void f<int*>(int *t) { ... } // (c)
...
int *pi; f(pi); // (b) is called, not (c)!

第7项

之上

更糟糕的是,如果省略模板特化中的类型,不同的函数模板可能会根据定义的顺序变得专门化,因此可能会调用或不调用专用函数。< / p>

案例1:

template<class T> void f(T t) { ... }  // (a)
template<class T> void f(T *t) { ... } // (b)
template<> void f(int *t) { ... }      // (c) - specializes (b)
...
int *pi; f(pi); // (c) is called

案例2:

template<class T> void f(T t) { ... }  // (a)
template<> void f(int *t) { ... }      // (c) - specializes (a)
template<class T> void f(T *t) { ... } // (b)
...
int *pi; f(pi); // (b) is called

答案 1 :(得分:7)

这可能不受欢迎,但我认为需要说明。

模板很复杂。

它们非常强大,但明智地使用它们。不要太疯狂,不要有太多的模板参数......没有太多的专业化......请记住,其他程序员也必须阅读它。

最重要的是,远离模板元编程...

答案 2 :(得分:5)

我不得不说Coplien的Curiously Recurring Template Pattern (CRTP)是一个模板技巧,我发现自己已经达到过&amp; amp;再次。本质上,它允许您通过从派生类名参数化的基类继承,将静态自定义功能注入派生类。令人难以置信,但非常有用(有些人称之为静态多态)。

另外,我将第二次看到Neil Butterworth的建议,阅读“C ++模板”并投入Alexandrescu的Modern C++ Design

答案 3 :(得分:4)

这个问题有点像“我将使用函数实现我的大多数库,使用函数时常见的错误是什么?”很难为这些问题提出合理的答案,但这是我的建议 - 读一本好书。我推荐Vandevoorde&amp; amp;的“C++ Templates”。约祖蒂斯,

答案 4 :(得分:4)

每日模板提示: 您是否知道可以专门选择模板实例化的功能:

#include <iostream>
#include <vector>

namespace std {
    template<>
    void vector<int>::clear() {
    std::cout << "Clearing..." << std::endl;
    resize(0);
    }
}

int main() {
    std::vector<int> v;
    v.push_back(1);
    v.clear();
}

OUPUTS: 清除...

答案 5 :(得分:4)

一个常见错误是模板构造函数或赋值运算符不会抑制编译器生成的一个:

template <typename T>
class A {
public:
  template <typename S>
  A(A<S> const &);

  template <typename S>
  A & operator=(A<S> const &);

private:
  int * i;
}; 

虽然这些函数看起来像复制构造函数和复制赋值运算符,但编译器不会这样看,并且无论如何都会生成隐式版本。结果是,当从相同类型复制或分配对象时,不会发生由这些函数执行的任何操作(例如,成员的深层复制):

void foo (A<int>);

void bar () {
  A<int> a1;
  foo (a1);   // Implicitly generated copy ctor called

  A<long> a2;
  foo (a2);   // Template ctor called.

  A<int> a3;
  a3 = a1;   // Implicitly generated copy assignment operator called

  a3 = a2;   // Template assignment operator called
}

此行为的原因是由于重载解析(13.3.3)中的特殊规则:

  

鉴于这些定义,一个可行的   函数F1被定义为更好   功能比另一个可行的功能   如果对于所有参数i,则为F2,ICSi(F1)为   转换顺序并不比   ICSi(F2),然后

[...]

  

- F1是非模板功能   F2是功能模板   专业化,或者,如果不是,

在上面的示例中,重载决策看到两个具有相同签名的函数,其中一个是模板。非模板函数(隐式生成的复制构造函数/复制赋值运算符)获胜,因此被称为。

答案 6 :(得分:2)

STL是你的朋友。

答案 7 :(得分:2)

以下是一些规则:

  1. 除非你正在编写一个非常非常通用的库,否则不要编写任何模板(STL和Boost是两个突出的例子)。
  2. 不要多次实例化任何非平凡的模板。实例化大型模板类尤其过度。你应该考虑使用继承和多态(简单的方法,我的意思是,使用虚函数)。
  3. 如果您正在编写任何模板,知道何时使用constmutablevolatile将为模板的用户节省编译和执行时间。
  4. 如果您要实例化任何模板,请使用一个好的编译器。

答案 8 :(得分:2)

阅读Meyers的有效STL和C ++书籍,以及Alexandrescu的现代C ++设计。

迈耶斯将为您提供易犯错误的基础知识以及如何避免错误。 Alexandrescu向您介绍了一个基于模板的编程模型,应该让您问“这个真的是个好主意吗?”整本书。

答案 9 :(得分:2)

我倾向于使用很多模板来避免重复代码,并通过编译检查来提高安全性。

通常,在键入编译器将要执行的操作以及如何为每种类型生成代码时进行思考会有所帮助。

在开发中非常迭代并逐渐构建模板复杂性帮助我避免了编译错误消息。不要忘记在某处保留模板的简单(或模拟)实例化,否则当您第一次实例化怪物模板时可能会有一些令人讨厌的惊喜。

最后,当没有出路时,请阅读这些编译错误消息!起初他们看起来可能很吓人,但他们真的很有帮助。也许最初提取第一个,在文本编辑器中复制它并使它看起来漂亮将有助于习惯它们,并且它很快成为第二个阅读本质。

答案 10 :(得分:0)

理解单独的编译以及导致可执行文件大小增加的可能性非常重要。如果在多个C ++文件中实例化具有相同类型的模板,则会多次重现该类型,至少在某些编译器上是这样。

答案 11 :(得分:0)

我经常使用c ++和模板,包括更高级的模板元编程,我的感觉是它们的用处被高估了。在创建c ++之后,它们最初被添加到c ++语言中,以实现通用编程功能。这简单地允许人们关注代码的逻辑而不考虑类型,可能使代码更清晰和可重用。

我的编程理念是理解语言的原始目的和设计及其功能,以便真正欣赏语言。我觉得模板元编程是模板的混蛋,应该避免。然而,模板可用于定义更高级别的泛型类型,例如元组的情况。