亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

C++ 中的Lambda表達式寫法

2020-05-23 13:54:15
字體:
來源:轉載
供稿:網友

小喵的嘮叨話:

寒假之后,小喵在家里無所事事,最近用C++寫代碼的時候,用到了std::sort這個函數,每次用這個函數,小喵似乎都得查一下lambda表達式的寫法。正好最近很閑,不如總結一下。

在Bing上搜索 C++ lambda ,第一條記錄就是MSDN上的C++ lambda的介紹。本文也是基于這篇文章來寫的。

那么接下來,我們分幾個部分來介紹。

一、什么是Lambda表達式

MSDN上對lambda表達式的解釋:

在 C++ 11 中,lambda 表達式(通常稱為 “lambda”)是一種在被調用的位置或作為參數傳遞給函數的位置定義匿名函數對象的簡便方法。 Lambda 通常用于封裝傳遞給算法或異步方法的少量代碼行。 [1]

看了這個解釋,相信大家已經理解lambda表達式是什么。簡而言之,lambda表達式就是一種定義函數的簡單的方法。

舉一個簡單的例子:求一個數的階乘。

這是一般的函數的寫法:

// 這里要求n>=0,同時n的取值不能太大,會溢出// 為了方便,這里并沒有處理上面說到的問題int factorial(int n) { int fact = 1; for (int i = 1; i <= n; ++ i) fact *= i; return fact;}

Lambda表達式的寫法:

autofactorial = [](int n) { int fact = 1; for (int i = 1; i <= n; ++ i) fact *= i; return fact;};

乍一看,這兩種定義方式十分的相似。但其實這是兩種完全不同的方式,前一種是函數定義式,而后一種是一個表達式。factorial是變量名,等于號后面的是值,也就是一個lambda表達式,本質上是一個匿名的函數。最終factorial就是一個函數。

很多時候,我們只是直接書寫lambda表達式,而不需要給他一個名字。比如排序的時候,sort可以接受一個自定義的比較函數,這時候直接書寫lambda表達式即可。

二、Lambda表達式的作用

由于lambda本身其實也就是一種函數的定義方式。因此它的主要作用還是和一般函數一樣。但是lambda表達式相對于一般函數,又有一些功能之外的作用。參考了知乎上的一些回答 [2] ,小喵也進行了總結。

1、可以用表達式來定義函數,這樣使得函數的定義和調用在一起,語意和邏輯上更為緊湊。同時,對于只是用一次的短小的函數,直接調用匿名的lambda表達式是最好的選擇,這樣就不需要給每個函數起名字了。 /* 起名字一直是一個很令人頭疼的問題 */

2、閉包(Closure)。這個小喵的寫javascript的時候時常會用到。閉包本質上就是能夠訪問上下文環境中變量的代碼塊。

這里我們簡單的舉個例子,還是之前的求階乘的問題,現在我們有些提高需求。

現在需要完成下面的三種階乘的運算:

n! = n * (n – 1) * (n – 2) * …

n!! = n * (n – 2) * (n – 4) * …

n!!! = n * (n – 3) * (n – 6) * …

要求編寫3個函數,分別完成上述3種計算。

使用一般的方式寫很容易實現,我們這里直接使用lambda表達式來實現:

#include <iostream>#include <functional>std::function<int(int)> getFactorialFunc(int n) { return [n](int x) {  int fact = 1;  for (; x >= 1; x -= n) fact *= x;  return fact; };}int main() { // 構造要求的三個函數 autofactorial1 = getFactorialFunc(1); autofactorial2 = getFactorialFunc(2); autofactorial3 = getFactorialFunc(3); // 調用 std::cout << factorial1(10) << std::endl; std::cout << factorial2(10) << std::endl; std::cout << factorial3(10) << std::endl;}

編譯的時候要注意,lambda表達式是C++11開始支持的,所以需要指定一下C++的版本。

g++ factorial_lambda.cpp -o factorial_lambda.out --std=c++11

