3.1.C++11语言新特性

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()可因lvaluervalue被调用。

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()。

猜你喜欢

转载自blog.csdn.net/qq_17810255/article/details/84142073