• <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>

            羅朝輝(飄飄白云)

            關(guān)注嵌入式操作系統(tǒng),移動(dòng)平臺(tái),圖形開(kāi)發(fā)。-->加微博 ^_^

              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              85 隨筆 :: 0 文章 :: 169 評(píng)論 :: 0 Trackbacks

             

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

            來(lái)源:vcblog 作者:Stephan T. Lavavej 翻譯:飄飄白云  

            (轉(zhuǎn)載時(shí)請(qǐng)注明作者和出處。未經(jīng)許可,請(qǐng)勿用于商業(yè)用途)

            簡(jiǎn)介

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

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


            本文為 Part 2 的第三頁(yè)

             

            轉(zhuǎn)發(fā)問(wèn)題

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

             

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

             

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

                inner(t);

            }

             

            問(wèn) 題來(lái)了,如果傳給它的參數(shù)是非常量 rvalue,那我們就無(wú)法調(diào)用 outer()。如果 inner() 接收 const int& 型的參數(shù),那 inner(5) 是可以通過(guò)編譯的,但是 outer(5) 就編譯不過(guò)了。因?yàn)?T 會(huì)被推導(dǎo)為 int, 而 int& 是不能綁定到常量 5 的。

             

            好吧,讓我們?cè)囋囘@個(gè):

             

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

                inner(t);

            }

             

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

             

            現(xiàn)在,你可以重載兩個(gè)分別帶 T& 和 const T& 參數(shù)的 outer(),這確實(shí)管用。當(dāng)你調(diào)用 outer()時(shí),就像直接調(diào)用 inner() 一樣。

             

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

             

            在 C++98/03 中,轉(zhuǎn)發(fā)問(wèn)題是很嚴(yán)重的,而且本質(zhì)上無(wú)解(必須求助于惡心的預(yù)處理機(jī)制,這會(huì)嚴(yán)重拖慢編譯速度,還讓代碼變得難以閱讀)。總算, rvalue 優(yōu)雅地解決了這個(gè)問(wèn)題。

             

            完美轉(zhuǎn)發(fā) 模式

             

            完美轉(zhuǎn)發(fā)讓你能簡(jiǎn)單而清晰地只寫(xiě)一個(gè)模板函數(shù)就可以轉(zhuǎn)發(fā)所有的參數(shù)給任意函數(shù),不管它帶幾個(gè)參數(shù),也不管參數(shù)類型是什么。而且參數(shù)的非常量/常量, lvalue/rvalue 屬性都能得以保留,讓你可以像使用 inner() 一樣使用 outer(),還可以和 move 語(yǔ)意一起用從而獲得額外的好處。( C++0x 的變長(zhǎng)模板技術(shù)解決了“任意數(shù)目”這部分,我們?cè)谶@里把 N 看做任意數(shù)目)。乍看之下很神奇,實(shí)際上很簡(jiǎ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&)

             

            兩行!完美轉(zhuǎn)發(fā)只用了兩行!夠簡(jiǎn)潔吧!

             

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

             

            跟 std::move() 一樣, std::identify 和 std::forward() 都是在 C++<utility> 中定義的( VC10 會(huì)有, VC10 CTP中沒(méi)有)。我將演示怎么來(lái)實(shí)現(xiàn)它們。(再次,我將交替使用 std::identity 和我的 Identity, std::forward() 和我的 Forward(),因?yàn)樗麄兊膶?shí)現(xiàn)是等價(jià)的。)

             

            現(xiàn)在,讓我們來(lái)揭開(kāi)“魔術(shù)“的神秘面紗,其實(shí)它靠的就是模板參數(shù)推導(dǎo)和引用折疊(reference collapsing)技術(shù)。

             

            rvalue 引用:模板參數(shù)推導(dǎo)和引用折疊(reference collapsing)

             

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

             

            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 的顯式規(guī)格說(shuō)明來(lái)打印出類型。

             

            當(dāng)我們調(diào)用 quark(up) 時(shí),會(huì)進(jìn)行模板參數(shù)推導(dǎo)。 quark() 是一個(gè)帶有模板參數(shù) T 的模板函數(shù),但是我們還沒(méi)有為它提供顯式的類型參數(shù)(比如像 quark<X>(up)這樣的)。通過(guò)比較函數(shù)形參類型 Type&& 和函數(shù)實(shí)參類型(一個(gè) string 類型的 lvalue)我們就能推導(dǎo)出模板實(shí)參類型。(譯注:原文用 argument 表示實(shí)參,parameter 表示形參)

             

            C++0x 會(huì)轉(zhuǎn)換函數(shù)實(shí)參的類型和形參的類型,然后再進(jìn)行匹配。

             

            首先,轉(zhuǎn)換函數(shù)實(shí)參的類型。這遵循一條特殊規(guī)則(提案N2798

             

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

             

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

             

            替換操作會(huì)在類型推導(dǎo)之后進(jìn)行。模板形參 T 出現(xiàn)的每一個(gè)地方都會(huì)被替換成推導(dǎo)出來(lái)的模板實(shí)參類型。在 quark(string())Tstring ,因此 T&& 會(huì)是 string&& 。同樣,在 quark(charm()) 中,Tconst string , 因此 T&&const string&& 。但 quark(up) 和 quark(down) 不同,它們遵循另外的特殊規(guī)則。

             

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

             

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

             

            完美轉(zhuǎn)發(fā): std::forward() 和 std::identidy 是怎工作的

             

            讓我們?cè)賮?lái)看看 outer() :

             

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

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

            }

             

            現(xiàn)在我們明白了為什么 outer() 的形參是 T1&&T2&& 類型的了,因?yàn)樗鼈兡軌虮3肿鹘o outer() 的實(shí)參的信息。那為什么這里要調(diào)用 Forward<T1>()Forward<T2>() 呢?還記得么,具名 lvalue 引用和具名 rvalue 引用都是 lvalue 。如果 outer() 調(diào)用 inner(t1, t2) ,那么 inner() 總是會(huì)當(dāng) lvalue 來(lái)引用 t1t2 ,這就破壞了完美轉(zhuǎn)發(fā)。

             

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

             

            template <typename T> struct Identity {

                typedef T type;

            };

             

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

                return t;

            }

             

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

             

            你可能會(huì)好奇如果不小心寫(xiě)成 Forward<T1&&>(t1) 又會(huì)怎樣呢?(這個(gè)錯(cuò)誤還是蠻誘人的,因?yàn)?outer() 接收的就是 T1&& t1 )。很幸運(yùn),沒(méi)什么壞事情會(huì)發(fā)生。 Forward<T1&&>() 接收與返回的都是 T1&& && ,這會(huì)被折疊成 T1&& 。于是,Forward<T1>(t1)Forward<T1&&>(t1) 是等價(jià)的,我們更偏好前者,是因?yàn)樗绦?/p>

             

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

             

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

                return t;

            }

             

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

             

            move 語(yǔ)意: std::move() 是怎工作的

             

            現(xiàn)在我們已經(jīng)學(xué)習(xí)了模板參數(shù)推導(dǎo)和引用折疊的特殊規(guī)則,讓我們?cè)賮?lái)看看 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 機(jī)制基本上是復(fù)制 C++0x <type_traits> 中的 std::remove_reference 。舉例來(lái)說(shuō),RemoveReference<string>::type , RemoveReference<string&>::typeRemoveReference<string&&>::type 都是 string

             

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

            · 當(dāng)調(diào)用 Move(string), string 是一個(gè) lvalue 時(shí), T 會(huì)被推導(dǎo)為 string& ,于是 Move() 接收的就是 string& (經(jīng)折疊之后)并返回 string&& (經(jīng) RemoveReference 之后)。

             

            · 當(dāng)調(diào)用 Move(const string), const string 是一個(gè) lvalue 時(shí), T 會(huì)被推導(dǎo)為 const string& ,于是 Move() 接收的就是 const string&& (經(jīng)折疊之后)并返回 const string&& (經(jīng) RemoveReference 之后)。

             

            · 當(dāng)調(diào)用 Move(string), string 是一個(gè) rvalue 時(shí), T 會(huì)被推導(dǎo)為 string ,于是 Move() 接收的就是 string&& 并返回 string&&

             

            · 當(dāng)調(diào)用 Move(const string), const string 是一個(gè) rvalue 時(shí), T 會(huì)被推導(dǎo)為 const string ,于是 Move() 接收的就是 const string&& 并返回 const string&&

             

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

             

            如果你想對(duì) rvalue 引用有更多了 解,你可以去讀有關(guān)它們的提案。要注意,提案與現(xiàn)在的決定可能已經(jīng)不同了, rvalue 引用已經(jīng)被整合到 C++0x 草案中來(lái)了,在那里它得到持續(xù)的改進(jìn)。有些提案或已不再正確,或已過(guò)時(shí),或已有了替代方案,就沒(méi)有被采納。無(wú)論怎樣,它們還是能提供一些有用信息的。

             

            N1377, N1385, 和 N1690 是主要的提案,N2118 包含被整合進(jìn)標(biāo)準(zhǔn)草案之前的最后版本。 N1784, N1821, N2377, 和 N2439 記錄了“將 Move 語(yǔ)意擴(kuò)展到 *this ”的演化過(guò)程,這個(gè)也被整合到 C++0x 中來(lái)了,但還沒(méi)有在VC10 中得到實(shí)現(xiàn)。

             

            展望

            N2812 “Rvalue 引用的安全問(wèn)題(以及如何解決)” 提出了對(duì)初始化規(guī)則的修改,它禁止 rvalue 引用綁定到 lvalue 。 這不會(huì)影響 move 語(yǔ)意和完美轉(zhuǎn)發(fā),所以它不會(huì)讓你剛學(xué)到的新技術(shù)失效(它只是修改了 std::move() 和 std::forward() 的實(shí)現(xiàn))。

             

            Stephan T. Lavavej

            Visual C++ Libraries Developer

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

            翻譯:飄飄白云

             

            (轉(zhuǎn)載時(shí)請(qǐng)注明作者和出處。未經(jīng)許可,請(qǐng)勿用于商業(yè)用途)

             < 第一頁(yè)第二頁(yè), 本頁(yè)>


             



            posted on 2009-06-05 15:09 羅朝輝 閱讀(2906) 評(píng)論(0)  編輯 收藏 引用 所屬分類: C/C++
            伊人色综合久久天天网| 久久久国产精华液| 日本精品久久久久久久久免费| 久久久久久亚洲Av无码精品专口| 亚洲国产天堂久久久久久| 精品欧美一区二区三区久久久| 免费精品99久久国产综合精品| 久久精品人人做人人爽97| 久久久久久午夜成人影院| 久久99精品久久久久婷婷| 久久影院综合精品| 久久精品国产亚洲av水果派| 久久精品亚洲精品国产色婷| 91久久婷婷国产综合精品青草| 久久久久亚洲av无码专区导航| 久久久噜噜噜久久中文福利| 久久精品国产99国产电影网| 曰曰摸天天摸人人看久久久| 国内精品欧美久久精品| 无码任你躁久久久久久久| 久久中文字幕人妻熟av女| 亚洲va国产va天堂va久久| 国产69精品久久久久777| 99久久精品国产一区二区| 久久精品一区二区影院 | 精品久久久久久无码不卡| 久久激情五月丁香伊人| 久久人人超碰精品CAOPOREN| 亚洲人成无码网站久久99热国产 | 亚洲欧美成人久久综合中文网| 怡红院日本一道日本久久| 欧美激情精品久久久久久| 久久久久久久久久久| 亚洲一本综合久久| 亚洲精品第一综合99久久| 热re99久久精品国99热| 久久久精品久久久久特色影视| 午夜久久久久久禁播电影| 97精品伊人久久久大香线蕉| 伊人久久大香线蕉亚洲| 久久精品国产99久久丝袜|