2017-02-10 64 views
1

已經編寫了一個算術包裝器,它可以幫助檢測溢出/下溢錯誤,但是在流程中存在一個相當迂迴的問題。更改模板返回類型似乎對超載分辨率有影響

假設我們有一個類,它處理所有能夠通過一些重載操作符導致溢出的類,並且可以隱式轉換爲其他所有類型的基礎類型。這個例子只包含一個二進制加法運算:

template<typename T_> 
class Wrapper 
{ 
    public: 
    Wrapper(T_ val_) : m_value(val_) { } // converting constructor 
    operator T_(void) const { return m_value; } // underlying type conversion 
    // some other methods 

    // binary plus operators: 
    template<typename U_> 
    const Wrapper<decltype(T_() + U_())> operator +(U_ val_) const 
    { 
     // supposed to handle 'Wrapped + Unwrapped' case 
     return m_value + val_; 
    } 
    template<typename U_> 
    const Wrapper<decltype(T_() + U_())> operator +(Wrapper<U_> other_) const 
    { 
     // supposed to handle 'Wrapped + Wrapped' case 
     return m_value + other_.m_value; 
    } 

    template<typename U0_, typename U1_> 
    friend const Wrapper<decltype(U0_() + U1_())> operator +(U0_ val_, Wrapper<U1_> wrapper_) 
    { 
     // supposed to handle 'Unwrapped + Wrapped' case 
     return val_ + wrapper_.m_value; 
    } 

    private: 
    T_ m_value; 
}; 

這(如果我沒有錯過的東西,而在這裏將其粘貼)編譯罰款,並預期於以下情況的工作 (他們中的每一個可能,基本上) :

Wrapper<int> val = 3.14f; 
::std::cout << val + 42 << ::std::endl; // Wrapped + Unwrapped 
::std::cout << 42 + val << ::std::endl; // Unwrapped + Wrapped 
::std::cout << val + val << ::std::endl; // Wrapped + Wrapped 

然而,每當我試圖建立一個別名的decltype(...)部分無論是「纏+無包裝」或「無包裝+裹」例如像這樣:

template<typename T0_, typename T1_> 
struct Result 
{ 
    typedef decltype(T0_() + T1_()) Type; 
}; 

template<typename T_> 
class Wrapper 
{ 
    //... 
    template<typename U_> 
    const Wrapper<typename Result<T_, U_>::Type> operator +(U_ val_) const 
    //... 
    template<typename U0_, typename U1_> 
    friend const Wrapper<typename Result<U0_, U1_>::Type> operator +(U0_ val_, Wrapper<U1_> wrapper_) 
    //... 
}; 

'Wrapped + Wrapped'示例不想編譯,因爲重載解析似乎會改變爲不需要的變體。它拋出一個關於Wrapper<int>的默認構造函數不可用的錯誤,暗示試圖使用'Wrapped + Unwrapped'或'Unwrapped + Wrapped',兩者都不適合正確處理有問題的案例。

這讓我非常困惑,因爲它看起來像返回類型中的變化導致重載解析行爲的變化。將感謝有關此事的任何建議。

+1

的不良超載被SFINAE'd走;現在它們不是,因爲潛在的無效表達式已經被移入「結果」的定義中並且在緊接的上下文之外。 –

+0

所以我錯了,首先假定'Wrapped + Wrapped'變體將被視爲更適合於解決這種情況下的超負荷問題。謝謝你的答案。 – so100217

+1

它可能是一個更好的超載(由於偏序排序等),但你甚至沒有達到:你試圖形成一組候選人時觸發了一個嚴重的錯誤。 –

回答

1

這裏的重載大致的工作原理:

  1. 名稱查找,以找到候選函數和函數模板。
  2. 推導每個函數模板的模板參數,並將推導出的參數替換爲模板以生成單個函數模板專門化作爲候選。拋出任何扣除失敗的函數模板(包括替換失敗)。
  3. 比較候選人。選擇最好的一個或抱怨如果沒有一個。

如果第2步觸發了一個硬錯誤 - 通過在函數模板簽名的直接上下文之外形成一個無效構造 - 那麼你的程序是不合格的;你永遠無法進入第3步。如果在第3步中如果錯誤不在那裏,那麼候選人將不會被選中。

這裏,decltype(T_() + U_())最初是在緊接着的上下文中。因此,當U_被推斷爲Wrapper<...>並且代入簽名時,該表達是不合格的,但是該錯誤在直接上下文中,因此它是替代失敗。但是,當您將該表達式移入單獨的類模板Result時,該錯誤不再位於函數模板簽名的直接上下文中,因此它是一個硬錯誤。

如果你不想表達多次重複,使用別名模板:

template<class T, class U> 
using result = decltype(T() + U()); 
+0

再次感謝您提供這個無疑有用的答案。它詳盡地解釋了我遇到的問題。不能學習所有的模板細節是非常令人沮喪的,因爲我絕對必須儘快完成我正在努力的工作。 P.S:我知道模板別名,srtuct是一些額外的靜態方法。 – so100217