精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Visual C++ 2015引入更新的C++ 特性到Windows API

開發 后端
這里我不會讓你看一個枯燥的新特性列表,或者走馬觀花地看下它的功能,而是會帶你瀏覽下一些傳統情況下的復雜代碼現在如何讓人相當愉快書寫。當然,這得益于成熟的Visual C++編譯器。我將會向你展示windows的一些本質,在現在或將來API中實際上都是很重要的本質。

Visual C++ 2015 是 C++ 團隊付出巨大努力將現代C++引入windows平臺的成果。在***的幾個發行版本里,VC++已經逐步添加了現代C++語言以及庫的特色,這些結合在一起會創造一個用于構建通用windows App和組件的絕對驚艷的開發環境。Visual C++2015建立在早期版本引入的驚人進步,提供了成熟的、支持大多數C++11特性以及C++ 2015子集的編譯器。你或許會懷疑編譯器支持的完整程度,公正地說,我認為他能支持大部分重要的語言特性,支持現代C++將會迎來windows 程序庫開發一片新的天地。這才是關鍵。只要編譯器支持一個高效優雅的庫的開發環境,開發者就能構建偉大的app和組件。

這里我不會讓你看一個枯燥的新特性列表,或者走馬觀花地看下它的功能,而是會帶你瀏覽下一些傳統情況下的復雜代碼現在如何讓人相當愉快書寫。當然,這得益于成熟的Visual C++編譯器。我將會向你展示windows的一些本質,在現在或將來API中實際上都是很重要的本質。

頗具諷刺意味的是,對于COM來說,C++已經足夠現代了.是的,我在談論組件對象模型(COM),多年以來,它一直是大多數Windows API的基石.同時,它也繼續作為Windows運行時的基石.COM無可爭辯的依附于C++的原始設計,借鑒了許多來自C++的二進制和語義約定,但是它從來都不夠優雅.C++的部分內容被認為可移植性不夠,如dynamic_cast,必須避免使用它,以采用可移植的解決方案,這使得C++的開發實現更具挑戰性.近些年已經為C++開發者提供了許多解決方案,讓COM變得更加可移植.C++/CX 語言拓展,可能是Visual C++團隊到目前為止***野心的.具有諷刺意味的是,這些提升標準C++支持的努力,已經將C++/CX棄之不顧了,也讓語言拓展變得冗余.

為了證明這點,我會展示給你如何完整的用現代C++實現IUnknown和IInspectable接口.關于這兩個接口沒有什么現代的或吸引力的東西.IUnknown繼續成為卓越API,如DirectX,的集中抽象.這些接口--IInspectable繼承自IUnknown--位于Windows運行時的中心.我將展示給你如何不用任何語言拓展來實現它們,接口表或其它宏--只需要包含大量類型信息的高效和優雅的C++,就可以讓編譯器和開發者擁有,關于如何創建所需的,優異的人機對話.

主要的問題是, 如何列出  COM 或 Windows Runtime 類需要實現的接口, 而且要方便開發者使用, 和編譯器訪問. 比如, 列出所有可用類型, 以便編譯器查詢, 甚至枚舉出相應的接口. 要是能實現這樣的功能, 也許就能讓編譯器生成 IUnknown QueryInterface 甚至 IInspectable GetIids 方法的代碼. 這兩個方法才是問題的關鍵. 按照傳統的觀念, 唯一的解決辦法涉及到語言擴展(language extensions), 萬惡的宏定義, 以及一堆難以維護的代碼.

兩種方法的實現, 都用到類需要實現的接口. 可變參數模板( variadic template)是***:

  1. template <typename ... Interfaces>  
  2. class __declspec(novtable) Implements : public Interfaces ...  
  3. {  
  4. }; 

__declspec(novtable)拓展屬性可以防止構造函數和析構函數初始化抽象類的vfptr,這通常意味著減少大量的代碼.實現類模板包括一個模板參數包,這使它成為一個可變模板.一個參數包即一個模板參數接受任意數目的模板參數變量.但是在這種情況下,我描述的模板參數將只會在編譯時進行查詢.接口將不會出現在函數的參數列表之中.

這些參數的一個使用已經顯而易見.參數包拓展后成為公共基礎類的參數列表.當然,我仍然有責任到***實現這些虛函數,但是此刻我會描述一個實現任意數目接口的一個具體類:

  1. class Hen : public Implements<IHen, IHen2>  
  2. {  
  3. }; 

因為參數包拓展為指定基礎類的列表,所有它等同于下面我可能會寫出的代碼:

  1. class Hen : public IHen, public IHen2  
  2. {  
  3. }; 

用這種方式結構化實現類模板的美妙之處在于,我現在可以,在實現類模板中,寫入各種樣版實現代碼,而Hen類的開發者則可以使用這種不唐突的抽象,同時大量忽略隱含的細節.

到目前為止,一切都很好.現在,我將考慮IUnknown的實現.我應該可以在實現類模板中完整的實現它,并提供編譯器現在所擁有的類型信息.IUnknown提供了對于COM類非常重要的兩種工具,就像氧氣和水對于人類一樣.***個可能簡單些的是引用計數,這也是COM對象跟蹤它們生命周期的方式.COM規定一種侵入式的引用計數,它借助于每個對象,統計多少個外部引用存在,來負責管理自己的生命周期.這與智能指針,如C++ 11的shared_ptr類,的引用計數恰恰相反,智能指針對象并不知道它的共享關系.你可能會爭論這兩種方式的優缺點.但是,實際上COM的方法通常更高效,這也是COM的工作方式,你必須處理它.如果沒有其它的,你很可能會同意這點,在shared_ptr里面包裝一個COM接口會是一件極不友好的事情!

