在标准论文P0092R1中,Howard Hinnant写道:
template <class To, class Rep, class Period,
class = enable_if_t<detail::is_duration<To>{}>>
constexpr
To floor(const duration<Rep, Period>& d)
{
To t = duration_cast<To>(d);
if (t > d)
--t;
return t;
}
此代码如何工作?问题是operator--
上的std::chrono::duration
不是constexpr操作。它被定义为:
duration& operator--();
然而这段代码编译,并在编译时给出了正确的答案:
static_assert(floor<hours>(minutes{3}).count() == 0, "”);
该怎么做?
答案 0 :(得分:10)
答案是并非所有编译时例程中的操作都必须是constexpr;只编译在编译时执行的。
在上面的示例中,操作是:
hours t = duration_cast<hours>(d);
if (t > d) {} // which is false, so execution skips the block
return t;
所有这些都可以在编译时完成。
另一方面,如果您尝试:
static_assert(floor<hours>(minutes{-3}).count() == -1, "”);
它会产生编译时错误(使用clang):
error: static_assert expression is not an integral constant expression
static_assert(floor<hours>(minutes{-3}).count() == -1, "");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: non-constexpr function 'operator--' cannot be used in a constant expression
--t;
^
note: in call to 'floor(minutes{-3})'
static_assert(floor<hours>(minutes{-3}).count() == -1, "");
编写constexpr代码时,您必须通过代码考虑所有路径。
P.S。您可以这样修复建议的floor
例程:
template <class To, class Rep, class Period,
class = enable_if_t<detail::is_duration<To>{}>>
constexpr
To floor(const duration<Rep, Period>& d)
{
To t = duration_cast<To>(d);
if (t > d)
t = t - To{1};
return t;
}
答案 1 :(得分:-1)
根据n3597和n3652的规则,constexpr
函数中的表达式本身不必是常量表达式,只要它们不修改全局可见状态即可
有一个
的例子constexpr int f(int a) {
int n = a;
++n; // '++n' is not a constant expression
return n * a;
}
int k = f(4); // OK, this is a constant expression.
// 'n' in 'f' can be modified because its lifetime
// began during the evaluation of the expression.
很可能这些是Howard Hinnant在撰写您提到的论文时遵循的规则。
要使用问题中的duration<T>
代码,operator--
必须成为constexpr
函数。由于constexpr
对图书馆的更改不是最终的,因此很容易理解霍华德如何依赖这样的改变。