函数模板
往里面传入变量编译器就能推断类型
对于函数模板, 可以使用非类型参数(即, 模板中不使用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)
放在<>里的从左到右被依次推断, 只有最后几个才会被自动推断
也可以通过指定显式模板的方式, 来允许模板进行自动类型转换(像普通函数的隐式自动转换一样)
网友评论