Ответ 1
Версия на основе SFINAE:
#include <cstdint>
#include <cmath>
#include <limits>
#include <type_traits>
constexpr std::intmax_t integer_power(std::intmax_t base,
std::intmax_t exponent)
{
return (exponent == 0) ? 1 :
(exponent % 2 == 0) ? integer_power(base, exponent/2)
*integer_power(base, exponent/2) :
base*integer_power(base, exponent-1);
}
namespace detail
{
template<std::intmax_t base, std::intmax_t exponent,
std::intmax_t res = integer_power(base,exponent)>
constexpr std::intmax_t pow_helper(int)
{
return res;
}
template<std::intmax_t base, std::intmax_t exponent>
constexpr std::intmax_t pow_helper(...)
{
return (exponent%2 == 0 || base > 0)
? std::numeric_limits<std::intmax_t>::max()
: std::numeric_limits<std::intmax_t>::min();
}
}
template<std::intmax_t base, std::intmax_t exponent>
constexpr std::intmax_t integer_power_bounded()
{
return detail::pow_helper<base,exponent>(0);
}
Пример использования:
#include <iostream>
int main()
{
std::cout << sizeof(std::intmax_t) << '\n';
constexpr auto p2t6 = integer_power_bounded<2, 6>();
constexpr auto p2t62 = integer_power_bounded<2, 62>();
constexpr auto p2t63 = integer_power_bounded<2, 63>();
constexpr auto p2t64 = integer_power_bounded<2, 64>();
constexpr auto p2t65 = integer_power_bounded<2, 65>();
std::cout << "2^6 == " << p2t6 << '\n';
std::cout << "2^62 == " << p2t62 << '\n';
std::cout << "2^63 == " << p2t63 << '\n';
std::cout << "2^64 == " << p2t64 << '\n';
std::cout << "2^65 == " << p2t65 << '\n';
constexpr auto pm2t6 = integer_power_bounded<-2, 6>();
constexpr auto pm2t62 = integer_power_bounded<-2, 62>();
constexpr auto pm2t63 = integer_power_bounded<-2, 63>();
constexpr auto pm2t64 = integer_power_bounded<-2, 64>();
constexpr auto pm2t65 = integer_power_bounded<-2, 65>();
std::cout << "-2^6 == " << pm2t6 << '\n';
std::cout << "-2^62 == " << pm2t62 << '\n';
std::cout << "-2^63 == " << pm2t63 << '\n';
std::cout << "-2^64 == " << pm2t64 << '\n';
std::cout << "-2^65 == " << pm2t65 << '\n';
}
Вывод:
8 2^6 == 64 2^62 == 4611686018427387904 2^63 == 9223372036854775807 2^64 == 9223372036854775807 2^65 == 9223372036854775807 -2^6 == 64 -2^62 == 4611686018427387904 -2^63 == -9223372036854775808 -2^64 == 9223372036854775807 -2^65 == -9223372036854775808
Пояснение:
Константное выражение не может содержать Undefined Поведение [expr.const]/2:
- операция, которая будет иметь поведение Undefined [Примечание: включая, например, переполнение целого числа со знаком, определенную арифметику указателя, деление на ноль или определенные операции сдвига - конечная нота];
Следовательно, всякий раз, когда неограниченный integer_power
создает переполнение, выражение, используемое для объявления std::integral_constant
, не является допустимым постоянным выражением; замена не выполняется, и используется функция возврата.