2017-07-31 103 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>(); 
}