C++11学习总线及相关资料----悲惨了好多天,奉献了!

C++ 11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。Lambda的语法形式如下:
              [函数对象参数] (操作符重载函数参数) mutable或exception声明 ->返回值类型 {函数体}
      可以看到,Lambda主要分为五个部分:[函数对象参数]、(操作符重载函数参数)、mutable或exception声明、->返回值类型、{函数体}。下面分别进行介绍。
      一、[函数对象参数],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:
           1、空。没有使用任何函数对象参数。
           2、=。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
           3、&。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
           4、this。函数体内可以使用Lambda所在类中的成员变量。
           5、a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
           6、&a。将a按引用进行传递。
           7、a, &b。将a按值进行传递,b按引用进行传递。
           8、=,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
           9、&, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。
      二、(操作符重载函数参数),标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。
      三、mutable或exception声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。exception声明用于指定函数抛出的异常,如抛出整数类型的异常,可以使用throw(int)。
      四、->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
      五、{函数体},标识函数的实现,这部分不能省略,但函数体可以为空。
      下面给出了一段示例代码,用于演示上述提到的各种情况,代码中有简单的注释可作为参考。




class CTest
{
public:
 CTest() : m_nData(20) { NULL; }
 void TestLambda()
 {
  vector<int> vctTemp;
  vctTemp.push_back(1);
  vctTemp.push_back(2);


  // 无函数对象参数,输出:1 2
  {
   for_each(vctTemp.begin(), vctTemp.end(), [](int v){ cout << v << endl; });
  }


  // 以值方式传递作用域内所有可见的局部变量(包括this),输出:11 12
  {
   int a = 10;
   for_each(vctTemp.begin(), vctTemp.end(), [=](int v){ cout << v+a << endl; });
  }


  // 以引用方式传递作用域内所有可见的局部变量(包括this),输出:11 13 12
  {
   int a = 10;
   for_each(vctTemp.begin(), vctTemp.end(), [&](int v)mutable{ cout << v+a << endl; a++; });
   cout << a << endl;
  }


  // 以值方式传递局部变量a,输出:11 13 10
  {
   int a = 10;
   for_each(vctTemp.begin(), vctTemp.end(), [a](int v)mutable{ cout << v+a << endl; a++; });
   cout << a << endl;
  }


  // 以引用方式传递局部变量a,输出:11 13 12
  {
   int a = 10;
   for_each(vctTemp.begin(), vctTemp.end(), [&a](int v){ cout << v+a << endl; a++; });
   cout << a << endl;
  }


  // 传递this,输出:21 22
  {
   for_each(vctTemp.begin(), vctTemp.end(), [this](int v){ cout << v+m_nData << endl; });
  }


  // 除b按引用传递外,其他均按值传递,输出:11 12 17
  {
   int a = 10;
   int b = 15;
   for_each(vctTemp.begin(), vctTemp.end(), [=, &b](int v){ cout << v+a << endl; b++; });
   cout << b << endl;
  }




  // 操作符重载函数参数按引用传递,输出:2 3
  {
   for_each(vctTemp.begin(), vctTemp.end(), [](int &v){ v++; });
   for_each(vctTemp.begin(), vctTemp.end(), [](int v){ cout << v << endl; });
  }


  // 空的Lambda表达式
  {
   [](){}();
   []{}();
  }
 }


private:
 int m_nData;

};


  C++ 11中引入的一个非常重要的概念就是右值引用。理解右值引用是学习“移动语义”(move semantics)的基础。而要理解右值引用,就必须先区分左值与右值。
       对左值和右值的一个最常见的误解是:等号左边的就是左值,等号右边的就是右值。左值和右值都是针对表达式而言的,左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时就不再存在的临时对象。一个区分左值与右值的便捷方法是:看能不能对表达式取地址,如果能,则为左值,否则为右值。下面给出一些例子来进行说明。


 int a = 10;
 int b = 20;
 int *pFlag = &a;
 vector<int> vctTemp;
 vctTemp.push_back(1);
 string str1 = "hello ";
 string str2 = "world";
 const int &m = 1;
       请问,a,b, a+b, a++, ++a, pFlag, *pFlag, vctTemp[0], 100, string("hello"), str1, str1+str2, m分别是左值还是右值?
           a和b都是持久对象(可以对其取地址),是左值;
           a+b是临时对象(不可以对其取地址),是右值;
           a++是先取出持久对象a的一份拷贝,再使持久对象a的值加1,最后返回那份拷贝,而那份拷贝是临时对象(不可以对其取地址),故其是右值;
           ++a则是使持久对象a的值加1,并返回那个持久对象a本身(可以对其取地址),故其是左值;
           pFlag和*pFlag都是持久对象(可以对其取地址),是左值;
           vctTemp[0]调用了重载的[]操作符,而[]操作符返回的是一个int &,为持久对象(可以对其取地址),是左值;
           100和string("hello")是临时对象(不可以对其取地址),是右值;
           str1是持久对象(可以对其取地址),是左值;
           str1+str2是调用了+操作符,而+操作符返回的是一个string(不可以对其取地址),故其为右值;
           m是一个常量引用,引用到一个右值,但引用本身是一个持久对象(可以对其取地址),为左值。
      区分清楚了左值与右值,我们再来看看左值引用。左值引用根据其修饰符的不同,可以分为非常量左值引用和常量左值引用。
      非常量左值引用只能绑定到非常量左值,不能绑定到常量左值、非常量右值和常量右值。如果允许绑定到常量左值和常量右值,则非常量左值引用可以用于修改常量左值和常量右值,这明显违反了其常量的含义。如果允许绑定到非常量右值,则会导致非常危险的情况出现,因为非常量右值是一个临时对象,非常量左值引用可能会使用一个已经被销毁了的临时对象。
      常量左值引用可以绑定到所有类型的值,包括非常量左值、常量左值、非常量右值和常量右值。
      可以看出,使用左值引用时,我们无法区分出绑定的是否是非常量右值的情况。那么,为什么要对非常量右值进行区分呢,区分出来了又有什么好处呢?这就牵涉到C++中一个著名的性能问题——拷贝临时对象。考虑下面的代码:


vector<int> GetAllScores()
{
 vector<int> vctTemp;
 vctTemp.push_back(90);
 vctTemp.push_back(95);
 return vctTemp;
}
       当使用vector<int> vctScore = GetAllScores()进行初始化时,实际上调用了三次构造函数。尽管有些编译器可以采用RVO(Return Value Optimization)来进行优化,但优化工作只在某些特定条件下才能进行。可以看到,上面很普通的一个函数调用,由于存在临时对象的拷贝,导致了额外的两次拷贝构造函数和析构函数的开销。当然,我们也可以修改函数的形式为void GetAllScores(vector<int> &vctScore),但这并不一定就是我们需要的形式。另外,考虑下面字符串的连接操作:
 string s1("hello");
 string s = s1 + "a" + "b" + "c" + "d" + "e";       在对s进行初始化时,会产生大量的临时对象,并涉及到大量字符串的拷贝操作,这显然会影响程序的效率和性能。怎么解决这个问题呢?如果我们能确定某个值是一个非常量右值(或者是一个以后不会再使用的左值),则我们在进行临时对象的拷贝时,可以不用拷贝实际的数据,而只是“窃取”指向实际数据的指针(类似于STL中的auto_ptr,会转移所有权)。C++ 11中引入的右值引用正好可用于标识一个非常量右值。C++ 11中用&表示左值引用,用&&表示右值引用,如:
 int &&a = 10;        右值引用根据其修饰符的不同,也可以分为非常量右值引用和常量右值引用。
       非常量右值引用只能绑定到非常量右值,不能绑定到非常量左值、常量左值和常量右值(VS2010 beta版中可以绑定到非常量左值和常量左值,但正式版中为了安全起见,已不允许)。如果允许绑定到非常量左值,则可能会错误地窃取一个持久对象的数据,而这是非常危险的;如果允许绑定到常量左值和常量右值,则非常量右值引用可以用于修改常量左值和常量右值,这明显违反了其常量的含义。
       常量右值引用可以绑定到非常量右值和常量右值,不能绑定到非常量左值和常量左值(理由同上)。
       有了右值引用的概念,我们就可以用它来实现下面的CMyString类。


class CMyString
{
public:
    // 构造函数
 CMyString(const char *pszSrc = NULL)
 {
  cout << "CMyString(const char *pszSrc = NULL)" << endl;
  if (pszSrc == NULL)
  {
   m_pData = new char[1];
   *m_pData = '\0';
  }
  else
  {
   m_pData = new char[strlen(pszSrc)+1];
   strcpy(m_pData, pszSrc);
  }
 }


    // 拷贝构造函数
 CMyString(const CMyString &s)
 {
  cout << "CMyString(const CMyString &s)" << endl;
  m_pData = new char[strlen(s.m_pData)+1];
  strcpy(m_pData, s.m_pData);
 }


    // move构造函数
 CMyString(CMyString &&s)
 {
  cout << "CMyString(CMyString &&s)" << endl;
  m_pData = s.m_pData;
  s.m_pData = NULL;
 }


    // 析构函数
 ~CMyString()
 {
  cout << "~CMyString()" << endl;
  delete [] m_pData;
  m_pData = NULL;
 }


    // 拷贝赋值函数
 CMyString &operator =(const CMyString &s)
 {
  cout << "CMyString &operator =(const CMyString &s)" << endl;
  if (this != &s)
  {
   delete [] m_pData;
   m_pData = new char[strlen(s.m_pData)+1];
   strcpy(m_pData, s.m_pData);
  }
  return *this;
 }


    // move赋值函数
 CMyString &operator =(CMyString &&s)
 {
  cout << "CMyString &operator =(CMyString &&s)" << endl;
  if (this != &s)
  {
   delete [] m_pData;
   m_pData = s.m_pData;
   s.m_pData = NULL;
  }
  return *this;
 }


private:
 char *m_pData;
};
        可以看到,上面我们添加了move版本的构造函数和赋值函数。那么,添加了move版本后,对类的自动生成规则有什么影响呢?唯一的影响就是,如果提供了move版本的构造函数,则不会生成默认的构造函数。另外,编译器永远不会自动生成move版本的构造函数和赋值函数,它们需要你手动显式地添加。
        当添加了move版本的构造函数和赋值函数的重载形式后,某一个函数调用应当使用哪一个重载版本呢?下面是按照判决的优先级列出的3条规则:
             1、常量值只能绑定到常量引用上,不能绑定到非常量引用上。
             2、左值优先绑定到左值引用上,右值优先绑定到右值引用上。
             3、非常量值优先绑定到非常量引用上。
        当给构造函数或赋值函数传入一个非常量右值时,依据上面给出的判决规则,可以得出会调用move版本的构造函数或赋值函数。而在move版本的构造函数或赋值函数内部,都是直接“移动”了其内部数据的指针(因为它是非常量右值,是一个临时对象,移动了其内部数据的指针不会导致任何问题,它马上就要被销毁了,我们只是重复利用了其内存),这样就省去了拷贝数据的大量开销。
        一个需要注意的地方是,拷贝构造函数可以通过直接调用*this = s来实现,但move构造函数却不能。这是因为在move构造函数中,s虽然是一个非常量右值引用,但其本身却是一个左值(是持久对象,可以对其取地址),因此调用*this = s时,会使用拷贝赋值函数而不是move赋值函数,而这已与move构造函数的语义不相符。要使语义正确,我们需要将左值绑定到非常量右值引用上,C++ 11提供了move函数来实现这种转换,因此我们可以修改为*this = move(s),这样move构造函数就会调用move赋值函数。


 C++ 11中引入的auto主要有两种用途:自动类型推断和返回值占位。auto在C++ 98中的标识临时变量的语义,由于使用极少且多余,在C++ 11中已被删除。
      auto自动类型推断,用于从初始化表达式中推断出变量的数据类型。通过auto的自动类型推断,可以大大简化我们的编程工作。下面是一些使用auto的例子。


 auto a;                 // 错误,没有初始化表达式,无法推断出a的类型
 auto int a = 10;        // 错误,auto临时变量的语义在C++ 11中已不存在
 auto a = 10;
 auto c = 'A';
 auto s("hello");
 vector<int> vctTemp;
 auto it = vctTemp.begin();
 auto ptr = [](){ cout << "hello world" << endl; };
      另外,在使用模板技术时,如果某个变量的类型依赖于模板参数,不使用auto将很难确定变量的类型(使用auto后,将由编译器自动进行确定)。下面是一个具体的例子。
