C++ 11 学习总结——模板
最近工作需要,学习C++ 11的知识,学习中总结了一些知识点,分享给大家。
函数模板
简介
建立一个通用函数,这个通用函数的形参类型不具体指定,用一个虚拟类型来代表,这个通用函数就被称为函数模板。
区别
模板区别在于其函数声明,前面加了个template<typename T>,这句话告诉编译器,函数体出现类型T时,不要报错,T是一个通用类型。
格式
template<parameter-list> function-declaration
函数模板在形式上分为两部分:模板、函数。在函数前面加上template<...>就成为函数模板,因此对函数的各种修饰(inline、constexpr等)需要加在function-declaration上,而不是template前。如
-
template<typename T>
-
-
inline T min(const T &, const T &);
-
注意
- 函数模板不是函数;
- 函数模板用来定义一族函数,而不是一个函数;
- 当用具体类型去替换T时,才会生成具体函数,该过程叫做函数模板的实例化;
- 如果我们只是编写了函数模板,但不在任何地方使用它(也不显式实例化),则编译器不会为该函数模板生成任何代码。
详细解说
1)显式实例化
简介
显式实例化的方式告诉编译器生成指定实参的函数,显式实例化声明会阻止隐式实例化
例子
-
template<typename R, typename T1, typename T2>
-
-
R add(T1 a, T2 b) {
-
-
return static_cast<R>(a + b);
-
-
}
-
-
// 显式实例化
-
-
template double add<double, int, double>(int, double);
-
-
// 显式实例化, 推导出第三个模板实参
-
-
template int add<int, int>(int, int);
-
-
// 全部由编译器推导
-
-
template double add(double, double);
在显式实例化时,只指定部分模板实参,则指定顺序必须s自左至右依次指定,不能越过前参模板形参,直接指定后面的。
2)隐式实例化
简介
当函数模板被调用,且在之前没有显式实例化时,即发生函数模板的隐式实例化。
例子
-
#include <iostream>
-
-
template<typename T>
-
-
void print(const T &r) {
-
-
std::cout << r << std::endl;
-
-
}
-
-
int main() {
-
-
// 隐式实例化print<int>(int)
-
-
print(1);
-
-
// 实例化print<char>(char)
-
-
print<>('c');
-
-
// 仍然是隐式实例化,我们希望编译器生成print<double>(double)
-
-
print<double>(1);
-
-
}
-
3)返回值为auto
简介
函数的返回值类型取决于函数参数某种运算后的类型。对于这种情况可以采用auto关键字作为返回值占位符
例子
-
template<typename T1, typename T2>
-
-
auto multi(T a, T b) -> decltype(a * b) {
-
-
return a * b;
-
-
}
decltype操作符用于查询表达式的数据类型,也是C++11标准引入的新的运算符,其目的是解决泛型编程中有些类型由模板参数决定,而难以表示的问题。
4)类成员函数模板
简介
类成员中定义函数模板
例子
-
#include <iostream>
-
-
-
-
class object {
-
-
public:
-
-
template<typename T>
-
-
void print(const char *name, const T &v) {
-
-
std::cout << name << ": " << v << std::endl;
-
-
}
-
-
};
-
-
-
-
int main() {
-
-
object o;
-
-
o.print("name", "Crystal");
-
-
o.print("age", 18);
-
-
}
-
注意:函数模板不能用作虚函数。这是因为C++编译器在解析类的时候就要确定虚函数表(vtable)的大小。
5)函数模板重载
简介
函数模板之间、普通函数和模板函数之间可以重载
调用顺序
顺序 |
行为 |
1 |
最符合函数名和参数类型的普通函数 |
2 |
特殊模板(具有非类型形参的模板,即对T有类型限制) |
3 |
普通模板(对T没有任何限制的) |
4 |
通过类型转换进行参数匹配的重载函数 |
例子
-
#include <iostream>
-
-
template<typename T>
-
-
const T &max(const T &a, const T &b) {
-
-
std::cout << "max(&, &) = ";
-
-
return a > b ? a : b;
-
-
}
-
-
-
-
// 函数模板重载
-
-
template<typename T>
-
-
const T *max(T *a, T *b) {
-
-
std::cout << "max(*, *) = ";
-
-
return *a > *b ? a : b;
-
-
}
-
-
-
-
// 函数模板重载
-
-
template<typename T>
-
-
const T &max(const T &a, const T &b, const T &c) {
-
-
std::cout << "max(&, &, &) = ";
-
-
const T &t = (a > b ? a : b);
-
-
return t > c ? t : c;
-
-
}
-
-
// 普通函数
-
-
const char *max(const char *a, const char *b) {
-
-
std::cout << "max(const char *, const char *) = ";
-
-
return strcmp(a, b) > 0 ? a : b;
-
-
}
-
-
-
-
int main() {
-
-
int a = 1, b = 2;
-
-
std::cout << max(a, b) << std::endl;
-
-
std::cout << *max(&a, &b) << std::endl;
-
-
std::cout << max(a, b, 3) << std::endl;
-
-
std::cout << max("en", "ch") << std::endl;
-
-
// 可以通过空模板实参列表来限定编译器只匹配函数模板
-
-
std::cout << max<>("en", "ch") << std::endl;
-
-
}
可以通过空模板实参列表来限定编译器只匹配函数模板。
6)普通函数模板
-
#include <iostream>
-
-
// N必须是编译时的常量表达式
-
-
template<typename T, int N>
-
-
void printArray(const T (&a)[N]) {
-
-
std::cout << "[";
-
-
const char *sep = "";
-
-
for (int i = 0; i < N; i++, (sep = ", ")) {
-
-
std::cout << sep << a[i];
-
-
}
-
-
std::cout << "]" << std::endl;
-
-
}
-
-
-
-
int main() {
-
-
// T: int, N: 3
-
-
printArray({1, 2, 3});
-
-
}
-
//输出:[1, 2, 3]
函数模板 .vs. 模板函数
函数模板重点在模板。表示这是一个模板,用来生成函数。
模板函数重点在函数。表示的是由一个模板生成而来的函数。
类模板
简介
C++中类模板通常是容器(如std::vector)或行为的封装,类模板只是定义了一组通用的操作,在具体实例化前是不占用程序空间的。
语法格式
template < parameter-list > class-declaration
parameter-list 形参
class-declaration 类声明
说明:构成类模板的形参(parameter-list)约束与函数模板相同,但是函数模板的类型自动推演不同,类模板实例化时,需要显式指定,例如:
std::vector<int> intArr;
详细解说
- 类模板--成员函数
成员函数(含成员函数模板)只有在使用时才生成。
-
template<typename T>
-
-
class A {
-
-
T a_;
-
-
public:
-
-
void add(int n) {
-
-
a_ += n;
-
-
}
-
-
}
-
-
class M{};
-
-
int main() {
-
-
A<M> s; // s未用到add函数,因此整个程序得以成功编译
-
-
// s.add(1); // 如果有这句话,则会编译失败
-
-
}
A<T>::add在变量s中未使用到,因此虽然a_ += n不合法,但整个程序仍然通过了编译。
2)类模板--虚函数
类模板中可以有虚函数,虚函数在类模板实例化为模板类时由编译器生成。
类模板的虚函数可以访问模板类中的泛型成员(变量、成员函数模板都可以访问)。 例子:
-
#include <iostream>
-
-
template<typename T>
-
-
class A {
-
-
T a_;
-
-
public:
-
-
virtual void say() {
-
-
std::cout << "a -> " << a_ << std::endl;
-
-
} };
-
-
class M{};
-
-
int main() {
-
-
// 尽管say函数未被使用,此处会编译仍会失败,因为std::cout << m.a_操作是非法的
-
-
A<M> m;
-
-
}
-
3)成员函数模板
类模板和函数模板结合就是成员函数模板。
-
#include <iostream>
-
-
template<typename T>
-
-
class Printer {
-
-
T prefix_;
-
-
public:
-
-
explicit Printer(const T &prefix) :prefix_(prefix) {
-
-
}
-
-
// 成员函数模板
-
-
template<typename U, typename ...Args> void print(const U &u, Args... args);
-
-
void print() {
-
-
std::cout << std::endl;
-
-
}
-
-
};
-
-
-
-
template<typename T> template<typename U, typename ...Args>
-
-
void Printer<T>::print(const U &u, Args... args) {
-
-
std::cout << this->prefix_ << u << std::endl;
-
-
print(args...);
-
-
}
希望对你有帮助。
文章来源: guo-pu.blog.csdn.net,作者:一颗小树x,版权归原作者所有,如需转载,请联系作者。
原文链接:guo-pu.blog.csdn.net/article/details/95385637
- 点赞
- 收藏
- 关注作者
评论(0)