C/C++教程

c++模板day4

本文主要是介绍c++模板day4,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
  1 太懒了,发现一个博主写得非常好,原文来源https://www.cnblogs.com/zhangyachen/p/13946442.html
  2 这里我是copy了一些我认为比较重要的
  3 
  4 模板的编译和链接问题
  5 
  6 大多数人会按照如下方式组织非模板代码:
  7 
  8 将类或者其他类型声明放在头文件(.hpp、.H、.h、.hh、.hxx)中。
  9 将函数定义等放到一个单独的编译单元文件中(.cpp、.C、.c、.cc、.cxx)。
 10 但是这种组织方式在包含模板的代码中却行不通,例如:
 11 
 12 头文件:
 13 // myfirst.hpp
 14 #ifndef MYFIRST_HPP
 15 #define MYFIRST_HPP
 16 // declaration of template
 17 template<typename T>
 18 void printTypeof (T const&);
 19 #endif // MYFIRST_HPP
 20 
 21 定义函数模板的文件:
 22 
 23 // myfirst.cpp
 24 #include <iostream>
 25 #include <typeinfo>
 26 #include "myfirst.hpp"
 27 // implementation/definition of template
 28 template<typename T>
 29 void printTypeof (T const& x) {
 30   std::cout << typeid(x).name() << '\n';
 31 }
 32 
 33 在另一个文件中使用该模板:
 34 
 35 // myfirstmain.cpp
 36 #include "myfirst.hpp"
 37 // use of the template
 38 int main() {
 39   double ice = 3.0;
 40   printTypeof(ice); // call function template for type double
 41 }
 42 在c/c++中,当编译阶段发现一个符号(printTypeof)没有定义只有声明时,编译器会假设它的定义在其他文件中,所以编译器会留一个”坑“给链接器linker,让它去填充真正的符号地址。
 43 
 44 但是上面说过,模板是比较特殊的,需要在编译阶段进行instantiation,即需要进行模板参数类型推断,实例化模板,当然也就需要知道函数的定义。但是由于上面两个cpp文件都是单独的编译单元文件,所以当编译器编译myfirstmain.cpp时,它没有找到模板的定义,自然也就没有instantiation。
 45 
 46 解决办法就是我们把模板的声明和定义都放在一个头文件。大家可以看一下自己环境下的vector等STL源文件,就是把类的声明和定义都放在了一个文件中。
 47 
 48 普通函数可以在头文件声明,cpp上定义,其他文件调用该函数此时可以通过链接阶段找到该函数的定义;
 49 而模板需要在编译过程中就需要找到定义。
 50 
 51 
 52 在C++11中,我们可以利用auto和trailing return type来让编译器找出返回值类型:
 53 
 54 template <typename T1, typename T2>
 55 auto max(T1 a, T2 b) -> decltype(b < a ? a : b) {
 56   return b < a ? a : b;
 57 }
 58 
 59 在C++14中,我们可以省略trailing return type:
 60 
 61 template<typename T1, typename T2>
 62 auto max (T1 a, T2 b) {
 63     return b < a ? a : b;
 64 }
 65 
 66 重载函数模板
 67 这个跟普通函数重载也类似:
 68 
 69 // maximum of two int values:
 70 int max(int a, int b) { 
 71   return b < a ? a : b; 
 72 }
 73 
 74 // maximum of two values of any type:
 75 template <typename T> 
 76 T max(T a, T b) { 
 77   return b < a ? a : b; 
 78 }
 79 
 80 int main() {
 81   max(7, 42);         // calls the nontemplate for two ints
 82   max(7.0, 42.0);     // calls max<double> (by argument deduction)
 83   max('a', 'b');      // calls max<char> (by argument deduction)
 84   max<>(7, 42);       // calls max<int> (by argument deduction)
 85   max<double>(7, 42); // calls max<double> (no argument deduction)
 86   max('a', 42.7);     // calls the nontemplate for two ints
 87 }
 88 这里需要解释下最后一个max('a', 42.7)。因为在模板参数类型推导过程中不允许类型自动转换,但是调用普通函数是允许的,所以这个会调用非模板函数 
 89 模板参数类型推导过程中不允许类型自动转换!!!
 90 
 91 
 92 重载时最好不要随便改变模板参数个数,最好可以显示的指定模板参数类型
 93 下面是段有问题的代码:
 94 
 95 // maximum of two values of any type (call-by-reference)
 96 template <typename T> T const &max(T const &a, T const &b) {
 97   return b < a ? a : b;
 98 }
 99 
100 // maximum of two C-strings (call-by-value)
101 char const *max(char const *a, char const *b) {
102   return std::strcmp(b, a) < 0 ? a : b;
103 }
104 
105 // maximum of three values of any type (call-by-reference)
106 template <typename T> T const &max(T const &a, T const &b, T const &c) {
107   return max(max(a, b), c);           // error if max(a,b) uses call-by-value
108 }
109 
110 int main() {
111   auto m1 = max(7, 42, 68);         // OK
112 
113   char const *s1 = "frederic";
114   char const *s2 = "anica";
115   char const *s3 = "lucas";
116   auto m2 = max(s1, s2, s3);         // run-time ERROR
117 }
118 问题出现在return max (max(a,b), c);,因为char const *max(char const *a, char const *b)的参数是按值传递,max(a,b)会产生一个指向已经销毁的栈帧地址,这会导致未定义行为。
119 如果不拿个中间变量保存函数返回值,那么它就会消亡了,在调用Max(消亡值,s3)产生错误;
120 
121 
122 
123 引子
124 T&&在代码里并不总是右值引用:
125 
126 void f(Widget&& param);      // rvalue reference
127 
128 Widget&& var1 = Widget();      // rvalue reference
129 
130 auto&& var2 = var1;        // not rvalue reference
131 
132 
133 template<typename T>
134 void f(std::vector<T>&& param);      // rvalue reference
135 
136 
137 template<typename T>
138 void f(T&& param);     // not rvalue reference
139 T&&代表两种含义:
140 
141 右值引用
142 万能引用(universal references, or forwarding references)
143 如何区分
144 万能引用一般出现在两个场景中:(万能引用可以推断是左值引用还是右值引用,泛型接口) 
145 
146 1.模板参数
147 template<typename T>
148 void f(T&& param); // param is a universal reference
149 2.auto声明
150 auto&& var2 = var1; // var2 is a universal reference
151 
152 总结:万能引用只存在于推理类型,而右值引用是确定类型 
153 
154 auto声明
155 对于auto的场景来说,所有的auto&&都是万能引用,因为它总是有参数推导的过程。例如定义一个记录函数执行时间的lambda(C++14中允许使用auto来声明lambda的函数):
156 
157 auto timeFuncInvocation = [](auto &&func, auto &&... params) {  
158   start timer;
159   std::forward<decltype(func)>(func)(                      // invoke func
160       std::forward<decltype(params)>(params)...      // on params
161   );
162   stop timer and record elapsed time;
163 };
164  

 

这篇关于c++模板day4的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!