C++11 新标准(一)

来源:互联网 发布:c语言模块化程序设计 编辑:程序博客网 时间:2024/05/27 19:25

1.外部模板

在标准C++中,只要在编译单元内遇到完整定义的模板,编译器就必须将其实例化(instantiate),特别是模板在许多编译单元内使用相同的参数实例化,这会大大增加编译时间
C++引入外部模板的用法,如:
模板在前面出现过定义:
template class std::vector<Myclass>
在之后重新出现相同的实例化是,使extern关键字,编译器将不再该实例单元实例化
extern template class std::vector<Myclass>

template <typename T>  void fun(T t){  }    //use1.cpp  void test1(){      fun<int>(1);  }    //use2.cpp    extern template void fun<int>(int);  void test2(){      fun<int>(1);  }  

2.初始化列表(initializer list)

考虑下面的代码:
vector<double> v = { 1, 2, 3.456, 99.99 };list<pair<string,string>> languages = {    {"Nygaard","Simula"}, {"Richards","BCPL"}, {"Ritchie","C"}};map<vector<string>,vector<int>> years = {    { {"Maurice","Vincent", "Wilkes"},              {1913, 1945, 1951, 1967, 2000} },    { {"Martin", "Ritchards"},              {1982, 2003, 2007} },    { {"David", "John", "Wheeler"},              {1927, 1947, 1951, 2004} }};

现在,初始化不再仅限于数组,可以接受一个{}列表对变量进行初始化实际上是通过一个可接受参数为std::initializer_list的函数实现的
例如:
#include <iostream>using namespace std;void func(initializer_list<int> List){for(auto i :List){cout << i << ' ';}cout << endl;}int main(){func({1,2,3,4});return 0; }/*输出结果为:1 2 3 4 */
initializer_list这个列表一经就不可更改(只是这个列表,而不是用这个列表生成的容器之类的东西)
#include <iostream>using namespace std;void func(initializer_list<int> List){for(auto &i :List){i = 1;}cout << endl;}int main(){func({1,2,3,4});return 0; }//[Error] assignment of read-only reference 'i'
初始化列表可以是任意长度,但必须是同质的(所有的元素必须属于某一模板类型T, 或可转化至T类型的)
标准库容器,string类型及正则表达式均具有初始化列表构造函数,以及(初始化列表)赋值函数等。初始化列表亦可作为一种“序列”以供“序列化for语句”使用

3.统一初始化

C++11的解决方法是对于所有的初始化,均可使用“{}-初始化变量列表”,如类,结构体,容器等
X x1 = X{1,2};X x2 = {1,2};     // 此处的'='可有可无X x3{1,2};X* p = new X{1,2};
与以往相比最为关键的变动是,X{a}方式的初始化,在所有的语境中都能构造出同样的结果,所以凡是能用“{}”的初始化,得到的结果都是一致的
X x{a};X* p = new X{a};z = X{a};         // 使用了类型转换f({a});           // a作为函数的X型实参return {a};       // a作为函数的X型返回值

4.类型推导auto/decltype与返回值语法

