由来
c++11由来已久,VS2015完全支持C++11标准,对C++14的支持已经基本完成,并支持部分C++17标准。但是,工作中使用到的语法却还是以c++98/03为主,对新引入的语法知之胜少,之前也一直想做一些深入的了解,但一直苦于没有一个良好的切入点,自从阅读《深入应用c++11代码优化与工程级应用》后发现本书对于c++11的使用写得相当透彻且配合例子很好理解,但粗看一遍往往不能深入领会其中的精髓,遂写次笔记加强理解。
auto使用
auto的使用可以说是我最先使用过的c++11特性了,没有之一(主要是容器的遍历用于标识迭代器变量)。
推导规则
当时使用就如上所说的一个应用例子,也没有细究auto推导的规则。书中做了总结,摘录下:1.当不声明为指针或引用时,auto的推导结果和初始化表达式抛弃引用和cv限定符(const和volatile限定符的统称)后类型一致;2.当声明为指针或引用时,auto的推导结果将保持初始化表达式的cv属性。
auto的限制
1.auto不能用于函数参数: void func(auto a = 1) {} // error
2.auto不能用于非静态成员变量:
c++
struct Foo
{
auto var1_ = 0;// error
static const auto var2_ = 0; // OK
}
3.auto 无法定义数组,无法推导出模板参数:
c++
int main()
{
int arr[10] = {0};
auto aa = arr;
auto rr[10] = arr; // error auto 无法定义数组
Bar<int> bar;
Bar<auto> bb = bar; // error auto无法推导出模板参数
return 0;
}
auto应用
1.用于容器遍历时声明迭代器类型;
2.用于在模板中接收一个模板函数的返回值用于后续处理。
c++
template<clas A>
void func()
{
auto val = A::get();
//...
}
decltype使用
对于decltype和auto这两个在应用上看着有些类似,有些情况下可以互换,auto主要根据变量的初始化表达式推到出边个两应该具有的类型,而若想要通过某个表达式得到类型,但不希望新变量和这个表达式具有同样的值,此时auto就不适用了。此外,对于一般的标记符表达式,decltype将精确地推到出表达式定义本身的类型,不会像auto那样在某些情况下舍弃掉引用和cv限定符。
decltype推导规则
decltype推导规则大部分比较显而易见,但一些情况还需要特别注意。
以下列出推导规则:decltype(exp)
- exp是标识符、类访问表达式–>decltype(exp)和exp类型一致;
- exp是函数调用–>decltype(exp)和返回值一致(函数返回值为纯右值时,只有类类型可以携带cv限定符,此外则一般忽略cv限定);
- 其他情况,若exp是一个左值,则decltype(exp)是exp的左值引用,否则和exp类型一致。
针对以上几点进行举例:
针对2中忽略cv限定符的情况:
const int func_cint(void);
int main()
{
decltype(func_cint()) m = 0;
return 0;
}
针对3中描述的则需要比较注意,常常由于一个括号导致完全不同的推导:
struct Foo { int x; };
int main()
{
const Foo foo = Foo();
decltype(foo.x) a = 0;
decltype((foo.x)) b = a; // 特别注意和上面的比较
int n = 0, m = 0;
decltype(m + n) c = 0;
decltype(m += n) d = c;
return 0;
}
附上vs上运行时的显示,可以对照规则3进行理解:
decltype应用
书中有一步一步引出这种方式的步骤及和c++98/03的实现比较,这里直接列出方案:
template<class ContainerT>
class Foo {
decltype(ContainerT().begin()) it_;
public:
void func(ConstinerT& container)
{
it_ = constiner.begin();
}
//...
}
auto与decltype结合使用
这两者结合主要用于返回类型后置语法,具体与c++98/03的比较就不罗列了,两个里字就行理解使用:
template <typename T, typename U>
auto add(T t, U u) -> decltype(t + u)
{
return t + u;
}
int& foo(int& i);
float foo(float& f);
template <typename T>
auto add(T &val) -> decltype(foo(val))
{
return foo(val);
}
模板别名
c++11中新添加的using别名,绝大部分和typedef相同,这里就举个不同的例子:
template <typename val>
using str_map_t = std::map<std::string, val>;
//...
str_map_t<int> map1;
模板默认模板参数
c++11中函数模板也支持默认参数,这个在之前c++98/03是不支持的。
注意:如果想禁止参数自动推导,可以使用外敷模板,示例如下
template <typename T>
struct identity
{
typedef T type;
};
template<typename T = int>
void func(typename identity<T>::type val, T = 0) // 禁用了val的自动推导
{
//...
}
列表初始化
具体就不多说了,举几个例子就可以知道用法了:
int a = {1}; // 虽然使用了等号,但它仍是列表初始化,私有的拷贝构造并不会影响它。
int a1{1};
int *a = new int{123};
int *arr = new int[3]{1, 2, 3};
如果自定义支持任意长度初始化列表,则需要使用到std::initializer_list,例子如下:
class Foo
{
public:
Foo(std::initializer_list<int>) {
}
};
Foo fool{ 1, 2, 3, 4 };
基于范围的for循环
这个使用比较简单,但有几点需要注意:
1.for(auto n : arr) { //…} //注意这里的arr只会计算一次,即首次即算出循环的终止条件,如果在循环中修改容器长度则循环次数仍然不会改变。
2.for循环可以直接遍历数组:
int fibarray[] = {0, 1, 1, 2, 3, 5, 8, 13};
for (auto v : fibarray) {
std::cout << v << std::endl;
}