3.1.1微小但重要的语法提升
nullptr和std::nullptr_t :
nullptr取代0或NULL,表示一个pointer(指针)只想指向的no value。std::nullptr_t定义在<cstddef>中,为基础类型。
3.1.2以auto完成类型自动推导
以auto声明的变量,会自动根据其初始值自动推导出类型:(必须初始化)
如:auto i=42;
double f();
auto d=f();
可加额外限定符static:如:static auto vat=0.19;
3.1.3一致性初始化(Uniform Initialization)与初始列(Initializer List)
一致性初始化:面对任何初始化动作,都可以使用大括号。
如:int values[] {1,2,3}
std::vector<int>v {2,3,4,6,9};
初始列:强制value initialization,在local变量为某基础类型时,若没有明确的初始值,则会被初始化为0.
如:int j{};
注意:窄化(narrowing)精度降低或者造成数值变动,对大括号不成立。
如:int x1(5.3);对
Int x2=5.3;对
Int x3{5,0};不对, narrowing
Int x4={5.3}:不对,narrowing
因此,为了支持“用户自定义类型之初值列”(initializer lista for user_defined types)概念,C++11提供了class template std::initializer_list<>,支持以一系列值进行初始化,或在“想要处理一系列值”的任何位置初始化。
如:
Void print (std::initializer_list<int>vals)
{
For(auto p=vals.begin();p!=vals.end();++p){ //process a list of values
std::cout<<*p<<”\n”;
}
}
print({12,3,5,6,3,767,54});//pass a list of values to print()
当“指明实参个数”和“指明一个初值列”的构造函数(ctor)同时存在,带有初值列的那个版本胜出。
如:class P
{
Public:
P(int ,int);
P(std::initializer_list<int>);
};
P p(77,5); //calls P::P(int,iint)
P q{77,5}; //calls P::P(initializer_list)
P r{77,5,42}; //calls P::P(initializer_list)
P s={77,5}; //calls P::P(initializer_list)
如果上述“带有一个初始值列”的构造函数不存在,那么结婚搜两个int的那个构造函数会被调用以初始化q和s,而r的初始化将无效。
explicit构造函数如果接受的是个初始列,会失去“初值列带有0个、1个或多个初值”的隐式转换能力。
3.1.4 Range-Based for 循环
C++11引入的一种崭新的for循环形式,可以逐一迭代某个给定的区间、数组、集合(range.array,or collection)内的每一个元素。其他语言可为foreach循环。
其一般性语法如下:
for(decl : coll){
statement
}
打印某集合内所有元素“的泛型函数如下:
template <typename T>
void printElements(const T& coll)
{
for(const auto& elem : coll){
std::cout<<elem<<std::endl;
}
}
注意:当元素在for循环中被初始化为decl,不得有任何显示类型转换(explicit type consversion)
3.1.5 Move语义和Rvalue Reference
C++11的一个重要特性:支持move semantic(搬迁语义),用以避免非必要拷贝(copy)和临时对象(temporary)。
Rvalue和Lvalue Reference的重载规则(overloading rule)如下:
1、实现void foo(X&);不实现void foo(X&&);其行为同C++98:foo()可因lvalue但不因rvalue被调用。
2、实现void foo(const X&);不·实现void foo(X&&);其行为同C++98:foo()可因lvalue或rvalue被调用。
3、实现void foo(X&);void foo(X&&);或void foo(const X&);void foo(X&&);则可以区分“为rvalue服务”和“为lvalue服务”。“为rvalue服务”的版本被允许且应该提供move语义。
4、实现void foo(X&&);不实现void foo(X&),也不实现void foo(const X&),这时,foo()可因rvalue被调用(只提供move语义),其在unique pointer、file stream或string stream使用。
总之,若classs未提供move语义,只提供惯常的copy构造函数和copy assignment操作符,rvalue reference 可调用它们。即,std::move()意味着“调用move语义(若提供),否则调用copy语义。
返回Rvalue Reference
不需要也不该move()返回值,对于下面代码:
X foo()
{
X x;
…
return x;
}
需保证:若X有可用的copy或move构造函数,则编译器可略去其中的copy版本;否则,若X有copy构造函数,X会被moved(搬移);若X有copy构造函数,X会被copied(复制);否则,报编译器错误(compile-time error).
3.1.6 新式的字符串字面常量(String Literal)
Raw String Literal
Raw string允许定义字符序列(character sequence),做法是确切写下其内容使其成为一个raw character sequence。
Raw string以R“(开头、以)”结尾,可内含line break.
如:“\\\\n”等价于R”(\\n)”,表示:两个反斜线和一个n。
若在raw string内写出,可使用定义符(delimiter),故raw string的完整语法为R”delim(..)delim”,delim是个字符序列(character sequence),最多16个基本字符,不能有反斜线(backslash)、空格(whitespace)和小括号(parenthesis)。
如:R”nc(a\
b\nc()”
)nc”;
等价于寻常的string literal : “a\\\n b\\nc()\”\n
含义:string内含一个a、一个反斜线、一个新行字符(newline)、若干空格(space),一个b、一个反斜线、一个n、一个c、一对小括号、一个双引号、一个新行字符,以及若干空格。
Raw string literal 可用于定义正则表达式(regular expression)。
编码的(Encoded)String Literal
使用编码前缀i(encoding prefix),可为string literal定义一个特殊的字符编码(character encoding)
如:1.u8定义UTF-8编码,
2.u 定义一个literal,字符为char16_t类型
3.U定义一个literal,字符为char32t类型
4.L定义一个wide string literal,字符为wchar_t的字符
3.1.7 关键字noexcept
C++11提供了关键字noexcept,用来指明某个函数无法或者不打算抛出异常。如:void foo () noexcept;
声明了foo()不打算抛出异常。若有异常未在foo()内被处理,程序会终止。
两种有用的抛出异常:操作可能抛出异常(任何异常),操作绝不会抛出任何异常。
指定在某种情况下函数不抛出异常:如,对于任意类型Type,全局性的swap()通常定义如下:
void swap(Type& x,Type& y)noexcept(noexcept(x.swap(y)))
{
x.swap(y);
}在noexcept(…)中可指定一个Boolean条件,若不符合就不抛出异常。
3.1.8 关键字constexpr
自C++11起,constexpr可用来让表达式核定于编译器。例如:
constexpr int square (int x)
{
return x*x;
}
float a[square(9)];//OK since C++11:a has 81 elements
关键字修复了一个在C++988使用数值权限时出现的问题,如:std::numeric_limits<short>::max()在功能上等同于宏INT_MAX,无法用作整数常量,但在C++11中,这式子被声明为constexpr.
3.1.9 崭新的Template特性
自C++11起,template可拥有“得以接受个数不定之template实参”得参数,此能力称为variadic template。例如:
void print ()
{
}
template<typename T,typename… Types>
void print (const T& firstArg,const Types&… args)
{
std::cout<<firstArg<<std:;endl; //print first argument
print(args…); //call print()for remaining arguments
}
若传入1个或者多个实参,上述的function template 就会被调用,通过递归调用,依此传入每一个实参,并打印。只有提供一个nontemplate重载函数print(),才能结束整个递归动作。
Alias Template(带别名的模板,或者叫Template Typedf)
自C++11起,支持template(partial)type definition>’然而由于关键字typename用于此处时总是出于某种原因而失败,因而要引入关键字using,并引入一个新术语alias template。如:
template <typename T>
using Vec=std::vector<T,MyAlloc<T>>; //standard vector using own allocator
后,Vec<int>coll;就等价于std::vector<int,MyAlloc<int>>coll;
其他的新特性Template新特性
自C++11起,function template可拥有默认的template实参。此外,LOCAL type可被当作template实参。
3.1.10 Lambda
C++11引入lanbda,允许inline函数的定义式被用作一个参数,或是一个local对象。
Lambda的语法
Lambda是一发呢功能定义式,课被定义于语句(statement)或表达式(expression)内部。故可拿Lambda当作inline函数使用。
最小型的lambda函数没有参数吗,什么也不做,如:
[]{
std::cout<<”hello lambda”<<std::endl;
}
可直接调用:
[]{
std::cout<<”hello lambda”<<std::endl;
}(); //prints”hello lambda”
或是把他传给对象,是其能被调用:
auto L=[]{
std::cout<<”hello lambda”<<std::endl;
};
…
L(); //prints”hello lambda”
Lambda由一个lambda introducer引入:是一组方括号,可在内指明一个capture,用来在lambda内访问“nonstatic外部对象”。
Lambda可以拥有参数,指明于小括号内,就像任何函数一样:
auto L=[](const std::string& s){
std::cout<<s<<std<<endl;
};
L(“hello lambda”); //prints “hello lambda”
但是lambda不能是template,必须指明所有类型。lambda也可以返回某物,无需指明返回类型,会被自栋推导出来。
Capture(用来访问外部作用域)
在lambda introducer(每个lambda最开始的方括号)内,指明一个capture处理外部作用域内未被传递为实参的数据:
1.[=]表示外部作用域以by value方式传递给lambda,故次lambda被定义时,只能读取可读数据,不能改动。
2.[&]表示外部作用域以by reference 方式传递给lambda,故lambda被定义时,可对数据涂写,前提是拥有权限。
声明lambda为mutable,则可以获得passing by value和passing by reference混合体。
如:
int id=0;
auto f=[id]()mutable{
std::cout<<”id:”<<id<<std:;endl;
++id; //OK
};
id=42;
f();
f();
f();
std::cout<<id<<std::endl;
会产生以下输出:
id:0
id:1
id:2
42
上述lambda的行为同以下function object:
class{
private:
int id; //coopy of outside id
public:
void operator()(){
std::cout<<”id”<<id<<std::endl;
++id; //OK
}
};
Lambda的类型
Lambda的类型,是不具名function object(或称function)。可使用template或auto给该类型声明对象,可使用decltype()写下该类型。也可使用C++标准库提供的std::function<>class template,指明一个一般化类型给function programming使用,class template提供了“明确指出函数的返回类型为lambda”的唯一办法:
//lang/lambda1.cpp
#include<functional>
#include<iostream>
std:;function<int(int,int)>returnLambda()
{
return [](int x,int y){
return x*y;
};
}
int main()
{
auto lf=returnLambda();
std::cout<<lf(6,7)<<std::endl;
}
输出:42
3.1.11 关键字decltype
新关键字decltype可让编译器找出表达式(express)类型,这是typeof得特性体现。C++11引入这个关键字是为了弥补原有的typeof缺乏的一致性和不完全。如:
std::map<std::string,float>coll;
decltype(coll)::value_type elem;
decltype的应用之一是应用声明返回类型,另一用途是在metaprogramming(超编程)或用来传递一个lambda类型。
3.1.12 新的函数声明语法(New Function Declaration Syntax)
在C++11中,可以将一个函数的返回类型转而声明于参数列之后:
template<typename T1,typename T2>
auto add(T1 x,T2 y)->decltype(x+y);
此语法同“为lambda声明返回类型”是一样的。
3.1.13 带领域的(Scoped)Enumeration
C++11允许定义scope enumeration—又称为strong enumeration 或enumeration class。如:
enum class Salutation:char{mr,ms,co,none};
重点在于,在enum之后指明关键字class.
优点如下:
1.不隐式转换至/自int;
2.若数值(如mr)不在enumeration被声明的作用域内,则必须改为Salutation::mr;
3.可明显定义地层类型(underlying type,本例是char),获得保证大小(若略去这里的“:char”,默认类型是int);
4.可提前声明(forward declaration)enumeration type,消除“为了新的enmerations value而重新变异“的必要—如果只有类型被使用的话。
注意:type trait std::underlying_type,可核定(evaluate)一个enumeration type的低层类型。标准异常的差错值(error condition value)也是个scoped enumerator.
3.1.12 新的基础类型(New Fundamental Data Type)
以下新的基本数据类型,定义于C++:
1.char16_t和char32_t
2.long long 和unsigned long long
3.std::nullptr_t
3.2 虽旧尤新的语言特性
Nontype Template Parameter(非类型模板参数)
对于标准的class bitset<>,可传递bit个数作为template实参,如下:
bitset<32>flags32; //bitset with 32 bits
Default Template Parameter(模板参数默认值)
Class template可拥有默认实参。如下声明式允许我们在声明class MyClass对象时指定1或2个template实参:
template<typename T,typename container=vector<T>>
class MyClass;
若只传入一个实参,第二个实参会采用默认值:
MyClass<int>x1; //equivalent to:MyClass<int,vector<int>>
注意,默认的template实参可以其前一个实参为依据为定义。
关键字typename
关键字typename用来指明紧跟其后的是个类型。如,
template<typename T>
class MyClass{
typename T::SubType*ptr;
…
};
这里的typename用来阐明“SubTypehi是个类型,定义于class T内”。因此ptr是个pointer,指向类型T::SubType。若没有typename,SubType会被视为一个static。
基于“SubType必须是个类型”这样的限定,任何被用来替换T的类型,都必须提供一个内层类型SubType.内层类型可以是个抽象数据类型(abstract data ttype)。template内的任何标识符都被视为一个value,除非为它加上typename,在template声明式中typename也可被用来取代class:template<typenmae T>class MyClass;
Member Template(成员模板)
Class的成员函数可以是template,但member template不可以是virtual。如:
class MyClass{
…
template<typename T>
void f(T);
};
上述的MyClass::f声明了“一组”成员函数,其参数可以是任意类型。可传递任何实参给它们。只要该实参的类型提供“f()用到的所有操作”.
Member template的一个特殊形式是所谓template构造函数,通常被提供用于“对象被复制时给与隐式转换”的能力。template构造函数并不压制copy构造函数的隐私声明。若类型完全吻合,则隐式的copy构造函数会被生成出来。
Nested(嵌套式)Class Template
嵌套式(Nested class)也可以是template;
template<typename T>
class MyClass{
..
template<typename T2>
class NestedClass;
…
};
3.2.1 基础类型的明确初始化(Explicit Initialization for Fun)
使用“一个明确的构造函数调用,但不给实参“的语法,基础类型会被设定初值为0。如果一个template强迫设置为0,其值就是所谓的zero initialized,否则就是default initialized。
3.2.2 main()定义式
main()只有两种定义式具备可移植性:
int main()
{
…
}
和int main(int argc,char* argv[])
{
…
}
其中的argv,也就是命令行实参(command-line argument)所形成的array,也可定义为char**。返回类型必须是int 。若不想以“main()返回“方式结束C++程序,通常应该调用exit()、quick_exit()(C++11之后)或terminate()。