编译时间检查LCM(a,b)是否没有溢出
我想进行一项编译时间检查,即需要两个数字中最不常见的倍数不会溢出。难度:关于std :: LCM
,
如果| m |,| n |或最不常见的倍数,则行为是不确定的 | M |和| n |不能表示类型的值 std :: common_type_t< m,n>。
( source : https://en.cppreference.com/ w/cpp/numeric/lcm )
这是我想到的:
#include <type_traits>
#include <cstdint>
#include <numeric>
template<int8_t a, int8_t b,
std::enable_if_t<
(std::lcm(a,b) > 0 && (std::lcm(a,b) % a == 0) && (std::lcm(a,b) % b == 0)),
std::nullptr_t> = nullptr>
void f() { }
这里的理由是,条件检查std :: lcm(a,b)已经产生了一个类型
和std :: common_type_t&lt; typeof(a),typeof(b)&gt;
是a
ab
。鉴于 a
和b
的某些常见倍数适合std :: common_type_t&lt; typeof(a),typeof(b)&gt ;
,请参见 最小值 common多拟合,因此,我们可以通过定义std :: lcm
来保证我们所计算的实际上是LCM。
我检查了这似乎正常工作,例如
f<3, 5>(); // compiles
f<127, 127>(); // compiles
f<35, 48>(); // doesn't compile
,我有几个问题。
- 该文档说,如果最小常见的倍数不可代表,行为是不确定的,而不仅仅是实现依赖性或其他东西。这是否意味着一个包含
f&lt; 35,48&gt;()
之类的程序是错误的,并且欢迎编译器实际上编译上述代码并进行任意结果? - 是否有一种更简单的方法来做我要做的事情?
我想我可以编写自己的constexpr safe_lcm
函数,该功能可以保证定义的行为并在溢出的情况下返回0,但这似乎是一个非常不高的解决方案,而且我也必须努力工作为了确保我涵盖了可能会喂养的算术类型的所有可能组合。
Update :听起来像是在恒定表达式中不允许使用未定义的行为。由于我显然需要这是一个恒定的表达方式才能在编译时使用它,这是否意味着我在这里很安全?
更新2 :这似乎是针对无定义的行为理论的绝对打击:
template<int n> struct S {};
template<int8_t a, int8_t b>
S<std::lcm(a, b)> g()
{
return S<std::lcm(a,b)>();
}
int main(int, char **)
{
g<35, 48>(); // compiles :'(
return 0;
}
I would like to have a compile-time check that taking the least common multiple of two numbers doesn't overflow. Difficulty: regarding std::lcm
,
The behavior is undefined if |m|, |n|, or the least common multiple of
|m| and |n| is not representable as a value of type
std::common_type_t<M, N>.
(Source: https://en.cppreference.com/w/cpp/numeric/lcm)
Here is what I have come up with:
#include <type_traits>
#include <cstdint>
#include <numeric>
template<int8_t a, int8_t b,
std::enable_if_t<
(std::lcm(a,b) > 0 && (std::lcm(a,b) % a == 0) && (std::lcm(a,b) % b == 0)),
std::nullptr_t> = nullptr>
void f() { }
The rationale here is that that the condition checks that std::lcm(a,b)
has produced a positive number of type std::common_type_t<typeof(a), typeof(b)>
which is a multiple of both a
and b
. Given that some common multiple of a
and b
fits in std::common_type_t<typeof(a), typeof(b)>
, it follows that the least common multiple fits, and therefore we are guaranteed by the definition of std::lcm
that what we have computed is in fact the lcm.
I checked that this appears to work correctly, e.g.
f<3, 5>(); // compiles
f<127, 127>(); // compiles
f<35, 48>(); // doesn't compile
However I have a couple of questions.
- The documentation says that if the least common multiple is not representable, the behavior is undefined, and not just implementation-dependent or something. Does this mean that a program containing something like
f<35,48>()
is ill-formed and that the compiler is welcome to actually compile said code with arbitrary results? - Is there a simpler way of doing what I'm trying to do?
I suppose I could write my own constexpr safe_lcm
function that would guarantee defined behavior and return 0 in the case of an overflow, but that seems like a pretty inelegant solution and also I'd have to work pretty hard to make sure I covered every conceivable combination of arithmetic types I might feed to it.
Update: It sounds like undefined behavior isn't allowed in constant expressions. Since I clearly need this to be a constant expression in order to use it at compile time, does that mean I'm safe here?
Update 2: This appears to be a definite strike against the no-undefined-behavior-in-constexpr theory:
template<int n> struct S {};
template<int8_t a, int8_t b>
S<std::lcm(a, b)> g()
{
return S<std::lcm(a,b)>();
}
int main(int, char **)
{
g<35, 48>(); // compiles :'(
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
假设A和B都是积极的,那么
您可以利用这种关系来利用自己的优势。计算GCD(a,b)不会溢出。然后,您可以检查
CT在哪里
std :: common_type_t&lt; exptype(a),dectType(b)&gt;
。Assuming that both a and b are positive, there is the relationship
that you can use to your advantage. Computing gcd(a,b) does not overflow. Then you can check whether
where CT is
std::common_type_t<decltype(a),decltype(b)>
.