template <class T, class U>
void Multiply(T t, U u)
{
 auto v = t*u;
}      auto返回值占位,主要与decltype配合使用,用于返回值类型后置时的占位。


template <class T, class U>
 auto Multiply(T t, U u)->decltype(t*u)
{
 typedef decltype(t*u) NewType;
 NewType *pResult = new NewType(t*u);
 return *pResult;
}
      至于为什么需要将返回值类型后置,这里简单说明一下。如果没有后置,则函数声明为decltype(t*u) Multiply(T t, U u),但此时模板参数t和u还未声明,编译无法通
过。另外,如果非要使用返回值类型前置的形式,也可以将函数声明为decltype((*(T *)0)*(*(U *)0)) Multiply(T t, U u),但这种形式比较晦涩难懂,因此不推荐采用。



  C++ 11中引入的一个非常重要也是比较难于理解的新特性就是完美转发(Perfect Forwarding)。完美转发中有两个关键词:“转发”和“完美”。
      我们先来看第一个关键词“转发”,那么在C++中,“转发”表示什么含义呢?转发通常用在模板编程中,假设有一个函数F(a1, a2, ..., an),如果存在另一个函数G(a1, a2, ..., an),调用G相当于调用了F,则我们说函数G将a1, a2, ..., an等参数正确地转发给了函数F。再来看第二个关键词“完美”,“完美”转发是相对于其他转发方案而言的。在目前已提出的7种转发方案中,只有完美转发能够正确地实现转发的语义,其他方案都存在这样或那样的问题。下面一一进行介绍。
      转发方案一:使用非常量左值引用。考虑下面的代码。


 1 void F(int a)
 2 {
 3  cout << a << endl;
 4 }
 5 
 6 template<class A>
 7 void G(A &a)
 8 {
 9  F(a); 
10 }
      使用非常量左值引用时,我们可以调用F(10),但无法调用G(10),即我们无法接收非常量右值的参数。
 
      转发方案二:使用常量左值引用。考虑下面的代码。


 1 void F(int &a)
 2 {
 3  cout << a << endl;
 4 }
 5 
 6 template<class A>
 7 void G(const A &a)
 8 {
 9  F(a); 
10 }
      使用常量左值引用时,函数G可以接收任意类型的值作为参数,包括非常量左值、常量左值、非常量右值和常量右值。但当F的参数类型为非常量左值引用时,我们无法将一个常量左值引用转发给一个非常量左值引用。
 
      转发方案三:使用非常量左值引用 + 常量左值引用。考虑下面的代码。


 1 template<class A>
 2 void G(A &a)
 3 {
 4  F(a); 
 5 }
 6 
 7 template<class A>
 8 void G(const A &a)
 9 {
10  F(a); 
11 }
      综合前面两种方案的分析结果,可以得出这种方案相当于对函数G进行了重载,此时可以接收任意类型的值作为参数,也可以顺利地实现转发。但由于使用了常量和非常量两种形式的重载,当参数的个数N较大时,需要重载的函数会呈指数级增长(2的N次方),因此这种方案实际上是不可取的。
 
      转发方案四:使用常量左值引用 + const_cast。
1 template<class A>
2 void G(const A &a)
3 {
4  F(const_cast<A &>(a)); 
5 }      这种方案克服了方案二的缺点,现在可以将常量左值引用转发给非常量左值引用了。但这又带来了新的问题,假如F的参数是一个非常量左值引用,则调用G后,我们可以通过F来修改传入的常量左值和常量右值了,而这是非常危险的。
 
      转发方案五:非常量左值引用 + 修改的参数推导规则。
      这种方案与方案一类似,但需要修改现有的参数推导规则,即传递一个非常量右值给模板类型时,将它推导成常量右值,这样就解决了方案一中无法接收非常量右值的参数的问题。但由于修改了现有的参数推导规则,因此会导致已有代码的语义发生改变。考虑下面的代码。


 1 template<class A>
 2 void F(A &a)
 3 {
 4  cout << "void F(A& a)" << endl;
 5 }
 6 
 7 void F(const long &a)
 8 {
 9  cout << "void F(const long &a)" << endl;
10 }
      在未修改参数推导规则前,调用F(10)会选择第二个重载函数,但修改后,却会调用第一个重载函数,这就给C++带来了兼容性的问题。
 
      转发方案六:右值引用。考虑下面的代码。
1 template<class A>
2 void G(A &&a)
3 {
4  F(a); 
5 }      在这种方案中,G将无法接收左值,因为不能将一个左值传递给一个右值引用。另外,当传递非常量右值时也会存在问题,因为此时a本身是一个左值,这样当F的参数是一个非常量左值引用时,我们就可以来修改传入的非常量右值了。
 
      转发方案七:右值引用 + 修改的参数推导规则。
      要理解修改后的参数推导规则,我们先来看一下引用叠加规则:
           1、T& + & = T&
           2、T& + && = T&
           3、T&& + & = T&
           4、T或T&& + && = T&&
      修改后的针对右值引用的参数推导规则为:若函数模板的模板参数为A,模板函数的形参为A&&,则可分为两种情况讨论:
           1、若实参为T&,则模板参数A应被推导为引用类型T&。(由引用叠加规则第2点T& + && = T&和A&&=T&,可得出A=T&)
           2、若实参为T&&,则模板参数A应被推导为非引用类型T。(由引用叠加规则第4点T或T&& + && = T&&和A&&=T&&,可得出A=T或T&&,强制规定A=T)
      应用了新的参数推导规则后,考虑下面的代码。
1 template<class A>
2 void G(A &&a)
3 {
4  F(static_cast<A &&>(a)); 
5 }      当传给G一个左值(类型为T)时,由于模板是一个引用类型,因此它被隐式装换为左值引用类型T&,根据推导规则1,模板参数A被推导为T&。这样,在G内部调用F(static_cast<A &&>(a))时,static_cast<A &&>(a)等同于static_cast<T& &&>(a),根据引用叠加规则第2点,即为static_cast<T&>(a),这样转发给F的还是一个左值。
      当传给G一个右值(类型为T)时,由于模板是一个引用类型,因此它被隐式装换为右值引用类型T&&,根据推导规则2,模板参数A被推导为T。这样,在G内部调用F(static_cast<A &&>(a))时,static_cast<A &&>(a)等同于static_cast<T&&>(a),这样转发给F的还是一个右值(不具名右值引用是右值)。
      可见,使用该方案后,左值和右值都能正确地进行转发,并且不会带来其他问题。另外,C++ 11为了方便转发的实现,提供了一个函数模板forward,用于参数的完美转发。使用forward后的代码可简化为:
1 template<class A>
2 void G(A &&a)
3 {
4  F(forward<A>(a)); 
5 }      为了便于进行各种转发方案的比较,下面以表格的形式列出了各自的特性。


转发方案非常量左值常量左值非常量右值常量右值修改语言已知问题


一、非常量左值引用非常量左值常量左值无法转发常量左值否无法接收非常量右值的参数
二、常量左值引用常量左值常量左值常量左值常量左值否无法将常量左值引用转发给非常量左值引用
三、非常量左值引用 + 常量左值引用非常量左值常量左值常量左值常量左值否重载函数过多,实际编码不可行
四、常量左值引用 + const_cast非常量左值非常量左值非常量左值非常量左值否可修改常量左值和常量右值,不安全
五、非常量左值引用 + 修改的参数推导规则非常量左值常量左值常量左值常量左值是会导致兼容性问题,且不支持移动语义
六、右值引用无法转发无法转发非常量左值常量左值是可修改非常量右值,不安全
七、右值引用 + 修改的参数推导规则非常量左值常量左值非常量右值常量右值是暂无,故简称为完美转发


      注:关于左值引用和右值引用,可以参考我的另一篇文章: 【原】C++ 11右值引用


 C++ 11中引入了许多简化编程工作的语法上的新特性,我们暂且美其名曰“语法甜点”。下面一一进行介绍。
      语法甜点1:序列for循环
      序列for循环是一种简化的for循环,可用于遍历一组序列,包括各种容器、string、数组、初始化列表以及由begin和end函数定义的序列。示例代码如下:
1  vector<int> vctTemp{1, 2, 3};
2  for (auto a : vctTemp)
3  {
4   cout << a << endl;
5  } 
      语法甜点2:委托构造函数
      在引入C++ 11之前,如果某个类有多个重载的构造函数,且这些构造函数中有一些共同的初始化逻辑,通常都需要再编写一个带参数的初始化函数,然后在这些构造函数中调用这个初始化函数。在C++ 11中,再也不用这么麻烦了。我们可以实现一个最基础的构造函数,其他构造函数都调用这个构造函数。示例代码如下:


 1 class CPerson
 2 {
 3 public:
 4  CPerson() : CPerson(0, "") { NULL; }
 5  CPerson(int nAge) : CPerson(nAge, "") { NULL; }
 6  CPerson(int nAge, const string &strName)
 7  {
 8   stringstream ss;
 9   ss << strName << "is " << nAge << "years old.";
10   m_strInfo = ss.str();
11  }
12 
13 private:
14  string m_strInfo;
15 };
      
      语法甜点3:统一的初始化语法
      在引入C++ 11之前,有各种不同的初始化语法。在C++ 11中,仍可以使用这些初始化语法,但也可以选择使用新引入的统一的初始化语法。统一的初始化语法用一对大括号{}表示,使用{}初始化语法还可有效地避免窄转换。示例代码如下:


1  int a{5};
2  char c{'X'};
3  int p[5] = {1, 2,3, 4, 5};
4  vector<int> vctTemp{1, 2, 3};
5  CPerson person{10, "Mike"};
6   int b = 5.3;                     // b赋值成5,发生了窄转换
7   int d{5.3};                      // 会提示编译错误,避免了窄转换
 
      语法甜点4:nullptr
      nullptr是C++ 11中新加的一个关键字,用于标识空指针。引入nullptr后,可以解决某些函数重载时的二义性问题。示例代码如下:


 1 void F(int a)
 2 {
 3  cout << a << endl;
 4 }
 5 
 6 void F(char *p)
 7 {
 8  assert(p != NULL);
 9 
10  cout << p << endl;
11 }
12 
13 int main(int argc, _TCHAR* argv[])
14 {
15  int *p = nullptr;
16  int *q = NULL;
17  bool bEqual = (p == q);  // 两个指针值是相等的,bEqual为true
18  int a = nullptr;   // 编译失败,nullptr不是转换为int
19 
20  F(0);          // 在C++ 98中编译失败,有二义性;在C++ 11中调用F(int)
21  F(nullptr);    // 调用F(char *)
22 
23  getchar();
24  return 0;
25 }
      
      语法甜点5:成员变量初始化
      与Java和C#中的用法一样,可以对成员变量进行就地初始化。示例代码如下:
1 class CPerson
2 {
3 private:
4  int m_nAge = 10;
5  string m_strName = "Mike";
6 }; 
      语法甜点6:默认或禁用函数
      当我们定义了自己的带参数的构造函数时,编译器将不再生成默认的构造函数,如果此时想使用默认的构造函数,则必须显式地声明并定义不带参数的构造函数。在C++ 11中,我们可以使用default关键字来表明我们希望使用默认的构造函数。类似的,当我们不想外部使用编译器自动生成的构造函数或赋值函数时,我们一般需要将其声明成protected或private的。在C++ 11中,我们可以使用delete关键字来表明我们不希望编译器生成默认的构造函数或赋值函数。示例代码如下:
1 class CPerson
2 {
3 public:
4  CPerson() = default;
5  CPerson(const CPerson &person) = delete;
6 }; 
      语法甜点7:继承的构造函数
      当一个派生类的某个函数隐藏了基类中的某个同名函数时,如果我们想在派生类中导出基类中的这个同名函数,可以通过using Base::Func的方式将基类中的这个同名函数引入到派生类的作用域内。当该方法只对普通成员函数有效,不能用于构造函数。在C++ 11中,如果派生类认为基类的构造函数已经足够,则也可以使用using Base::Base的方式将基类的构造函数引入到派生类的作用域内。但需要注意的是,此时派生类中的成员变量并没有进行初始化,所以应当对这些成员变量进行就地初始化。示例代码如下:


 1 class CBase
 2 {
 3 };
 4 
 5 class CDerived : public CBase
 6 {
 7 public:
 8  using CBase::CBase;
 9  CDerived(int nData) : m_nData(nData) { NULL; }
10 
11 private:
12  int m_nData = 10;
13 };
 
      语法甜点8:模板右边双括号
      在C++ 98中,vector<vector<int>> vctTemp是一个非法的表达式,编译器会认为右边的>>是一个移位操作符,因此必须修改为vector<vector<int> > vctTemp,即在右边的两个>中间添加一个空格。在C++ 11中,这将不再是一个问题,编译器将能够识别出右边的双括号是两个模板参数列表的结尾。
 
      语法甜点9:static_assert
      静态断言static_assert由一个常量表达式和一个字符串构成。在编译期间,将计算常量表达式的值,如果为false,字符串将作为错误信息输出。示例代码如下:
