在C++11中,赋予了auto全新的含义,不再用于修饰变量,而是作为一个类型指示符,指示编译器在编译时推导auto声明的变量的数据类型。
auto 变量名 = 初始值;
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
int main()
{
auto a = 3; cout<<"a="<<a<<endl;
auto b = 3.3; cout<<"b="<<b<<endl;
auto c = "西施"; cout<<"c="<<c<<endl;
}
不要滥用auto,auto在编程时真正的用途如下:
1)代替冗长复杂的变量声明。
2)在模板中,用于声明依赖模板参数的变量。
3)函数模板依赖模板参数的返回值。
4)用于lambda表达式中。
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
double func(double b, const char* c, float d, short e, long f)
{
cout << ",b=" << b << ",c=" << c << ",d=" << d << ",e=" << e << ",f=" << f << endl;
return 5.5;
}
int main()
{
cout<<"使用函数指针"<<endl;
double (*pf)( double , const char* , float , short , long ); // 声明函数指针pf。
pf = func;
pf( 2, "西施", 3, 4, 5);
cout<<"使用auto"<<endl;
auto pf1 = func;
pf1(2, "西施2", 3, 4, 5);
}
函数模板是通用的函数描述,使用任意类型(泛型)来描述函数。
编译的时候,编译器推导实参的数据类型,根据实参的数据类型和函数模板,生成该类型的函数定义。
创建交换两个变量的函数模板:
template <typename T>
void Swap(T &a, T &b)
{
T tmp = a;
a = b;
b = tmp;
}
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template <typename T>
void Swap(T &a, T &b)
{
T tmp = a;
a = b;
b = tmp;
}
int main()
{
int a,b;
a = 1;
b = 2;
Swap(a,b);
cout<<a<<" "<<b<<endl;
}
函数模板使用typename,类模板使用class
1)可以为类的成员函数创建模板,但不能是虚函数和析构函数。
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl{
public:
template <typename T>
CGirl(T a){
cout<<"a = "<<a<<endl;
}
template <typename T>
void show(T b){
cout<<"b = "<<b<<endl;
}
};
int main()
{
CGirl girl("aa");
girl.show(111);
CGirl girl2(2);
girl2.show(222);
return 0;
}
2)使用函数模板时,必须明确数据类型,确保实参与函数模板能匹配上。
3)使用函数模板时,推导的数据类型必须适应函数模板中的代码。
4)使用函数模板时,如果是自动类型推导,不会发生隐式类型转换,如果显式指定了函数模板的数据类型,可以发生隐式类型转换。
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template <typename T>
T Add(T a, T b){
return a+b;
}
int main()
{
int a = 10;
char b = 20;
int c = Add<int>(a,b);
cout<<"c = "<<c<<endl;
return 0;
}
5)函数模板支持多个通用数据类型的参数。
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template <typename T1,typename T2>
void show(T1 a, T2 b){
cout<<"a = "<<a<<" b = "<<b<<endl;
}
int main()
{
int a = 1;
string b = "b";
show(a,b);
}
6)函数模板支持重载,可以有非通用数据类型的参数。
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template <typename T1>
void show(T1 a){
cout<<"a = "<<a<<endl;
}
template <typename T1,typename T2>
void show(T1 a, T2 b){
cout<<"a = "<<a<<" b = "<<b<<endl;
}
template <typename T1,typename T2,typename T3>
void show(T1 a, T2 b,T3 c){
cout<<"a = "<<a<<" b = "<<b<<"c = "<<c<<endl;
}
int main()
{
int a = 1;
string b = "b";
float c = 1.1111;
show(a,b);
show(a,b,c);
}
可以提供一个具体化的函数定义,当编译器找到与函数调用匹配的具体化定义时,将使用该定义,不再寻找模板。
template<> void 函数模板名<数据类型>(参数列表)
template<> void 函数模板名 (参数列表)
{
// 函数体。
}
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl // 超女类。
{
public:
int m_bh; // 编号。
string m_name; // 姓名。
int m_rank; // 排名。
};
template <typename T>
void Swap(T& a, T& b); // 交换两个变量的值函数模板。
template<>
void Swap<CGirl>(CGirl& g1, CGirl& g2); // 交换两个超女对象的排名。
// template<>
// void Swap(CGirl& g1, CGirl& g2); // 交换两个超女对象的排名。
int main()
{
int a = 10, b = 20;
Swap(a, b); // 使用了函数模板。
cout << "a=" << a << ",b=" << b << endl;
CGirl g1, g2;
g1.m_rank = 1; g2.m_rank = 2;
Swap(g1, g2); // 使用了超女类的具体化函数。
cout << "g1.m_rank=" << g1.m_rank << ",g2.m_rank=" << g2.m_rank << endl;
}
template <typename T>
void Swap(T& a, T& b) // 交换两个变量的值函数模板。
{
T tmp = a;
a = b;
b = tmp;
cout << "调用了Swap(T& a, T& b)\n";
}
template<>
void Swap<CGirl>(CGirl& g1, CGirl& g2) // 交换两个超女对象的排名。
// template<>
// void Swap(CGirl& g1, CGirl& g2) // 交换两个超女对象的排名。
{
int tmp = g1.m_rank;
g1.m_rank = g2.m_rank;
g2.m_rank = tmp;
cout << "调用了Swap(CGirl& g1, CGirl& g2)\n";
}
对于给定的函数名,可以有普通函数、函数模板和具体化的函数模板,以及它们的重载版本。
1)具体化优先于常规模板,普通函数优先于具体化和常规模板。
2)如果希望使用函数模板,可以用空模板参数强制使用函数模板。
Swap<>(1,2);
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
void Swap(int a, int b) // 普通函数。
{
cout << "使用了普通函数。\n";
}
template <typename T>
void Swap(T a, T b) // 函数模板。
{
cout << "使用了函数模板。\n";
}
template <>
void Swap(int a, int b) // 函数模板的具体化版本。
{
cout << "使用了具体化的函数模板。\n";
}
int main()
{
Swap('c', 'd');
float a = 1.11;
float b =2.222;
Swap(a,b);
Swap<>(1,2);
}
- 函数模板只是函数的描述,没有实体,创建函数模板的代码放在头文件中。
- 函数模板的具体化有实体,编译的原理和普通函数一样,所以,声明放在头文件中,定义放在源文件中。
/
// public.h
#pragma once
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
void Swap(int a, int b); // 普通函数。
template <typename T>
void Swap(T a, T b) // 函数模板。
{
cout << "使用了函数模板。\n";
}
template <>
void Swap(int a, int b); // 函数模板的具体化版本。
/
/
// public.cpp
#include "public.h"
void Swap(int a, int b) // 普通函数。
{
cout << "使用了普通函数。\n";
}
template <>
void Swap(int a, int b) // 函数模板的具体化版本。
{
cout << "使用了具体化的函数模板。\n";
}
/
/
// demo01.cpp
#include "public.h"
int main()
{
Swap(1,2); // 将使用普通函数。
Swap(1.3, 3.5); // 将使用具体化的函数模板。
Swap('c', 'd'); // 将使用函数模板。
}
/
在C++11中,decltype
操作符,用于查询表达式的数据类型。
语法:
decltype(expression) var;
decltype分析表达式并得到它的类型,不会计算执行表达式。函数调用也一种表达式,因此不必担心在使用decltype时执行了函数。
推导规则:
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
// 1)如果expression是一个没有用括号括起来的标识符,则var的类型与该标识符的类型相同,包括const等限定符。
// 2)如果expression是一个函数调用,则var的类型与函数的返回值类型相同(函数不能返回void,但可以返回void *)。
// 3)如果expression是一个左值(能取地址)(要排除第一种情况)、或者用括号括起来的标识符,那么var的类型是expression的引用。
// 4)如果上面的条件都不满足,则var的类型与expression的类型相同。
int func1(){
cout<<"调用了func"<<endl;
return 1;
};
int main()
{
cout<<"如果expression是一个没有用括号括起来的标识符,则var的类型与该标识符的类型相同,包括const等限定符。"<<endl;
short a = 5;
decltype(a) da;
short* b = &a;
decltype(b) db;
short& c = a;
decltype(c) dc = a;
cout<<dc<<endl;
cout<<"如果expression是一个函数调用,则var的类型与函数的返回值类型相同(函数不能返回void,但可以返回void *)。"<<endl;
int a1 = 1;
cout<<"不会调用函数"<<endl;
decltype(func1())dfunc = a1;
cout<<"传递函数名,并使用函数指针.."<<endl;
decltype(func1)* pfunc = func1;
pfunc();
cout<<"如果expression是一个左值(能取地址)(要排除第一种情况)、或者用括号括起来的标识符,那么var的类型是expression的引用。"<<endl;
decltype((a1)) a2=a1;
cout<<a1<<endl;
decltype((func1)) myFunc = func1;
myFunc();
return 0;
}
如果expression是一个没有用括号括起来的标识符,则var的类型与该标识符的类型相同,包括const等限定符。
如果expression是一个函数调用,则var的类型与函数的返回值类型相同(函数不能返回void,但可以返回void *)
没有调用函数
如果expression是一个左值(能取地址)(要排除第一种情况)、或者用括号括起来的标识符,那么var的类型是expression的引用。
decltype的结果要么和表达式的类型相同,要么就是表达式类型的引用
int func(int x,double y);
等同:
auto func(int x,double y) -> int;
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template <typename T1, typename T2>
auto func(T1 x, T2 y) -> decltype(x + y)
{
// 其它的代码。
decltype(x+y) tmp = x + y;
cout << "tmp=" << tmp << endl;
return tmp;
}
int main()
{
cout<<func(3, 5.8)<<endl;
return 0;
}
C++14标准对函数返回类型推导规则做了优化,函数的返回值可以用auto,不必尾随返回类型。
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template <typename T1, typename T2>
auto func(T1 x, T2 y)
{
// 其它的代码。
decltype(x+y) tmp = x + y;
cout << "tmp=" << tmp << endl;
return tmp;
}
int main()
{
cout<<func(3, 5.8)<<endl;
return 0;
}
类模板是通用类的描述,使用任意类型(泛型)来描述类的定义。
函数模板建议用typename描述通用数据类型,类模板建议用class
template <class T>
class 类模板名
{
类的定义;
};
1)在创建对象的时候,必须指明具体的数据类型。
2)使用类模板时,数据类型必须适应类模板中的代码。
3)类模板可以为通用数据类型指定缺省的数据类型(C++11标准的函数模板也可以)。
4)模板类的成员函数可以在类外实现。
5)可以用new创建模板类对象。
6)在程序中,模板类的成员函数使用了才会创建。
#include // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template <class T1, class T2=string>
class AA
{
public:
T1 m_a; // 通用类型用于成员变量。
T2 m_b; // 通用类型用于成员变量。
AA() { } // 默认构造函数是空的。
// 通用类型用于成员函数的参数。
AA(T1 a,T2 b):m_a(a),m_b(b) { }
// 通用类型用于成员函数的返回值。
T1 geta() // 获取成员m_a的值。
{
T1 a = 2; // 通用类型用于成员函数的代码中。
return m_a + a;
}
T2 getb(); // 获取成员m_b的值。
};
template <class T1, class T2>
T2 AA<T1,T2>::getb() // 获取成员m_b的值。
{
return m_b;
}
int main()
{
AA<int, string>* a = new AA<int, string>(3, "西施"); // 用模板类AA创建对象a。
cout << "a->geta()=" << a->geta() << endl;
cout << "a->getb()=" << a->getb() << endl;
delete a;
}