Chapter 16 Template 模板

2020-04-24  本文已影响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) 放在<>里的从左到右被依次推断, 只有最后几个才会被自动推断
也可以通过指定显式模板的方式, 来允许模板进行自动类型转换(像普通函数的隐式自动转换一样)

上一篇下一篇

猜你喜欢

热点阅读