美文网首页
Chapter 16 Template 模板

Chapter 16 Template 模板

作者: 再凌 | 来源:发表于2020-04-24 23:33 被阅读0次

    函数模板

    往里面传入变量编译器就能推断类型
    对于函数模板, 可以使用非类型参数(即, 模板中不使用typename, 而是某种特定的类型)

    template <unsigned M, unsigned N>
    int compare ( const char (&p)[M], const char (&q)[N])
    {
      //....
    }
    
    //可以直接传入两个实参是常量的表达式, 若不是常量, 则无法生成函数
    compare("Hello", "World");
    

    对于模板函数, 建议将声明和定义都放在头文件中, 因为每一个要使用了这个函数的源文件都需要知道函数的定义.

    只有当模板被实例化调用的时候, 编译器才能发现大多数的错误


    尾置返回可以表示无法表示的类型

    如果T表示的是迭代器, 函数想返回迭代器的解引用, 但是编译器在开头时无法推断返回值到底是什么类型. 相反, 尾置返回出现在参数列表的后面, 因此此时编译器知道返回值类型

    template <typename T>
    auto f( T beg) ->decltype(*beg)
    {
      return *beg;
    }
    
    //更牛逼的是, *beg返回的是一个引用, 如果不想返回引用, 使用<type_traits>头文件的remove_reference<类型>::type 可以得到某种类型的非引用类型
    
    //变成:
    auto f(T beg) -> typename remove_reference<decltype(*beg)>::type 
    

    模板函数也可以重载, 重载后的候选时选择最合适的, 如果有多个最合适的, 那么就选择最""特殊的""(这个版本仿佛就是为了这次候选而存在)

    模板特例化: 部分重写一个模板. 将<>留空, 函数真正实参自定义重写, 那么这个特例化模板只会用于这个特定的类型. 这种方法也可以特例化一个成员函数.

    引用折叠

    对于template<typename T> f(T&&), 如果实例化为f(i), 那么在"传递普通变量到右值的情况下", 编译器会推断T为int&, 传入到f中就变成右值的引用, 引发了引用折叠:

    • 三个&以下的, 都折叠为左值引用(&)
    • 四个& 的, 折叠为右值(&&)
      但是同时也会引发重载问题
    template<typename T> void f(T&& val)
    {
      T t = val;
      t = addsomething(val); //val有没有改变?
    }
    

    如果传入f()的是1, 那么T就被推断成int类型, 如果传入的是i, 那么就被推断为int&, 那么折叠之后又是int&.(但是在函数中, 如果形参要求是右值, 则不能传左值)

    一个好的解决方法是防止类型转换, 手动重载这两个模板:

    template<typename T> void f(T&& val);
    template<typename T> void f(const T& val);
    
    右值形参有什么用

    可以用于保持传入参数的类型(const或&)保持不变

    template<typename T> void f(T&& val)
    {
      fn(val);
    }
    

    如果传入的val是左值, 那么传入到f()中就是左值引用
    如果此时fn(int&&), 就会报错. 因为常规函数不能从左值到右值的转换. 使用std::forward<T>(val), 方法略.

    类模板

    Tip: typename关键字让编译器知道 后面紧跟着的是一个类型, 而不是T的成员. 例如: typename T::inner *myClass, 是声明了一个T::inner*类型的变量, 而不是两个变量相乘

    如果模板类中含有模板类友元: 如果使用了相同的T则是一对一的特定关系
    一般类包含模板类友元: 如果这个类友元开头是template <....>...., 则会在编译时再次展开, 进入模板替换, 为一对多的友元关系.
    如果这个模板作为友元时已经指定了模板类型, 那么就是一对一的特定友元关系

    对于模板,不能使用typedef, 但是可以对已经特例化的模板使用typedef, 即typedef Blob<string> strBlob是OK的

    但是对于模板是可以使用using的
    template <typename T> using twin = pair<T, T>;, 之后就可以用twin<type>来直接调用pair模板

    模板类中的static只在同类型的类中共享, 只有在被使用的时候才初始化, 对象实例化的时候是不被一起实例化的.

    如果要使用类模板的成员, 那么类前面必须加上typename关键字, 不然比如编译器不能推断 myTem::getptr *p究竟是定义一个变量p, 还是在做乘法.


    多个源文件可能有相同的模板函数的相同特例化, 那么可以在其他源文件中使用extern声明, 这个特例化在其他源文件中, 如extern template class Blob<string>; 注意, 在另外的文件中的这种显式实例化, 会实例化所有的成员函数.

    unique_ptr的删除器必须编译到模板中, 这反应了unique_ptr的删除器可以有更高效的调用效率;
    shared_ptr的删除器可以随意修改, 这反应了shared_ptr(大概)使用一个指针来指向删除器, 调用的时候需要多次跳转

    模板实参推断

    模板传参的自动类型转换只能是有限几种类型: 非const传递给const, 数组变成指针, 函数变成函数指针

    指定显式模板:template <typename T1, typename T2, typename T3> T1 sum(T2, T3), 没有方式自动推断出T1类型, 因此需要显式说明auto res = sum<long> (i, j) 放在<>里的从左到右被依次推断, 只有最后几个才会被自动推断
    也可以通过指定显式模板的方式, 来允许模板进行自动类型转换(像普通函数的隐式自动转换一样)

    相关文章

      网友评论

          本文标题:Chapter 16 Template 模板

          本文链接:https://www.haomeiwen.com/subject/edrcwhtx.html