原文出處:http://www.bfilipek.com/2016/02/sfinae-followup.html
SFINAE Followup
我上一篇發的SFINAE的反應似乎不錯!我收到非常寶貴的評論跟建議。於是動筆寫這篇來做為補充。
運用現代C++表達
在其中意見回覆中,STL (Stephan T. Lavavej) 說我在文章中的解決方案是舊式的C++風格。那最新現代的風格會有什麼效果呢?
運用現代C++表達
在其中意見回覆中,STL (Stephan T. Lavavej) 說我在文章中的解決方案是舊式的C++風格。那最新現代的風格會有什麼效果呢?
decltype
template <typename C>
static YesType& test( decltype(&C::ToString) ) ;
decltype會返回C::ToString成員方法的型別(如果類別內存在這方法的話)。
declval
decltype(declval<T>().toString())
constexpr
void_t
言講影片:
從29分開始,尤其39分段落。
這是令人驚奇的超編程(meta-programming)典範!我不想去講解它,您只需看影片就能理解void_t的概念思維! :)
detection 慣用法
- WG21 N4436, PDF - Walter E. Brown 計劃在C++標準庫中加入 Detection 慣用法
- std::is_detected
- wikibooks: C++ Member Detector
Walter E. Brown計劃一個具完全通用的class,能用來檢測介面跟其它class屬性。當然,大部份是以void_t技術為基礎來實現。
檢驗返回型別
上一次我丟出未解問題:如何約束ToString()方法的返回型別。我之前的原始碼能檢驗方法的名稱,但無法檢驗它的返回型別。
Björn Fahller 給了我以下的回答: (在文章下面的評論)
此程式將輸出:
檢驗返回型別
上一次我丟出未解問題:如何約束ToString()方法的返回型別。我之前的原始碼能檢驗方法的名稱,但無法檢驗它的返回型別。
Björn Fahller 給了我以下的回答: (在文章下面的評論)
template <typename T>
class has_string{
template <typename U>
static constexpr std::false_type test(...) { return {};}
template <typename U>
static constexpr auto test(U* u) ->
typename std::is_same<std::string, decltype(u->to_string())>::type { return {}; }
public:
static constexpr bool value = test<T>(nullptr);
};
class with_string {
public:
std::string to_string();
};
class wrong_string{
public:
const char* to_string();
};
int main() {
std::cout
<< has_string<int>::value
<< has_string<with_string>::value
<< has_string<wrong_string>::value << '\n';
}
此程式將輸出:
010
在test方法中檢驗to_string()返回型別是否為我們渴望的std::string()。這class包含兩個層級的測試 : 先使用SFINAE -檢驗class有無to_string方法(若無,我們則退回採用test(...))。在這之後,檢驗返回型別是不是我們想要的。最終當我們傳入錯誤的class或class裡的to_string()返回錯誤的型別時,都會從has_string<T>::value中獲得false。這範例太棒了!
請注意constexpr被放置在::value跟test的前面,所以我們明確運用了現代C++表達方式。
更多範例
指標轉換 :
Andre Weissflog的twitter:
讓我們看看程式碼:
這是 Ptr.h - smart pointer class 裡某一部份的程式碼,這class是在 oryol - Experimental C++11 multi-platform 3D engine 裡面。
它可能很難讀懂,但我們試著理解看看:
最關鍵的部份是
還有其它的範例嗎? 讓我欣賞下! :)
更新的版本
我假設確定你的編譯器/程式庫有支援void_t,新的程式碼如下:
http://melpon.org/wandbox/permlink/ZzSz25GJVaY4cvzw
相當不錯...對吧? :)
它使用了以「detection慣用法為基礎」的void_t。基本上,在class內無
總結
再次感謝大家的意見回饋。此文貼上來後,SFINAE/Templates使我更加混淆且對它們更一知半解:) 但仍值得去嘗試了解此機制的背後運作原理。
更多範例
指標轉換 :
Andre Weissflog的twitter:
讓我們看看程式碼:
/// cast to compatible type
template<class U,
class=typename std::enable_if<std::is_convertible<T*,U*>::value>::type>
operator const Ptr<U>&() const
{
return *(const Ptr<U>*)this;
};
這是 Ptr.h - smart pointer class 裡某一部份的程式碼,這class是在 oryol - Experimental C++11 multi-platform 3D engine 裡面。
它可能很難讀懂,但我們試著理解看看:
最關鍵的部份是
std::is_convertible<T*,U*>(請看
std::is_convertible 介紹)。這個組件被裝載到if_enable運算式組件中。基本上,當T*能被轉換到U*時,我們就能獲得有效用的函式重載。否則編譯器將會對此做出報怨。還有其它的範例嗎? 讓我欣賞下! :)
更新的版本
我假設確定你的編譯器/程式庫有支援void_t,新的程式碼如下:
// default template:
template< class , class = void >
struct has_toString : false_type { };
// specialized as has_member< T , void > or sfinae
template< class T >
struct has_toString< T , void_t<decltype(&T::toString) > > : std::is_same<std::string, decltype(declval<T>().toString())>
{ };
http://melpon.org/wandbox/permlink/ZzSz25GJVaY4cvzw
相當不錯...對吧? :)
它使用了以「detection慣用法為基礎」的void_t。基本上,在class內無
T::toString()時
,SFINAE將會啟用且我們用泛化方式來做出結果,也就是default Template(繼承自false_type)。但是當toString()存在於class內時,特化(specialized)版本的Template將被選上。我們不在乎toString方法的返回型別的話,這檢驗應該算結束了。但這版本我們還能繼續檢驗用來繼承的is_same。就能檢驗出方法的返回型別是否為std::string。然後我們就能以 true_type
或false_type方式來做為結果。總結
再次感謝大家的意見回饋。此文貼上來後,SFINAE/Templates使我更加混淆且對它們更一知半解:) 但仍值得去嘗試了解此機制的背後運作原理。
沒有留言:
張貼留言