1  char a = 10;
2  static_assert(sizeof(a)==4, "a is not an integer."); 
      语法甜点10:初始化列表
      在引入C++ 11之前,只有数组能使用初始化列表。在C++ 11中,vector、list等各种容器以及string都可以使用初始化列表了。初始化列表对应的类为initializer_list,vector、list等各种容器以及string之所以可以使用初始化列表,是因为它们重载了参数类型为initializer_list的构造函数(称为初始化列表构造函数)和赋值函数(称为初始化列表赋值函数)。下面是一些使用初始化列表的例子。


 1 void Print(const initializer_list<int> &ilData)
 2 {
 3  for (auto a : ilData)
 4  {
 5   cout << a << endl;
 6  }
 7 }
 8 
 9 int main(int argc, _TCHAR* argv[])
10 {
11  vector<int> vctNum = {1, 2, 3, 4, 5};
12  map<string, string> mapID2Name = {{"92001", "Jack"}, {"92002", "Mike"}};
13  string strText{"hello world"};
14  Print({});
15  Print({1, 2});
16  Print({1, 2, 3, 4, 5});
17 
18  getchar();
19  return 0;
20 }


C++ 11中引入的tuple是一个N元组。它相当于有N个成员的结构体,只不过这个结构体的成员都是匿名的。tuple中有两个特殊的函数,一个是head(),用于获取第一个成员的值,另一个是tail(),用于获取剩下所有成员的值,tail()本身又是一个tuple。这样,如果我们想取tuple中第二个成员的值,则可以先取tail()的值,再取tail()的head()的值。当然,这样使用的话比较麻烦,所以C++ 11提供了get函数通过索引来获取tuple中某个成员的值。另外,通过make_tuple可以很方便地构造一个tuple对象。有关tuple使用的例子可以参考下面的代码。


1  tuple<int, char, string> tupInfo(10, 'A', "hello world");
2  int a = tupInfo.head();
3  int a2 = tupInfo.tail().head();
4  tuple<char, string> tupTail = tupInfo.tail();
5  int b = get<0>(tupInfo);
6  char c = get<1>(tupInfo);
7  string s = get<2>(tupInfo);
8  auto tupInfo2 = make_tuple(5, 'B', string("C++ 11"), 4.6);
      前面说过,tuple是一个N元组,而N的个数是没有限制的,也就是说,tuple可以包含0个、1个、2个或更多的元素,每个元素的类型则通过模板参数指定。那么,tuple是如何做到这些的呢?答案是可变参数模板。
      学习C++的人应当对printf函数都非常熟悉,printf的一个特点就是它的参数个数是可变的。而在C++ 11中,则允许模板的参数个数也是可变的。下面是一个模板参数可变的函数模板,用于获取传入的参数的个数。
1 template<typename... Args>
2 UINT GetParameterCount(Args... args)
3 {
4  return sizeof...(args);
5 }      可以看到,可变参数模板使用typename再加...来表示模板参数包,使用Args再加...来表示函数参数包。上面代码中的sizeof...专门用于获取函数参数包中参数的个数,它的参数必须是一个函数参数包类型的对象。熟悉了可变参数模板的基本语法后,下面我们使用它来编写一个Print函数,该函数的参数个数和类型都是可变的,它简单地输出传入的各个参数的值,值之间用逗号进行分割,并在输出最后一个参数的值后自动换行。


 1 template<typename T>
 2 void Print(T value)
 3 {
 4  cout << value << endl;
 5 }
 6 
 7 template<typename Head, typename... Rail>
 8 void Print(Head head, Rail... rail)
 9 {
10  cout << head << ",";
11  Print(rail...);
12 }
13 
14 int main(int argc, char *argv[])
15 {
16  Print(1);                  // 输出:1
17  Print(1, "hello");         // 输出:1,Hello
18  Print(1, "hello", 'H');    // 输出:1,Hello,H
19  return 0;
20 }
       在上面的代码中,我们先定义了一个只有一个模板参数的函数模板,它简单地输出传入的参数的值。然后又定义了一个可变参数的函数模板,它输出第一个参数的值,然后递归地调用自己。注意rail...这种写法,它表示将函数参数包分割成一个一个的参数,并传入Print中。这样,函数参数包中的第一个参数传递给head,剩余的参数又重新构成一个函数参数包传递给rail。当递归调用到函数参数包中只有一个参数时,则会调用只有一个模板参数的Print函数。
      理解了可变模板参数的使用原理后,我们再来编写一个自己的Printf函数。


 1 void MyPrintf(const char *pszText)
 2 {
 3  assert(pszText != NULL);
 4 
 5  cout << pszText;
 6 }
 7 
 8 template<typename T, typename... Args>
 9 void MyPrintf(const char *pszText, T value, Args... args)
10 {
11  assert(pszText != NULL);
12 
13  while (*pszText)
14  {
15   if (*pszText == '%' && *++pszText != '%')
16   {
17    cout << value;
18    MyPrintf(++pszText, args...);
19    return;
20   }
21   cout << *pszText++;
22  }
23 }
      调用MyPrintf函数时的推理过程与Print的推理过程类似,这里就不再赘述了。另外,如果想更深入地学习可变参数模板,还可以参阅tuple的源代码,或者自己动手实现一个简化版的tuple。


语法甜点11:非成员的begin和end
    在C++ 03中,标准容器都提供了begin和end成员函数,但对于普通数组,则只能使用不同的写法。比如:
1 vector<int> v; 
2 int a[100]; 
3 sort(v.begin(), v.end()); 
4 sort(a, a+sizeof(a)/sizeof(a[0]));    为了统一语法,C++ 11提供了非成员的begin和end函数。用法如下:
1 sort(begin(v), end(v)); 
2 sort(begin(a), end(a));    
    语法甜点12:显式虚函数重载    
    在引入C++ 11之前,基类和派生类中的虚函数很容易产生错误使用的情况。比如:
    a、基类添加了一个虚函数,但该虚函数与派生类中的某个已有普通函数相同。
    b、派生类添加了一个普通函数,但该函数与基类中的某个已有虚函数相同。
    为了避免这些情况,在C++ 11中可以使用override来显式地表明需要进行虚函数重载。比如: 


 1 class Base 
 2 {
 3     virtual void some_func(float);
 4 };
 5 
 6 class Derived : public Base
 7  {
 8     virtual void some_func(int) override;        // 将产生编译错误
 9    virtual void some_func(float) override;    // 正确
10 };    注意:为了保持向后兼容,此功能是选择性的。
    C++ 11中还引入了final指示符,用于防止类或接口被继承。比如:


 1 class  Base1 final { };
 2 class Derived1 : public Base1 { };            // 将产生编译错误
 3 class Base2
 4 {
 5     virtual void f() final;
 6 };
 7 class Derived2 : public Base2
 8 {
 9     void f();                                             // 将产生编译错误
10 };    
    语法甜点13:强类型枚举 
    在C++ 03中,枚举类型不是类型安全的。枚举类型被视为整数,这使得两种不同的枚举类型之间可以进行比较。C++ 03唯一提供的安全机制就是一个整数或一个枚举型值不能隐式转换为另一个枚举型值。
    在C++ 11中,引入了enum class来声明类型安全的枚举类型。比如:
enum class IColor1 { Red, Blue, Gree=100, Black };    IColor1不能隐式地转换为整数类型,也不能与整数类型比较大小。使用枚举名时,必须明确指定其所属范围,比如:必须使用IColor1::Red,而不能单独使用Red。
    在C++ 11中,使用enum class和传统的enum时,还可以指定其所用的数据类型,不指定时默认为int。比如:
1 enum class IColor2 : unsigned int { Red, Blue, Gree=100, Black };
2 enum IColor3 : unsigned int { Red, Blue, Gree=100, Black };    另外,在C++ 03中,无法对枚举类型进行前置声明。而在C++ 11中,只要是使用了指定数据类型的新式枚举,都可以进行前置声明。比如:
1 enum class IColor1;
2 enum class IColor2 : unsigned int;
3 enum IColor3 : unsigned int;    
    语法甜点14:模板别名
    在C++ 03中,可以使用typedef给模板类指定一个新的类型名称,但却不能给类模板指定别名。比如:
1 template< typename first, typename second, int third>
2 class SomeType;   template< typename second>
3 typedef SomeType<OtherType, second, 5> TypedefName;  // 在C++ 03中是不合法的    为了能够定义类模板的别名,C++ 11允许像下面这样使用using关键字: 
1 template< typename first, typename second, int third>
2 class SomeType;
3 template< typename second>
4 using TypedefName = SomeType<OtherType, second, 5>;    另外,using也能定义一般类型的别名,此时等同于typedef。比如: 
1 typedef void (*Func)(int);
2 using  Func = void (*)(int);    
    语法甜点15:无限制的union
    在C++ 03中,并非任意的数据类型都能做为union的成员。比方说,带有non-trivial构造函数的类型就不能是 union 的成员。在C++ 11中,移除了所有对union的使用限制,除了其成员仍然不能是引用类型这种情况。 


 1 struct point
 2 {
 3      point() {}
 4      point(int x, int y): m_x(x), m_y(y) {}
 5      int m_x, m_y;
 6 };
 7 union
 8 {
 9      int z;
10      double w;
11      point p;                     // 在C++ 03中不合法;在C++ 11中合法
12 };    备注:C++ 03中不适合做union成员变量的情形有以下几种:
        1、类或结构体中含有non-trival的构造函数(拷贝构造函数)、析构函数、拷贝赋值操作符、虚函数等。
        2、类的基类和成员变量中含有1中所述几个函数。
        3、静态变量。
        4、变量引用。
 
    语法甜点16:新的字符串字面值
    C++ 03提供了两种字符串字面值。第一种,包含有双引号,产生以空字符结尾的const char数组。第二种,有着前标L,产生以空字符结尾的const wchar_t数组,其中wchar_t代表宽字符。C++ 03不支持Unicode编码。
    在C++ 11中,为了加强C++编译器对Unicode的支持,类别char的定义被修改为其大小至少能够存储UTF-8的8位编码,并且能够容纳编译器的基本字符集的任何成员。
    C++ 11 支持三种Unicode编码方式:UTF-8,UTF-16,和UTF-32。除了上述char定义的变更, C++ 11还增加了两种新的字符类别:char16_t和char32_t,用于存储UTF-16和UTF-32的字符。
    下面展示了如何产生使用这些编码的字符串字面值: 
1 u8"I'm a UTF-8 string."
2 u"This is a UTF-16 string."
3 U"This is a UTF-32 string."    第一个字符串的类型是通常的const char[],第二个字符串的类型是const char16_t[],第三个字符串的类型是const char32_t[]。 
    为了避免在字符串中频繁使用转义字符的麻烦,C++11还提供了raw字符串字面值。比如: 
1 R"(The String Data \ Stuff " )"
2 R"delimiter(The String Data \ Stuff " )delimiter"    raw字符串字面值能够和宽字面值或Unicode字面值结合起来使用,比如: 
1 u8R"XXX(I'm a "raw UTF-8" string.)XXX"
2 uR"*@(This is a "raw UTF-16" string.)*@"
3 UR"(This is a "raw UTF-32" string.)"     
    语法甜点17:sizeof 
    在C++ 11中,允许sizeof运算符作用在类型的数据成员上,而无须明确的对象。在C++ 03中,这是不允许的,会导致编译错误。比如: 
1 struct SomeType { OtherType member; };
2 sizeof(SomeType::member);        // 在C++ 03中不合法;在C++ 11中合法 
    语法甜点18:新的算法
    C++ 11中新增了一些比较实用的算法。比如all_of、any_of、none_of、copy_n、copy_if和iota等。参考代码如下: 


