Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sqlite/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 关于C++;模板&x2026;吸取的教训_C++_Templates - Fatal编程技术网

C++ 关于C++;模板&x2026;吸取的教训

C++ 关于C++;模板&x2026;吸取的教训,c++,templates,C++,Templates,关于模板,您知道的最重要的事情是什么:隐藏功能、常见错误、最佳和最有用的做法、提示……常见错误/疏忽/假设 我开始使用模板实现我的大多数库/API,并希望收集实践中发现的最常见的模式、技巧等 让我把这个问题形式化:关于模板,你学到的最重要的东西是什么 请试着提供一些例子——这会更容易理解,而不是复杂和过于枯燥的描述 感谢了解单独编译以及导致可执行文件大小增加的可能性非常重要。如果在几个C++文件中实例化模板,则至少可以在一些编译器上复制多次类型。p> 这个问题有点像“我将使用函数实现我的大多数库

关于模板,您知道的最重要的事情是什么:隐藏功能、常见错误、最佳和最有用的做法、提示……常见错误/疏忽/假设

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

让我把这个问题形式化:关于模板,你学到的最重要的东西是什么

请试着提供一些例子——这会更容易理解,而不是复杂和过于枯燥的描述


感谢

了解单独编译以及导致可执行文件大小增加的可能性非常重要。如果在几个C++文件中实例化模板,则至少可以在一些编译器上复制多次类型。p> 这个问题有点像“我将使用函数实现我的大多数库,在使用函数时有哪些常见错误?”很难对这些问题给出合理的答案,但我的建议是——读一本好书。我推荐Vandevoorde&Josuttis的“”,他是你的朋友。

以下是一些规则:

  • 不要编写任何模板,除非您正在编写非常通用的库(STL和Boost是两个突出的示例)
  • 不要多次实例化任何非平凡的模板。实例化巨大的模板类尤其过分。你应该考虑使用继承和多态性(简单的方法,我的意思是,使用虚拟函数)。
  • 如果您正在编写任何模板,知道何时使用
    const
    mutable
    volatile
    将节省模板用户的编译和执行时间
  • 如果要实例化任何模板,请使用好的编译器
  • 我不得不说,Coplien's是我发现自己一次又一次尝试的模板技巧。本质上,它允许您通过继承派生类名上参数化的基类,将静态自定义功能注入派生类。令人难以置信,但非常有用(有人称之为静态多态性)


    也可以,我会建议Neil Butterworth阅读“C++模板”,然后输入Alexandrescu的.< /p> < P>阅读迈尔斯的STL和C++书籍,以及Alexandrescu的现代C++设计。 Meyers将为您提供容易犯错误以及如何避免这些错误的基础知识。亚历山大ReCuCu介绍了一个基于模板的编程模型,它应该让你问“这真的是个好主意吗?”整本书。

    < P>从<强>“特殊C++风格”,第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)!
    
    template void f(T T){…}//(a)
    模板空f(T*T){…}//(b)
    模板空f(int*t){…}//(c)
    ...
    int*pi;f(pi);//(b) 是叫,不是(c)!
    
    在第7项上方:

    更糟糕的是,如果在模板专门化中省略了类型,则不同的函数模板可能会根据定义的顺序进行专门化,因此可能会调用也可能不会调用专门化的函数

    案例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
    
    template void f(T T){…}//(a)
    模板空f(T*T){…}//(b)
    模板空f(int*t){…}//(c)-f(b)
    ...
    int*pi;f(pi);//(c) 被称为
    
    案例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
    
    template void f(T T){…}//(a)
    模板空f(int*t){…}//(c)-a
    模板空f(T*T){…}//(b)
    ...
    int*pi;f(pi);//(b) 被称为
    
    我倾向于大量使用模板,以避免代码重复,并通过编译检查提高安全性

    一般来说,在键入时考虑编译器将要做什么,以及如何为每种类型生成代码是有帮助的

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


    最后,当没有出路时,开始阅读这些编译错误消息!一开始它们可能看起来很吓人,但它们确实很有帮助。也许一开始提取第一个,在文本编辑器中复制并使其看起来漂亮会有助于习惯它们,并且很快就会成为通读的第二天性。

    这可能不流行,但我认为需要说一句

    模板很复杂

    它们有惊人的力量,但要明智地使用它们。不要太疯狂,不要有太多的模板参数。。。不要有太多的专业。。。记住,其他程序员也必须阅读这篇文章


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

    模板提示: 您是否知道可以专门化模板实例化的选定函数:

    #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();
    }
    
    #包括
    #包括
    名称空间标准{
    模板
    void向量::clear(){
    
    std::cout一个常见错误是模板构造函数或赋值运算符不会抑制编译器生成的构造函数或赋值运算符:

    template <typename T>
    class A {
    public:
      template <typename S>
      A(A<S> const &);
    
      template <typename S>
      A & operator=(A<S> const &);
    
    private:
      int * i;
    }; 
    
    模板
    甲级{
    公众:
    模板
    A(常数&);
    模板
    A&运算符=(常数&);
    私人:
    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
    }
    
    void foo(A);
    空心条(){
    A a1;
    foo(a1);//隐式生成