My understanding is that:
It seems to follow that something like the following should not compile, and indeed on my compiler it doesn't.
template<int n> struct S { };
template<int a, int b>
S<a * b> f()
{
return S<a * b>();
}
int main(int, char **)
{
f<50000, 49999>();
return 0;
}
However, now I try the following instead:
#include <numeric>
template<int n> struct S { };
template<int a, int b>
S<std::lcm(a, b)> g()
{
return S<std::lcm(a,b)>();
}
int main(int, char **)
{
g<50000, 49999>();
return 0;
}
Each of g++, clang, and MSVC will happily compile this, despite the fact that
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)
Is this a bug in all three compilers? Or is cppreference wrong about lcm's behavior being undefined if it can't represent the result?
A late reply, but it was a bug in gcc and is now fixed: See https://gcc.gnu.org/PR105844
Quoting some details:
When I fixed PR libstdc++/92978 I introduced a regression whereby std::lcm(INT_MIN, 1) and std::lcm(50000, 49999) would no longer produce errors during constant evaluation. Those calls are undefined, because they violate the preconditions that |m| and the result can be represented in the return type (which is int in both those cases). The regression occurred because __absu<unsigned>(INT_MIN) is well-formed, due to the explicit casts to unsigned in that new helper function, and the out-of-range multiplication is well-formed, because unsigned arithmetic wraps instead of overflowing.