1 int a[5] = {-2, -1, 0, 1, 2};
2 auto funIsPositive = [](int v){return v>0;};
3 bool bRet = all_of(a, a+5, funIsPositive);             // false
4 bRet = any_of(a, a+5, funIsPositive);                  // true
5 bRet = none_of(a, a+5, funIsPositive);                // false
6 int b[5] = {0};
7 copy_n(a, 5, b);                                                // 将a开始的5个元素拷贝到b中
8 copy_if(a, a+5, b, funIsPositive);                        // 将1, 2两个数拷贝到b中
9 iota(a, a+5, 10);                                               // a中的每个元素加10    
    语法甜点19:泛化的常数表达式
    C++ 03中本来就已经具有常数表示式的概念,比如:3+5,6*7等。常数表示式对编译器来说是优化的机会,编译器常在编译期运行它们并且将值存入程序中。同样地,在许多场合下,C++规范要求使用常数表示式。比如数组大小、枚举值等。
    然而,常数表示式总是在遇到了函数调用时就终结。比如: 
1 int GetFive() { return 5; }
2 int some_value[GetFive() + 5];         // 不合法    C++ 11引进关键字constexpr允许用户保证函数是编译期常数。比如: 
1 constexpr int GetFive() { return 5; }
2 int some_value[GetFive() + 5];    
    语法甜点20:包装引用
    包装引用类似于一般的引用。对于任意对象,我们可以通过模板类ref得到一个包装引用 (至于常引用,则可以通过 cref 得到)。考虑下面的代码:


1 void f (int &r)  { r++; }
2 template<class F, class P> void g (F f, P t)  { f(t); }

4 int n = 0 ;
5 g(f, n) ;
6 cout << n << endl;                     // 輸出0
7 g(f, ref(n));
8 cout << n << endl;                     // 輸出1


 function是一组函数对象包装类的模板,实现了一个泛型的回调机制。function与函数指针比较相似,优点在于它允许用户在目标的实现上拥有更大的弹性,即目标既可以是普通函数,也可以是函数对象和类的成员函数,而且可以给函数添加状态。
    声明一个function时,需要给出所包装的函数对象的返回值类型和各个参数的类型。比如,声明一个function,它返回一个bool类型并接受一个int类型和一个float类型的参数,可以像下面这样:
function<bool (int, float)> f;    下面简要介绍一下function的比较重要的几个接口。
function();    缺省构造函数,创建一个空的函数对象。如果一个空的function被调用,将会抛出一个类型为bad_function_call的异常。
    
template <typename F> function(F g);    这个泛型的构造函数接受一个兼容的函数对象,即这样一个函数或函数对象,它的返回类型与被构造的function的返回类型或者一样,或者可以隐式转换,并且它的参数也要与被构造的function的参数类型或者一样,或者可以隐式转换。注意,也可以使用另外一个function实例来进行构造。这样做,并且function g为空,则被构造的function也为空。使用空的函数指针和空的成员函数指针也会产生空的function。如果这样做,并且function g为空,则被构造的function也为空。使用空的函数指针和空的成员函数指针也会产生空的function。
 
template <typename F> function(reference_wrapper<F> g);    这个构造函数与前一个类似,但它接受的函数对象包装在一个reference_wrapper中,用以避免通过值来传递而产生函数或函数对象的一份拷贝。这同样要求函数对象兼容于function的签名。
 
function& operator=(const function& g);    赋值操作符保存g中的函数或函数对象的一份拷贝;如果g为空,被赋值的函数也将为空。
 
template<typename F> function& operator=(F g);    这个泛型赋值操作符接受一个兼容的函数指针或函数对象。注意,也可以用另一个 function 实例(带有不同但兼容的签名)来赋值。这同样意味着,如果g是另一个function实例且为空,则赋值后的函数也为空。赋值一个空的函数指针或空的成员函数指针也会使function为空。
 
bool empty() const;    这个成员函数返回一个布尔值,表示该function是否含有一个函数或函数对象。如果有一个目标函数或函数对象可被调用,它返回 false 。因为一个function可以在一个布尔上下文中测试,或者与0进行比较,因此这个成员函数可能会在未来版本的库中被取消,你应该避免使用它。
 
void clear();    这个成员函数清除 function, 即它不再关联到一个函数或函数对象。如果function已经是空的,这个调用没有影响。在调用后,function肯定为空。令一个function为空的首选方法是赋0给它;clear 可能在未来版本的库中被取消。
 
result_type operator()(Arg1 a1, Arg2 a2, ..., ArgN aN) const;    调用操作符是调用function的方法。你不能调用一个空的 function ,那样会抛出一个bad_function_call的异常。调用操作符的执行会调用function中的函数或函数对象,并返回它的结果。
    下面分别给出使用function来包装普通函数,函数对象和类的成员函数的参考代码。
    1、普通函数


1 int Add(int x, int y)

3 {
4             return x+y;
5 }
6 function<int (int,int)> f = Add;
7 int z = f(2, 3);    2、函数对象


 1 class CStudent
 2 {
 3 public:
 4             void operator() (string strName, int nAge)
 5             {
 6                 cout << strName << " : " << nAge << endl; 
 7             }
 8 };
 9 
10 CStudent stu;
11 function<void (string, int)> f = stu;
12 f("Mike",  12);    3、类的成员函数


 1 struct TAdd
 2 {
 3     int Add(int x,int y)
 4     {
 5         return x+y;
 6     }
 7 };
 8 
 9 function<int  (TAdd *, int, int)> f = TAdd::Add;
10 TAdd tAdd;
11 f(&tAdd, 2, 3);   // 如果前面的模板参数为传值或引用,直接传入tAdd即可     接下来我们来看看使用function来保存函数对象状态的情况。考虑下面的代码:


 1 class CAdd
 2 {
 3 public:
 4     CAdd():m_nSum(0) { NULL; }
 5     int operator()(int i)
 6     {
 7           m_nSum += i;
 8           return m_nSum;
 9     }
10 
11     int Sum() const 
12     {
13         return m_nSum;
14     }
15 
16 private:
17     int m_nSum;
18 };
19 
20 int main() 
21 {
22     CAdd add;
23     function<int (int)> f1 = add;
24     function<int (int)> f2 = add;
25     cout << f1(10) << "," << f2(10) << "," << add.Sum() << endl;
26     return 0;
27 }     可能和大家想象的结果不一样,上面程序的输出是:10,10,0。我们将同一个函数对象赋值给了两个function,然后分别调用了这两个function,但函数对象中m_nSum的状态并没有被保持,问题出在哪儿呢?这是因为function的缺省行为是拷贝一份传递给它的函数对象,于是f1和f2中保存的都是add对象的拷贝,调用f1和f2后,add对象中的值并没有被修改。
    C++ 11中提供了ref和cref函数,来提供对象的引用和常引用的包装。要使function能够正确地保存函数对象的状态,我们可以这样来修改代码:
1 CAdd add;
2 function<int(int)> f1 = ref(add);
3 function<int(int)> f2 = ref(add);     另外,在两个function之间赋值时,如果源function保存的是函数对象的拷贝,则目标function保存的也是函数对象的拷贝;如果源function保存的是函数对象的引用,则目标function保存的也是函数对象的引用。


 bind是一组用于函数绑定的模板。在对某个函数进行绑定时,可以指定部分参数或全部参数,也可以不指定任何参数,还可以调整各个参数间的顺序。对于未指定的参数,可以使用占位符_1、_2、_3来表示。-1表示绑定后的函数的第1个参数,_2表示绑定后的函数的第2个参数,其他依次类推。
    bind可以绑定到普通函数、函数对象、类的成员函数和类的成员变量。下面分别进行介绍。
    1、普通函数
