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 可访问
该网址。
b) 编码的 (Encoded) String Literal
9.关键字 noexcept
10.关键字 constexpr
11.崭新的 Template 特性
Variadic Template
Alias Template(带别名的模板,或者叫Template Typedef)
其他的 Template 新特性
13.关键字 decltype
14.新的函数声明语法
详细了解scoped enumeration单击
这里。
16.新的基础类型
c++11 定义了以下新式基本数据类型:
char16_t 和 char32_t
long long 和 unsigned long long
std::nullptr_t
8.新式的字符串字面常量 (String Literal)
自 c++11 起,你可以定义 raw string 和 multibyte/wide-character 等字符串字面常量。
a) Raw String Literal
a) Raw String Literal
Raw string 允许我们定义字符序列,做法是确切写下其内容使其成为一个 raw character sequence。于是你可以省下很多用来装饰特殊字符的escape符号。
Raw string 以 R"( 开头,以 )" 结尾,可以内含 line break (行中断符)。例如以下两种写法是相同的:
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;
- 范围枚举不能通过=来直接和数值类型相互转化, 这就增加了安全性。
16.新的基础类型
c++11 定义了以下新式基本数据类型:
char16_t 和 char32_t
long long 和 unsigned long long
std::nullptr_t