Modern C++ 学习笔记——易用性改进篇

往期精彩:

  • Modern C++ 学习笔记——易用性改进篇
  • Modern C++ 学习笔记 —— 右值、移动篇
  • Modern C++ 学习笔记 —— 智能指针篇
  • Modern C++ 学习笔记 —— lambda表达式篇
  • Modern C++ 学习笔记 —— C++面向对象编程
  • Modern C++ 学习笔记 —— C++函数式编程

Modern C++ 学习笔记——易用性改进篇

关键字:自动类型推导、初始化、字面量

文章目录

  • Modern C++ 学习笔记——易用性改进篇
    • 自动类型推导
      • auto
      • decltype
      • decltype(auto)
      • 类模板的模板参数推导
      • 结构化绑定
    • 初始化
      • 列表初始化
      • 类数据成员的默认初始化
      • 创建对象是注意区分()和{}
    • 成员函数说明符
      • default 和 delete 成员函数
      • override 和 final 说明符
    • 参考文档

自动类型推导

auto

自动类型推导,就是编译器能够更加表达式的类型,自动决定变量的类型,从C++14开始,还有函数的返回类型。但需要说明的是,auto并没有改变C++是静态语言这一事实。使用auto的变量类型仍然是编译时就确定了,只不过编译器能自动帮你填充而已。有了自动类型推导使得如下赘述称为历史

for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
    // 成为历史
    // 循环体
}
for (auto it = v.begin(); it != end(); ++it) {
    // 现在可以直接这么写,当然,是不使用基于范围的for循环的情况
    // 循环体
}

不使用自动类型推导时,若容器类型未知,还需要加上typename.

template <typename T>
void foo(const T& container) // 此处const引用还要求const_iterator作为迭代器的类型
{
   
    for (typename T::const_iterator it = container.begin(); it != container.end(); ++it) {
   
        // 循环体
    }
}

此外,如果begin返回的类型不是该类型的const_iterator嵌套类型的话,那实际上不用自动类型推断就没法表达了。举个例子,若我们的遍历函数还要求支持C数组的话,不使用自动类型推断就只能在上述代码增加一个对应的重载函数:

template <typename T, size_t N>
void foo(const T (&a)[N]) // 此处const引用还要求const_iterator作为迭代器的类型
{
   
    typedef const T* ptr;
    for (ptr it = container.begin(); it != a + N; ++it) {
   
        // 循环体
    }
}

如果使用自动类型推导,并且再加上C++11提供的begin和end函数,上面的代码就以统一了:

template <typename T>
void foo(const T& c)
{
   
    using std::begin;
    using std::end; // 使用依赖参数查找(ADL)见[1]
    for (auto it = begin(c); it != end(c); ++it) {
   
    // 循环体
    }
}

从这个例子来看,自动类型推导不仅降低了代码的啰嗦程度,也提高了代码的抽象性。
你以为auto带来只是有这些可就错了,它带来的好处远远不止如此:

  • 用auto声明的变量,其型别都推导自其初始化物,所以他们必须初始化:
int x1; // 存在潜在的未初始化风险
auto x2; // 编译错误!必须有初始化物
auto x3 = 0; // 没问题
  • auto可以避免一类称为“型别捷径”的问题,其中潜在存在性能问题,没错就是性能问题。不太相信?那就继续往下看:
std::unordered_map<std::string, int> m;
...
for (const std::pair<std::string, int> &p : m) {
   
    // 在p上实施某些操作
}

似乎,这块代码看起来合情合理。单其中暗藏隐患。原因是std::unordered_map的键值部分是const,所以哈希表中的std::pair(也就是std::unordered_map本身)的型别并不是std::pair(std::string, int),而是std::pair(const std::string, int).
可是在上面的循环中用以声明变量p的类型并不是这个。结果编译器为了将std::pair对象转换为std::pair对象,将m中的每个对象都做了一次复制操作,形成一个p想要绑定的临时对象。循环的每次迭代结束时,该临时对象都会被析构一次。

使用auto就可以轻松化解:

unordered_map<std::string, int> m

你可能感兴趣的:(学习笔记,c++,c++11,编程语言,经验分享,面试)