(1)有被明确初始化的变量可以使用auto关键字,这会根据初始化子的具体类型产生变量,如:
auto Variable = 5;
template<class T> void printall(const vector<T>& v){      // 根据v.begin()的返回值类型自动推断p的数据类型      for (auto p = v.begin(); p!=v.end(); ++p)          cout << *p << “n”;}
(2)decltype用于在编译期决定一个表达式的类型:
int someInt;
decltype(someInt) otherVariable = 5;
decltype与auto一起使用会更有用
(3)在谈到decltype与auto共同使用之前先介绍一下返回值语法:
在C++11中,你可以把返回类型放在函数声明的后面,用auto代替前面的返回类型,像这样:
auto multiply(int x,int y)->int{return x*y;}
但是为什么我要这样用?让我们看一个证明这个语法好处的例子。一个包含枚举的类:
class Person{enum Person_type{CHILD,ADULT,SENIOR};Person_type get_person_type();};
对于函数get_person_type(),由于编译器不知道Person_type是什么,因为Person_type在Person类外使用,因此你必须这么写:
Person::Person_type Person::get_person_type(){return person_type;}
这可能不算大问题,不过会容易出错,尤其是牵连进模板的时候。
不过我们可以这么设计:
auto Person::get_person_type()->Person_type{return person_type;}
而且,更强的是和decltype一起使用
template <typename Bulider>auto makeProcess(const Bulider& bulider)->decltype(bulider->makeObject){auto val = bulider->makeObject()return val;}
(5)auto:引用、指针和常量
在C++11中,auto处理引用时默认是值类型,因此,下面的代码中,即使返回值是引用,auto仍然将其作为值处理
#include <iostream>using namespace std;int& get(int& a){return a;}int main(){int a = 12;auto b = get(a); //auto仍然将其作为值处理b = 13;cout << a << ' ' << b << endl;return 0;}/*输出结果为:12 13 */
不过你可以指定&作为修饰符强制它作为引用:auto& b = get(a);
不过对于指针,auto会自动转化为指针,如:
#include <iostream>using namespace std;int main(){int a = 12;auto c = &a; cout << c << ' ' << *c << endl;}/*一个可能的结果为:0x28fe88 12 */

5.以范围为基础的for循环

for ( range_declaration : range_expression ) loop_statement
range_declaration -一个具名变量的声明,其类型是由 range_expression 所表示的序列的元素的类型,或到该类型的引用。通常由于自动类型推导而用 auto 指定符
range_expression -任何表示一个适合序列(数组或定义了 begin 和 end 成员函数或自由函数的对象之一)的表达式或一个花括号初始化列表。
任何名为 begin 或 end 成员的存在,无关乎它是类型、数据成员、函数或枚举项,且无关乎其可访问性,将导致 __range.begin() 和 __range.end() 被各自用作 begin_expr 和 end_expr 。结果,基于范围的 for 循环不能用于含有名为 begin 或 end 的类型或枚举项的类,即使提供了适当命名空间的自由函数
#include <iostream>#include <vector> int main() {    std::vector<int> v = {0, 1, 2, 3, 4, 5};     for (const int& i : v) // 以 const 引用访问        std::cout << i << ' ';    std::cout << '\n';     for (auto i : v) // 以值访问, i 的类型是 int        std::cout << i << ' ';    std::cout << '\n';     for (auto&& i : v) // 以引用访问, i 的类型是 int&        std::cout << i << ' ';    std::cout << '\n';     for (int n : {0, 1, 2, 3, 4, 5}) // 初始化器可以是花括号初始化列表        std::cout << n << ' ';    std::cout << '\n';     int a[] = {0, 1, 2, 3, 4, 5};    for (int n : a) // 初始化器可以是数组        std::cout << n << ' ';    std::cout << '\n'; }/*输出结果为:0 1 2 3 4 50 1 2 3 4 50 1 2 3 4 50 1 2 3 4 50 1 2 3 4 51 1 1 1 1 1 */

6.Lambda表达式

lambda 表达式(通常称为 "lambda")是一种在被调用的位置或作为参数传递给函数的位置定义匿名函数对象的简便方法。 Lambda 通常用于封装传递给算法或异步方法的少量代码行

[=]()mutable throw()->int{
return x + y;
}
[=] Capture子语句
() 参数列表(可选)
mutable 可变规范(可选)
throw 异常规范(可选)
->int 返回类型(可选)
(1)Capture子语句
空 capture 子句 [ ] 指示 lambda 表达式的主体不访问封闭范围中的变量
[&] 表示通过引用捕获引用的所有变量,而 [=] 表示通过值捕获它们,[this]表示值传递方式捕捉当前的this指针
例如,如果 lambda 体通过引用访问外部变量 total 并通过值访问外部变量 factor,则以下 capture 子句等效:
[&total, factor]  [factor, &total]  [&, factor]  [factor, &]  [=, &total]  [&total, =]  
如果 capture 子句包含 capture-default &,则该 capture 子句的 identifier 中没有任何 capture 可采用 & identifier 形式。 同样,如果 capture 子句包含 capture-default =,则该 capture 子句的 capture 不能采用 = identifier 形式:
struct S { void f(int i); };  
void S::f(int i) {      [&, i]{};    // OK      [&, &i]{};   // ERROR: i preceded by & when & is the default      [=, this]{}; // ERROR: this when = is the default      [i, i]{};    // ERROR: i repeated  }  

capture 后跟省略号是包扩展,如以下可变参数模板示例中所示:template<class... Args>  void f(Args... args) {      auto x = [args...] { return g(args...); };      x();  }  
如果一个lambda函数被定义于某类型的成员函数中,则会被当作该类型的friend:
[](SomeType* typePtr) {typePtr->SomePrivateMemberFunction;};
这只有该lambda函数被创建的作用域是在Sometype的成员函数内部才会生效
(2)参数列表
除了捕获变量,lambda 还可接受输入参数。 参数列表(在标准语法中称为 lambda 声明符)是可选的,它在大多数方面类似于函数的参数列表
int y = [] (int first, int second)  {      return first + second;  };  
lambda 表达式只能捕获具有自动存储持续时间的变量,不能捕获static变量
(3)可变规范
mutable修饰符。默认情况下,Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)
(4)异常规范
你可以使用 throw() 异常规范来指示 lambda 表达式不会引发任何异常
(5)返回类型
将自动推导 lambda 表达式的返回类型。 无需使用 auto 关键字,除非指定尾随返回类型
(6)如果用户希望将lambda作为参数传入,可以使用auto关键字:
auto mylambdaFunction = [] (int first, int second) {  return first + second; }; 


0 0
原创粉丝点击