2014-09-10 51 views
14

我想檢查是否存在接受T參數類型的非成員函數。爲此,我使用void_t「技巧」,由Walter E.Brown在cppcon中介紹(同樣的技巧在沒有任何問題的情況下檢查成員類型或成員函數是否存在)。檢查是否存在接受T參數的非成員函數

#include <iostream> 
#include <type_traits> 

template<typename...> 
using void_t = void; 

void Serialize(float&) 
{ 
} 

template<typename T, typename = void> 
struct has_external_serialize : std::false_type 
{ 
}; 

template<typename T> 
struct has_external_serialize<T, void_t<decltype(Serialize(std::declval<T&>()))>> : std::true_type 
{ 
}; 

void Serialize(int&) 
{ 
} 

int main(int argc, const char * argv[]) 
{ 
    std::cout<<has_external_serialize<float>::value<<has_external_serialize<int>::value; 
} 

此代碼打印11時當與鐺(xcode中5.1.1)編譯使用GCC和10編譯。

我的問題是 - 這段代碼是否正確?如果是的話,clang中有bug還是GCC中的錯誤,或者代碼在某個「實現定義」區域,我不能認爲它會在所有平臺上都有相同的行爲?

+0

在你的專業化之前,支持依賴類型和'std'類型。其他類型使用ADL。然後鏗鏘會很高興。我相信你所做的是不成形的,但沒有時間來證明它 – Yakk 2014-09-10 22:25:20

+3

似乎更像是一個GCC中的錯誤(至少4.9.0),因爲即使對於struct struct_exsert_serialize ()))>>:std :: true_type' – 2014-09-10 22:25:50

+1

@Felics:歡迎來到CppCon! :-) – 2014-09-10 23:07:58

回答

4

編譯器之間的差異是由void_t定義造成的: Is there a compiler bug exposed by my implementation of an is_complete type trait? 總之,標準不清別名模板特未使用的參數是否會導致替代故障或簡單地被忽略。 CWG issue 1558的解決方案澄清了該問題中void_t的較短定義應該起作用。

隨着這一問題的工作圍繞使用

template<typename... Ts> 
struct make_void { typedef void type;}; 

template<typename... Ts> 
using void_t = typename make_void<Ts...>::type; 

both compilers produce 10

§14.6.4.2[temp.dep.candidate]:

對於函數調用依賴於模板參數,所述 候選函數所使用的通常的查找規則(3.4.1發現, 3.4.2,3.4.3)不同之處在於:

  • 對於使用不合格的名稱查找(3.4.1)或限定名查找(3.4.3)查找的部分,只從 模板定義函數聲明上下文被找到。
  • 對於使用關聯名稱空間(3.4.2)的部分查找,只能找到在模板定義上下文 或模板實例化上下文中找到的函數聲明。

如果函數名是不合格-ID和形成不良的通話將 或會找到更好的比賽不得不考慮所有的函數聲明與被引入 外部鏈接的 相關的命名空間內查找在所有翻譯 單位的那些命名空間中,不僅僅考慮在模板 定義和模板實例化上下文中發現的那些聲明,那麼該程序具有 未定義的行爲。在模板定義的上下文中執行

Serialize不合格查找,找不到Serialize(int &),而且也沒有爲ADL型int&的參數,所以10是正確的輸出。