2014-02-16 23 views
10

在下面的一段代碼中,我試圖構建一個類型的格。例如,floatint之間,促進結果floatC++ 11:超載無法解決遞歸decltype

float join(float f, int) { return f; } 
float join(float f, float) { return f; } 

然後我介紹一個wrapper類型:

template <typename Inner> 
struct wrapper 
{ 
    using inner_t = Inner; 
    inner_t value; 
}; 

其行爲與join操作相當自然:

template <typename Inner1, typename Inner2> 
auto 
join(const wrapper<Inner1>& w1, const wrapper<Inner2>& w2) 
    -> wrapper<decltype(join(w1.value, w2.value))> 
{ 
    return {join(w1.value, w2.value)}; 
} 

它也可以是join,帶有「標量」類型:

template <typename Inner1, typename T2> 
auto 
join(const wrapper<Inner1>& w1, const T2& value2) 
    -> wrapper<decltype(join(w1.value, value2))> 
{ 
    return {join(w1.value, value2)}; 
} 

到目前爲止,這麼好,它的工作原理。但是,因爲在實際情況中,我實際上有更多這樣的規則,所以我想避免重複一些規則來表達join操作的交換性,因此,我表示join(scalar, wrapper) := join(wrapper, scalar)(實際上,我會喜歡的東西一樣join(v1, v2) := join(v2, v1),但讓我們開始更具體的東西):

template <typename T1, typename Inner2> 
auto 
join(const T1& value1, const wrapper<Inner2>& w2) 
    -> decltype(join(w2, value1)) 
{ 
    return join(w2, value1); 
} 

這個工作正常進行join(scalar, scalar)join(wrapper, scalar)join(scalar, wrapper)。但是然後join(wrapper, wrapper)導致G ++ 4.9和Clang ++ 3.5無限擴展模板函數,我不明白。

int main() 
{ 
    int i; 
    float f; 
    wrapper<float> fr; 
    join(f, i); 
    join(fr, i); 
    join(i, fr); 
    join(fr, fr); // Loops. 
} 

鏘:

clang++-mp-3.5 -std=c++11 bar.cc 
bar.cc:21:5: fatal error: recursive template instantiation exceeded maximum depth of 
     256 
    join(const wrapper<Inner1>& w1, const T2& value2) 
    ^
bar.cc:29:5: note: while substituting deduced template arguments into function 
     template 'join' [with T1 = wrapper<float>, Inner2 = float] 
    join(const T1& value1, const wrapper<Inner2>& w2) 
    ^

GCC:

g++-mp-4.9 -std=c++11 bar.cc 
bar.cc:30:34: error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum) substituting 'template<class T1, class Inner2> decltype (join(w2, value1)) join(const T1&, const wrapper<Inner2>&) [with T1 = <missing>; Inner2 = <missing>]' 
     -> decltype(join(w2, value1)) 
           ^
bar.cc:30:34: recursively required by substitution of 'template<class T1, class Inner2> decltype (join(w2, value1)) join(const T1&, const wrapper<Inner2>&) [with T1 = wrapper<float>; Inner2 = float]' 
bar.cc:30:34: required by substitution of 'template<class T1, class Inner2> decltype (join(w2, value1)) join(const T1&, const wrapper<Inner2>&) [with T1 = wrapper<float>; Inner2 = float]' 
bar.cc:43:18: required from here 

我不明白爲什麼超載不削減遞歸。到底是怎麼回事? (類)模板專門化可能有一個替代實現,但我沒有尋找替代實現:我想了解這個問題。提前致謝。

+2

不是你的'join(wrapper,wrapper)= wrapper','join(wrapper,wrapper) =標量不明確? – melak47

回答

3

有幾個問題與此,其中一個導致錯誤。

template <typename Inner1, typename T2> 
auto 
join(const wrapper<Inner1>& w1, const T2& value2) // (A) 
    -> wrapper<decltype(join(w1.value, value2))>; 

join這裏的名稱查找無法找到通過不合格的查找功能相同的模板,因爲尾隨收益型是聲明的一部分,和名稱才能找到一旦他們已被宣佈爲。但是該語法允許ADL找到相同的函數模板。以後(從實例化的角度)執行從屬名稱的ADL。

據我瞭解問題,問題來自重載決議:在decltype(join(w1.value, value2))試圖解決重載之前,所有具有該名稱的功能模板需要實例化。對於每個函數模板,將一個實例化添加到重載集(如果實例化成功)。

因此,所有join s需要實例化。實例化包括確定返回類型。對於這個特定的函數模板(A)的每個實例,具有相同模板參數的相同函數模板(A)是重載分辨率集合的候選。 也就是說,要確定哪種返回類型(A)具有,需要有重載解析,這需要確定(A)的返回類型等等。

扣除&替代從來沒有失敗的遞歸的任何一步,唯一的原因選擇這種過載被稱爲join不同函數模板之間的部分排序。部分排序僅在中作爲重載解析過程的一部分進行檢查 - 這對於阻止進一步實例化來說太晚了。

正如錯誤消息中所述,此錯誤作爲實施限制發生。因此,它不屬於SFINAE類別,請參見Solving the SFINAE problem for expressions。因此,即使沒有選擇這種過載,它的存在也會導致程序不正常,就像

struct tag_for_ADL {}; 

template<class T> 
auto foo(T p) -> decltype(foo(p)); 

foo(tag_for_ADL{}); // ill-formed, leads to infinite recursive instantiation 
+0

所以我的理解是,我的代碼的主要問題是,我沒有拼出命名空間?這可以很容易地解決,似乎正在工作,因爲我期待:http://coliru.stacked-crooked.com/a/47378b37eb430b62。有沒有我可能忽視的陷阱? – akim

+0

@akim當只依賴不合格的查找時,您將無法加入任何類似'wrapper >'的東西。 – dyp

+0

你的意思是「合格的查找」我猜。無賴:(感謝您的幫助!現在我需要了解如何找到正確的實施:) – akim

0

定義join_traits模板。專注於標量和包裝。使用

join_traits<T1>::type 
join_traits<T2>::type 

和/或

join_traits<T1>::get_value(v1) 
join_traits<T2>::get_value(v2) 

在單一功能模板

template<class T1, class T2> 
auto join (T1 v1, T2 v2) -> ... 
+0

我想這個答案可以做一些更多的解釋,說明如何以及爲什麼這可以用來解決問題。目前,我認爲它只能幫助那些本可能想出來的人。 – Agentlien

+0

@Agentlien:至於「如何」,是否真的有兩種不同的方式來做到這一點?至於「爲什麼」我認爲這很明顯,你定義了N個特徵和一個函數模板,而不是N^2。 –

+0

重新表述:您的回答只聲明模板類型的存在,其潛在成員的兩個名稱和函數的部分原型(跳過一個重要細節)。我確實認爲這是一個很好的解決方案,並沒有太多明智的方法來實施它。不過,我仍然認爲你至少可以寫出完整的'join'函數並解釋它如何解決這個問題。正如我在最近的評論中提到的:目前,我認爲這個答案無法幫助大多數需要幫助的人回答這個問題。 – Agentlien