青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

羅朝輝(飄飄白云)

關注嵌入式操作系統,移動平臺,圖形開發。-->加微博 ^_^

  C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
  85 隨筆 :: 0 文章 :: 169 評論 :: 0 Trackbacks

 

【譯】VC10中的C++0x特性 Part 2 (3):右值引用

來源:vcblog 作者:Stephan T. Lavavej 翻譯:飄飄白云  

(轉載時請注明作者和出處。未經許可,請勿用于商業用途)

簡介

這一系列文章介紹Microsoft Visual Studio 2010 中支持的C++ 0x特性,目前有三部分。
Part 1 :介紹了Lambdas, 賦予新意義的auto,以及 static_assert;
Part 2( , , ):介紹了右值引用(Rvalue References);
Part 3:介紹了表達式類型(decltype)

VC10中的C++0x特性 Part 1,2,3 譯文打包下載(doc 和 pdf 格式): 點此下載


本文為 Part 2 的第三頁

 

轉發問題

在程序員不用寫高度泛化的代碼的時候,C++98/03 的 lvalue, rvalue, 引用,還有模板看起來是很完美的。假設你要寫一個完全泛化的函數 outer(),這個函數的目的是將任意數目個任意類型的參數傳遞(也就是“轉發”)給函數 inner()。已有很多不錯的解決方案,比如 factory 函數 make_shared<T>(args) 是把 args 傳給 T 的構造函數,然后返回 shared_ptr<T>。(這樣就把 T 對象和用于對它進行引用計數的代碼存儲到同一塊動態內存中,性能上與侵入式引用計數一樣好); 而像 function<Ret(args)> 這樣的包裝類是把參數傳給其內部存儲的函數對象(functor),等等。在這篇文章里,我們只對 outer() 是如何把參數傳遞給 inner() 這部分感興趣。至于 outer() 的返回類型是怎么決定的是另外的問題(有時候很簡單,如 make_shared<T>(args) 總是返回 shared_prt<T>,),但要在完全搞定這個問題的一般化情況,你就要用到 C++0x的 decltype 特性了)。

 

如果不帶參數,就不存在這樣的問題,那么帶一個參數情況呢?讓我們嘗試寫個 outer() :

 

template <typename T> void outer(T& t) {

    inner(t);

}

 

問 題來了,如果傳給它的參數是非常量 rvalue,那我們就無法調用 outer()。如果 inner() 接收 const int& 型的參數,那 inner(5) 是可以通過編譯的,但是 outer(5) 就編譯不過了。因為 T 會被推導為 int, 而 int& 是不能綁定到常量 5 的。

 

好吧,讓我們試試這個:

 

template <typename T> void outer(const T& t) {

    inner(t);

}

 

如果 inner()接收 int& 型參數,那就會違法 const 正確性,編譯都過不了。

 

現在,你可以重載兩個分別帶 T& 和 const T& 參數的 outer(),這確實管用。當你調用 outer()時,就像直接調用 inner() 一樣。

 

可惜的是,這中方法在多參數的情況下就麻煩了(譯注:要寫的重載函數太多了)。你就得為每一個參數像 T1& 和 const T1&, T2& 和 const T2& 等這樣進行重載,要重載的函數數目呈指數級增長。(VC9 SP1 的 tr1::bind() 就夠讓人感到絕望了,它為 5 個參數這么重載出了 63 個函數。如果不這么蠻干的話,沒有像這里的長篇累述,我們就很難跟使用者解釋為什么不能調用用 1729 這樣的 ravlue 做參數的函數。為了產生出這些重載函數使用了令人作嘔的預處理機制,惡心到你都不想知道它)。

 

在 C++98/03 中,轉發問題是很嚴重的,而且本質上無解(必須求助于惡心的預處理機制,這會嚴重拖慢編譯速度,還讓代碼變得難以閱讀)。總算, rvalue 優雅地解決了這個問題。

 

完美轉發 模式

 

完美轉發讓你能簡單而清晰地只寫一個模板函數就可以轉發所有的參數給任意函數,不管它帶幾個參數,也不管參數類型是什么。而且參數的非常量/常量, lvalue/rvalue 屬性都能得以保留,讓你可以像使用 inner() 一樣使用 outer(),還可以和 move 語意一起用從而獲得額外的好處。( C++0x 的變長模板技術解決了“任意數目”這部分,我們在這里把 N 看做任意數目)。乍看之下很神奇,實際上很簡單:

 