1 void nine_arguments(int i1,int i2,int i3,int i4,int i5,int i6,int i7,int i8,int i9);
2 int i1=1, i2=2, i3=3, i4=4, i5=5, i6=6, i7=7, i8=8, i9=9;
3 bind(nine_arguments,_9,_2,_1,_6,_3,_8,_4,_5,_7(i1,i2,i3,i4,i5,i6,i7,i8,i9);
4 bind(nine_arguments,i9,i2,i1,i6,i3,i8,_1,_2,_1)(i8,i9);
5 bind(nine_arguments, i9,i2,i1,i6,i3,i8,i4,i5,i7)();    2、函数对象  


1 class CStudent
2 {
3 public:
4             void operator() (string strName, int nAge)
5             {
6                 cout << strName << " : " << nAge << endl; 
7             }
8 };
9 bind(CStudent(), "Mike", _1)(12);    3、类的成员函数


 1 struct TAdd
 2 {
 3     int Add(int x,int y)
 4     {
 5         return x+y;
 6     }
 7 };
 8 TAdd tAdd;
 9 TAdd *p = new TAdd();
10 shared_ptr<TAdd> *q(p);
11 bind(TAdd::Add, tAdd, 2, 3)();
12 bind(TAdd::Add, p, 2, 3)();
13 bind(TAdd::Add, q, 2, 3)();    4、类的成员变量


1 void Output(const string &name)
2 {
3       cout << name << endl;
4 }

6 map<int, string> map1;
7 for_each(map1.begin(), map1.end(), bind(Output, bind(map<int,              
8       string>::value_type::second, _1)));    bind还可以进行嵌套绑定。假设有一个CPerson类,该类有一个获取年龄的接口int GetAge(),现在有一个CPerson对象的vector,需要对其进行排序,则可以如下使用bind:
1 vector<CPerson> vctPerson;
2 sort(vctPerson.begin(), vctPerson.end(), bind(less<int>(), 
3 bind(CPerson::GetAge, _1), bind(CPerson::GetAge, _2)));    假设有一个整数的vector, 现在想要获取其中大于20但小于30的整数的个数,则有:
1 count_if(vctNum.begin(),  vctNum.end, bind(logic_and<bool>(), 
2     bind(greater<int>(), _1, 20), bind(less<int>(), _1, 30)));    在使用bind时,还有一些需要特别注意的地方,下面逐一进行介绍。
    1、对于指定了值的参数,bind返回的函数对象会保存这些值,并且缺省是以传值方式保存的。考虑下面的代码:
1 void inc(int &a)            { a++; }
2 int n = 0;
3 bind(inc, n)();    调用bind返回的函数对象后,n仍然等于0。这是由于bind时,传入的是n的拷贝。如果需要传入n的引用,则可以使用ref或cref函数,比如:
1 bind(inc, ref(n))();            // n现在等于1了    2、bind的第一个参数是一个函数对象,不能用占位符来代替。考虑下面的代码:
1 typedef function<void (int)> Func;
2 vector<Func> vctFunc;
3 for_each(vctFunc.begin(), vctFunc.end(), bind(_1, 5));         // 编译出错    此时,可以借助apply模板。apply模板的第一个参数是传入的函数对象,后面可以有若干个参数,表示函数对象的参数。比如:
1 apply<void>  a;                // void是函数对象的返回值类型
2 a(f);                                 // 相当于调用f()
3 a(f, x);                             // 相当于调用f(x)
4 a(f, x, y);                         // 相当于调用f(x, y)    使用apply后,我们可以将vctFunc中的元素当作占位符传递过来。参考代码如下:
1 for_each(vctFunc.begin(), vctFunc.end(), bind(apply<void>(), _1, 5));


 shared_ptr是一个引用计数智能指针,用于共享对象的所有权。它可以从一个裸指针、另一个shared_ptr、一个auto_ptr、或者一个weak_ptr构造。还可以传递第二个参数给shared_ptr的构造函数,它被称为删除器(deleter)。删除器用于处理共享资源的释放,这对于管理那些不是用new分配也不是用delete释放的资源时非常有用。shared_ptr被创建后,就可以像普通指针一样使用了,除了一点,它不能被显式地删除。shared_ptr的比较重要的接口如下:
    template <class T> 
    explicit shared_ptr(T* p);
    这个构造函数获得给定指针p的所有权。参数p必须是指向T的有效指针。构造后引用计数设为1。唯一从这个构造函数抛出的异常是std::bad_alloc(仅在一种很罕见的情况下发生,即不能获得引用计数器所需的空间)。
 
    template <class T,class D> 
    shared_ptr(T* p,D d);
    这个构造函数带有两个参数。第一个是shared_ptr将要获得所有权的那个资源,第二个是shared_ptr被销毁时负责释放资源的一个对象,被保存的资源将以d(p)的形式传给那个对象。如果引用计数器不能分配成功,shared_ptr抛出一个类型为std::bad_alloc的异常。
 
    shared_ptr(const shared_ptr& r);
    r中保存的资源被新构造的shared_ptr所共享,引用计数加一。这个构造函数不会抛出异常。
 
    template <class T> 
    explicit shared_ptr(const weak_ptr<T>& r);
    从一个weak_ptr构造shared_ptr。这使得weak_ptr的使用具有线程安全性,因为指向weak_ptr参数的共享资源的引用计数将会自增(weak_ptr不影响共享资源的引用计数)。如果weak_ptr为空(r.use_count()==0), shared_ptr抛出一个类型为bad_weak_ptr的异常。
 
    template <typename T> 
    shared_ptr(auto_ptr<T>& r);
    这个构造函数从一个auto_ptr获取r中保存的指针的所有权,方法是保存指针的一份拷贝并对auto_ptr调用release。构造后的引用计数为1,而r则变为空的。如果引用计数器不能分配成功,则抛出std::bad_alloc。
 
    ~shared_ptr();
    shared_ptr析构函数,对引用计数减一。如果计数为零,则保存的指针被删除。删除指针的方法是调用operator delete,或者,如果给定了一个执行删除操作的删除器对象,就把保存的指针作为唯一参数调用这个对象。析构函数不会抛出异常。
 
    shared_ptr& operator=(const shared_ptr& r);
    赋值操作共享r中的资源,并停止对原有资源的共享。赋值操作不会抛出异常。
 
    void reset();
    reset函数用于停止对保存指针的所有权的共享。共享资源的引用计数减一。
 
    T& operator*() const;
    这个操作符返回对已存指针所指向的对象的一个引用。如果指针为空,调用operator*会导致未定义行为。这个操作符不会抛出异常。
 
    T* operator->() const;
    这个操作符返回保存的指针。这个操作符与operator*一起使得智能指针看起来象普通指针。这个操作符不会抛出异常。
 
    T* get() const;
    get函数是当保存的指针有可能为空时(这时 operator* 和 operator-> 都会导致未定义行为)获取它的最好办法。注意,你也可以使用隐式布尔类型转换来测试shared_ptr是否包含有效指针。这个函数不会抛出异常。
 
    bool unique() const;
    这个函数在shared_ptr是它所保存指针的唯一拥有者时返回true;否则返回false。 unique不会抛出异常。
 
    long use_count() const;
    use_count 函数返回指针的引用计数。它在调试的时候特别有用,因为它可以在程序执行的关键点获得引用计数的快照。小心地使用它,因为在某些可能的shared_ptr实现中,计算引用计数可能是昂贵的,甚至是不行的。这个函数不会抛出异常。
 
    operator unspecified-bool-type() const;
    这是个到unspecified-bool-type类型的隐式转换函数,它可以在Boolean上下文中测试一个智能指针。如果shared_ptr保存着一个有效的指针,返回值为True;否则为false。注意,转换函数返回的类型是不确定的。把返回类型当成bool用会导致一些荒谬的操作,所以典型的实现采用了safe bool idiom,它很好地确保了只有可适用的Boolean测试可以使用。这个函数不会抛出异常。
 
    void swap(shared_ptr<T>& b);
    这可以很方便地交换两个shared_ptr。swap函数交换保存的指针(以及它们的引用计数)。这个函数不会抛出异常。
 
    template <typename T,typename U>  shared_ptr<T> static_pointer_cast(const shared_ptr<U>& r);
    要对保存在shared_ptr里的指针执行static_cast,我们可以取出指针然后强制转换它,但我们不能把它存到另一个shared_ptr里;新的shared_ptr会认为它是第一个管理这些资源的。解决的方法是用static_pointer_cast,使用这个函数可以确保被指对象的引用计数保持正确。static_pointer_cast不会抛出异常。
    使用shared_ptr的示例代码如下:


 1 {
 2 shared_ptr<int> pInt1;
 3 assert(pInt1.use_count() == 0);         // 还没有引用指针
 4 {
 5       shared_ptr<int> pInt2(new int(5));
 6       assert(pInt2.use_count() == 1);        // new int(5)这个指针被引用1次
 7 
 8     pInt1 = pInt2;
 9       assert(pInt2.use_count() == 2);       // new int(5)这个指针被引用2次
10     assert(pInt1.use_count() == 2);
11 }                                                   //pInt2离开作用域, 所以new int(5)被引用次数-1
12 
13 assert(pInt1.use_count() == 1);
14 }         // pInt1离开作用域,引用次数-1,现在new int(5)被引用0次,所以销毁它    如果资源的创建销毁不是以new和delete的方式进行的,该怎么办呢?通过前面的接口可以看到,shared_ptr的构造函数中可以指定删除器。示例代码如下: 


 1 class FileCloser
 2 {
 3 public:
 4     void operator()(FILE *pf)
 5     {
 6          if (pf != NULL)
 7          {
 8                fclose(pf);
 9                pf = NULL;
10          }
11     }
12 };
13 
14 shared_ptr<FILE> fp(fopen(pszConfigFile, "r"), FileCloser());    在使用shared_ptr时,需要避免同一个对象指针被两次当成shard_ptr构造函数里的参数的情况。考虑如下代码:


1 {
2      int *pInt = new int(5);
3      shared_ptr<int> temp1(pInt);
4      assert(temp1.use_count() == 1);
5      shared_ptr<int> temp2(pInt);
6      assert(temp2.use_count() == 1);
7 }      // temp1和temp2都离开作用域,它们都销毁pInt,会导致两次释放同一块内存    正确的做法是将原始指针赋给智能指针后,以后的操作都要针对智能指针了。参考代码如下: 
1 {
2      shared_ptr<int> temp1(new int(5));
3      assert(temp1.use_count() == 1);
4      shared_ptr<int> temp2(temp1);
5      assert(temp2.use_count() == 2);
6 }      // temp1和temp2都离开作用域,引用次数变为0,指针被销毁。    另外,使用shared_ptr来包装this时,也会产生与上面类似的问题。考虑如下代码:


 1 class A
 2 {
 3 public:
 4         shared_ptr<A> Get()
 5         {
 6              return shared_ptr<A>(this);
 7         }
 8 }
 9 
10 shared_ptr<A> pA(new A());
11 shared_ptr<A> pB = pA->Get();    当pA和pB离开作用域时,会将堆上的对象释放两次。如何解决上述问题呢?C++ 11提供了如下机制:将类从enable_shared_from_this类派生,获取shared_ptr时使用shared_from_this接口。参考代码如下:


1 class A :public enable_shared_from_this<A>
2 {
3 public:
4         shared_ptr<A> Get()
5         {
6              return shared_from_this();
7         }
8 }    在多线程中使用shared_ptr时,如果存在拷贝或赋值操作,可能会由于同时访问引用计数而导致计数无效。解决方法是向每个线程中传递公共的week_ptr,线程中需要使用shared_ptr时,将week_ptr转换成shared_ptr即可。

unique_ptr是一个独享所有权的智能指针,它提供了一种严格语义上的所有权,包括:
    1、拥有它所指向的对象。
    2、无法进行复制构造,也无法进行复制赋值操作。也就是说,我们无法得到指向同一个对象的两个unique_ptr。但是可以进行移动构造和移动赋值操作。
    3、保存指向某个对象的指针,当它本身被删除释放的时候(比如,离开了某个作用域),会使用给定的删除器释放它指向的对象。
    使用unique_ptr,可以实现以下功能,包括:
    1、为动态申请的内存提供异常安全。
    2、将动态申请内存的所有权传递给某个函数。
    3、从某个函数返回动态申请内存的所有权。
    4、在容器中保存指针。
    5、所有auto_ptr应该具有的(但无法在C++ 03中实现的)功能。
    下面是一段传统的会产生不安全异常的代码: 
1 X* f()
2 {
3     X* p = new X;
4     // 做一些事情,可能会抛出某个异常
5    return p;
6 }    解决方法是,使用unique_ptr来管理这个对象的所有权,由其进行这个对象的释放工作。 
1 X* f()
2 {
3      unique_ptr<X> p(new X);
4      // 做一些事情,可能会抛出异常
5     return p.release();
6 }    如果程序执行过程中抛出了异常,unique_ptr就会释放它所指向的对象。但是,除非我们真的需要返回一个内建的指针,我们还可以返回一个unique_ptr。 
1 unique_ptr<X> f()
2 {
3       unique_ptr<X> p(new X);
4       // 做一些事情,可能会抛出异常
5     return p;
6 }    现在,我们可以这样使用函数f(): 
1 void g()
2 {
3        unique_ptr<X> q = f();              // 使用移动构造函数(move constructor)
4      q->DoSomething();                   // 使用q
5        X x = *q;                                  // 复制指针q所指向的对象
6 }    // 在函数退出的时候,q以及它所指向的对象都被删除释放    unique_ptr具有移动语义,所以我们可以使用函数f()返回的右值对q进行初始化,这样就简单地将所有权传递给了q。

 week_ptr是对对象的一种弱引用,它不会增加对象的引用计数。week_ptr和shared_ptr之间可以相互转换,shared_ptr可以直接赋值给week_ptr,week_ptr可通过调用lock函数来获得shared_ptr(如果对象已经被释放,则返回一个空的shared_ptr)。
    单纯使用shared_ptr有时会产生问题,考虑下面的代码:


 1 class A;
 2 class B;
 3 typedef shared_ptr<A> A_Share;
 4 typedef shared_ptr<B> B_Share;
 5 class A
 6 {
 7 public:
 8         B_Share m_b;
 9 };
10 
11 class B
12 {
13 public:
14         A_Share m_a;
15 };
16 
17 A_Share a(new A());
18 B_Share b(new B());
19 a.m_b = b;
20 b.m_a = a;    在上面的代码中,a和b相互进行引用。在a和b离开作用域时,a和b的引用计数都是1,内存没有正常释放。解决方法是将A和B中的任意一个类声明的变量改为week_ptr类型的。比如,修改类B后的代码如下:
1 class B
2 {
3 public:
4        week_ptr<A>  m_a;
5 }    修改后,b.m_a = a不会增加A对象的引用计数,因此a离开作用域时,引用计数为0。B对象的引用计数为2,在a和b离开作用域时,引用计数各减1后也为0。


 在C++ 11中,可以使用异步任务async和future配合来完成一些轻量级的并发编程工作。async使用比较简单,只需要传入要并发执行的函数即可。future用于占位,以获取并发执行的函数的结果;调用其get接口时,将等待并发任务结束,并返回结果。下面的代码演示了async和future的基本用法:
1 int f() { return 100; }
2 future<int> fu = async(f);
3 int nRet = fu.get();    如果要完成多任务交互,操作互斥体等复杂功能,则可以使用C++ 11提供的线程类thread。可以在thread类的构造函数中传入要在线程中执行的函数及其参数,其join方法用于等待线程结束。示例代码如下:


 1 void count()
 2 {
 3     for (int i = 0; i < 10; ++i)
 4     {
 5         cout << i << endl;
 6     }
 7 }
 8 
 9 thread th1(count); 
10 th1.join();    C++ 11还提供了一套用于线程间同步的机制,包括mutext、lock_guard、unique_lock、try_to_lock_t、condition_variable等等。使用起来都比较简单,这里就不再赘述了。



Lambda Expression Syntax

Visual Studio 2015
 

This article demonstrates the syntax and structural elements of lambda expressions. For a description of lambda expressions, see Lambda Expressions in C++.

Function Objects vs. Lambdas

When you write code, you probably use function pointers and function objects to solve problems and perform calculations, especially when you use STL algorithms. Function pointers and function objects each have advantages and disadvantages—for example, function pointers have minimal syntactic overhead but do not retain state within a scope, and function objects can maintain state but require the syntactic overhead of a class definition.

A lambda combines the benefits of function pointers and function objects and avoids their disadvantages. Like a function objects, a lambda is flexible and can maintain state, but unlike a function object, its compact syntax doesn't require an explicit class definition. By using lambdas, you can write code that's less cumbersome and less prone to errors than the code for an equivalent function object.

The following examples compare the use of a lambda to the use of a function object. The first example uses a lambda to print to the console whether each element in a vector object is even or odd. The second example uses a function object to accomplish the same task.

Example 1: Using a Lambda

This example passes a lambda to the for_each function. The lambda prints a result that states whether each element in a vector object is even or odd.

Code

C++
// even_lambda.cpp
// compile with: cl /EHsc /nologo /W4 /MTd
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() 
{
   // Create a vector object that contains 10 elements.
   vector<int> v;
   for (int i = 1; i < 10; ++i) {
      v.push_back(i);
   }

   // Count the number of even numbers in the vector by 
   // using the for_each function and a lambda.
   int evenCount = 0;
   for_each(v.begin(), v.end(), [&evenCount] (int n) {
      cout << n;
      if (n % 2 == 0) {
         cout << " is even " << endl;
         ++evenCount;
      } else {
         cout << " is odd " << endl;
      }
   });

   // Print the count of even numbers to the console.
   cout << "There are " << evenCount 
        << " even numbers in the vector." << endl;
}

Output

1 is odd
2 is even
3 is odd
4 is even
5 is odd
6 is even
7 is odd
8 is even
9 is odd
There are 4 even numbers in the vector.

Comments

In the example, the third argument to the for_each function is a lambda. The [&evenCount] part specifies the capture clause of the expression, (int n) specifies the parameter list, and remaining part specifies the body of the expression.

Example 2: Using a Function Object

Sometimes a lambda would be too unwieldy to extend much further than the previous example. The next example uses a function object instead of a lambda, together with the for_each function, to produce the same results as Example 1. Both examples store the count of even numbers in a vector object. To maintain the state of the operation, the FunctorClass class stores the m_evenCount variable by reference as a member variable. To perform the operation, FunctorClass implements the function-call operator, operator(). The Visual C++ compiler generates code that is comparable in size and performance to the lambda code in Example 1. For a basic problem like the one in this article, the simpler lambda design is probably better than the function-object design. However, if you think that the functionality might require significant expansion in the future, then use a function object design so that code maintenance will be easier.

For more information about the operator(), see Function Call (C++). For more information about the for_eachfunction, see for_each.

Code

C++
// even_functor.cpp
// compile with: /EHsc
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

class FunctorClass
{
public:
    // The required constructor for this example.
    explicit FunctorClass(int& evenCount)
        : m_evenCount(evenCount) { }

    // The function-call operator prints whether the number is
    // even or odd. If the number is even, this method updates
    // the counter.
    void operator()(int n) const {
        cout << n;

        if (n % 2 == 0) {
            cout << " is even " << endl;
            ++m_evenCount;
        } else {
            cout << " is odd " << endl;
        }
    }

private:
    // Default assignment operator to silence warning C4512.
    FunctorClass& operator=(const FunctorClass&);

    int& m_evenCount; // the number of even variables in the vector.
};


int main()
{
    // Create a vector object that contains 10 elements.
    vector<int> v;
    for (int i = 1; i < 10; ++i) {
        v.push_back(i);
    }

    // Count the number of even numbers in the vector by 
    // using the for_each function and a function object.
    int evenCount = 0;
    for_each(v.begin(), v.end(), FunctorClass(evenCount));

    // Print the count of even numbers to the console.
    cout << "There are " << evenCount
        << " even numbers in the vector." << endl;
}

Output

1 is odd
2 is even
3 is odd
4 is even
5 is odd
6 is even
7 is odd
8 is even
9 is odd
There are 4 even numbers in the vector.

Lambda Expressions in C++

Visual Studio 2015
 

In C++11, a lambda expression—often called a lambda—is a convenient way of defining an anonymous function object right at the location where it is invoked or passed as an argument to a function. Typically lambdas are used to encapsulate a few lines of code that are passed to algorithms or asynchronous methods. This article defines what lambdas are, compares them to other programming techniques, describes their advantages, and provides a basic example.

Parts of a Lambda Expression

The ISO C++ Standard shows a simple lambda that is passed as the third argument to the std::sort() function:

C++
#include <algorithm>
#include <cmath>

void abssort(float* x, unsigned n) {
    std::sort(x, x + n,
        // Lambda expression begins
        [](float a, float b) {
            return (std::abs(a) < std::abs(b));
        } // end of lambda expression
    );
}

This illustration shows the parts of a lambda:

Structural elements of a lambda expression
  1. capture clause (Also known as the lambda-introducer in the C++ specification.)

  2. parameter list Optional. (Also known as the lambda declarator)

  3. mutable specification Optional.

  4. exception-specification Optional.

  5. trailing-return-type Optional.

  6. lambda body)

