STL学习笔记(二)——C++11新特性
2017-12-29 18:07
344 查看
C++11新特性
1.Template 表达式内的空格
//“在两个 template 闭符之间放一个空格”的要求已经过时了: std::vector<std::list<int> >; // OK in each C++ version std::vector<std::list<int>>; // OK since C++11
2.nullptr 和 std::nullptr_t
//nullptr 是个新关键字。 //它被自动转换为各种 pointer 类型,但不会被转换为任何整数类型, //其类型为std::nullptr_t,定义于 <cstddef>. void f(int); void f(void *); f(0); // 调用 f(int). f(NULL); // 如果定义NULL为0,则调用 f(int),否则调用 f(void *). f(nullptr); // 调用 f(void *).
3.以 auto 完成类型自动推导
// C++11 允许你声明一个变量或对象而不需要指明其类型. // auto 声明的变量,其类型会根据其初始值被自动推导出来, // 因此一定需要一个初始化操作. auto w; // 未指定初始值,错误! auto i = 100; // i 为int类型. double f() { return 3.1415926; } auto d = f(); // d 为double类型. static auto VAT = 0.19; // VAT 为static double类型.
4.一致性初始化 与 初值列
// 一致性初始化:面对任何初始化动作,均可使用大括号. int iNum[] { 1, 2, 3, 4, 5 }; std::vector<int> vecNum { 1, 2, 3, 4, 5 }; std::vector<std::string> vecStr { "aaa", "bbb", "ccc"}; std::complex<double> comNum { 4.0, 3.0}; //复数 // 初值列:强迫初始化为 0 (或nullptr). int i; // i 初始化为未定义值. int j{}; // j 初始化为 0 . int * p; // p 初始化为未定义值. int * q{}; // q 初始化为 0 . // 窄化(精度降低或造成数值变动)对大括号而言是不成立的. int x0(3.4); // ok. int x1 = 3.4; // ok. int x2 { 3.4 }; // wrong. int x3 = { 3.4 }; // wrong. std::vector<int> v1 { 1, 2, 3 }; // ok. std::vector<int> v2 { 1.1, 2.2, 3.3 }; // wrong. // 用户自定义类型之初值列(扩展:可用于定义可变参数函数). void print(std::initializer_list<int> vals) { // 处理一系列值. for(auto p = vals.begin(); p != vals.end(); ++p) { std::cout << *p << std::endl; } } print({11,22,33,44,55,66});
5.关键字 explicit
C++中的explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显示的, 而非隐式的, 跟它相对应的另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为implicit(隐式).// 没有使用explicit关键字的类声明, 即隐式声明. class CxString { public: CxString(int length) { // TODO: } }; // 调用. CxString str1(100); // ok. CxString str2 = 100; // ok. // 使用explicit关键字的类声明, 即显式声明. class CxString { public: explicit CxString(int length) { // TODO: } }; // 调用. CxString str1(100); // ok. CxString str2 = 100; // wrong. // 使用explicit关键字且带默认参数的类声明. class CxString { public: explicit CxString(int length, const char * p = "") { // TODO: } }; // 调用. CxString str1(100); // ok. CxString str2 = 100; // wrong.
6.Range-Based for 循环
C++11引入了一种崭新的 for 循环,可以逐一迭代某个给定的区间、数组、集合内的每一个元素(即 C# 中的 foreach 循环)。// 1.成员函数有 begin() 和 end() 的类. std::vector<double> vecDbl {0.12, 1.13, 4.567}; for(auto& elem : vecDbl) { elem *= 10.0; } std::initializer_list<double> initDbl {0.12, 1.13, 4.567}; for(const auto& elem : initDbl) { std::cout << elem << std::endl; } // 2.同样适用于普通数组. int array[] = { 0, 1, 2, 3, 4, 5 }; for(auto& elem : array) { elem++; } // 3.当元素在 for 循环中被初始化为 decl,不得有任何显式类型转换. class Test { public: explicit Test(const std::string& s); }; std::vector<std::string> vs; for(const Test& elem : vs) // Error! { std::cout << elem << std::endl; }
7.Move 语义 和 Rvalue Reference
在 c++11 标准之前,对于非必要拷贝和临时对象只能再copy一份副本,如以下代码所示:X x; coll.insert(x); // insert中建立x的一份拷贝. coll.insert(x+x); // insert中建立(x+x)临时变量的一份拷贝. coll.insert(x); // insert中建立x的一份拷贝(尽管x变量不再被使用).然而,对于后两次x的插入操作,实参值(x和x+x)在本次函数调用后不再被使用,因此我们完全可以把这两类实参值的内存数据直接分配(move)给coll集合,并且把原实参值(x和x+x)清空(这样做的目的是考虑到x对象的析构函数可能对move的内存块执行一些操作)。
自 c++11 起,上述行为成为可能,但是需要程序员显式地指明不再被使用的实参,具体代码实现如下所示:
X x; coll.insert(x); // insert中建立x的一份拷贝. coll.insert(x+x); // 将(x+x)临时变量的内存搬迁到 coll 集合中. coll.insert(std::move(x)); // 将实参 x 的内存搬迁到 coll 集合中.【注】当且仅当X为有特定实现的复杂类型时才支持搬迁语义(如C++11标准实现的STL模板类string、complex等),int、double等基本类型不支持。
详细了解 Move 语义和 Rvalue Reference 可访问该网址。
8.新式的字符串字面常量 (String Literal)
自 c++11 起,你可以定义 raw string 和 multibyte/wide-character 等字符串字面常量。a) Raw String Literal
Raw string 允许我们定义字符序列,做法是确切写下其内容使其成为一个 raw character sequence。于是你可以省下很多用来装饰特殊字符的escape符号。Raw string 以 R"( 开头,以 )" 结尾,可以内含 line break (行中断符)。例如以下两种写法是相同的:
// 均表示字符串:\\n. const char * pStr1 = "\\\\n"; const char * pStr2 = R"(\\n)";Raw string 的完整语法是 R"delim(...)delim",其中 delim 是个字符序列,最多 16 个基本字符,不可含反斜线、空格和小括号。举个例子,以下两种形式是等同的:
// 均表示字符串:a\[\n] b\nc()"[\n]123456. const char * pStr1 = "a\\\n b\\nc()\"\n123456"; const char * pStr2 = R"nc(a\ b\nc()" 123456)nc"; // 注:()两端的nc表示raw string以"nc(和"nc)为起止符,遇到"(和)"不中断.注:定义正则表达式(regular expression)时 raw string literal 特别有用。
b) 编码的 (Encoded) String Literal
只要使用编码前缀,你就可以为 string literal 定义一个特殊的字符编码。下面这些编码前缀都预先定义好了:u8 定义一个 UTF-8 编码。UTF-8 string literal 以 UTF-8 编订的某个给定字符起头,字符类型为 const char。
u 定义一个 string literal,带着类型为 char16_t 的字符。
U 定义一个 string literal,带着类型为 char32_t 的字符。
L 定义一个 wide string literal,带着类型为 wchar_t 的字符。
代码示例:
wchar_t * pStr = L"Hello world!"; // 将 Hello world! 定义为 wchar_t string literal类型的字符串.
9.关键字 noexcept
关键字 noexcept 用来指明某个函数无法(或不打算)抛出异常。例如:void foo() noexcept; // 如果foo()抛出异常,程序会被终止,然后std::terminate()被调用并默认调用std::abort().
10.关键字 constexpr
关键字 constexpr 可用来让表达式核定于编译期。例如:constexpr int square(int x) { return x * x; } float a[square(10)]; // 由于square()函数在编译期即被运行,因此此处可成功分配100个单元的float型数组.a) constexpr函数要求所定义的函数足够简单以使得编译时就可以计算其结果:
constexpr int MAX(int a, int b) { return a > b ? a : b; }b) constexpr还能修饰类的构造函数,即保证传递给该构造函数的所有参数都是constexpr,那么产生的对象的所有成员都是constexpr,该对象也是constexpr对象了,可用于只使用constexpr的场合。
class Test { public: constexpr Test(int arg1, int arg2) : v1(arg1), v2(arg2) {} private: int v1; int v2; } constexpr Test A(1,2) enum e = {x = A.v1, y = A.v2};注:constexpr构造函数的函数体必须为空,所有成员变量的初始化都放到初始化列表中。
c) 使用 constexpr 的好处:是一种很强的约束,更好的保证程序的正确定语义不被破坏;
编译器可以对constexper代码进行非常大的优化,例如:将用到的constexpr表达式直接替换成结果;
相比宏来说没有额外的开销。
11.崭新的 Template 特性
Variadic Template
自 c++11 起, template 可拥有“得以接受个数不定之template实参”的参数。此能力称为 variadic template(即可变参数函数)。例如:#include <iostream> #include <string> using namespace std; void print(){} //// 注掉void print(){},保留以下函数编译器也不会报错. //template <typename T> //void print(const T& arg) //{ // std::cout << 0 << "\t" << arg << std::endl; //} template <typename T, typename ... Types> void print(const T& firstArg, const Types&... args) { // 在variadic template内,sizeof...(args)会生成实参个数. // std::tuple<> 大量使用了这一特性. std::cout << sizeof...(args) << "\t" << firstArg << std::endl; // 递归调用print函数. print(args...); } int main() { print("hello world", 123, 123.21, 789.6f, 'W', std::string("std::string")); return 0; }输出结果:
5 hello world 4 123 3 123.21 2 789.6 1 W 0 std::string
Alias Template(带别名的模板,或者叫Template Typedef)
自 c++11 起,支持template (partial) type definition。然而由于关键字typename用于此处时总是出于某种原因而失败,所以引入关键字using,并因此引入了一个新术语alias template。例如:template <typename T> using Vec = std::vector<T, MyAlloc<T>>; // 标准容器使用自己的allocator. Vec<int> coll; // 等价于: std::vector<int, MyAlloc<int>> coll;
其他的 Template 新特性
自 c++11 起,函数模板可拥有默认的模板实参。此外,自定义类型可被当作模板实参。12.Lambda
详细了解lambda单击这里和这里。13.关键字 decltype
新关键字 decltype 可让编译器找出表达式类型(即升级版的typeof)。例如:std::map<std::string, float> coll; decltype(coll) coll_copy; // coll_copy的类型与coll一致. decltype(coll)::value_type map_pair; // map_pair的类型为coll的子集类型(pair).decltype的应用之一是声明返回类型,另一用途是在metaprogramming(超编程)或用来传递一个lambda类型。
14.新的函数声明语法
有时候,函数的返回值类型取决于某个表达式对实参的处理,例如:// 合乎逻辑,但不合乎c++11语法. template <typename T1, typename T2> decltype(x + y) Add1(T1 x, T2 y) { return x + y; } // 合乎逻辑,且合乎c++11语法. template <typename T1, typename T2> auto Add2(T1 x, T2 y) -> decltype(x + y) { return x + y; }
15.带领域的(Scoped)Enumeration
枚举类型在C++里用的最多就是声明某种数值类型,之后用switch来分别处理这些类型;C++11 定义了一种 enumeration(枚举类型),它区别于C的 enumerators(枚举器),这种类型可以定义枚举常量的类型为数值类型,比如char,不能定义非数值类型的枚举常量有点遗憾. 还可以给枚举常量增加使用范围, 也就是scoped enumerations;
范围枚举不能通过=来直接和数值类型相互转化, 这就增加了安全性。
详细了解scoped enumeration单击这里。
16.新的基础类型
c++11 定义了以下新式基本数据类型:char16_t 和 char32_t
long long 和 unsigned long long
std::nullptr_t
相关文章推荐
- C++11新特性学习笔记(二)
- C++11新特性学习笔记
- STL学习笔记----C++的新语言特性
- C++11新特性学习笔记
- c++学习笔记(部分语法及c++11新特性)
- C++11新特性学习笔记
- C++11新特性学习笔记—使用花括号就地初始化
- C++11新特性学习笔记—委托构造函数的使用
- C++11新特性学习笔记—noexcept关键字
- C++11新特性学习笔记
- C++11新特性学习笔记
- C++ 学习笔记:C++11 新特性学习
- C++11 新特性 学习笔记(1)
- C++11新特性学习笔记—变长参数的宏定义以及__VA_ARGS__
- C++11新特性学习笔记(一)
- C++11新特性学习笔记
- C++11新特性学习笔记
- C++11新特性学习笔记
- C++11新特性学习笔记之移动构造函数
- C++11新特性学习笔记—final和override关键字