C:\Temp>type perfect.cpp

#include <iostream>

#include <ostream>

using namespace std;

 

template <typename T> struct Identity {

    typedef T type;

};

 

template <typename T> T&& Forward(typename Identity<T>::type&& t) {

    return t;

}

 

void inner(int&, int&) {

    cout << "inner(int&, int&)" << endl;

}

 

void inner(int&, const int&) {

    cout << "inner(int&, const int&)" << endl;

}

 

void inner(const int&, int&) {

    cout << "inner(const int&, int&)" << endl;

}

 

void inner(const int&, const int&) {

    cout << "inner(const int&, const int&)" << endl;

}

 

template <typename T1, typename T2> void outer(T1&& t1, T2&& t2) {

    inner(Forward<T1>(t1), Forward<T2>(t2));

}

 

int main() {

    int a = 1;

    const int b = 2;

 

    cout << "Directly calling inner()." << endl;

 

    inner(a, a);

    inner(b, b);

    inner(3, 3);

 

    inner(a, b);

    inner(b, a);

 

    inner(a, 3);

    inner(3, a);

 

    inner(b, 3);

    inner(3, b);

 

    cout << endl << "Calling outer()." << endl;

 

    outer(a, a);

    outer(b, b);

    outer(3, 3);

 

    outer(a, b);

    outer(b, a);

 

    outer(a, 3);

    outer(3, a);

 

    outer(b, 3);

    outer(3, b);

}

 

C:\Temp>cl /EHsc /nologo /W4 perfect.cpp

perfect.cpp

 

C:\Temp>perfect

Directly calling inner().

inner(int&, int&)

inner(const int&, const int&)

inner(const int&, const int&)

inner(int&, const int&)

inner(const int&, int&)

inner(int&, const int&)

inner(const int&, int&)

inner(const int&, const int&)

inner(const int&, const int&)

 

Calling outer().

inner(int&, int&)

inner(const int&, const int&)

inner(const int&, const int&)

inner(int&, const int&)

inner(const int&, int&)

inner(int&, const int&)

inner(const int&, int&)

inner(const int&, const int&)

inner(const int&, const int&)

 

兩行!完美轉發只用了兩行!夠簡潔吧!

 

這個例子示范了怎么把 t1 和 t2 從 outer() 透明地轉發給 inner(); inner() 可以知道它們的非常量/常量, lvalue/ravlue 屬性,就像inner是被直接調用的那樣。

 

跟 std::move() 一樣, std::identify 和 std::forward() 都是在 C++<utility> 中定義的( VC10 會有, VC10 CTP中沒有)。我將演示怎么來實現它們。(再次,我將交替使用 std::identity 和我的 Identity, std::forward() 和我的 Forward(),因為他們的實現是等價的。)

 

現在,讓我們來揭開“魔術“的神秘面紗,其實它靠的就是模板參數推導和引用折疊(reference collapsing)技術。

 

rvalue 引用:模板參數推和引用折疊(reference collapsing)

 

rvalue 引用與模板以一種特別的方式相互作用。下面是一個示例:

 

C:\Temp>type collapse.cpp

#include <iostream>

#include <ostream>

#include <string>

using namespace std;

 

template <typename T> struct Name;

 

template <> struct Name<string> {

    static const char * get() {

        return "string";

    }

};

 

template <> struct Name<const string> {

    static const char * get() {

        return "const string";

    }

};

 

template <> struct Name<string&> {

    static const char * get() {

        return "string&";

    }

};

 

template <> struct Name<const string&> {

    static const char * get() {

        return "const string&";

    }

};

 

template <> struct Name<string&&> {

    static const char * get() {

        return "string&&";

    }

};

 

template <> struct Name<const string&&> {

    static const char * get() {

        return "const string&&";

    }

};

 

template <typename T> void quark(T&& t) {

    cout << "t: " << t << endl;

    cout << "T: " << Name<T>::get() << endl;

    cout << "T&&: " << Name<T&&>::get() << endl;

    cout << endl;

}

 

string strange() {

    return "strange()";

}

 