Capture Clause

A lambda can introduce new variables in its body (in C++14), and it can also access—or capture--variables from the surrounding scope. A lambda begins with the capture clause (lambda-introducer in the Standard syntax), which specifies which variables are captured, and whether the capture is by value or by reference. Variables that have the ampersand (&) prefix are accessed by reference and variables that do not have it are accessed by value.

An empty capture clause, [ ], indicates that the body of the lambda expression accesses no variables in the enclosing scope.

You can use the default capture mode (capture-default in the Standard syntax) to indicate how to capture any outside variables that are referenced in the lambda: [&] means all variables that you refer to are captured by reference, and [=] means they are captured by value. You can use a default capture mode, and then specify the opposite mode explicitly for specific variables. For example, if a lambda body accesses the external variable total by reference and the external variable factor by value, then the following capture clauses are equivalent:

C++
[&total, factor]
[factor, &total]
[&, factor]
[factor, &]
[=, &total]
[&total, =]

Only variables that are mentioned in the lambda are captured when a capture-default is used.

If a capture clause includes a capture-default &, then no identifier in a capture of that capture clause can have the form & identifier. Likewise, if the capture clause includes a capture-default =, then no capture of that capture clause can have the form = identifier. An identifier or this cannot appear more than once in a capture clause. The following code snippet illustrates some examples.

C++
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 followed by an ellipsis is a pack expansion, as shown in this variadic template example:

C++
template<class... Args>
void f(Args... args) {
    auto x = [args...] { return g(args...); };
    x();
}

To use lambda expressions in the body of a class method, pass the this pointer to the capture clause to provide access to the methods and data members of the enclosing class. For an example that shows how to use lambda expressions with class methods, see "Example: Using a Lambda Expression in a Method" in Examples of Lambda Expressions.

When you use the capture clause, we recommend that you keep these points in mind, particularly when you use lambdas with multithreading:

  • Reference captures can be used to modify variables outside, but value captures cannot. (mutable allows copies to be modified, but not originals.)

  • Reference captures reflect updates to variables outside, but value captures do not.

  • Reference captures introduce a lifetime dependency, but value captures have no lifetime dependencies. This is especially important when the lambda runs asynchronously. If you capture a local by reference in an async lambda, that local will very possibly be gone by the time the lambda runs, resulting in an access violation at run time.

Generalized capture (C++ 14)

In C++14, you can introduce and initialize new variables in the capture clause, without the need to have those variables exist in the lambda function’s enclosing scope. The initialization can be expressed as any arbitrary expression; the type of the new variable is deduced from the type produced by the expression. One benefit of this feature is that in C++14 you can capture move-only variables (such as std::unique_ptr) from the surrounding scope and use them in a lambda.

pNums = make_unique<vector<int>>(nums);
//...
      auto a = [ptr = move(pNums)]()
        {
           // use ptr
        };

Parameter List

In addition to capturing variables, a lambda can accept input parameters. A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function.

int y = [] (int first, int second)
{
    return first + second;
};

In C++ 14, if the parameter type is generic, you can use the auto keyword as the type specifier. This tells the compiler to create the function call operator as a template. Each instance of auto in a parameter list is equivalent to a distinct type parameter.

auto y = [] (auto first, auto second)
{
    return first + second;
};

A lambda expression can take another lambda expression as its argument. For more information, see "Higher-Order Lambda Expressions" in the topic Examples of Lambda Expressions.

Because a parameter list is optional, you can omit the empty parentheses if you do not pass arguments to the lambda expression and its lambda-declarator: does not contain exception-specificationtrailing-return-type, or mutable.

Mutable Specification

Typically, a lambda's function call operator is const-by-value, but use of the mutable keyword cancels this out. It does not produce mutable data members. The mutable specification enables the body of a lambda expression to modify variables that are captured by value. Some of the examples later in this article show how to use mutable.

Exception Specification

You can use the throw() exception specification to indicate that the lambda expression does not throw any exceptions. As with ordinary functions, the Visual C++ compiler generates warning C4297 if a lambda expression declares the throw() exception specification and the lambda body throws an exception, as shown here:

C++
// throw_lambda_expression.cpp
// compile with: /W4 /EHsc 
int main() // C4297 expected
{
   []() throw() { throw 5; }();
}

For more information, see Exception Specifications (throw) (C++).

Return Type

The return type of a lambda expression is automatically deduced. You don't have to use the auto keyword unless you specify a trailing-return-type. The trailing-return-type resembles the return-type part of an ordinary method or function. However, the return type must follow the parameter list, and you must include the trailing-return-type keyword -> before the return type.

You can omit the return-type part of a lambda expression if the lambda body contains just one return statement or the expression does not return a value. If the lambda body contains one return statement, the compiler deduces the return type from the type of the return expression. Otherwise, the compiler deduces the return type to be void. Consider the following example code snippets that illustrate this principle.

C++
auto x1 = [](int i){ return i; }; // OK: return type is int
auto x2 = []{ return{ 1, 2 }; };  // ERROR: return type is void, deducing 
                                  // return type from braced-init-list is not valid

A lambda expression can produce another lambda expression as its return value. For more information, see "Higher-Order Lambda Expressions" in Examples of Lambda Expressions.

Lambda Body

The lambda body (compound-statement in the Standard syntax) of a lambda expression can contain anything that the body of an ordinary method or function can contain. The body of both an ordinary function and a lambda expression can access these kinds of variables:

  • Captured variables from the enclosing scope, as described previously.

  • Parameters

  • Locally-declared variables

  • Class data members, when declared inside a class and this is captured

  • Any variable that has static storage duration—for example, global variables

The following example contains a lambda expression that explicitly captures the variable n by value and implicitly captures the variable m by reference:

C++
// captures_lambda_expression.cpp
// compile with: /W4 /EHsc 
#include <iostream>
using namespace std;

int main()
{
   int m = 0;
   int n = 0;
   [&, n] (int a) mutable { m = ++n + a; }(4);
   cout << m << endl << n << endl;
}

Output:

5
0

Because the variable n is captured by value, its value remains 0 after the call to the lambda expression. The mutablespecification allows n to be modified within the lambda.

Although a lambda expression can only capture variables that have automatic storage duration, you can use variables that have static storage duration in the body of a lambda expression. The following example uses the generatefunction and a lambda expression to assign a value to each element in a vector object. The lambda expression modifies the static variable to generate the value of the next element.

C++
void fillVector(vector<int>& v)
{
    // A local static variable.
    static int nextValue = 1;

    // The lambda expression that appears in the following call to
    // the generate function modifies and uses the local static 
    // variable nextValue.
    generate(v.begin(), v.end(), [] { return nextValue++; }); 
    //WARNING: this is not thread-safe and is shown for illustration only
}

For more information, see generate.

The following code example uses the function from the previous example, and adds an example of a lambda expression that uses the STL algorithm generate_n. This lambda expression assigns an element of a vector object to the sum of the previous two elements. The mutable keyword is used so that the body of the lambda expression can modify its copies of the external variables x and y, which the lambda expression captures by value. Because the lambda expression captures the original variables x and y by value, their values remain 1 after the lambda executes.

C++
// compile with: /W4 /EHsc
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>

using namespace std;

template <typename C> void print(const string& s, const C& c) {
    cout << s;

    for (const auto& e : c) {
        cout << e << " ";
    }

    cout << endl;
}

void fillVector(vector<int>& v)
{
    // A local static variable.
    static int nextValue = 1;

    // The lambda expression that appears in the following call to
    // the generate function modifies and uses the local static 
    // variable nextValue.
    generate(v.begin(), v.end(), [] { return nextValue++; });
    //WARNING: this is not thread-safe and is shown for illustration only
}

int main()
{
    // The number of elements in the vector.
    const int elementCount = 9;

    // Create a vector object with each element set to 1.
    vector<int> v(elementCount, 1);

    // These variables hold the previous two elements of the vector.
    int x = 1;
    int y = 1;

    // Sets each element in the vector to the sum of the 
    // previous two elements.
    generate_n(v.begin() + 2,
        elementCount - 2,
        [=]() mutable throw() -> int { // lambda is the 3rd parameter
        // Generate current value.
        int n = x + y;
        // Update previous two values.
        x = y;
        y = n;
        return n;
    });
    print("vector v after call to generate_n() with lambda: ", v);

    // Print the local variables x and y.
    // The values of x and y hold their initial values because 
    // they are captured by value.
    cout << "x: " << x << " y: " << y << endl;

    // Fill the vector with a sequence of numbers
    fillVector(v);
    print("vector v after 1st call to fillVector(): ", v);
    // Fill the vector with the next sequence of numbers
    fillVector(v);
    print("vector v after 2nd call to fillVector(): ", v);
}

