C++11 -- ラムダ式

ラマバ式の導入

  • C++11 以前では、カスタム タイプの Goods を並べ替える場合、名前、価格、学生番号に従って、大から小、または小から大に並べ替えることができましたが、関連する Functor を 6 つ追加で記述する必要がありました。
  • また、カスタムファンクターに名前を付ける過程では、不規則な名前付けによりコード解釈の問題が発生しやすく、ファンクターの名前だけでは実際の関数を理解することができず、対応する関数のファンクターしか見つけることができません。わかりやすい名前と実際の機能。
struct Goods
{
    
    
	string _name;  //名字
	double _price; //价格
	int _num;      //数量
};

struct ComparePriceLess
{
    
    
	bool operator()(const Goods& g1, const Goods& g2)
	{
    
    
		return g1._price < g2._price;
	}
};
struct ComparePriceGreater
{
    
    
	bool operator()(const Goods& g1, const Goods& g2)
	{
    
    
		return g1._price > g2._price;
	}
};
struct CompareNumLess
{
    
    
	bool operator()(const Goods& g1, const Goods& g2)
	{
    
    
		return g1._num < g2._num;
	}
};
struct CompareNumGreater
{
    
    
	bool operator()(const Goods& g1, const Goods& g2)
	{
    
    
		return g1._num > g2._num;
	}
};
int main()
{
    
    
	vector<Goods> v = {
    
     {
    
     "苹果", 2, 20 }, {
    
     "香蕉", 3, 30}, {
    
     "橙子", 4,40 }, {
    
     "菠萝", 5,50 } };
	sort(v.begin(), v.end(), ComparePriceLess());    //按Goods价格升序排序
	sort(v.begin(), v.end(), ComparePriceGreater()); //按Goods价格降序排序
	sort(v.begin(), v.end(), CompareNumLess());      //按Goods升序排序
	sort(v.begin(), v.end(), CompareNumGreater());   //按Goods降序排序
	return 0;
}


C++11以降では、実際には匿名関数であるlamada式で解くことができるようになり、sortの比較方法をlamada式で直接理解できるようになり、コードセックスの可読性が向上しました。

int main()
{
    
    
	vector<Goods> v = {
    
     {
    
     "苹果", 2.1, 300 }, {
    
     "香蕉", 3.3, 100 }, {
    
     "橙子", 2.2, 1000 }, {
    
     "菠萝", 1.5, 1 } };
	sort(v.begin(), v.end(), []( const Goods& g1,const Goods& g2) {
    
    return g1._price < g2._price; });
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
    
    return g1._price < g2._price; });
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
    
     return g1._num < g2._num; });
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
    
     return g1._num < g2._num; });
	return 0;
}


ラムダ式の構文

lamada式の記述形式:

[capture-list] (parameters) mutable -> return-type 
{
    
      
  statement
}

ラムダ式の各部の説明

  • [capture-list]: ラムダ式の先頭に常に現れるキャプチャ リスト。コンパイラは [] に従って次のコードがラムダ式であるかどうかを判断します。キャプチャ リストは変数に従ってラムダ式を作成できます。使用されるコンテキスト表現で。
  • (パラメータ): 通常のパラメータリストと一致するパラメータリスト。パラメータを渡す必要がない場合は、() を使用してパラメータを省略できます。
  • mutable: デフォルトでは、ラムダ式は常に const 関数 (関数のパラメーターは変更できません) であり、mutable は const 性をキャンセルできます。この修飾子を使用する場合、パラメーター リストは省略できません (パラメーター リストが null の場合でも)。
  • ->returntype: 戻り値の型 一般に戻り値の型はコンパイラが推測するため、戻り値が明らかな場合は省略可能です。
  • {statement}: 関数本体 この関数本体では、関数本体内の仮パラメータに加えて、キャプチャされたすべての変数も使用できます。

ラムダ式の簡単な使用

int mian()
{
    
    
	//由于lambda表达式实际上就是一个匿名对象,没有函数名不好调用,但是我们可以通过auto自动获取.
	auto add1 = [](int a, int b) {
    
     return a + b; };    //省略返回值.

	cout << add1(1, 2) << endl;

}

キャプチャリストの説明