運行之后的結果為:

./factorial_lambda.out
3628800
3840
280

這里作為返回值的lambda表達式,可以訪問先前傳入的參數,這也就是閉包。具體的語法,我們后面會講到。

3、柯里化(Currying)。這部分小喵也是第一次接觸,維基百科有如下解釋:

在計算機科學中,柯里化(英語:Currying),又譯為卡瑞化或加里化,是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,并且返回接受余下的參數而且返回結果的新函數的技術。 [3]

下面給出一個例子(也是實現之前的階乘):

#include <iostream>#include <functional>// 兩個參數的階乘int factorial(int n, int step) { int r = 1; for (; n >= 1; n -= step) {  r *= n; } return r;}// curring化的階乘std::function<int(int)> currying_factorial(int step) { return [step](int n) {  return factorial(n, step); };}int main() { // 調用普通函數 std::cout << factorial(10, 1) << std::endl; std::cout << factorial(10, 2) << std::endl; std::cout << factorial(10, 3) << std::endl; // 調用currying函數 std::cout << currying_factorial(1)(10) << std::endl; std::cout << currying_factorial(2)(10) << std::endl; std::cout << currying_factorial(3)(10) << std::endl; return 0;}

4、lambda表達式整體可以被當做函數的參數或者返回值。

閉包和currying的例子就是將整個lambda表達式作為返回值?,F在再舉一個作為參數的例子:

#include <iostream>#include <functional>int operate(int x, int y, const std::function<int(int, int)> &op) { return op(x, y);}int main() { autoadd = [](int x, int y) { return x + y;}; automul = [](int x, int y) { return x - y;};  std::cout << operate(10, 5, add) << std::endl; std::cout << operate(10, 5, mul) << std::endl; return 0;}

運行的結果:

其實函數也可以當參數傳入的(函數指針),但是lambda表達式要更為直觀和靈活一些。誰能一眼看出int (*func(int))(int)究竟是什么意思呢(這是一個函數的定義,輸入的參數是int,返回值是一個函數指針,函數指針對應的函數的輸入和輸出類型都是int)。

三、Lambda表達式的語法

看到前面的lambda表達式的各種有趣的功能,現在是不是非常迫切的想嘗試一把?

ISO C++ 標準展示了作為第三個參數傳遞給 std::sort() 函數的簡單 lambda:

#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  ); }

lambda表達式的組成部分見下圖:

c++,lambda表達式

Capture 子句(在 C++ 規范中也稱為 lambda 引導。)

參數列表(可選)。 (也稱為 lambda 聲明符)

可變規范(可選)。

異常規范(可選)。

尾隨返回類型(可選)。

“lambda 體”

接下來我們需要學習這6個部分。

1、Capture 子句

我們知道,一般情況下,函數只能訪問自己的參數和外部的全局變量。而lambda表達式卻可以訪問上下文的變量(參見閉包的例子)。那么如何指定要訪問的變量,以及訪問的方式(值或者引用)呢?這就是Capture 子句要解決的問題。

Lambda 可在其主體中引入新的變量(用 C++14),它還可以訪問(或 “捕獲” )周邊范圍內的變量。 Lambda 以 Capture 子句(標準語法中的  lambda 引導 )開頭,它指定要捕獲的變量以及是通過值還是引用進行捕獲。 有與號 ( & ) 前綴的變量通過引用訪問,沒有該前綴的變量通過值訪問。

空 capture 子句 [ ] 指示 lambda 表達式的主體不訪問封閉范圍中的變量。

可以使用默認捕獲模式(標準語法中的 capture-default )來指示如何捕獲 lambda 中引用的任何外部變量:[&] 表示通過引用捕獲引用的所有變量,而 [=] 表示通過值捕獲它們。 可以使用默認捕獲模式,然后為特定變量顯式指定相反的模式。 例如,如果 lambda 體通過引用訪問外部變量  total 并通過值訪問外部變量  factor ,則以下 capture 子句等效:

[&total, factor] 
[factor, &total] 
[&, factor] 
[factor, &] 
[=, &total] 
[&total, =]

我們之前的閉包中使用的就是通過值訪問。

使用 capture-default 時,只有 lambda 中提及的變量才會被捕獲。

如果 capture 子句包含 capture-default & ,則該 capture 子句的  identifier 中沒有任何  capture 可采用  & identifier 形式。 同樣,如果 capture 子句包含  capture-default = ,則該 capture 子句的  capture 不能采用  = identifier 形式。 identifier 或  this 在 capture 子句中出現的次數不能超過一次。 以下代碼片段給出了一些示例。

struct S { void f(int i); };  void S::f(int i) {  [&, i]{}; // OK  [=, &i]{}; // OK [&, &i]{}; // ERROR: i preceded by & when & is the default  [=, this]{}; // ERROR: this when = is the default  [i, i]{}; // ERROR: i repeated }

capture 后跟省略號是包擴展,如以下可變參數模板 [4] 示例中所示:

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

要在類方法的正文中使用 lambda 表達式,需要將this指針傳遞給 Capture 子句,以提供對封閉類的方法和數據成員的訪問權限。

這里大家可能覺得有點奇怪,將this指針傳給Capture子句?

其實我們常使用的成員函數也是用類似的方法實現的。我們知道,使用成員函數需要有一個類實例,但是調用類函數就不需要。這是因為成員函數的第一個參數是this,當然這個參數我們編寫代碼的時候不需要自己手動寫出,而是默認的。使用像python這樣的語言的時候就是需要顯示的寫出的。在使用類實例調用成員函數的時候,會默認將this指針傳入。成員函數有這么一個參數,就可以訪問類實例的各種變量和方法。而類函數是沒有這個參數的,也就是沒有this這個指針,因此它的調用并不需要類實例,當然也就不能訪問類實例的變量。

在使用 capture 子句時,要記住以下幾點(尤其是使用采取多線程的 lambda 時):

引用捕獲可用于修改外部變量,而值捕獲卻不能實現此操作。 (mutable允許修改副本,而不能修改原始項。)

引用捕獲會反映外部變量的更新,而值捕獲卻不會反映。

引用捕獲引入生存期依賴項,而值捕獲卻沒有生存期依賴項。 當 lambda 以異步方式運行時,這一點尤其重要。 如果在異步 lambda 中通過引用捕獲本地變量,該本地變量將很可能在 lambda 運行時消失,從而導致運行時訪問沖突。

通用捕獲 (C++14)

在 C++14 中,可在 Capture 子句中引入并初始化新的變量,而無需使這些變量存在于 lambda 函數的封閉范圍內。 初始化可以任何任意表達式表示;且將從該表達式生成的類型推導新變量的類型。 此功能的一個好處是,在 C++14 中,可從周邊范圍捕獲只移動的變量(例如 std::unique_ptr)并在 lambda 中使用它們。

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

2、參數列表

除了捕獲變量,lambda 還可接受輸入參數。 參數列表(在標準語法中稱為 lambda 聲明符 )是可選的,它在大多數方面類似于函數的參數列表。

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

在 C++14 中,如果參數類型是泛型,則可以使用 auto 關鍵字作為類型說明符。 這將告知編譯器將函數調用運算符創建為模板。 參數列表中的每個 auto 實例等效于一個不同的類型參數。

autoadd = [] (autofirst, autosecond) {  return first + second; };

lambda 表達式可以將另一個 lambda 表達式作為其參數。

由于參數列表是可選的,因此在不將參數傳遞到 lambda 表達式,并且其 lambda-declarator: 不包含  exception-specification 、 trailing-return-type 或  mutable 的情況下,可以省略空括號。

[]{}; // 這就是最簡單的lambda表達式

3、可變規范

通常,lambda 的函數調用運算符為 const-by-value,但對 mutable 關鍵字的使用可將其取消。 它不會生成可變的數據成員。 利用可變規范,lambda 表達式的主體可以修改通過值捕獲的變量。 本文后面的一些示例將顯示如何使用  mutable 。

#include <iostream>int main(){ int n = 10; autolambda1 = [n](int x) {  /* ++ n; */ // 這句編譯會出錯,錯誤信息如下:     // error: cannot assign to a variable captured     // by copy in a non-mutable lambda  return x + n; }; autolambda2 = [n](int x) mutable {  ++ n;  return x + n; }; std::cout << lambda1(5) << " " << n << std::endl; std::cout << lambda2(5) << " " << n << std::endl; return 0;}

輸出的結果是:

可以看出n確實是通過值來訪問,在lambda1中,我們運行++n,在編譯的時候會報錯。使用mutable修飾之后,就可以修改參數(副本)的值。

4、異常規范

你可以使用 throw() 異常規范來指示 lambda 表達式不會引發任何異常。與普通函數一樣,如果 lambda 表達式聲明  C4297 異常規范且 lambda 體引發異常,Visual C++ 編譯器將生成警告  throw() ,如下所示:

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

在MSDN的異常規范 [5] 中,明確指出異常規范是在 C++11 中棄用的 C++ 語言功能。因此這里不建議不建議大家使用。

5、返回類型

將自動推導 lambda 表達式的返回類型。 無需使用 auto 關鍵字,除非指定 尾隨返回類型 。 trailing-return-type 類似于普通方法或函數的返回類型部分。 但是,返回類型必須跟在參數列表的后面,你必須在返回類型前面包含 trailing-return-type 關鍵字  -> 。

如果 lambda 體僅包含一個返回語句或其表達式不返回值,則可以省略 lambda 表達式的返回類型部分。 如果 lambda 體包含單個返回語句,編譯器將從返回表達式的類型推導返回類型。 否則,編譯器會將返回類型推導為 void 。

#include <iostream>#include <typeinfo>int main() { autolambda1 = [](int i) {return i;}; autolambda2 = [](int i) -> bool {return i;}; autolambda3 = [](int i) -> float {return i;}; /* auto lambda4 = []{ return {1, 2}; };*/ // ERROR: return type is void            // cannot deduce lambda return type  autox1 = lambda1(10); autox2 = lambda2(10); autox3 = lambda3(10);  std::cout << x1 << " " << typeid(x1).name() << std::endl; std::cout << x2 << " " << typeid(x2).name() << std::endl; std::cout << x3 << " " << typeid(x3).name() << std::endl; return 0;}

typeinfo的功能是獲取一個變量的類型,由于它的實現依賴于編譯器,所以在不同平臺下的輸出可能不完全一樣。小喵這邊的輸出是:

10 i
1 b
10 f

可以看出,三個lambda的輸出是不相同的。默認情況下,會返回一個最直接的類型。

6、lambda體

lambda體其實和函數體幾乎完全相同。

lambda 表達式的 lambda 體(標準語法中的 compound-statement )可包含普通方法或函數的主體可包含的任何內容。 普通函數和 lambda 表達式的主體均可訪問以下變量類型:

從封閉范圍捕獲變量,如前所述(Capture)。

參數

本地聲明變量

類數據成員(在類內部聲明并且捕獲  this  時)

具有靜態存儲持續時間的任何變量(例如,全局變量)

這里要注意我們在Capture 規范中說到的值訪問和引用訪問的特點。

下面的例子都是MSDN上給出的。

以下示例包含通過值顯式捕獲變量 n 并通過引用隱式捕獲變量  m 的 lambda 表達式:

// 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; }

輸出結果:

5

0

由于變量 n 是通過值捕獲的,因此在調用 lambda 表達式后,變量的值仍保持  0 不變。   mutable 規范允許在 lambda 中修改  n 。

盡管 lambda 表達式只能捕獲具有自動存儲持續時間的變量,但你可以在 lambda 表達式的主體中使用具有靜態存儲持續時間的變量。 以下示例使用 generate 函數和 lambda 表達式為  vector 對象中的每個元素賦值。 lambda 表達式將修改靜態變量以生成下一個元素的值。

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 }