Output:

vector v after call to generate_n() with lambda: 1 1 2 3 5 8 13 21 34
x: 1 y: 1
vector v after 1st call to fillVector(): 1 2 3 4 5 6 7 8 9
vector v after 2nd call to fillVector(): 10 11 12 13 14 15 16 17 18

For more information, see generate_n.

Microsoft-Specific

Lambdas are not supported in the following common language runtime (CLR) managed entities: ref classref struct,value class, or value struct.

If you are using a Microsoft-specific modifier such as __declspec, you can insert it into a lambda expression immediately after the parameter-declaration-clause—for example:

C++
auto Sqr = [](int t) __declspec(code_seg("PagedMem")) -> int { return t*t; };

To determine whether a modifier is supported by lambdas, see the article about it in the Microsoft-Specific Modifierssection of the documentation.

Visual Studio supports C++11 Standard lambda expression syntax and functionality, with these exceptions:

  • Like all other classes, lambdas don't get automatically generated move constructors and move assignment operators. For more information about support for rvalue reference behaviors, see the "Rvalue References" section in Support For C++11/14/17 Features (Modern C++).

  • The optional attribute-specifier-seq is not supported in this version.

Visual Studio includes these features in addition to C++11 Standard lambda functionality:

  • Stateless lambdas, which are omni-convertible to function pointers that use arbitrary calling conventions.

  • Automatically deduced return types for lambda bodies that are more complicated than { return expression; }, as long as all return statements have the same type. (This functionality is part of the proposed C++14 Standard.)



尽管C++社区对C++ 0x很是追捧,但是各厂商对于新标准的支持并不热乎。盼星星盼月亮,微软作为Windows平台上最强势的C++编译器厂商也终于在Visual Studio 2010中开始支持C++ 0x的特性。

 

Lambda表达式,auto 和静态断言(static_assert)

Visual Studio 2010中的Visual C++编译器,即VC10, 包含了4个C++ 0x的语言特性 - lambda表达式,auto,static_assert 和 rvalue reference (右值引用).


 

相关链接:

 

lambdas

使用过函数式编程语言(如lisp, F#)或一些动态语言(如Python,Javascript)的大侠对于lambda表达式一定不会陌生。

在C++ 0x中,引入了lambda表达式来定义无名仿函数。下面是一个lambda表达式的简单例子:

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: green;">// File: meow.cpp<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#include </span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);"><algorithm><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#include </span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);"><iostream><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#include </span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);"><ostream><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#include </span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);"><vector><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">using namespace </span>std;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 10; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    for_each(v.begin(), v.end(), [](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) { cout << n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>; });<br style="margin: 0px; padding: 0px;" />    cout << endl;    <br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">return </span>0;<br style="margin: 0px; padding: 0px;" />}

 

C:\Temp>cl /EHsc /nologo /W4 meow.cpp > NUL && meow

0 1 2 3 4 5 6 7 8 9

 

for_each一行中,中括号[]称为lambda introducer, 它告诉编译器接下来的是一个lambda表达式;接下来(int n)是lambda表达式的参数声明;最后大括号里边就是“函数体”了。

注意这里因为lambda表达式生成的是functor,所以“函数体”实际上是指这个functor的operator ()的调用部分。你也许会问:那么返回值呢?缺省情况下lambda表达式生成的functor调用

返回类型为void.

 

所以,可以理解为上边的代码会被编译器翻译成如下:

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#include </span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);"><algorithm><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#include </span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);"><iostream><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#include </span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);"><ostream><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#include </span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);"><vector><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">using namespace </span>std;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">struct </span>LambdaFunctor {<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">void operator</span>()(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const </span>{<br style="margin: 0px; padding: 0px;" />        cout << n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>;<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" />};<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 10; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    for_each(v.begin(), v.end(), LambdaFunctor());<br style="margin: 0px; padding: 0px;" />    cout << endl;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">return </span>0;<br style="margin: 0px; padding: 0px;" />}
为了方便,以下会用"lambda返回void"的简短表述来代替冗长啰嗦的表述:lambda表达式生成一个functor类型,这个functor类型的函数调用操作符(operator())返回的类型是void.<br style="margin: 0px; padding: 0px;" />请大家一定记住:lambda表达式生成了类型,并构造该类型的实例。

 

下面的例子中lambda表达式的“函数体”包含多条语句:

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#include </span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);"><algorithm><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#include </span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);"><iostream><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#include </span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);"><ostream><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#include </span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);"><vector><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">using namespace </span>std;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 10; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    for_each(v.begin(), v.end(), [](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) {<br style="margin: 0px; padding: 0px;" />        cout << n;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />        <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">if </span>(n % 2 == 0) {<br style="margin: 0px; padding: 0px;" />            cout << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" even "</span>;<br style="margin: 0px; padding: 0px;" />        } <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">else </span>{<br style="margin: 0px; padding: 0px;" />            cout << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" odd "</span>;<br style="margin: 0px; padding: 0px;" />        }<br style="margin: 0px; padding: 0px;" />    });<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    cout << endl;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">return </span>0;<br style="margin: 0px; padding: 0px;" />}
上文提到了lambda表达式缺省情况下返回void. 那么如果需要返回其他类型呢?<br style="margin: 0px; padding: 0px;" />答案是:lambda表达式的“函数体”中如果有一个return的表达式,例如{ return expression; },那么编译器将自动推演expression的类型作为返回类型。

#include <algorithm> 
#include <deque> 
#include <iostream> 
#include <iterator> 
#include <ostream> 
#include <vector> 

using namespace std; 

int main() { 
    vector<int> v; 

    for (int i = 0; i < 10; ++i) { 
        v.push_back(i); 
    } 

    deque<int> d; 

    transform(v.begin(), v.end(), front_inserter(d), [](int n) { return n * n * n; }); 

    for_each(d.begin(), d.end(), [](int n) { cout << n << " "; }); 

    cout << endl; 

上例中返回值n * n * n很简单,类型推演是显而易见的。但是如果lambda表达式中有非常复杂的表达式时,编译器可以无法推演出其类型,或者是推演出现二义性,这时候你可以

显式地指明返回值类型。如下所示:

 

transform(v.begin(), v.end(), front_inserter(d), <span style="margin: 0px; padding: 0px;">[](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) -> <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">double </span>{<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">if </span>(n % 2 == 0) {<br style="margin: 0px; padding: 0px;" />        <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">return </span>n * n * n;<br style="margin: 0px; padding: 0px;" />    } <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">else </span>{<br style="margin: 0px; padding: 0px;" />        <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">return </span>n / 2.0;<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" />}</span>);

 

黑体部分中有的“-> double”显式地指明了lambda表达式的返回类型是double.

 

以上例子中的lambda都是无状态的(stateless),不包含任何数据成员。很多时候我们需要lambda包含数据成员以保存状态,这一点可以通过“捕获”(capturing)局部变量来实现。

lambda表达式的导入符(lambda-introducer)是空的,也就是“[]”,表明该lambda是一个无状态的。但是在lambda导入符中可以指定一个“捕获列表”(capture-list)。

 

 

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 10; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>x = 0;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>y = 0;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    cout << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">"Input: "</span>;<br style="margin: 0px; padding: 0px;" />    cin >> x >> y;<br style="margin: 0px; padding: 0px;" />    v.erase(remove_if(v.begin(), v.end(), [x, y](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) { <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">return </span>x < n && n < y; }), v.end());<br style="margin: 0px; padding: 0px;" />    for_each(v.begin(), v.end(), [](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) { cout << n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>; });<br style="margin: 0px; padding: 0px;" />    cout << endl;<br style="margin: 0px; padding: 0px;" />}

 

上边的代码中的lambda使用了局部变量x和y,将值介于x和y之间的元素从集合中删除。

程序运行示例如下 -

Input: 4 7

0 1 2 3 4 7 8 9

 

上边的代码可以理解为:

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">class </span>LambdaFunctor {<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">public</span>:<br style="margin: 0px; padding: 0px;" />    LambdaFunctor(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>a, <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>b) : m_a(a), m_b(b) { }<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">bool operator</span>()(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const </span>{ <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">return </span>m_a < n && n < m_b; }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">private</span>:<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>m_a;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>m_b;<br style="margin: 0px; padding: 0px;" />};<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 10; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>x = 0;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>y = 0;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    cout << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">"Input: "</span>;<br style="margin: 0px; padding: 0px;" />    cin >> x >> y;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    v.erase(remove_if(v.begin(), v.end(), LambdaFunctor(x, y)), v.end());<br style="margin: 0px; padding: 0px;" />    copy(v.begin(), v.end(), ostream_iterator<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>>(cout, <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>));<br style="margin: 0px; padding: 0px;" />    cout << endl;<br style="margin: 0px; padding: 0px;" />}

 

上面代码中很重要的一点信息是:lambda中捕获的局部变量是以“传值”的方式传给匿名函数对象的。在匿名函数对象中,保存有“捕获列表”中局部变量的拷贝。

这一点使得匿名函数对象的生命周期能够长于main中的x,y局部变量。然而这样的传值方式带来几个限制:

  1. lambda中的这两个拷贝并不能被改变,因为缺省情况下函数对象的operator()是const;
  2. 有的对象的拷贝操作开销很大或者不可能(例如如果上面代码中的x, y是数据库链接或者某个singleton)
  3. 即使在lambda内部修改了m_a, m_b也不能够影响外边main函数中的x和y

 

既然有了“传值”,你一定猜到了还会有“传引用”。bingo! 你是对的。

在讨论“传引用”之前,我们先来看看另一个比较有用的东西。假设你有一大堆的局部变量需要被lambda使用,那么你的“捕获列表”将会写的很长,这肯定不是件愉快的事情。

好在C++委员会的老头们也想到了,C++ 0x中提供了一个省心的东西:如果捕获列表写成 [=],表示lambda将捕获所有的局部变量,当然也是传值方式。这种方式姑且被称为“缺省捕获”(capture-default)。

 

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 10; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>x = 0;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>y = 0;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    cout << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">"Input: "</span>;<br style="margin: 0px; padding: 0px;" />    cin >> x >> y; <span style="margin: 0px; padding: 0px; line-height: 1.5; color: green;">// EVIL!<br style="margin: 0px; padding: 0px;" />    </span>v.erase(remove_if(v.begin(), v.end(), [=](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) { <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">return </span>x < n && n < y; }), v.end());<br style="margin: 0px; padding: 0px;" />    for_each(v.begin(), v.end(), [](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) { cout << n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>; });<br style="margin: 0px; padding: 0px;" />    cout << endl;<br style="margin: 0px; padding: 0px;" />}

 

当编译器在lambda的作用范围内看到局部变量x, y时,它会以传值的方式从main函数中将他们捕获。

下面我们来看如何突破前面提到的3点限制。

 

第一点,修改lambda表达式中的局部变量拷贝(e.g. m_a, m_b)

缺省情况下,lambda的operator ()是const 修饰的,但是你可以使用mutable关键字改变这一点。

 

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 10; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>x = 1;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>y = 1;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    for_each(v.begin(), v.end(), [=](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>& r) <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">mutable </span>{<br style="margin: 0px; padding: 0px;" />        <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const int </span>old = r;<br style="margin: 0px; padding: 0px;" />        r *= x * y;<br style="margin: 0px; padding: 0px;" />        x = y;<br style="margin: 0px; padding: 0px;" />        y = old;<br style="margin: 0px; padding: 0px;" />    });<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    for_each(v.begin(), v.end(), [](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) { cout << n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>; });<br style="margin: 0px; padding: 0px;" />    cout << endl;<br style="margin: 0px; padding: 0px;" />    cout << x << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">", " </span><< y << endl;<br style="margin: 0px; padding: 0px;" />}

 

代码运行结果如下

0 0 0 6 24 60 120 210 336 504

1, 1

 

这里我们解决了第一个限制,但是却产生了一个新的限制

4.  lambda中对捕获变量的修改并不会影响到main函数中的局部变量,因为lambda捕获局部变量使用的是传值方式

 

下面该“传引用”的方式登场了,它能够有效地解决2,3,4三个限制。

 

传引用的语法为: lambda-introducer [&x, &y]

