2013-05-22 64 views
7

我有一個函數可以通過通用引用接受任何類型,並且希望爲特定類型重載它(其中一些模板本身是模板化的,儘管我認爲這並不重要)。不幸的是,我似乎不太可能讓重載以正確的順序得到解決。帶有通用引用的重載分辨率

我會假設foo的第二個聲明是首選,因爲它更具體(模板較少),儘管看起來我對重載分辨率的理解還是有些欠缺。有趣的是,將第二個聲明更改爲X的值會使其打印出「好,好」,並通過非const引用使其成爲X,使其打印出「不好,很好」。顯然,刪除第一個聲明完全使它返回「好,好」,因爲沒有別的選擇。

那麼爲什麼會發生這種情況呢?最重要的是,如果下面的代碼不起作用,那麼如何用這個簽名重載一個函數?

#include <iostream> 
#include <string> 

class X {}; 

template<typename T> 
inline std::string foo(T && rhs) { 
    return "bad"; 
} 

inline std::string foo(const X & rhs) { 
    return "good"; 
} 

int main() { 
    std::cout << foo(X()) << std::endl; 
    X x; 
    std::cout << foo(x) << std::endl; 
    return 0; 
} 

編輯:

也許更迂迴的解決方案,這是間接地做到這一點。擺脫foo的第一種形式,並使用SFINAE檢查是否存在有效的過載,然後它不會調用foo_fallback

+0

我想出了一個解決超載部分:http://mortoray.com/2013/06/03/overriding-the-broken-universal-reference-t/ –

+1

1 +小時[youtube video](https://www.youtube.com/watch?v=T5swP3dr190)對這個確切的問題,這有趣地鏈接回到這個問題。 – Yakk

回答

3

要回答你的question.comment到Kerre的回答,您可以嘗試使用SFINAE:

#include <type_traits> 
#include <string> 

template <class T> 
struct HasFooImpl_ { 
    template <typename C> 
    static std::true_type test(decltype(fooImpl(std::declval<C>()))*); 
    template <typename C> 
    static std::false_type test(...); 
    typedef decltype(test<T>(0)) type; 
}; 

template <typename T> 
using HasFooImpl = typename HasFooImpl_<T>::type; 

template <typename T> 
typename std::enable_if<HasFooImpl<T>::value, std::string>::type 
foo(T&& t) 
{ 
    return fooImpl(std::forward<T>(t)); 
} 

template <typename T> 
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type 
foo(T&& t) 
{ 
    return "generic!"; 
} 

你不得不實施任何類型的,你不想處理genericly功能fooImpl

該實現有點棘手,我首先嚐試了enable_if<is_same<string, decltype(fooImpl(declval<C>()))>::value,但是對於後備,!is_same<>::value給了我編譯器錯誤,因爲它試圖實例化decltype。

此實現了,你可能會或可能不會想使用一個警告:如果T轉換到已定義了fooImpl一些其他類型,該轉換將在踢

你可以看到整體。在這裏的行動的事情:http://ideone.com/3Tjtvj

更新: 如果你不希望允許類型轉換,它實際上變得更容易:

#include <type_traits> 
#include <string> 

template <typename T> void fooImpl(T); 

template <typename T> 
using HasFooImpl = typename std::is_same<std::string, decltype(fooImpl(std::declval<T>()))>; 

template <typename T> 
typename std::enable_if<HasFooImpl<T>::value, std::string>::type 
foo(T&& t) 
{ 
    return fooImpl(std::forward<T>(t)); 
} 

template <typename T> 
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type 
foo(T&& t) 
{ 
    return "generic!"; 
} 

http://ideone.com/miaoop

+0

我喜歡這個。 [Here's](http://ideone.com/vkyScJ)另一個相同想法的實現,但是使用表達式SFINAE而不是輔助類和'enable_if'(缺點是它需要另一個間接級別)。 – jleahy

+0

我其實更喜歡後者的轉換語義,而且實現非常整齊。優秀。 – jleahy

2

Xconst X的轉化被認爲是比過載模板與T = XT = X &直接匹配惡化。

+0

那麼你如何讓通用參考版本能夠支持更具體的版本呢? – jleahy

+2

你也想在某個時候吃午飯嗎? – jleahy

+0

@jleahy:你可以爲'X&','X const&'和'X &&'覆蓋所有情況,並且是:-)編輯:[see here](http ://ideone.com/KNqnRB)。 –