我將以只有運行時的開銷作為開始,它是通過實現類模板介紹的:

  1. protected:  
  2.   unsigned long m_references = 1;  
  3.   Implements() noexcept = default;  
  4.   virtual ~Implements() noexcept  
  5.   {} 

默認構造函數并不是真正的開銷所在,它只是簡單的確保最終的構造函數--它將初始化引用計數--為protected而不是public的.引用計數和虛構造函數都是protected的.讓派生類訪問引用計數,是為了允許更復雜的類組合.大多數類可以簡單的忽略它,但是需要注意的是,我正初始化引用計數為1.這和通常建議初始化引用計數為0,形成鮮明的對比,因為此時并沒有處理引用.這個方式在ATL中非常流行,明顯受到Don Box的COM本質論的影響,但是這是非常有問題的,ATL的源代碼的研究可以作為佐證.開始于這個假設,即引用的所有權將會立即由調用者獲得,或者依附于一個提供更少錯誤構造處理的智能指針.

虛析構函數提供了很大的便利性,它允許實現類模板實現引用計數,而不是強制實現類本身來提供實現.另一個選項,是使用奇特的遞歸模板模式(Curiously Recurring Template Pattern)來避免使用虛函數.通常我會選擇這個方法,但是它會稍微增加抽象的復雜性,同時,因為COM類本身有一個vtable,所以這里也沒有什么理由去避免使用虛函數.有了這些基本類型之后,在實現類模板中實現AddRef和Release將會變得非常簡單.首先,AddRef方法可以簡單的使用InterlockedIncrement來增加引用計數:

  1. virtual unsigned long __stdcall AddRef() noexcept override  
  2. {  
  3.   return InterlockedIncrement(&m_references);  

這不言自明.不要想出某些復雜的方法,通過使用C++的加減操作符來有條件的替換InterlockedIncrement和InterlockedDecrement函數.ATL通過極大的增加復雜性去做這個嘗試.如果你考慮效率,寧可為避免調用AddRef和Release產生謬誤而多花心思.同樣的,現代C++增加了對move語義的支持,以及增加轉移引用所有權的能力.現在,Release方法只是略顯復雜:

  1. virtual unsigned long __stdcall Release() noexcept override  
  2. {  
  3.   unsigned long const remaining = InterlockedDecrement(&m_references);  
  4.   if (0 == remaining)  
  5.   {  
  6.     delete this;  
  7.   }  
  8.   return remaining;  

引用計數減少后,結果被賦值給臨時變量.這很重要,因為結果需要返回.但是如果對象銷毀了,引用此對象的成員變量就是非法的了.假定沒有其它未處理的引用,這個對象就通過前面說到的虛析構函數刪除了.這就是引用計數的結論,實現類Hen仍然和之前的一樣簡單:

  1. class Hen : public Implements<IHen, IHen2>  
  2. {  
  3. }; 

現在,到了想象一下QueryInterface的奇妙世界的時間了。實現IUnknown方法是一個很重要的實踐。在我的Pluralsight課程中,我廣泛的實現了它。你可以在Don Box編寫的<<COM本質論>>(Addison-Wesley Professional,1998)一書中,閱讀關于實現你自己的IUnknown的奇妙的和不可思議的方法。需要注意的是,雖然這是一本關于COM的優秀書籍,但是它是基于C++98的,并沒有呈現出任何現代C++的特征。為了節省時間,我假定你已經熟悉了QueryInterface的實現過程,并集中于如何用現代C++實現它。下面是虛函數本身:

  1. virtual HRESULT __stdcall QueryInterface(  
  2.   GUID const & id, void ** object) noexcept override  
  3. {  

給定一個GUID用來標識一個特別的接口之后,QueryInterface應該來決定一個對象是否實現了需要的接口。如果實現了,它必須減少這個對象的引用計數,同時通過外部參數來返回所需的接口指針。如果沒有實現,它必須返回一個空指針。因此,我將以一個粗略的輪廓來作為開始:

  1. *object = // Find interface somehow  
  2. if (nullptr == *object)  
  3. {  
  4.   return E_NOINTERFACE;  
  5. }  
  6. static_cast<::IUnknown *>(*object)->AddRef();  
  7. return S_OK; 

QueryInterface首先會嘗試設法查找所需的接口。如果接口受不支持,則返回E_NOINTERFACE錯誤碼。請注意,我是如何按照要求處理接口指針不支持的情況。你應該把QueryInterface接口看作是二元的操作。它要么成功找到所需的接口,要么查找失敗。不要嘗試發揮創造性,只需要依據條件響應即可。盡管COM規范有一些限制項,但是大多數消費者都會簡單的假定接口不受支持,而不管你會返回何種錯誤碼。在你的實現中的任何錯誤,都毫無疑問的會導致你陷入調試的深淵。QueryInterface是非常基礎的,不能胡亂對待。***,AddRef由接口指針再次調用,用來支持某種極少的而又允許的類組合場景。這些不受實現類模板的顯式支持,但是我情愿在這里做一個表率。重要的是,記住引用計數操作是面向接口的,而不是面向對象的。你不能 簡單的,在屬于一個對象的任意接口上面,調用AddRef或者Release。你必須依賴COM規則來管理對象,否則你會冒險引入以不可思議的方式崩潰的非法代碼。

但是我如何得知,請求的GUID是否就代表著類想要實現的接口呢?我需要回到實現類模板收集的類型信息的地方,其中類型信息通過它的模板參數包來收集。請記住,我的目標是準許編譯器為我實現它。我希望最終代碼,和我手寫的一樣高效,甚至更好。我會通過可變函數模板集合來進行查詢,函數模板自身包括模板參數包。我將以BaseQueryInterface函數模板作為開始:

  1. virtual HRESULT __stdcall QueryInterface(  
  2.   GUID const & id, void ** object) noexcept override  
  3. {  
  4.   *object = BaseQueryInterface<Interfaces ...>(id); 

BaseQueryInterface本質上是IUnknown QueryInterface的現代C++版本。它直接返回接口指針而不是HRESULT類型。空指針則表明失敗的情況。它接受單一函數參數,GUID標識著要查找的接口。更重要的是,我拓展了類模板參數包為完整模式,這樣,BaseQueryInterface函數就可以開始枚舉接口的處理過程。 起初你可能會認為,由于BaseQueryInterface是實現類模板的成員函數,所以它可以簡單直接的訪問接口的鏈表,但是我需要準許這個函數剝離鏈表中的***個接口,就像下面這樣:

  1. template <typename First, typename ... Rest>  
  2. void * BaseQueryInterface(GUID const & id) noexcept  
  3. {  

按照這種方式,BaseQueryInterface函數能識別***個接口,并且給接下來的搜索留有余地。看吧,COM有一定數量的特殊規則來支持QueryInterface 實現或至少接受對象識別。尤其是請求IUnknown,必須總是返回確切相同的指針,客戶端才能確定兩個接口的指針是否來自同一個對象。因此,BaseQueryInterface函數最棒的地方就是實現了這些假設。所以,可以從***代表類想要實現的***個接口的模板參數的GUID請求的對比開始。如果不匹配,我會檢查IUnknown是否開始請求了:

 
  1. if (id == __uuidof(First) || id == __uuidof(::IUnknown))  
  2. {  
  3.   return static_cast<First *>(this);  

假設有一個匹配的,我直接準確無誤的返回了***個接口的指針。static_cast 能確保編譯器基于IUnknown的多種接口不會引起歧義。cast只是校準了指針,讓類的vtable能找到正確的指針位置,因為所有vtable接口是以IUnknown的三個方法開始的,這非常符合邏輯。

而我不妨同樣添加IInspectable查詢的可選支持。IInspectable相當變態,在某種意義上,它是Windows運行時接口,因為每個Windows運行時預計編程語言(如 C# 和 JavaScript)必須直接來自IInspectable,而不僅僅只是IUnknown接口。相對于C++的工作方式和COM傳統的定義方式,以適應公共語言運行庫的方式實現對象和接口是不幸的事實。更不幸的是當對象組合的時候對性能的影響,我會在下文中討論。至于QueryInterface,我只需確保IInspectable能被查詢,它應該是一個Windows運行時類的實現,而不是一個簡單的典型COM類。雖然關于IUnknown的明確的COM規則不適用于IInspectable,我可以簡單的用相同的方式對待后者。但這兩個挑戰。首先,需要了解是否有任何IInspectable派生出來的接口實現。第二,需要了解接口的類型,這樣就可以正確的返回一個沒有歧義的調整過的接口指針。假定列表中的***個接口都是基于IInspectable,那可以只更新BaseQueryInterface 如下

  1. if (id == __uuidof(First) ||  
  2.   id == __uuidof(::IUnknown) ||  
  3.   (std::is_base_of<::IInspectable, First>::value &&  
  4.   id == __uuidof(::IInspectable)))  
  5. {  
  6.   return static_cast<First *>(this);  

注意,我用的是C++ 11中的is_base_of 的特性,來確定***個模板參數是一個IInspectable的衍生接口。萬一實現典型的COM類不支持Windows運行時,就能確保隨后的對照是由編譯器排除的。這樣我可以無縫地支持Windows運行時和經典的COM類,即沒有增加組件開發人員的語句復雜性,也沒有任何不必要的運行時開銷。但是,如果恰好遇列舉出來得首位不是IInspectable接口,就會有不容易察覺的Bug的隱患。所需要做的就是,用某種方法替代is_base_of來掃描整個接口的列表:

  1. template <typename First, typename ... Rest>  
  2. constexpr bool IsInspectable() noexcept  
  3. {  
  4.   return std::is_base_of<::IInspectable, First>::value ||  
  5.     IsInspectable<Rest ...>();  

IsInspectable 也是基于is_base_of特性的,但是當前適用于匹配接口。如果沒找到基于IInspectable 的接口則終止:

  1. template <int = 0>  
  2. constexpr bool IsInspectable() noexcept  
  3. {  
  4.   return false;  

我會禁用掉稀奇古怪的默認參數。假定 IsInspectable 返回的是 true,我需要找到***個IInspectable-based 接口:

  1. template <int = 0>  
  2. void * FindInspectable() noexcept  
  3. {  
  4.   return nullptr;  
  5. }  
  6. template <typename First, typename ... Rest>  
  7. void * FindInspectable() noexcept  
  8. {  
  9.   // Find somehow  

再次使用 is_base_of 特性,但這次要返回一個真實匹配的接口指針:

  1. #pragma warning(push)  
  2. #pragma warning(disable:4127) // conditional expression is constant  
  3. if (std::is_base_of<::IInspectable, First>::value)  
  4. {  
  5.   return static_cast<First *>(this);  
  6. }  
  7. #pragma warning(pop)  
  8. return FindInspectable<Rest ...>(); 

BaseQueryInterface 這時可以利用IsInspectable 和 FindInspectable 一起來支持查詢 IInspectable:

  1. if (IsInspectable<Interfaces ...>() &&   
  2.   id == __uuidof(::IInspectable))  
  3. {  
  4.   return FindInspectable<Interfaces ...>();  

然后指定具體的 Hen 類:

  1. class Hen : public Implements<IHen, IHen2>  
  2. {  
  3. }; 

實現類的模板,可以確保編譯器能生成更高效的代碼,不管 IHen、Hen2 來自 IInspectable 還是 IIUnknown (或者其他接口)。現在,我可以***實現 QueryInterface 的遞歸部分,以及任何追加的接口,例如上面例子中的 IHen2。BaseQueryInterface 是靠調用 FindInterface 函數模板結束的: 

  1. template <typename First, typename ... Rest>  
  2. void * BaseQueryInterface(GUID const & id) noexcept  
  3. {  
  4.   if (id == __uuidof(First) || id == __uuidof(::IUnknown))  
  5.   {  
  6.     return static_cast<First *>(this);  
  7.   }  
  8.   if (IsInspectable<Interfaces ...>() &&   
  9.     id == __uuidof(::IInspectable))  
  10.   {  
  11.     return FindInspectable<Interfaces ...>();  
  12.   }  
  13.   return FindInterface<Rest ...>(id);  

注意,我調用這個FindInterface函數模板,大致等同于我原來調用的BaseQueryInterface,在這個例子中,我向它傳遞接口的其余部分。我特意再次擴大參數包,這樣它可以在列表的其余部分識別***接口。但會提示一個故障。由于模板參數包不是以函數實參來擴展的,這可能會變得棘手,編程語言寫不出來我想要的。更多的時候,這種“遞歸的”FindInterface可變模板正是你想要的:

  1. template <typename First, typename ... Rest>  
  2. void * FindInterface(GUID const & id) noexcept  
  3. {  
  4.   if (id == __uuidof(First))  
  5.   {  
  6.     return static_cast<First *>(this);  
  7.   }  
  8.   return FindInterface<Rest ...>(id);  

它會從模板參數的其余部分中分離,如果有匹配就返回調整過的接口指針。另外,它也會調用自己,直到list取完。當我籠統地提及編譯期遞歸時,重要的是要注意這個函數模板,以及其他類似的實現類模板的例子,在技術上遞歸,而不是在編譯期。每個函數模板的實例調用不同的函數模板的實例。例如,FindInterface<IHen, IHen2> 調用 FindInterface<IHen2>, FindInterface<IHen2>調用 FindInterface<>。為了讓它遞歸, FindInterface<IHen, IHen2>不需要調用FindInterface<IHen, IHen2>。

#p#

盡管如此,還是要記住,這樣的“遞歸”發生在編譯時,它就像你自己手寫的一條條if語句。但是,現在,我遇到麻煩了。這個序列如何終止呢?當然是當模板參數列表為空的時候。這個問題在于C++已經定義了空模板參數列表的含義:

  1. template <>  
  2. void * FindInterface(GUID const &) noexcept  
  3. {  
  4.   return nullptr;  

這幾乎是正確的,但是編譯器會提示你,函數模板在這個特化中無法使用。同時,如果我不提供終止函數,當參數包為空的時候,編譯器將無法編譯最終的調用。這不是函數重載的情況,因為參數列表依舊是相同的。幸運的是,解決方案非常簡單。我可以通過提供一個無名的默認參數,來避免終止函數看起來像一個特化:

  1. template <int = 0>  
  2. void * FindInterface(GUID const &) noexcept  
  3. {  
  4.   return nullptr;  

編譯器樂于此,同時,如果請求一個不支持的接口,終止函數會簡單的返回一個空指針,同時虛函數QueryInterface將返回E_NOINTERFACE錯誤碼。對IUnknown而言,這考慮得很周到。如果你所關心的是經典的COM,你可以安全的停在那里,因為那就是你所需要的所有內容。關于這點,可以反復的操作,編譯器將優化QueryInterface的實現,其間使用各種各樣的“遞歸的”函數調用和常量表達式,代碼至少和你手工寫的一樣好。對于IInspectable而言,同樣的方式也可以實現。

對于Windows 運行時類,實現IInspectable會增加額外的復雜度。這個接口并非是和IUnknown一樣的基礎性接口,它提供了一些不確定的工具的集合,而IUnknown則提供了絕對基礎的函數。關于此,我會為以后的文章留下一個討論,并聚焦于支持任意Windows運行時類的高效和現代的C++實現。首先,我會避開GetRuntimeClassName和GetTrustLevel虛函數。實現這兩個方法相對而言微不足道,同時由于極少使用,所以它們的實現可以簡單搪塞一下。GetRunTimeClassName方法,需要返回這個對象所代表的運行時類的完整名字的字符串。我將把這留給類自身去完成,決定是否去這樣做。實現類模板可以簡單地返回E_NOTIMPL,用來表明此方法并未實現:

  1. HRESULT __stdcall GetRuntimeClassName(HSTRING * name) noexcept  
  2. {  
  3.   *name = nullptr;  
  4.   return E_NOTIMPL;  

同樣的,GetTrustLevel 方法也會簡單返回枚舉類型的常量:

  1. HRESULT __stdcall GetTrustLevel(TrustLevel * trustLevel) noexcept  
  2. {  
  3.   *trustLevel = BaseTrust;  
  4.   return S_OK;  

需要注意的是,我并沒有顯示的標記這些IInspectable方法為虛函數。避免聲明為虛函數,是為了讓編譯器剔除這些函數,COM類無需真正的實現任何 IInspectable 接口。現在,我將注意力轉移到IInspectable GetIids 方法。這比 QueryInterface 更容易產生錯誤。盡管它的實現不是那么嚴格,但是一個高效的編譯器生成的實現也是可取的。GetIids 返回一個動態分配的 GUID 數組。每個 GUID 代表一個對象要實現的接口。起初你可能會認為,這只是對象通過 QueryInterface 所支持的一個簡單的聲明,但是那只在表面上看是正確的。GetIids 方法可能會保留一些接口而不公開。不管怎樣,我會以基本定義作為開始:

  1. HRESULT __stdcall GetIids(unsigned long * count,   
  2.   GUID ** array) noexcept  
  3. {  
  4.   *count = 0;  
  5.   *array = nullptr; 

***個參數指向調用者提供的變量,其中 GetIids 方法必須設置它為結果數組中的接口數目。第二個參數指向一個 GUID 數組,同時也表示著這里的實現是如何將動態分配的數組返回給調用者的。此處,我清除了這兩者參數,只是為了安全起見。現在我需要決定這個類實現多少個接口。我想說的是,使用 sizeof 操作符,它可以確定這個參數包的大小,如下:

  1. unsigned const size = sizeof ... (Interfaces); 

這相當方便,同時,編譯器也會報告參數包拓展后要展現的模板參數的數目。這也是一個有效的常量表達式,它會在編譯時產生一個值。我之前略為提及的,這無法實現的原因是,GetIids的實現會保留一些他們不愿和其他人共享的接口,這相當普遍。這些接口被稱為隱含接口。任何人都可以通過QueryInterface來查詢它們,但是GetIids不會告知這些接口是可用的。這樣,我需要為排除了隱含接口的可變sizeof操作符,提供一個編譯時的替代品。同時,我需要提供某種方式來聲明和標識這些隱含接口。我以后者作為開始。對于組件開發人員,我希望讓實現類變得盡可能簡單,這樣就需要一個不太引人注意的技巧。我簡單的提供一個Cloaked類模板,用來“修飾”任意的隱含接口:

  1. template <typename Interface>  
  2. struct Cloaked : Interface {}; 

然后,我決定在類Hen上實現一個特別的"IHenNative"接口,并非所有的使用者都知道它的存在:

  1. class Hen : public Implements<IHen, IHen2, Cloaked<IHenNative>>  
  2. {  
  3. }; 

由于Cloaked類模板繼承自它的模板參數,所以已存的QueryInterface實現可以繼續無縫工作。我已經增加了一點額外的編譯時的類型信息,現在我可以查詢它們。由此,我會定義一個IsCloaked類型,這樣,我就可以很容易的查詢任意接口以判斷它是否被隱藏:

  1. template <typename Interface>  
  2. struct IsCloaked : std::false_type {};  
  3. template <typename Interface>  
  4. struct IsCloaked<Cloaked<Interface>> : std::true_type {}; 

現在,我可以使用一個遞歸的可變函數模板,來計算未隱藏的接口數目:

  1. template <typename First, typename ... Rest>  
  2. constexpr unsigned CounInterfaces() noexcept  
  3. {  
  4.   return !IsCloaked<First>::value + CounInterfaces<Rest ...>();  

當然,我需要一個終止函數,可以簡單的返回0:

  1. template <int = 0>  
  2. constexpr unsigned CounInterfaces() noexcept  
  3. {  
  4.   return 0;  

使用現代C++在編譯時進行算術計算的強大能力令人目瞪口呆,同時也簡單的令人驚嘆。現在我通過請求數量來繼續完善GetIids的實現:

  1. unsigned const localCount = CounInterfaces<Interfaces ...>(); 

一個不太圓滿的地方是,編譯器對常量表達式的支持還不是很成熟。盡管,這毫無疑問是一個常量表達式,但是編譯器卻不會如此看待constexpr成員函數。理想情況下,我可以標識CountInterfaces函數模板為constexpr,同時結果表達式也將是一個常量表達式,但是,編譯器不會這認為。另一方面,毋庸置疑,編譯器會優化這段代碼。現在,不管是什么原因,如果CountInterfaces沒有找到隱含接口,GetIids會簡單的返回成功,因為結果數組會為空:

  1. if (0 == localCount)  
  2. {  
  3.   return S_OK;  

同樣的,這也是一個有效的常量表達式,編譯器會無需任何條件的生成代碼。換句話說,如果沒有未隱藏的接口,剩下的代碼會簡單的從實現中刪除。否則,代碼的實現,會強制要求使用傳統的COM分配器,分配一個合理大小的GUID數組:

  1. GUID * localArray = static_cast<GUID *>(CoTaskMemAlloc(sizeof(GUID) * localCount)); 

當然,這可能失敗,在這種情況下,我簡單的返回合適的HRESULT值:

  1. if (nullptr == localArray)  
  2. {  
  3.   return E_OUTOFMEMORY;  

在這點上,GetIids準備好一個數組用來存放GUID。就像你所期待的那樣,我需要***一次枚舉接口,然后拷貝每個未隱藏的接口的GUID到這個數組之中。我將使用一組函數模板,就像我之前使用的那樣:

  1. template <int = 0>  
  2. void CopyInterfaces(GUID *) noexcept {}  
  3. template <typename First, typename ... Rest>  
  4. void CopyInterfaces(GUID * ids) noexcept  
  5. {  

這個可變模板(第二個函數)可以簡單的使用IsCloaked類型來決定,在增加指針之前,是否拷貝由First模板參數標識的接口的GUID。使用這種方式,可以遍歷數組,而無需記錄它包含多少個元素,或者它將寫入數組的哪個位置。我也禁止了關于常量表達式的警告:

  1. #pragma warning(push)  
  2. #pragma warning(disable:4127) // Conditional expression is constant  
  3. if (!IsCloaked<First>::value)  
  4. {  
  5.   *ids++ = __uuidof(First);  
  6. }  
  7. #pragma warning(pop)  
  8. CopyInterfaces<Rest ...>(ids); 

如你所見,在***“遞歸”調用CopyInterfaces使用了可能增加的指針的值。我幾乎完成了(整個實現過程)。在返回給調用者之前,GetIids的實現可以通過調用CopyInterfaces來填充數組:

  1. CopyInterfaces<Interfaces ...>(localArray);  
  2.   *count = localCount;  
  3.   *array = localArray;  
  4.   return S_OK;  

對于Hen類來說,編譯器在它上面的所有操作都是透明的(它完全不知道這些操作):

  1. class Hen : public Implements<IHen, IHen2, Cloaked<IHenNative>>  
  2. {  
  3. }; 

這就是一個好的庫所應該有的樣子。Visual C++ 2015編譯器提供了Windows平臺下面對于標準C++的***支持。它允許C++開發者創建優雅而高效的庫。這同樣支持使用標準C++開發Windows運行時組件,以及完全使用標準C++編寫的通用的Windows應用程序。實現類模板僅僅只是現代C++針對Windows運行時的一個例子。(查看 moderncpp.com).

Kenny Kerr 是一位加拿大的開發者,同樣也是Pluralsight的作者,以及微軟的MVP。 它的博客是 kennykerr.ca。你可以關注他的Twitter賬號twitter.com/kennykerr

多謝微軟的技術專家James McNellis審閱這篇文章。

英文原文:Visual C++ 2015 Brings Modern C++ to the Windows API

譯文鏈接:http://www.oschina.net/translate/visual-cpp-2015-brings-modern-cpp-to-to-windows-api

責任編輯:林師授 來源: 開源中國社區編譯
相關推薦

2010-01-14 11:07:59

Visual C++

2010-01-18 15:40:37

Visual C++工

2010-01-15 14:52:33

Visual C++

2010-01-14 10:56:43

Visual C++

2009-11-12 11:00:56

Visual C++

2009-11-12 10:05:09

Visual C++

2010-01-26 17:44:32

Visual C++開

2010-01-25 18:19:17

C++特性

2010-01-25 18:19:17

C++特性

2010-01-25 18:19:17

C++特性

2010-01-26 14:10:22

Visual C++

2010-12-30 16:11:10

Visual StudWindows部署VC++

2010-01-08 17:13:46

Visual C++環

2010-01-18 14:41:52

Visual C++開

2011-03-30 14:49:00

Visual C++CPU

2010-01-14 13:39:45

Visual C++優

2010-01-18 15:19:41

Visual C++開

2010-01-26 13:14:48

2010-01-27 16:05:06

C++堆棧

2010-01-21 09:34:57

C++語法
點贊
收藏

51CTO技術棧公眾號

久久精品这里都是精品| 成人无号精品一区二区三区| 一区二区三区四区五区视频在线观看 | 日韩成人在线观看| 国产精品久久中文字幕| 亚洲欧洲综合在线| 9国产精品视频| 亚洲精品自在久久| 日本www.色| 成视频免费观看在线看| 大胆亚洲人体视频| 国模精品视频一区二区三区| 美国黄色a级片| 国产一区二区精品调教| 亚洲欧美日韩系列| 国产欧美日韩伦理| 日韩不卡高清视频| 一区二区中文| 日韩成人av网| 激情黄色小视频| 黄页网站大全在线免费观看| 本田岬高潮一区二区三区| 欧美自拍视频在线| 国产精品白丝喷水在线观看| 狼人精品一区二区三区在线 | 青娱乐极品盛宴一区二区| 一区二区三区中文字幕精品精品| 国产中文一区二区| 91在线精品入口| 亚洲精品1234| 日韩一区av在线| a级一a一级在线观看| av成人在线看| 欧美性生交大片免费| 中文字幕中文字幕在线中一区高清| 黄色三级网站在线观看| 看电视剧不卡顿的网站| 91精品国产91久久| 日本妇女毛茸茸| 欧美日韩国产高清电影| 亚洲成年人在线| 天天干天天玩天天操| 中老年在线免费视频| 亚洲精品国产高清久久伦理二区| 欧美激情第一页在线观看| 性做久久久久久久| 久久精品国产99国产| 91成品人片a无限观看| 私库av在线播放| 欧美三级伦理在线| 亚洲欧美成人精品| 中国免费黄色片| 国产美女视频一区二区| 欧美天堂亚洲电影院在线播放| 男女激情免费视频| 午夜在线激情影院| 亚洲欧洲国产专区| 亚洲高清精品中出| 欧美日韩影视| 北岛玲一区二区三区四区| 国产精品久久久久久久久免费| 日韩黄色在线视频| 亚洲香蕉网站| 欧美激情综合亚洲一二区| 日本爱爱爱视频| 国产一区不卡| 国产一区二区三区久久精品| 亚欧洲乱码视频| 九九热精品视频在线观看| 国产视频亚洲精品| 三叶草欧洲码在线| 日日狠狠久久偷偷综合色| 亚洲国产精品嫩草影院久久| 亚洲欧美高清在线| 久久久久观看| 日韩精品在线视频| 激情综合丁香五月| 亚洲精品中文字幕99999| 日韩精品在线免费观看视频| www.自拍偷拍| 国产区精品区| 自拍偷拍亚洲欧美| 性欧美一区二区| 成人女性视频| 久久视频免费观看| 国产亚洲精久久久久久无码77777| 婷婷综合视频| 欧美丰满少妇xxxxx| 欧美日韩激情在线观看| 国产真实久久| 欧美在线一级视频| 最新中文字幕免费| 国产精品一区在线| 91免费的视频在线播放| 午夜精品久久久久久久99| 丁香婷婷综合五月| 久久国产欧美精品| 香蕉视频免费在线看| 久久久久久久久久久黄色| 午夜精品亚洲一区二区三区嫩草| 国产成人在线视频免费观看| 亚洲国产视频在线| 国产亚洲天堂网| 日韩一区二区三区四区五区| 日韩欧美在线网站| www.超碰97| 欧美独立站高清久久| 欧美人交a欧美精品| 国产亚洲小视频| 亚洲综合日本| 91精品视频免费| 污污网站在线免费观看| 国产精品天干天干在观线| 日本一区午夜艳熟免费| 成人va天堂| 欧美一区二区三区婷婷月色| 风间由美一二三区av片| 99久久精品网| 57pao精品| 国产成人久久精品77777综合| 国产精品亚洲午夜一区二区三区| 九色91在线视频| av福利在线播放| 亚洲www啪成人一区二区麻豆 | 视频一区二区三区中文字幕| 成人网中文字幕| 青青操视频在线| 亚洲精品欧美激情| chinese少妇国语对白| 中文字幕久久精品一区二区| 综合av色偷偷网| 亚洲va在线观看| av资源站一区| 岛国大片在线播放| 9l亚洲国产成人精品一区二三| 久久精品99久久久久久久久| 免费在线不卡av| 国产亚洲va综合人人澡精品| aaaaaa亚洲| 免费黄色成人| 日韩美女免费观看| 你懂的在线播放| 欧美三级免费观看| 中文字幕免费看| 亚洲欧美视频| 日韩.欧美.亚洲| 欧美影视资讯| 最新的欧美黄色| 亚洲视频在线观看免费视频| 国产精品色哟哟网站| 三上悠亚av一区二区三区| 成人激情在线| 国产日韩欧美中文| 黄色成年人视频在线观看| 91精品国产综合久久蜜臀| 天天做夜夜爱爱爱| 国产成人在线免费| 国产精彩视频一区二区| 欧美深夜视频| 国产成人精品日本亚洲专区61| 日韩电影免费| 欧美性大战久久久久久久蜜臀| ass极品国模人体欣赏| 久久国产精品一区二区| 久久久久久久久影视| 色播一区二区| 97人人模人人爽人人喊中文字 | 欧美日韩黄网站| 欧美激情区在线播放| 深夜福利在线观看直播| 91久久精品一区二区二区| 日本精品久久久久中文| 国产一区二区在线观看免费| 欧美中文字幕在线观看视频| 亚洲区小说区| 91精品国产综合久久久久久蜜臀 | 天堂在线精品视频| 亚洲精品成人无限看| 不卡视频一区| 成人欧美magnet| 色黄久久久久久| 亚洲精品网站在线| 一本一道波多野结衣一区二区| 成熟人妻av无码专区| 国产一区在线精品| 免费av观看网址| 成人综合久久| 99re国产视频| 成人软件在线观看| 欧美成人午夜激情在线| 午夜视频在线免费播放| 欧美三级在线看| 日操夜操天天操| 国产嫩草影院久久久久| 久久久久久国产精品日本| 亚洲视频www| 在线观看一区二区三区三州| 久久精品亚洲成在人线av网址| 国产一区二区在线免费| 成人免费观看在线观看| 精品激情国产视频| 久草视频在线看| 欧美精品一区二区三区四区| 在线亚洲欧美日韩| 色综合久久66| 精品无码久久久久久久| 国产精品美女久久久久久久网站| 欧美大喷水吹潮合集在线观看| 久久精品国产成人一区二区三区| 国产h视频在线播放| 亚洲h色精品| 蜜桃免费一区二区三区| 午夜精品在线| 国产日韩欧美成人| av日韩电影| 久久久久久久久电影| 黄色免费网站在线| 中文字幕亚洲欧美一区二区三区| 神马久久久久久久久久| 欧美一区二区国产| 在线观看日批视频| 日本乱码高清不卡字幕| 欧美一二三区视频| 亚洲成人av电影| 久久久久久久久99| 亚洲男人的天堂在线观看| 精品一区二区三区蜜桃在线| 久久综合九色综合97婷婷女人| 国产乱国产乱老熟300部视频| 久久爱www久久做| 午夜免费福利在线| 日韩成人午夜精品| 可以免费在线看黄的网站| 一区二区三区成人精品| 青青青在线视频播放| 欧美午夜精品| 久久久99精品视频| 一本一本久久a久久综合精品| 亚洲一区二区不卡视频| 北条麻妃国产九九九精品小说| 秋霞毛片久久久久久久久| 亚洲丝袜美腿一区| 日本黄网免费一区二区精品| 岳的好大精品一区二区三区| 欧美不卡福利| 九九久久电影| 色综合久久久久久久久五月| 国产欧美一区二区精品久久久| 日韩免费电影一区二区| 狠狠综合久久av一区二区蜜桃| 欧美二区三区在线| 久操精品在线| 神马欧美一区二区| 日韩片欧美片| 91免费视频黄| 欧美日本三区| 成人在线观看你懂的| 亚洲激情偷拍| 农村妇女精品一二区| 日韩激情中文字幕| www.亚洲高清| 极品美女销魂一区二区三区| 永久免费黄色片| 国产精品一区2区| 好男人香蕉影院| 久久久五月婷婷| 国产精品69久久久久孕妇欧美| 国产精品久久久久9999吃药| 久久r这里只有精品| 亚洲成人激情自拍| www.国产一区二区| 欧美精品色综合| 亚洲欧美另类日韩| 亚洲人成欧美中文字幕| 日本高清视频在线播放| 欧美人在线视频| 亚洲欧美一区二区三区| 国产精品一区二区久久久| 亚洲国产一区二区三区网站| 精品欧美国产| 91精品国产福利在线观看麻豆| 男人天堂新网址| 日韩国产高清在线| 亚洲天堂一区二区在线观看| 99久久婷婷国产综合精品| 美国美女黄色片| 亚洲成人一区二区| 中文字幕激情视频| 亚洲高清在线观看| аⅴ资源新版在线天堂| 欧美激情综合色| 成人国产精品| 精品亚洲欧美日韩| 97精品视频| 久久久免费视频网站| 狠狠色丁香久久婷婷综合_中| www.17c.com喷水少妇| 国产精品久久久久久久久动漫| 国产一级aa大片毛片| 欧美日韩一区二区三区在线看 | 国产小视频国产精品| 午夜av在线免费观看| 国产精品久久久久不卡| 精品中国亚洲| 福利网在线观看| 日韩avvvv在线播放| 日本一区二区在线观看视频| 中文字幕制服丝袜一区二区三区 | 品久久久久久久久久96高清| 欧美久久成人| 污色网站在线观看| 91丨九色丨国产丨porny| 朝桐光av在线| 欧美日韩三级一区二区| 亚州男人的天堂| 色综合久综合久久综合久鬼88| 成人不卡视频| 免费电影一区| 亚洲国产精品一区制服丝袜| 性生活一级大片| 中文字幕一区二区三区四区| 国产午夜无码视频在线观看| 日韩av有码在线| 波多野结衣中文字幕久久| 91天堂在线观看| 欧美aaaaaaaaaaaa| 色七七在线观看| 久久综合久久综合久久| 日韩黄色三级视频| 精品国产精品一区二区夜夜嗨| 黄视频网站在线| 91精品国产综合久久久久久蜜臀| 精品美女久久| 熟妇人妻va精品中文字幕| 久久天天做天天爱综合色| 男人的天堂一区二区| 亚洲电影天堂av| 精精国产xxxx视频在线中文版| 99r国产精品视频| 亚洲精品久久| 久久aaaa片一区二区| 亚洲精品免费视频| 亚洲成人黄色片| 欧美另类极品videosbestfree| 高清久久一区| 激情图片qvod| 国产激情一区二区三区四区 | 999亚洲国产精| 粉嫩av懂色av蜜臀av分享| 精品久久久国产| 色视频免费在线观看| 国产成人av网| 色男人天堂综合再现| 99九九精品视频| 亚洲激情自拍偷拍| 免费看国产片在线观看| 992tv在线成人免费观看| 色愁久久久久久| 免费在线观看毛片网站| 国产精品网站在线观看| 国产精品视频第一页| 欧美成人黄色小视频| 大桥未久女教师av一区二区| a级黄色一级片| 国产亚洲一区二区三区在线观看| 中国老头性行为xxxx| 美女精品视频一区| 超碰成人97| 久久精品网站视频| 中文字幕在线不卡视频| 成人黄色免费视频| 人人爽久久涩噜噜噜网站| 三区四区不卡| 日韩高清一二三区| 狠狠色香婷婷久久亚洲精品| 91在线导航| 国产富婆一区二区三区| 久久www成人_看片免费不卡| 999久久久国产| 精品奇米国产一区二区三区| 欧美黑人疯狂性受xxxxx野外| 亚洲国产精品www| 国产91精品久久久久久久网曝门| 亚洲免费黄色网址| 按摩亚洲人久久| 任你弄精品视频免费观看| 午夜国产一区二区三区| 亚洲成人av一区二区| 97人人在线| 国产乱码精品一区二区三区不卡| 日韩专区欧美专区| 久久精品一级片| 国产午夜一区二区| 视频免费一区二区| 日日摸天天爽天天爽视频| 一区二区三区在线不卡| 国产区av在线| 国产亚洲欧美另类一区二区三区| 麻豆精品视频在线| 中文字字幕在线中文|