C++ 11 学习总结——模板

举报
一颗小树x 发表于 2020/12/03 00:47:11 2020/12/03
【摘要】 最近工作需要,学习C++ 11的知识,学习中总结了一些知识点,分享给大家。   函数模板 简介 建立一个通用函数,这个通用函数的形参类型不具体指定,用一个虚拟类型来代表,这个通用函数就被称为函数模板。 区别 模板区别在于其函数声明,前面加了个template<typename T>,这句话告诉编译器,函数体出现类型T时,不要报错,T是一个通用...

最近工作需要,学习C++ 11的知识,学习中总结了一些知识点,分享给大家。

 

函数模板

简介

建立一个通用函数,这个通用函数的形参类型不具体指定,用一个虚拟类型来代表,这个通用函数就被称为函数模板

区别

模板区别在于其函数声明,前面加了个template<typename T>,这句话告诉编译器,函数体出现类型T时,不要报错,T是一个通用类型。

格式

template<parameter-list> function-declaration
 

函数模板在形式上分为两部分:模板、函数。在函数前面加上template<...>就成为函数模板,因此对函数的各种修饰(inline、constexpr等)需要加在function-declaration上,而不是template前。如


  
  1. template<typename T>
  2. inline T min(const T &, const T &);

 

注意

  1. 函数模板不是函数;
  2. 函数模板用来定义一族函数,而不是一个函数;
  3. 当用具体类型去替换T时,才会生成具体函数,该过程叫做函数模板的实例化
  4. 如果我们只是编写了函数模板,但不在任何地方使用它(也不显式实例化),则编译器不会为该函数模板生成任何代码。

 

 

详细解说

1)显式实例化

简介

显式实例化的方式告诉编译器生成指定实参的函数,显式实例化声明会阻止隐式实例化

例子


  
  1. template<typename R, typename T1, typename T2>
  2. R add(T1 a, T2 b) {
  3.     return static_cast<R>(a + b);
  4. }
  5. // 显式实例化
  6. template double add<double, int, double>(int, double);
  7. // 显式实例化, 推导出第三个模板实参
  8. template int add<int, int>(int, int);
  9. // 全部由编译器推导
  10. template double add(double, double);

  在显式实例化时,只指定部分模板实参,则指定顺序必须s自左至右依次指定,不能越过前参模板形参,直接指定后面的。

 

2)隐式实例化

简介

当函数模板被调用,且在之前没有显式实例化时,即发生函数模板的隐式实例化。

 

例子


  
  1. #include <iostream>
  2. template<typename T>
  3. void print(const T &r) {
  4.     std::cout << r << std::endl;
  5. }
  6. int main() {
  7.     // 隐式实例化print<int>(int)
  8.     print(1);
  9.     // 实例化print<char>(char)
  10.     print<>('c');
  11.     // 仍然是隐式实例化,我们希望编译器生成print<double>(double)
  12.     print<double>(1);
  13. }

 

 

3)返回值为auto

简介

函数的返回值类型取决于函数参数某种运算后的类型。对于这种情况可以采用auto关键字作为返回值占位符

 

例子


  
  1. template<typename T1, typename T2>
  2. auto multi(T a, T b) -> decltype(a * b) {
  3.     return a * b;
  4. }

decltype操作符用于查询表达式的数据类型,也是C++11标准引入的新的运算符,其目的是解决泛型编程中有些类型由模板参数决定,而难以表示的问题。

 

4)类成员函数模板

简介

类成员中定义函数模板

 

例子


  
  1. #include <iostream>
  2. class object {
  3. public:
  4.     template<typename T>
  5.     void print(const char *name, const T &v) {
  6.         std::cout << name << ": " << v << std::endl;
  7.     }
  8. };
  9. int main() {
  10.     object o;
  11.     o.print("name", "Crystal");
  12.     o.print("age", 18);
  13. }

注意:函数模板不能用作虚函数。这是因为C++编译器在解析类的时候就要确定虚函数表(vtable)的大小。

 

5)函数模板重载

简介

函数模板之间、普通函数和模板函数之间可以重载

 

调用顺序

顺序

行为

1

最符合函数名和参数类型的普通函数

2

特殊模板(具有非类型形参的模板,即对T有类型限制)

3

普通模板(对T没有任何限制的)

4

通过类型转换进行参数匹配的重载函数

 