四、應用Lambda的比較函數的編寫

為什么要補充這一部分呢?因為我們在寫程序的時候,往往最常用到lambda的地方就是數組的sort。

首先,我們知道std::sort默認是接受2個參數的,表示需要排序的序列的開始和結尾。對于一些復雜的數據類型,我們可以給它添加一個用來比較的函數 operator <。但更多的是通過給sort添加第三個參數來實現。而這個參數就是一個比較器。

sort默認使用<比較符來進行比較,排序的結果是升序。我們寫的比較函數的功能就是代替<。記住這個特點,就不會在編寫比較函數的時候理不清思路。

這里舉一個小例子,給一組點坐標,按歐氏距離排序:

#include <algorithm>#include <iostream>#include <vector>using namespace std; int main() { vector< pair<int, int> > arr; arr.push_back(make_pair(1, 4)); arr.push_back(make_pair(2, 3)); arr.push_back(make_pair(5, 7)); arr.push_back(make_pair(6, 2));  sort(arr.begin(), arr.end(),  [](pair<int, int> left, pair<int, int> right) {   int d1 = left.first * left.first + left.second * left.second;   int d2 = right.first * right.first + right.second * right.second;   return d1 < d2;  });  for (auto &p: arr) {  cout << "(" << p.first << ", " << p.second << ")" << endl; } return 0;}