这里的捕获列表应该理解为:X& x, Y& y ; 因为我们实际上是取的x,y的引用而不是地址。

 

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 10; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>x = 1;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>y = 1;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    for_each(v.begin(), v.end(), [&x, &y](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>& r) {<br style="margin: 0px; padding: 0px;" />        <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const int </span>old = r;<br style="margin: 0px; padding: 0px;" />        r *= x * y;<br style="margin: 0px; padding: 0px;" />        x = y;<br style="margin: 0px; padding: 0px;" />        y = old;<br style="margin: 0px; padding: 0px;" />    });<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    for_each(v.begin(), v.end(), [](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) { cout << n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>; });<br style="margin: 0px; padding: 0px;" />    cout << endl;<br style="margin: 0px; padding: 0px;" />    cout << x << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">", " </span><< y << endl;<br style="margin: 0px; padding: 0px;" />}

 

运行结果如下 -

0 0 0 6 24 60 120 210 336 504

8, 9

 

上面代码会被编译器“翻译”成:

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#pragma warning</span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">push</span>)<br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#pragma warning</span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">disable</span>: 4512) <span style="margin: 0px; padding: 0px; line-height: 1.5; color: green;">// assignment operator could not be generated<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">class </span>LambdaFunctor {<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">public</span>:<br style="margin: 0px; padding: 0px;" />    LambdaFunctor(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>& a, <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>& b) : m_a(a), m_b(b) { }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">void operator</span>()(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>& r) <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const </span>{<br style="margin: 0px; padding: 0px;" />        <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const int </span>old = r;<br style="margin: 0px; padding: 0px;" />        r *= m_a * m_b;<br style="margin: 0px; padding: 0px;" />        m_a = m_b;<br style="margin: 0px; padding: 0px;" />        m_b = old;<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">private</span>:<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>& m_a;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>& m_b;<br style="margin: 0px; padding: 0px;" />};<br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">#pragma warning</span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">pop</span>)<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 10; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>x = 1;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>y = 1;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    for_each(v.begin(), v.end(), LambdaFunctor(x, y));<br style="margin: 0px; padding: 0px;" />    copy(v.begin(), v.end(), ostream_iterator<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>>(cout, <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>));<br style="margin: 0px; padding: 0px;" />    cout << endl;<br style="margin: 0px; padding: 0px;" />    cout << x << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">", " </span><< y << endl;<br style="margin: 0px; padding: 0px;" />}

 

注意:当你使用lambda时,VC10编译器会为lambda的定义部分自动禁用C4512警告。

当以传引用方式捕获局部变量时,lambda的函数对象在自己内部以引用方式保存main函数中的局部变量。

当然因为使用的是局部对象的引用,使用lambda表达式时一定要注意不能够超出局部变量的生命周期。

 

和上文提高的[=]类似,我们可以用[&]来以“传引用”的方式捕获所有的局部变量。

 

到目前为止,局部变量的捕获方式要么是“值语义”要么是“引用语义”,那么可以混合这两种方式吗?可以!

例如:[a, b, c, &d, e, &f, g],其中变量d和f是按引用语义捕获,而a,b,c,e和g是按值语义捕获。

 

另外很有用的一点是:你可以指定一个缺省捕获(capture-default),然后重载(override)某些局部变量的捕获方式。

下边例子中[=, &sum, &product]告诉编译器用值语义方式捕获所有的局部变量,但是有两个例外 - sum和product是按引用语义来捕获。

 

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 10; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>sum = 0;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>product = 1;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>x = 1;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>y = 1;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px;">    for_each(v.begin(), v.end(), [=, &sum, &product](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>& r) <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">mutable </span>{<br style="margin: 0px; padding: 0px;" />        sum += r;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />        <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">if </span>(r != 0) {<br style="margin: 0px; padding: 0px;" />            product *= r;<br style="margin: 0px; padding: 0px;" />        }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />        <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const int </span>old = r;<br style="margin: 0px; padding: 0px;" />        r *= x * y;<br style="margin: 0px; padding: 0px;" />        x = y;<br style="margin: 0px; padding: 0px;" />        y = old;<br style="margin: 0px; padding: 0px;" />    });<br style="margin: 0px; padding: 0px;" /></span>
    for_each(v.begin(), v.end(), [](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) { cout << n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>; });<br style="margin: 0px; padding: 0px;" />    cout << endl;<br style="margin: 0px; padding: 0px;" />    cout << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">"sum: " </span><< sum << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">", product: " </span><< product << endl;<br style="margin: 0px; padding: 0px;" />    cout << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">"x: " </span><< x << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">", y: " </span><< y << endl;<br style="margin: 0px; padding: 0px;" />}<br style="margin: 0px; padding: 0px;" />

 

运行结果如下 -

0 0 0 6 24 60 120 210 336 504

sum: 45, product: 362880

x: 1, y: 1

 

再来看看下边的代码 - 在lambda中使用类成员变量

 

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">class </span>Kitty {<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">public</span>:<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">explicit </span>Kitty(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>toys) : m_toys(toys) { }<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">void </span>meow(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const </span>vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>>& v) <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const </span>{<br style="margin: 0px; padding: 0px;" />        for_each(v.begin(), v.end(), [m_toys](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) {<br style="margin: 0px; padding: 0px;" />            cout << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">"If you gave me " </span><< n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" toys, I would have " </span><< n + m_toys << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" toys total." </span><< endl;<br style="margin: 0px; padding: 0px;" />        });<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">private</span>:<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>m_toys;<br style="margin: 0px; padding: 0px;" />};<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 3; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    Kitty k(5);<br style="margin: 0px; padding: 0px;" />    k.meow(v);<br style="margin: 0px; padding: 0px;" />}

 

不幸的是,编译这段代码将产生这样的错误:

 error C3480: 'Kitty::m_toys': a lambda capture variable must be from an enclosing function scope

为什么呢?lambda表达式能够让你不活局部变量,但是类的数据成员并不是局部变量。

解决方案呢?别着急。lambda为捕获类的数据成员大开方便之门,你可以捕获this指针。

 

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">class </span>Kitty {<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">public</span>:<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">explicit </span>Kitty(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>toys) : m_toys(toys) { }<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">void </span>meow(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const </span>vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>>& v) <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const </span>{<br style="margin: 0px; padding: 0px;" />        for_each(v.begin(), v.end(), <span style="margin: 0px; padding: 0px;">[<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">this</span>]</span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) {<br style="margin: 0px; padding: 0px;" />            cout << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">"If you gave me " </span><< n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" toys, I would have " </span><< n + m_toys << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" toys total." </span><< endl;<br style="margin: 0px; padding: 0px;" />        });<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">private</span>:<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>m_toys;<br style="margin: 0px; padding: 0px;" />};<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 3; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    Kitty k(5);<br style="margin: 0px; padding: 0px;" />    k.meow(v);<br style="margin: 0px; padding: 0px;" />}<br style="margin: 0px; padding: 0px;" />

 

运行结果 -

If you gave me 0 toys, I would have 5 toys total.

If you gave me 1 toys, I would have 6 toys total.

If you gave me 2 toys, I would have 7 toys total.

 

当lambda表达式捕获“this”时,编译器看到m_toys后会在this所指向对象的范围内进行名字查找,m_toys被隐式地推演为this->m_toys。当然你也可以让编译器省省力气。显式地在

捕获列表中使用 this->m_toys。

 

lambda比较智能,你也可以隐式地捕获this指针。如下所示:

 

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">class </span>Kitty {<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">public</span>:<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">explicit </span>Kitty(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>toys) : m_toys(toys) { }<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">void </span>meow(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const </span>vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>>& v) <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const </span>{<br style="margin: 0px; padding: 0px;" />        for_each(v.begin(), v.end(), [=](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) {<br style="margin: 0px; padding: 0px;" />            cout << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">"If you gave me " </span><< n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" toys, I would have " </span><< n + m_toys << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" toys total." </span><< endl;<br style="margin: 0px; padding: 0px;" />        });<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">private</span>:<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>m_toys;<br style="margin: 0px; padding: 0px;" />};<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 3; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    Kitty k(5);<br style="margin: 0px; padding: 0px;" />    k.meow(v);<br style="margin: 0px; padding: 0px;" />}
</pre><a target=_blank href="http://11011.net/software/vspaste" style="color: black; text-decoration: none; margin: 0px; padding: 0px;"></a><p style="margin: 5px auto; padding-top: 0px; padding-bottom: 0px;">运行结果:</p><p style="margin: 5px auto; padding-top: 0px; padding-bottom: 0px;">If you gave me 0 toys, I would have 5 toys total.</p><p style="margin: 5px auto; padding-top: 0px; padding-bottom: 0px;">If you gave me 1 toys, I would have 6 toys total.</p><p style="margin: 5px auto; padding-top: 0px; padding-bottom: 0px;">If you gave me 2 toys, I would have 7 toys total.</p><p style="margin: 5px auto; padding-top: 0px; padding-bottom: 0px;"> </p><p style="margin: 5px auto; padding-top: 0px; padding-bottom: 0px;">注意你也可以在上面代码中用 [&],但是结果是一样的 - this指针永远是按值语义被传递(捕获)的。你也不能够使用 [&this],呵呵。</p><p style="margin: 5px auto; padding-top: 0px; padding-bottom: 0px;">如果你的lambda表达式是没有参数的,那么lambda表达式的导入符后边的括号()也可以省掉。例如:</p><p style="margin: 5px auto; padding-top: 0px; padding-bottom: 0px;"> </p><pre class="code" name="code" style="white-space: pre-wrap; word-wrap: break-word; margin-top: 0px; margin-bottom: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0;<br style="margin: 0px; padding: 0px;" />    generate_n(back_inserter(v), 10, [&] { <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">return </span>i++; });<br style="margin: 0px; padding: 0px;" />    for_each(v.begin(), v.end(), [](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) { cout << n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>; });<br style="margin: 0px; padding: 0px;" />    cout << endl;<br style="margin: 0px; padding: 0px;" />    cout << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">"i: " </span><< i << endl;<br style="margin: 0px; padding: 0px;" />}
 
运行结果如下:

0 1 2 3 4 5 6 7 8 9

i: 10

 

上边是 [&]() { return i++; }的简写形式。个人认为省掉括号并不是什么好的coding style。

下面是纯粹搞笑的写法:

 

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" />    [](){}();<br style="margin: 0px; padding: 0px;" />    []{}();<br style="margin: 0px; padding: 0px;" />}<br style="margin: 0px; padding: 0px;" />

 

注意lambda的语法为:

lambda-parameter-declaration-listopt ) mutableopt exception-specificationopt lambda-return-type-clauseopt 

 

所以如果你需要用到mutable或者指定lambda的返回类型,空的括号就不能够省略了。

最后尽然lambda表达式生成是普通的函数对象,所以函数对象支持的用法lambda都支持。例如和tr1的function一起使用,

看看下边的代码,是不是很酷?

 

<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">using namespace </span>std;<br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">using namespace </span>std::tr1;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">void </span>meow(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const </span>vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>>& v, <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">const </span>function<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">void </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>)>& f) {<br style="margin: 0px; padding: 0px;" />    for_each(v.begin(), v.end(), f);<br style="margin: 0px; padding: 0px;" />    cout << endl;<br style="margin: 0px; padding: 0px;" />}<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>main() {<br style="margin: 0px; padding: 0px;" />    vector<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>> v;<br style="margin: 0px; padding: 0px;" />    <span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">for </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>i = 0; i < 10; ++i) {<br style="margin: 0px; padding: 0px;" />        v.push_back(i);<br style="margin: 0px; padding: 0px;" />    }<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    meow(v, [](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) { cout << n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>; });<br style="margin: 0px; padding: 0px;" />    meow(v, [](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) { cout << n * n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>; });<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />    function<<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">void </span>(<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int</span>)> g = [](<span style="margin: 0px; padding: 0px; line-height: 1.5; color: blue;">int </span>n) { cout << n * n * n << <span style="margin: 0px; padding: 0px; line-height: 1.5; color: rgb(163, 21, 21);">" "</span>; };<br style="margin: 0px; padding: 0px;" />    meow(v, g);<br style="margin: 0px; padding: 0px;" />}<br style="margin: 0px; padding: 0px;" />

 

运行结果:

0 1 2 3 4 5 6 7 8 9

0 1 4 9 16 25 36 49 64 81

0 1 8 27 64 125 216 343 512 729

 

【THE END】

If you love him, teach him C++, for it's heaven;
If you hate him, teach him C++, for it's hell




猜你喜欢

转载自blog.csdn.net/u010258235/article/details/49128759