2014-01-14 21 views
1

我正在編寫一些模板代碼以確定給定類型是否可以作爲任何可用的函數重載參數傳遞。在下面的例子中,我使用了日誌功能,但是我也在數學庫中的其他人上嘗試了這些代碼,結果是一樣的。我們的想法是使用函數重載和sizeof運算符來區分哪些類型可以合法地傳遞給有問題的函數(在本例中爲log)。在模板代碼中未檢測到std :: string的重載

如果有效,我們可以有sizeof(overload<type>(NULL)) == sizeof(True),當'type'可以合法地傳遞到日誌時,sizeof(overload<type>(NULL)) == sizeof(False)否則。這似乎適用於大多數類型,但對於std::string而言失敗。

下面是它究竟是如何失敗:

在正常情況下,我們有sizeof(overload<std::string>(NULL)) == sizeof(False),因爲我們應該。但是,當我聲明一個帶有字符串的日誌重載時,它仍然不會觸發邏輯的sizeof(True)分支。請注意,我實際上並不想聲明log(std::string)函數,我只是測試此代碼以確保它能夠檢測到所有可能的重載。

起初我以爲它只是沒有正確地檢測到重載,但是當我用用戶定義的類(下面的例子中的'MyClass')嘗試它時,它工作正常:當log(MyClass)被聲明時它產生了sizeof(True),和sizeof(False),否則。

#include <iostream> 
#include <math.h> 

template<int> 
struct TakesInt{}; 


struct True 
{ 
}; 

struct False 
{ 
    // guarantees that sizeof(False) != sizeof(True) 
    True array[2]; 
}; 


// takes anything; fall back if no match can be found 
template<typename T> 
False overload(...); 

// takes a specific type; does not actually call log 
template<typename T> 
True overload(TakesInt<sizeof(log(T()))>*); 

// As a test, this is an overload of log that takes a string. 
// I don't actually want to implement this, but it should make the compiler 
// think that a string is a valid argument. 
double log(std::string); 


// a placeholder for user defined class; could really be anything, 
// like an arbitrary number class 
struct MyClass{}; 

// declaring log for the arbitrary class above 
// note that this is the same as for the log(std::string) 
// if one works, the other should 
double log(MyClass); 

int main() 
{ 
    std::cout << sizeof(True) << '\t' << sizeof(False) << std::endl; 
    std::cout << sizeof(overload<std::string>(NULL)) << std::endl; 
    std::cout << sizeof(overload<double>(NULL)) << std::endl; 
    std::cout << sizeof(overload<MyClass >(NULL)) << std::endl; 
    return 0; 
} 
+0

聽起來像名稱查找問題。從'True overload'(TakesInt *);'定義的角度看,通過非限定的查找找不到'double log(std :: string)從實例化的角度來看,通過ADL可以找到MyClass。我只是驗證了當你在'overload'之前加載'log(std :: string)'的重載時,它會通過非限定查找找到:[Live example](http://coliru.stacked-crooked。com/a/17d44cadd598a97c) – dyp

回答

2

這是同樣的問題,W/O的SFINAE分心:

#include <iostream> 

namespace ns 
{ 
    struct string {}; 
} 

void bar(...) { std::cout << "void bar(...)\n"; } 

template<class T> 
void foo() 
{ 
    T x{}; 
    bar(x); 
} 

void bar(ns::string) { std::cout << "void bar(ns::string)\n"; } 

int main() 
{ 
    foo<int>(); 
    foo<ns::string>(); 
} 

輸出:

 
void bar(...) 
void bar(...) 

的依賴函數名稱的查詢將被執行:

  • 作爲定義的(純粹的)不合格的查找上
  • 從實例化點

因此(純)參數相關的查找,下面的實施例的不同:

#include <iostream> 

namespace ns 
{ 
    struct string {}; 
} 

void bar(...) { std::cout << "void bar(...)\n"; } 

template<class T> 
void foo() 
{ 
    T x{}; 
    bar(x); 
} 

namespace ns 
{ 
    void bar(ns::string) { std::cout << "void bar(ns::string)\n"; } 
} 

int main() 
{ 
    foo<int>(); 
    foo<ns::string>(); 
} 

輸出:

 
void bar(...) 
void bar(ns::string) 

對於std::string,所述只有關聯的命名空間是std。全局命名空間是而不是關聯,並且不會在OP的代碼中進行搜索。因此,在之後聲明的超負荷模板定義將不會被發現。

N.B.請不要將過載注入命名空間std。這會導致根據[namespace.std]/1導致未定義的行爲。