2017-07-31 117 views
5

我想在編譯時計算e值(不要擔心,不是作業),但出了問題。可能的模板&constexpr-if不兼容

template<size_t limit = 3, class result = std::ratio<0, 1>, size_t factorial = 1, size_t count = 1> 
constexpr double e_impl() { 
    if constexpr(limit == 0) { 
     return static_cast<double>(result{}.num)/result{}.den; 
    } 
    return e_impl<limit - 1, std::ratio_add<result, std::ratio<1, factorial>>, factorial * count, count + 1>(); 
} 

雖然計算值正確,但編譯器會在模板中引發有關溢出的錯誤。看起來好像limit變量超出範圍(低於0),但它不應該發生,因爲0 -case正在由if constexpr(…)語句處理。

所以問題是,我錯了,這種行爲應該是預期的,或者它是一個編譯器錯誤?用GCC 7.1.0編譯。

回答

6

要避免錯誤,把第二返回明確納入else分支:

template<size_t limit = 3, class result = std::ratio<0, 1>, size_t factorial = 1, size_t count = 1> 
constexpr double e_impl() { 
    if constexpr(limit == 0) { 
     return static_cast<double>(result{}.num)/result{}.den; 
    } 
    else 
    { 
     return e_impl<limit - 1, std::ratio_add<result, std::ratio<1, factorial>>, factorial * count, count + 1>(); 
    } 
} 

https://godbolt.org/g/PdV7m7

理性:

在封閉函數模板或通用拉姆達的一個實例如果轉換後的條件爲真,並且該語句包含constexpr else子態,則子態不會被實例化。

http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0128r1.html

它隻字未提不受約束else塊,或者是不應該運行塊,所以它被實例化,引發錯誤。 (注:叮噹時也失敗)

+2

A爲什麼會使這個答案更有用。 –

+0

@CrazyEddie Nouning副詞。如何表達。 – Yakk

5

不,這不是一個錯誤。這裏的問題是,即使limit0你停止遞歸編譯器仍然郵票出來

return e_impl<limit - 1, std::ratio_add<result, std::ratio<1, factorial>>, factorial * count, count + 1>(); 

,因爲它是無條件的。你需要把它放到一個else塊中,以便只在limit不是0時編譯它。

template<size_t limit = 3, class result = std::ratio<0, 1>, size_t factorial = 1, size_t count = 1> 
constexpr double e_impl() { 
    if constexpr(limit == 0) 
     return static_cast<double>(result{}.num)/result{}.den; 
    else 
     return e_impl<limit - 1, std::ratio_add<result, std::ratio<1, factorial>>, factorial * count, count + 1>(); 
}