内容目录
第一章
类型推导
auto类型推导
auto关键字新意义
auto x = 5;
auto pi = new auto(1);
const auto *y = &x, u = 6;
static auto z = 0.0;
// 错误写法:const auto *y = &x, u; 这里的u类型不确定
// 错误写法: auto s;
在旧标准中,auto关键字用于表示具有自动存储期的局部变量,例如auto int i = 0;
,但实际上,默认写成int i = 0;
,因为非static变量默认都是具有自动存储期的局部变量。
auto推导规则
cv限定符:表示const和volatile。
推导规则:
1、当不声明为指针或引用时,auto的推导结果和初始化表达式抛弃引用和cv限定符后类型一致。
例如:
const auto a = 1; // a -> const int
auto b = a; // b -> int
2、当声明为指针或引用时,auto的推导结果和将保持初始化表达式的cv属性。
例如:
const auto a = 1; // a -> const int
auto& b = a; // b -> const int &
注意:auto是不能用于函数参数的。
auto的限制
void add(auto a) {} // auto: 不能用户函数参数
struct Food
{
auto price = 0; // auto不能用于非静态成员变量
};
Food<int> f;
Food<auto> d = f; // auto不能用于推导模板参数
auto用处
用于迭代器,因为类型声明太长了。
decltype关键字
获取表达式的类型
decltype(exp)
int x = 0;
decltype(x) y = 1; // y -> int
const int& i = x;
decltype(i) j = y; // j -> const int &
decltype可以像auto一样加上const限定符,且decltype可以进行精确推导,而不会舍弃掉cv限定符。
decltype的推导规则
- 推导规则1:exp是标识符、类访问表达式,decltype(exp)和exp的类型一致。
- 推导规则2:exp是函数调用,decltype(exp)和返回值的类型一致。
- 推导规则3:其他情况,若exp是一个左值,则decltype(exp)是exp类型的左值引用,否则和exp的类型一致。
第一种情况:decltype作为标识符和类访问表达式的示例
class F00
{
public:
static const int Number = 0;
int x;
};
int n = 0;
volatile const int & x = n;
decltype(n) a = n; // a -> int
decltype(x) b = n; // b -> const volatile int &
decltype(Foo::Number) c = 0; // c -> const int
Foo foo;
decltype(foo.x) d = 0; // d -> int
第二种情况:decltype作用于函数调用
int& func_int_r(void);
int&& func_int_rr(void);
int func_int(void); // 纯右值
const int& func_cint_r(void);
const int&& func_cint_rr(void);
const int func_cint(void); // 纯右值
const Foo func_cfoo(void);
// 下面是测试语句
int x = 0;
decltype(func_int_r()) a1 = x; // int &
decltype(func_int_rr()) b1 = 0; // int &&
decltype(func_int()) c1 = 0; // int
decltype(func_cint_r()) a2 = x; // const int&
decltype(func_cint_rr()) b2 = 0; // const int&&
decltype(func_cint()) c2 = 0; // int
decltype(func_cfoo()) ff = Foo();
c2是int而不是const int, 这是因为函数返回的int是一个纯右值,对于纯右值,则只有类类型可以携带cv限定符。
第三种情况:带括号的表达式和加法运算表达式:
struct Foo { int x; };
const Foo foo = Foo();
decltype(foo.x) a = 0; // int
decltype((foo.x)) b = a; // const int&
int n = 0, m = 0;
decltype(n + m) c = 0; // c -> int
decltype(n += m) d = c; // d -> int&
decltype的实际应用
// 泛型类型定义可能存在问题的示例
template<class ContainerT>
class Foo
{
typename ContainerT::iterator it_;
public:
void func(ContainerT& container)
{
it_ = container.begin();
}
};
int main()
{
typedef const std::vector<int> container_t;
container_t arr;
Foo<container_t> foo;
foo.func(arr);
return 0;
}
这会报一堆错误。因为ContainerT::iterator不能包括所有的迭代器类型,当ContainerT是一个const类型时,应当使用const_iterator
// C++98/03 下使用模板特化解决:
template<class ContainerT>
class Foo<const Container>
{
typename ContainerT::const_iterator it_;
public:
void func(const ContainerT& container)
{
it_ = container.begin();
}
};
使用了decltype以后,就可以直接这样写:
template<class ContainerT>
class Foo
{
decltype(ContainerT().begin()) it_;
public:
void func(ContainerT& container)
{
it_ = container.begin();
}
};
返回类型后置语法
对于该例子:
template<typename T, typename U>
decltype(T() + U()) add(T t, U u)
{
return t + u;
}
//考虑到T、U可能是没有无参构造函数的类
template<typename T, typename U>
decltype((*(T*)0) + (*(U*)0)) add(T t, U u)
{
return t + u;
}
// 返回类型后置语法
template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u)
{
return t + u;
}
模板的细节改进
模板的右尖括号
// C++ 98/03不支持连续两个右尖括号的示例
//例如:
Foo<vector<int>> sb; // 编译出错
Foo<vector<int> > sb; // 正确写法加个括号
模板的别名
// 想象下面这个场景:
typedef std::map<std::string , int> map_int_t;
// ...
typedef std::map<std::string , std::string> map_int_t;
// 我们想定义一个以string为key,以其他类型为value的映射
// 在 C++ 98/03中不得不这样做:
template<typename Val>
struct str_map
{
typedef std::map<std::string, Val> type;
};
str_map<int>::type map1;
// C11标准中的定义方式
template<typename Val>
using str_map_t = std::map<std:;string, Val>;
// 定义函数指针
typedef void (*func_t)(int, int); // C98
using func_t = void (*)(int, int); // c11
函数模板的默认模板参数
不想写了
列表初始化
初始化列表
class Foo
{
public:
Foo(std::initializer_list<int>) {}
};
Foo foo = {1, 2, 3, 4, 5};
// 通过std::initializer_list 给自定义容器赋值示例
class FooVector
{
std::vector<int> content_;
public:
FooVector(std::initializer_list<int> list)
{
for(auto it = list.begin(); it != list.end(); ++it)
{
content_.push_back(*it);
}
}
};