キャプチャ リストには、コンテキスト内のどのデータがラムダで使用できるか、およびそれが値によって使用されるか参照によって使用されるかが記述されます。

  • [var]: 値転送メソッドが変数 var をキャプチャすることを示します。
  • [=]: 値転送メソッドが親スコープ内のすべての変数 (これを含む) をキャプチャすることを示します。
  • [&var]: キャプチャ変数 var が参照によって渡されることを示します。
  • [&]: 参照転送が親スコープ内のすべての変数 (これを含む) をキャプチャすることを示します。
  • [this]: 値転送メソッドが現在の this ポインターをキャプチャすることを示します。

ラムダ式でキャプチャ リストを使用しない場合とキャプチャ リストを使用する場合の比較。

キャプチャリストを適用しないと、関数のパラメータを追加で記述しなければならず、呼び出す際に実パラメータも渡さなければならず、面倒です。

int main()
{
    
    
	//之前做法
	int x = 0,y = 1;
	
	auto swap1 = [](int& x1, int& x2) {
    
     int tmp = x1; x1 = x2; x2 = tmp;};

	swap1(x, y);

	return 0;
}

したがって、キャプチャ リストを使用することができ、値によってキャプチャされた x と y は通常は変更可能ではなく (可変修飾子が使用可能)、このときにキャプチャされた x と y は実際のパラメータのコピーにすぎないため、通常は、コードをより簡潔にする参照キャプチャを使用します。


int main()
{
    
    
	auto swap2 = [&x, &y] {
    
    int tmp = x; x = y; x = tmp;}; //引用捕捉.
    
	swap2();             //不需要传递实参.
	
	cout << x << ":" << y << endl;
	
}

キャプチャ リストの他の機能の簡単な使用

int main()
{
    
    
	int a, b, c, d, e;
	
	auto f1 = [=] {
    
    cout << a << b << d << e; }; //a,b,c,d,e全部传值捕获.

	f1();

	auto f2 = [=, &a] {
    
     a++; cout << a << b << c << d << e; }; //b,c,d,e传值捕获,a传引用捕获.

	f2();
	
}

ただし、キャプチャ リストでは変数を繰り返し渡すことができないことに注意してください。そうしないと、コンパイル エラーが発生します。

int main()
{
    
    
	int a, b, c, d, e;
	
	auto f = [=,a] {
    
    cout << a << b << d << e; }; //重复捕获.

}

ラマバ表現の根底にある原理の探求

コンパイラーによるラムダの処理は、実際にはファンクターの処理と同じです

ラムダ式の基本原理を検証するために、ファンクターとラムダ式をそれぞれ作成しましたが、それらの機能は同じです。

class Rate
{
    
    
public:
	Rate(double rate) : _rate(rate)
	{
    
    }
	double operator()(double money, int year)
	{
    
    
		return money * _rate * year;
	}
private:
	double _rate;
};
int main()
{
    
    
	// 函数对象
	double rate = 0.49;
	Rate r1(rate);
	r1(10000, 2);
	// lamber表达式
	auto r2 = [=](double monty, int year)->double {
    
    return monty * rate * year;
	};
	r2(10000, 2);
	return 0;
}

ファンクターとラムダ式を別々に呼び出す場合は、逆アセンブリ ビューに移動します。
ここに画像の説明を挿入

概要:

  • 使用法の観点から見ると、ファンクターはラムダ式とまったく同じです。関数オブジェクトはメンバー変数として rate を使用し、オブジェクトの定義時に実際のパラメータを渡すことができます。ラムダ式は変数を直接キャプチャして、キャプチャ リストを通過する実際のパラメータ。
  • 基礎となる実装の観点から見ると、それは完全にファンクターの方法で処理されます。ラムダ式を定義すると、コンパイラーは積極的にファンクターを生成します。処理の便宜上、コンパイラーは UUID を使用する必要があります。基本的には一意の UUID を生成します。ファンクター名。次に、呼び出し時にラムダ式を通じてこのファンクターを呼び出します。ファンクターは演算子を呼び出して、operator() をオーバーロードします。したがって、実際には、キャプチャ リストによってキャプチャされた変数は、関数の本体に実装されている Operator() に渡されます。 。

おすすめ

転載: blog.csdn.net/m0_63300413/article/details/130877704