const string charm() {

    return "charm()";

}

 

int main() {

    string up("up");

    const string down("down");

 

    quark(up);

    quark(down);

    quark(strange());

    quark(charm());

}

 

C:\Temp>cl /EHsc /nologo /W4 collapse.cpp

collapse.cpp

 

C:\Temp>collapse

t: up

T: string&

T&&: string&

 

t: down

T: const string&

T&&: const string&

 

t: strange()

T: string

T&&: string&&

 

t: charm()

T: const string

T&&: const string&&

 

這里藉由 Name 的顯式規格說明來打印出類型。

 

當我們調用 quark(up) 時,會進行模板參數推導。 quark() 是一個帶有模板參數 T 的模板函數,但是我們還沒有為它提供顯式的類型參數(比如像 quark<X>(up)這樣的)。通過比較函數形參類型 Type&& 和函數實參類型(一個 string 類型的 lvalue)我們就能推導出模板實參類型。(譯注:原文用 argument 表示實參,parameter 表示形參)

 

C++0x 會轉換函數實參的類型和形參的類型,然后再進行匹配。

 

首先,轉換函數實參的類型。這遵循一條特殊規則(提案N2798

 

然后,轉換函數形參的類型。不管是 C++98/03 還是 C++0x 都會解除引用( lvalue 引用和 rvalue 引用在 C++0x 中都會被解除掉)。在前面例子的四種情形中,這樣我們會把 T&& 轉換成 T

 

于是, T 會被推成函數轉換之后的updown 都是 lvalue,它們遵循那條特殊規則,這就是為什么 quark(up)  打印出"T:string&" ,而 quark(down) 打印出 "T: cosnt string&"的原因。strange()charm() 都是右值,它們遵循一般規則,這就是為什么 quark(strange()) 打印出 "T: string" 而 quark(charm()) 打印出"T: const string" 的原因。

 

替換操作會在類型推導之后進行。模板形參 T 出現的每一個地方都會被替換成推導出來的模板實參類型。在 quark(string())Tstring ,因此 T&& 會是 string&& 。同樣,在 quark(charm()) 中,Tconst string , 因此 T&&const string&& 。但 quark(up) 和 quark(down) 不同,它們遵循另外的特殊規則。

 

quark(up) 中, Tstring& 。進行替換的話 T&& 就成了 string& && ,在 C++0x 中會折疊(collapse)引用的引用,引用折疊的規則就是“lvalue 引用是染性的X& &, X& &&X&& & 都會被折疊成 X& ,只有 X&& && 會被折疊成 X&& 。因此 string& && 被折疊成 string& 。在模板世界里,那些看起來像 rvalue 引用的東西并不一定真的就是。 因而 quark(up) 被實例化為 quark<string&>() ,進而 T&& 經替換與折疊之后變成 string& 。我們可以調用 Name<T&&>::get() 來驗證這個。 同樣, quark(down) 被實例化為 quark<const string&>() ,進而 T&& 經替換與折疊之后變成 const string& 。在 C++98/03中,你可能習慣了常量性(constness)隱藏于模板形參中(也就是說可以傳 const Foo 對象作實參來調用形參為 T& 的模板函數,就像 T& 會是 const Foo& 一樣),在 C++0x 中,左值屬性(lvalueness) 也能隱藏于模板形參中。

 

那好,這兩條特殊規則對我們有什么影響?在 quark() 內部,類型 T&& 有著和傳給 quark() 的實參一樣的左/右值屬性(lvalueness/rvalueness)和常量性。這樣 rvalue 引用就能保持住左右屬性和常量性,做到完美轉發

 

完美轉發: std::forward() 和 std::identidy 是怎工作的

 

讓我們再來看看 outer() :

 

template <typename T1, typename T2> void outer(T1&& t1, T2&& t2) {

    inner(Forward<T1>(t1), Forward<T2>(t2));

}

 

現在我們明白了為什么 outer() 的形參是 T1&&T2&& 類型的了,因為它們能夠保持住傳給 outer() 的實參的信息。那為什么這里要調用 Forward<T1>()Forward<T2>() 呢?還記得么,具名 lvalue 引用和具名 rvalue 引用都是 lvalue 。如果 outer() 調用 inner(t1, t2) ,那么 inner() 總是會當 lvalue 來引用 t1t2 ,這就破壞了完美轉發。

 

幸 運的是,不具名 lvalue 引用是 lvalue,不具名 rvalue 引用還是 rvalue 。因此,為了將 t1 和 t2 轉發給 inner(),我們需要將它們傳到一個幫助函數中去,這個幫助函數移除它們的名字,保持住它們的屬性信息。這就是 std::forward() 做的事情:

 

template <typename T> struct Identity {

    typedef T type;

};

 

template <typename T> T&& Forward(typename Identity<T>::type&& t) {

    return t;

}

 

當我們調用 Forward<T1>(t1)Identidy 并沒有修改 T1 (很快我們講到 IdentidyT1 做了什么)。因此 Forward<T1>() 接收 T1&& ,返回 T1&& 。這樣就移除了 t1 的名字,保持住 t1 的類型信息(而不論 t1 是什么類型, string& 也好, const string& 也好, string&& 也好或 const string&& 也好)。這樣 inner() 看到的 Forward<T1>(t1) ,與 outer() 接收的第一個實參有著相同的信息,包括類型,lvalueness/rvalueness,常量性等等。完美轉發就是這樣工作的。

 

你可能會好奇如果不小心寫成 Forward<T1&&>(t1) 又會怎樣呢?(這個錯誤還是蠻誘人的,因為 outer() 接收的就是 T1&& t1 )。很幸運,沒什么壞事情會發生。 Forward<T1&&>() 接收與返回的都是 T1&& && ,這會被折疊成 T1&& 。于是,Forward<T1>(t1)Forward<T1&&>(t1) 是等價的,我們更偏好前者,是因為它要短些。

 

Identidy 是做什么用的呢?為什么下面的代碼不能工作?

 

template <typename T> T&& Forward(T&& t) { // BROKEN

    return t;

}

 

如果 Forward() 像是上面那樣,它就能被隱式調用(不帶明確的模板參數)。當我們傳給 Forward() 一個 lvalue 實參時,模板參數推導就介入了,如我們前面看到的那樣會將 T&& 變成 T&,也就是變成一個 lvalue 引用。問題來了,即使形參 T1&&T2&& 指明是 rvalue 引用,但在 outer() 中,具名的 t1t2 卻是 lvaue ,這個問題是我們一直想要解決的!使用上面那個錯誤的實現, Forward<T1>(t1) 是可以工作的,而 Foarward(t1) 雖然能通過編譯(很誘人哦)但會出錯,就如它就是 t1 一樣。真是痛苦的源泉啊,因此,Identity 被用來阻止模板參數推typename Identity<T>::type 中的那對冒號就像絕緣體,模板參數推導無法穿越它,有模板編程經驗的程序員應該對此很熟悉了,因為這在 C++98/03 和 C++0x 中是一樣的。(要解釋這個是另外的事情了)

 

move 意: std::move() 是怎工作的

 

現在我們已經學習了模板參數推導和引用折疊的特殊規則,讓我們再來看看 std::move() :

 

template <typename T> struct RemoveReference {

     typedef T type;

};

 

template <typename T> struct RemoveReference<T&> {

     typedef T type;

};

 

template <typename T> struct RemoveReference<T&&> {

     typedef T type;

};

 

template <typename T> typename RemoveReference<T>::type&& Move(T&& t) {

    return t;

}

 

RemoveReference 機制基本上是復制 C++0x <type_traits> 中的 std::remove_reference 。舉例來說,RemoveReference<string>::type , RemoveReference<string&>::typeRemoveReference<string&&>::type 都是 string

 

同樣, move() 機制也基本上是復制 C++0x <utility> 中的 std::move()

· 當調用 Move(string), string 是一個 lvalue 時, T 會被推導為 string& ,于是 Move() 接收的就是 string& (經折疊之后)并返回 string&& (經 RemoveReference 之后)。

 

· 當調用 Move(const string), const string 是一個 lvalue 時, T 會被推導為 const string& ,于是 Move() 接收的就是 const string&& (經折疊之后)并返回 const string&& (經 RemoveReference 之后)。

 

· 當調用 Move(string), string 是一個 rvalue 時, T 會被推導為 string ,于是 Move() 接收的就是 string&& 并返回 string&&

 

· 當調用 Move(const string), const string 是一個 rvalue 時, T 會被推導為 const string ,于是 Move() 接收的就是 const string&& 并返回 const string&&

 

這就是 Move() 如何保持其參數的類型和常量性,還能把 lvalue 轉換成 rvalue 的過程。

 

如果你想對 rvalue 引用有更多了 解,你可以去讀有關它們的提案。要注意,提案與現在的決定可能已經不同了, rvalue 引用已經被整合到 C++0x 草案中來了,在那里它得到持續的改進。有些提案或已不再正確,或已過時,或已有了替代方案,就沒有被采納。無論怎樣,它們還是能提供一些有用信息的。

 

N1377, N1385, 和 N1690 是主要的提案,N2118 包含被整合進標準草案之前的最后版本。 N1784, N1821, N2377, 和 N2439 記錄了“將 Move 語意擴展到 *this ”的演化過程,這個也被整合到 C++0x 中來了,但還沒有在VC10 中得到實現。

 

展望

N2812 “Rvalue 引用的安全問題(以及如何解決)” 提出了對初始化規則的修改,它禁止 rvalue 引用綁定到 lvalue 。 這不會影響 move 語意和完美轉發,所以它不會讓你剛學到的新技術失效(它只是修改了 std::move() 和 std::forward() 的實現)。

 

Stephan T. Lavavej

Visual C++ Libraries Developer

Published Tuesday, February 03, 2009 9:27 AM by vcblog

翻譯:飄飄白云

 

(轉載時請注明作者和出處。未經許可,請勿用于商業用途)

 < 第一頁第二頁, 本頁>


 



posted on 2009-06-05 15:09 羅朝輝 閱讀(2910) 評論(0)  編輯 收藏 引用 所屬分類: C/C++
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            在线视频欧美日韩| 欧美超级免费视 在线| 欧美午夜免费电影| 99国产精品私拍| 在线视频你懂得一区| 欧美天天视频| 久久福利视频导航| 久久久久www| 亚洲国产婷婷香蕉久久久久久99| 欧美aaa级| 欧美激情aⅴ一区二区三区| 日韩亚洲欧美中文三级| 一本色道久久综合狠狠躁篇怎么玩 | 亚洲在线一区二区| 亚洲一区自拍| 国产在线国偷精品产拍免费yy| 久久国产精品久久精品国产| 亚洲一区二区在| 亚洲一区二区三区乱码aⅴ蜜桃女| 国产美女诱惑一区二区| 美日韩精品免费观看视频| 欧美激情精品久久久久久黑人| 亚洲视频在线观看一区| 欧美在线一级视频| 亚洲精品小视频在线观看| 一区二区三区日韩欧美| 国精品一区二区三区| 亚洲国产二区| 国产精品爱啪在线线免费观看| 久久久99久久精品女同性| 麻豆成人综合网| 午夜在线电影亚洲一区| 美女爽到呻吟久久久久| 亚洲欧美另类在线| 久久久另类综合| 亚洲欧美影音先锋| 欧美国产精品中文字幕| 欧美一区二区黄| 欧美激情五月| 美女精品网站| 国产欧美日韩在线播放| 亚洲欧洲在线一区| 一区二区亚洲欧洲国产日韩| 99国内精品久久| 在线欧美三区| 久久xxxx| 欧美与欧洲交xxxx免费观看| 欧美日韩国产色视频| 亚洲第一区色| 一区二区三区在线免费观看| 亚洲婷婷国产精品电影人久久| 亚洲国产成人高清精品| 久久福利电影| 久久av一区二区| 国产精品成人免费精品自在线观看| 欧美不卡视频一区发布| 精品9999| 欧美在线电影| 久久久噜噜噜久久狠狠50岁| 国产精品老牛| 亚洲视频福利| 亚洲一区视频| 欧美天天综合网| 一区二区三区精品视频| 一区二区欧美日韩视频| 欧美日韩国产一区| 亚洲精品三级| 在线综合+亚洲+欧美中文字幕| 欧美sm重口味系列视频在线观看| 久久婷婷国产麻豆91天堂| 国产一区二区三区日韩| 欧美一区二区成人| 鲁大师影院一区二区三区| 狠狠色噜噜狠狠狠狠色吗综合| 性做久久久久久免费观看欧美 | 国产精一区二区三区| 亚洲一区二区三区影院| 午夜精品免费| 国产一区二区三区在线播放免费观看| 亚洲一级网站| 久久国产婷婷国产香蕉| 激情亚洲成人| 欧美成人免费视频| 亚洲欧美综合一区| 国产麻豆午夜三级精品| 久久大逼视频| 亚洲成色999久久网站| 亚洲免费观看高清完整版在线观看熊| 女同一区二区| 亚洲无人区一区| 久久精品夜色噜噜亚洲a∨| 一区二区三区在线视频播放| 免费欧美在线视频| 99国产精品久久| 欧美在线一二三四区| 在线精品视频一区二区| 欧美激情视频免费观看| 亚洲午夜精品福利| 免费中文日韩| 亚洲综合大片69999| 国产一区欧美| 欧美精品一区二区三区蜜臀| 亚洲香蕉网站| 欧美成人在线免费观看| 亚洲午夜免费视频| 黄网站免费久久| 欧美三级欧美一级| 久久亚洲精品中文字幕冲田杏梨 | 亚洲免费网址| 亚洲国产一成人久久精品| 欧美少妇一区二区| 久久久亚洲国产美女国产盗摄| 亚洲激情影院| 久久在线视频| 午夜精品久久久久久99热软件| 亚洲大片精品永久免费| 国产精品久久久久久久久久免费 | 欧美性色视频在线| 欧美v国产在线一区二区三区| 亚洲一区在线播放| 亚洲人成小说网站色在线| 久久综合久久久久88| 亚洲网站在线观看| 亚洲国产美国国产综合一区二区| 国产精品一区二区三区四区| 欧美激情精品久久久久久久变态| 久久av二区| 亚洲综合精品自拍| 一区二区三区欧美在线| 亚洲第一福利视频| 久久一二三国产| 欧美在线一区二区三区| 亚洲性图久久| aⅴ色国产欧美| 亚洲国产日本| 1024成人网色www| 韩日精品中文字幕| 黑人巨大精品欧美黑白配亚洲| 国产精品久久久久久亚洲毛片| 欧美日韩国产成人在线| 欧美精品日韩| 欧美精品成人一区二区在线观看 | 欧美在线|欧美| 久久成人免费电影| 久久精品91久久香蕉加勒比| 亚洲女女女同性video| 亚洲视频在线一区| 亚洲一区二区三区乱码aⅴ| 性做久久久久久免费观看欧美| 在线性视频日韩欧美| 亚洲手机视频| 亚洲欧美日本另类| 欧美亚洲日本一区| 欧美一区二区三区喷汁尤物| 亚洲男人的天堂在线观看| 亚洲一区在线播放| 午夜激情一区| 久久五月天婷婷| 老司机精品视频网站| 欧美成人激情在线| 亚洲乱码国产乱码精品精98午夜| 一本到12不卡视频在线dvd| 国产精品99久久久久久久久| 亚洲一区二区在线免费观看| 欧美一区免费| 免费观看亚洲视频大全| 欧美激情综合色| 欧美日韩亚洲高清| 国产日韩亚洲| 在线观看日韩欧美| 亚洲美女电影在线| 亚洲欧美经典视频| 久久久久网站| 亚洲精品小视频在线观看| 在线亚洲一区观看| 久久九九全国免费精品观看| 欧美激情第8页| 国产精品亚洲美女av网站| 加勒比av一区二区| 亚洲精品一二| 久久精品久久综合| 亚洲国产综合在线看不卡| 亚洲一区二区三区在线观看视频| 欧美在线免费一级片| 欧美激情一区二区三区成人| 国产精品亚洲视频| 亚洲精品欧美| 久久精品国产77777蜜臀| 亚洲电影一级黄| 亚洲欧美日韩国产综合| 欧美xxxx在线观看| 国产欧美在线观看一区| 亚洲精品一品区二品区三品区| 亚洲欧美日韩中文视频| 欧美成人一区二区三区在线观看| 欧美激情一区二区三区蜜桃视频 | 亚洲天堂av图片| 蜜臀久久99精品久久久久久9| 国产精品久久久久久av福利软件 | 欧美午夜不卡视频|