例子


  
  1. #include <iostream>
  2. template<typename T>
  3. const T &max(const T &a, const T &b) {
  4.     std::cout << "max(&, &) = ";
  5.     return a > b ? a : b;
  6. }
  7. // 函数模板重载
  8. template<typename T>
  9. const T *max(T *a, T *b) {
  10.     std::cout << "max(*, *) = ";
  11.     return *a > *b ? a : b;
  12. }
  13. // 函数模板重载
  14. template<typename T>
  15. const T &max(const T &a, const T &b, const T &c) {
  16.     std::cout << "max(&, &, &) = ";
  17.     const T &t = (a > b ? a : b);
  18.     return t > c ? t : c;
  19. }
  20. // 普通函数
  21. const char *max(const char *a, const char *b) {
  22.     std::cout << "max(const char *, const char *) = ";
  23.     return strcmp(a, b) > 0 ? a : b;
  24. }
  25. int main() {
  26.     int a = 1, b = 2;
  27.     std::cout << max(a, b) << std::endl;
  28.     std::cout << *max(&a, &b) << std::endl;
  29.     std::cout << max(a, b, 3) << std::endl;
  30.     std::cout << max("en", "ch") << std::endl;
  31.     // 可以通过空模板实参列表来限定编译器只匹配函数模板
  32.     std::cout << max<>("en", "ch") << std::endl;
  33. }

可以通过空模板实参列表来限定编译器只匹配函数模板。

 

6)普通函数模板


  
  1. #include <iostream>
  2. // N必须是编译时的常量表达式
  3. template<typename T, int N>
  4. void printArray(const T (&a)[N]) {
  5.     std::cout << "[";
  6.     const char *sep = "";
  7.     for (int i = 0; i < N; i++, (sep = ", ")) {
  8.         std::cout << sep << a[i];
  9.     }
  10.     std::cout << "]" << std::endl;
  11. }
  12. int main() {
  13.     // T: int, N: 3
  14.     printArray({1, 2, 3});
  15. }
  16. //输出:[1, 2, 3]

 

函数模板 .vs. 模板函数

函数模板重点在模板。表示这是一个模板,用来生成函数。
模板函数重点在函数。表示的是由一个模板生成而来的函数

 

 

类模板

简介

C++中类模板通常是容器(如std::vector)或行为的封装,类模板只是定义了一组通用的操作,在具体实例化前是不占用程序空间的。

 

语法格式

  template < parameter-list > class-declaration
 

parameter-list  形参

class-declaration 类声明

说明:构成类模板的形参(parameter-list)约束与函数模板相同,但是函数模板的类型自动推演不同,类模板实例化时,需要显式指定,例如:

std::vector<int> intArr;
 

 

详细解说

  1. 类模板--成员函数

成员函数(含成员函数模板)只有在使用时才生成


  
  1. template<typename T>
  2. class A {
  3.     T a_;
  4. public:
  5.     void add(int n) {
  6.         a_ += n;
  7.     }
  8. }
  9. class M{};
  10. int main() {
  11.     A<M> s;      // s未用到add函数,因此整个程序得以成功编译
  12.    // s.add(1);  // 如果有这句话,则会编译失败
  13. }

A<T>::add在变量s中未使用到,因此虽然a_ += n不合法,但整个程序仍然通过了编译。

 

2)类模板--虚函数

类模板中可以有虚函数,虚函数在类模板实例化为模板类时由编译器生成。

类模板的虚函数可以访问模板类中的泛型成员(变量、成员函数模板都可以访问)。 例子:


  
  1. #include <iostream>
  2. template<typename T>
  3. class A {
  4.     T a_;
  5. public:
  6.     virtual void say() {
  7.         std::cout << "a -> " << a_ << std::endl;
  8. }  };
  9. class M{};
  10. int main() {
  11.     // 尽管say函数未被使用,此处会编译仍会失败,因为std::cout << m.a_操作是非法的
  12.     A<M> m;
  13. }

 

3)成员函数模板

类模板和函数模板结合就是成员函数模板。


  
  1. #include <iostream>
  2. template<typename T>
  3. class Printer {
  4. T prefix_;
  5. public:
  6. explicit Printer(const T &prefix) :prefix_(prefix) {
  7. }
  8. // 成员函数模板
  9. template<typename U, typename ...Args> void print(const U &u, Args... args);
  10. void print() {
  11. std::cout << std::endl;
  12. }
  13. };
  14. template<typename T> template<typename U, typename ...Args>
  15. void Printer<T>::print(const U &u, Args... args) {
  16. std::cout << this->prefix_ << u << std::endl;
  17. print(args...);
  18. }

 

希望对你有帮助。

文章来源: guo-pu.blog.csdn.net,作者:一颗小树x,版权归原作者所有,如需转载,请联系作者。

原文链接:guo-pu.blog.csdn.net/article/details/95385637

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。