1

我想知道是否有可能讓ADL選擇在其中一個參數(或在其他一些明確定義的地方)的類的命名空間中定義的函數模板當其他功能模板可見時的情況。我有一個激勵的例子,儘管我知道這個特定案例的解決方法(我在下面討論),但總的來說這個問題似乎是有道理的。如何讓ADL更喜歡功能模板到另一個

我覺得挺酷的,以避免使用朋友的聲明,而是工作委託給方法,從而想出了

namespace n 
    { 
    struct a 
    { 
    auto swap(a& a2) -> void; 
    }; 
    auto swap(a& a1, a& a2) -> void 
    { 
    a1.swap(a2); 
    } 
    } 
auto main(void) -> int 
    { 
    n::a a1, a2; 
    using std::swap; 
    swap(a1,a2); // use case 1 
    n::swap(a1,a2); // use case 2 
    } 

到目前爲止,一切都很好,都使用的情況下正常工作,但後來,我加入用自己的交換方法的第二類,並決定通過轉動獨立交換到模板上樣板保存:

namespace n 
    { 
    struct a 
    { 
    auto swap(a& a2) -> void; 
    }; 
    struct b 
    { 
    auto swap(b& b2) -> void; 
    }; 
    template<class T> 
    auto swap(T& t1, T& t2) -> void 
    { 
    t1.swap(t2); 
    } 
    } 
auto main(void) -> int 
    { 
    n::a a1, a2; 
    using std::swap; 
    swap(a1,a2); // use case 1 
    n::swap(a1,a2); // use case 2 
    } 

這裏使用的情況下1條斷裂時,編譯器抱怨與std::swap模板歧義。如果一個預期的問題,它可以定義swap功能rahter比方法(它們通常是朋友,因爲他們更換方法):

namespace n 
    { 
    struct a 
    { 
    friend auto swap(a& a1, a& a2) -> void; 
    }; 
    struct b 
    { 
    friend auto swap(b& b1, b& b2) -> void; 
    }; 
    } 

現在一切正常,所以在swap的情況下,它是剛夠要記住使用比方法更好的朋友函數,但一般情況如何?是否有任何黑客,無論是骯髒的,這將讓編譯器明確地選擇n::foo<a>(或在我們控制下的其他一些foo<a>)在其他template<class T> foo可見的情況下,無論是在全局命名空間還是由於某些using子句,特別是如果後者是不是我們的修改?

+0

A ** **劈爲'swap'是使第二'T&'不計免賠(如像'identity_t ')所以名稱空間中的模板將比標準模板更專業...... – Jarod42

+0

我喜歡這個想法,但它似乎不起作用。對於建議的'template struct identity typedef T type; };使用 模板 identity_t =類型名身份 ::類型;'和交換修飾以'模板 自動交換(類型&T1,identity_t &T2) - > void'它是被選中的'的std :: swap' 。 –

+0

確實,'s :: swap'不太專業......:/ – Jarod42

回答

0

這裏的罪魁禍首不只是你寫using std::swap,但從根本上說你提供你自己無限制的函數模板swap每當namespace std被名稱查找時認爲會給超載分辨率誤差與std::swap(無論是由明確的using指令或ADL)。

舉例說明:剛剛離開了using std::swap會來救你在這種情況下

Live On Coliru

auto main() -> int 
{ 
    n::a a1, a2; 
    swap(a1,a2); // use case 1 
    n::swap(a1,a2); // use case 2 
} 

但是假設你重構你的類ab成類模板b<T>b<T> ,並使用來自namespace std的模板參數(例如std::string)調用它們,然後你得到一個重載解析錯誤:

Live On Coliru

#include <iostream> 
#include <string> 

namespace n 
{ 

template<class>  
struct a /* as before */; 

template<class> 
struct b /* as before */; 

} 

auto main() -> int 
{ 
    n::a<std::string> a1, a2; // oops, ADL will look into namespace std 
    swap(a1,a2); // use case 1 (ERROR) 
    n::swap(a1,a2); // use case 2 (OK) 
} 

結論:如果你定義自己的swap版本相同的簽名std::swap(只要重載關注)始終限定對它的調用以禁用ADL。

提示:更重要的是,不要偷懶,只是提供自己的swap功能(不是函數模板),每類在自己的名稱空間。

又見this Q&A通過這種類似的機制,爲什麼這是一個壞主意,提供自己的beginend模板,並期望他們與ADL工作解釋。

+0