輸出結果:

(2, 3)
(1, 4)
(6, 2)
(5, 7)

唯一需要注意的是,我們的比較函數取代的是<。

PS:

lambda 怎么傳遞ref參數

ambda 傳遞ref參數有個語法bug,必須要顯式書寫參數類型。

//如delegate bool FuncType(ref int num);FuncType func1;func1 = num => true; //錯func1 = (ref num) => true;//錯func1 = (ref int num) => true;//ok//并且,當一個參數書寫類型,其他參數也要書寫,總之很煩。

以上所述是小編給大家介紹的C++ 中的Lambda表達式寫法,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VEVB武林網網站的支持!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91久久精品视频| 亚洲欧美国产日韩中文字幕| 91色视频在线观看| 国产精品高潮在线| 欧美特级www| 久久精品国产清自在天天线| 国产一区二区丝袜高跟鞋图片| 亚洲欧美国产一本综合首页| 国产精品久久久久秋霞鲁丝| 日韩中文在线中文网三级| 亚洲午夜国产成人av电影男同| 日韩成人在线视频网站| 91精品国产乱码久久久久久蜜臀| 欧美精品www| 亚洲第一福利在线观看| 欧美丰满少妇xxxxx做受| 亚洲一区二区在线| www.亚洲人.com| 欧美成人精品在线视频| 日本精品视频在线播放| 中日韩美女免费视频网站在线观看| 最近2019中文字幕第三页视频| 国产激情综合五月久久| 视频在线一区二区| 91国产高清在线| zzijzzij亚洲日本成熟少妇| 成人精品久久一区二区三区| 国产精品视频导航| 亚洲 日韩 国产第一| 精品国产乱码久久久久久虫虫漫画| 久久影院模特热| 日韩精品高清视频| 欧美午夜精品久久久久久人妖| 国产最新精品视频| 91欧美视频网站| 亚洲精品大尺度| 亚洲天堂男人天堂| 亚洲欧美国产一区二区三区| 九九热这里只有在线精品视| 成人精品福利视频| 一区二区三区天堂av| 久久精品国产96久久久香蕉| 91精品久久久久久久久久久久久久| 日本伊人精品一区二区三区介绍| 国产精品吹潮在线观看| 久久精品夜夜夜夜夜久久| 久久香蕉精品香蕉| 欧美激情视频网| 欧美激情精品久久久久久久变态| 精品日韩美女的视频高清| 91香蕉嫩草神马影院在线观看| 欧美日韩爱爱视频| 国产精品99久久99久久久二8| 久久天天躁夜夜躁狠狠躁2022| 国产综合香蕉五月婷在线| 国产精品久久久久久五月尺| 亚洲欧美日韩国产中文| 亚洲视频自拍偷拍| 国产精品扒开腿做爽爽爽男男| 亚洲风情亚aⅴ在线发布| 精品国产视频在线| 久久成人一区二区| 97在线观看免费| 81精品国产乱码久久久久久| 66m—66摸成人免费视频| 狠狠色狠色综合曰曰| 国产精品日韩一区| 日韩精品免费看| 欧美日韩国产精品一区二区不卡中文| 日韩欧美在线第一页| 欧美久久精品午夜青青大伊人| 精品福利视频导航| 日韩国产一区三区| 国产精品热视频| 亚洲自拍偷拍视频| 欧美孕妇毛茸茸xxxx| 亚洲精品aⅴ中文字幕乱码| 欧美专区日韩视频| 久久久天堂国产精品女人| 国产成人精品在线播放| 亚洲一区av在线播放| 久久久久久久激情视频| 成人久久久久久| 日本精品视频在线观看| 久久香蕉频线观| 国产成人精品网站| 欧美另类交人妖| 久久久久久久av| 欧美日韩中文字幕综合视频| 不卡在线观看电视剧完整版| 亚洲深夜福利在线| 日韩av色在线| 中文字幕国产精品| 亚洲国产精品成人va在线观看| 91精品国产一区| 17婷婷久久www| 欧美成人精品三级在线观看| 欧美色播在线播放| 国产亚洲精品综合一区91| 国产精品福利在线观看| 欧美日韩国产成人在线| 国产欧美va欧美va香蕉在| 亚洲国产精品久久精品怡红院| 欧美美最猛性xxxxxx| 欧美黄网免费在线观看| 一区二区三欧美| 丝袜美腿精品国产二区| 热门国产精品亚洲第一区在线| 欧美性xxxxx| 国内精品久久久久伊人av| 日韩人体视频一二区| 亚洲午夜精品久久久久久久久久久久| 成人午夜在线影院| 九九精品在线视频| 一区二区欧美日韩视频| 国产视频一区在线| 欧美亚洲成人xxx| 不用播放器成人网| 日韩av片免费在线观看| 亚洲日本成人女熟在线观看| 久久在线免费视频| 91在线播放国产| 久久在线视频在线| 成人在线视频网站| 久久亚洲电影天堂| 奇米四色中文综合久久| 黑人巨大精品欧美一区二区| 精品福利免费观看| 91久久久久久久一区二区| 欧美精品国产精品日韩精品| 欧美一级成年大片在线观看| 91精品国产91久久久久久最新| 一区二区三区视频免费| 久久久久女教师免费一区| 日韩精品极品视频免费观看| 91在线国产电影| 一本大道久久加勒比香蕉| 欧美性资源免费| 国产精品国语对白| 成人www视频在线观看| 亚洲成人av资源网| 欧美一二三视频| 国外成人免费在线播放| 日韩电影中文字幕av| 久久久免费高清电视剧观看| 国产日本欧美在线观看| 亚州欧美日韩中文视频| 亚洲美女在线视频| 色老头一区二区三区在线观看| 久久人人爽人人爽人人片av高请| 日韩欧美在线看| 日韩视频在线观看免费| 国内精品美女av在线播放| 久久影视免费观看| 在线成人中文字幕| 欧美日韩免费区域视频在线观看| 欧美日韩在线另类| 久久亚洲精品毛片| 国产成人高清激情视频在线观看| 激情懂色av一区av二区av| 国产精品久久久久久久久男| 91久久嫩草影院一区二区| 91久久久久久久| 国产日韩欧美91|