2017-03-16 43 views
3

我正在解決一個經典問題:檢查某些名稱空間中是否存在自由函數。討論了例如here模板元編程:檢查後面定義的函數的存在

但是,有一個輕微的扭曲:函數的定義可能會出現後面比檢查器類。這是一個例子。

struct Yes {}; 
struct No {}; 
struct YesButLater {}; 

void f(Yes) {} 

template<typename T, typename Enable = void> 
struct HasF : public std::false_type {}; 

template<typename T> 
struct HasF<T, decltype(void(::f(T())))> : public std::true_type {}; 

void f(YesButLater) {} 

int main() { 
    cout << HasF<Yes>::value << endl; // 1 
    cout << HasF<No>::value << endl; // 0 
    cout << HasF<YesButLater>::value << endl; // 0, expected 1 
} 

f(YesButLater)晚於HasF助手類中聲明,而且,雖然我實例化模板f(YesButLater)定義後,助手不會注意到它。

所以,這裏是問題1:我該如何處理它?

現在又多了一個更好奇的例子。

template<typename T> 
struct HasF<T, decltype(void(f(T())))> : public std::true_type {}; 

void f(YesButLater) {} 
void f(std::string) {} 

int main() { 
    cout << HasF<YesButLater>::value << endl; // 1 (but what's the difference?) 
    cout << HasF<std::string>::value << endl; // 0, expected 1 
} 

注意,我從decltype(...)表達除去::。現在由於某種原因f(YesButLater)被注意到HasF,但f(std::string)仍然不明確。

問題2:爲什麼我們在本例中觀察到::f(T())f(T())的不同行爲?此外,YesButLaterstd::string之間的區別是什麼?

我認爲有一些與命名空間查找技巧,但我無法得到的東西。

+0

我的猜測 - 這是由於[adl](http://en.cppreference.com/w/cpp/language/adl)規則... –

+0

@ W.F。是的,可能,儘管我仍然不明白爲什麼::重要,因爲我在示例中始終使用全局名稱空間。 –

回答

3

看來我已經知道發生了什麼。

當我編寫::f(...)時,名稱f正在搜索限定名稱查找。這種查找只會遇到直到調用點的聲明。現在很清楚爲什麼第一個版本找不到f(YesButLater):它的聲明稍後發生。

當我寫f(...),不合格名稱查找發生。同樣,它沒有找到任何在呼叫點之前聲明的名稱。這裏參數依賴查找出現。它在整個名稱空間中搜索f(T),其中T屬於。在f(YesButLater)的情況下,這個命名空間是全局的,因此找到了該函數。在f(std::string)的情況下,ADL嘗試搜索std::f(std::string),當然失敗。

這裏有兩個例子來說明這種情況。

namespace foo { 
    class C {}; 
} 

template<typename T> 
void call() { 
    f(T()); 
} 

namespace foo { 
    void f(C) {} 
} 

int main() { 
    call<foo::C>(); 
} 

這裏f(T())正在搜索與ADL和發現,雖然其聲明是call()後。如果我們修改call()功能...

template<typename T> 
void call() { 
    foo::f(T()); 
} 

這會導致編譯錯誤,因爲foo::f(T)執行合格查找和不申報可在一時無法找到所需的功能。