那麼,你通過很好地解釋導致問題的機制來完成我的問題,謝謝。我不想這麼做,因爲害怕讓我的問題過於冗長。但我在這個問題上的觀點並不是如何編寫'swap',而是用函數而不是方法來控制。這需要紀律,但很容易記住。真正的一點是,你可以欺騙你描述的機制,通過注入,從嵌套命名空間繼承,無論你怎麼命名它,這樣在編譯時就可以獲得所需的行爲。你有什麼想法,還是顯然不可能? –

+0

@ŁukaszWojakowski我已經告訴過你了:不要使'swap'成爲一個函數模板,只需爲每個類型提供一個簡單的'swap(a,a)'和一個'swap(b,b)'等等在你的'namespace n'中。另外,如果你真的討厭這個樣板,只需編寫一個可變的宏,像'PP_PROVIDE_SWAP(a,b,...)'一樣產生它們。 – TemplateRex

+0

你說我不應該使用一個模板,而我問是否有任何機制可以讓我這樣做,如果我堅持保持它的話。交換是微不足道的,我再次不問交換,這只是一個例子。我做了我的RTFM分享,我知道你說的是通常的方式(我已經在問題中提到過了),但是我不能確定標準是否允許一些不明智的來源,那就是我在問什麼。宏,是的,這是最後的手段。想更好。 –

-1

我知道我必須看起來很傻,才能回答我自己的問題,但發佈它的事實和討論確實給我帶來了一些新的理解。

在反思,我們應該首先打了我,是序列

using std::swap; 
swap(a1,a2); 

真是太舊帽子,它顯然一定是錯誤的,因爲反覆使用它需要一個複製粘貼算法(使用using然後交換)。即使算法是雙線程的,你也不應該複製粘貼。那麼有什麼可以做得更好呢?怎麼樣把它變成一個班輪:

stdfallback::do_swap(a1,a2); 

讓我提供允許該代碼:

namespace stdfallback 
    { 

     template<class T> 
    auto lvalue(void) -> typename std::add_lvalue_reference<T>::type; 


     template <typename T> 
    struct has_custom_swap 
    { 
     template<class Tp> 
    using swap_res = decltype(swap(lvalue<Tp>(),lvalue<Tp>())); 

     template <typename Tp> 
    static std::true_type test(swap_res<Tp> *); 

     template <typename Tp> 
    static std::false_type test(...); 

    static const bool value = decltype(test<T>(nullptr))::value; 
    }; 


     template<class T> 
    auto do_swap(T& t1, T& t2) -> typename std::enable_if<has_custom_swap<T>::value,void>::type 
    { 
    swap(t1,t2); 
    } 

     template<class T> 
    auto do_swap(T& t1, T& t2) -> typename std::enable_if<!has_custom_swap<T>::value,void>::type 
    { 
    std::swap(t1,t2); 
    } 
    } 

在你找到一個基於SFINAE-traits類has_custom_swapvalue的解決方案是真還是取決於是否發現了非交換的實例化類型的左值交換的非限定調用(對於那個需要lvalue模板,類似於declval但解析爲l值而不是r值),然後是do_swap方法的兩個重載自定義交換的情況現在,當它不是。必須調用它們不同於swap,否則調用非限定定製交換的那個不會被編譯,因爲它本身對它嘗試調用的交換不明確。

所以,也許我們應該考慮使用這種模式,而不是建立的using

(要給予適當的信貸,性狀溶液通過http://blog.quasardb.net/sfinae-hell-detecting-template-methods/啓發)

+0

可以嗎?爲兩個用戶定義的類「a'和'b'顯示了一個工作示例,其中一個帶有和沒有成員交換? – TemplateRex

+0

這個例子不能真正起作用。首先,'has_custom_swap'實際上並不工作([見這裏](http://coliru.stacked-crooked.com/a/b8845934bae5c348)),甚至它會,然後用帶模板的類模板調用它來自'namespace std'的參數仍然會引入一般的'std :: swap'模板並引起歧義。 – TemplateRex

+0

我真的很喜歡我從與你的討論中得到的動機,但我不明白什麼不適合你(和downvote),請看[這裏](http://coliru.stacked-crooked.com/a/1de4ca83688ff1f3)。我有一個關於如何使用'std'類型實例化類模板的觀點,在代碼中有一個註釋掉的'static_assert'與它相對應,但使用'using'舊式在這裏沒有幫助,對吧?我知道如何在這裏建立一個ADL屏障,等待下一個評論。我還會有一個問題,我可以在哪裏使用一些幫助。 –

相關問題