Я изучаю константные выражения в сочетании с шаблонами в C ++ и столкнулся с проблемой, которую не могу понять.
Я хочу проверить, равно ли значение аргумента шаблона (в моем случае целое число без знака) нулю, но компилятор никогда не думает, что значение равно нулю, даже если у меня есть static_assert, которое подтверждает, что оно фактически прошло ниже нуля.
Я реализую простую (или, по крайней мере, я так думал) шаблонную функцию, которая должна просто суммировать все целочисленные значения в диапазоне, например, от От 5 до нуля.
Предполагается выполнять рекурсивный вызов функции шаблона до тех пор, пока она не достигнет нуля, а затем она должна остановиться, но компилятор никогда не думает, что значение параметра шаблона равно нулю.
Вот моя проблемная функция:
template <unsigned int Value>
constexpr unsigned int sumAllValues()
{
static_assert (Value >= 0, "Value is less than zero!");
return Value == 0 ? 0 : Value + sumAllValues<Value - 1>();
}
И он вызывается так:
constexpr unsigned int sumVals = sumAllValues<5>();
По какой-то причине компилятор никогда не думает, что Value == 0, и, следовательно, он продолжается до тех пор, пока не остановится на static_assert. Если я удалю assert, то компилятор продолжит работу, пока не достигнет максимальной глубины создания:
ошибка: глубина создания шаблона превышает максимум 900 (используйте -ftemplate-deep = для увеличения максимума)
Возвращаемое значение == 0? 0: значение + sumAllValues ();
Что я делаю не так в вышеуказанной функции? Могу ли я не проверить значение самого параметра шаблона?
Меня вдохновил пример, который я нашел в Википедии:
template<int B, int N>
struct Pow
{
// recursive call and recombination.
enum{ value = B*Pow<B, N-1>::value };
};
template< int B >
struct Pow<B, 0>
{
// ''N == 0'' condition of termination.
enum{ value = 1 };
};
int quartic_of_three = Pow<3, 4>::value;
См. Ссылку: C ++ 11
И у меня действительно есть рабочий пример, который построен больше так, как приведенный выше пример кода Pow:
template <unsigned int Value>
constexpr unsigned int sumAllValues()
{
static_assert (Value > 0, "Value too small!");
return Value + sumAllValues<Value - 1>();
}
template <>
constexpr unsigned int sumAllValues<0>()
{
return 0;
}
И он вызывается так же, как моя проблемная функция:
constexpr unsigned int sumVals = sumAllValues<5>();
Он также выполняет рекурсивный вызов функции шаблона, пока не достигнет нуля. Нулевой случай был специализирован для прерывания рекурсии. Этот код работает и выдает значение 15, если я ввожу 5 в качестве аргумента шаблона.
Но я думал, что смогу упростить это с помощью функции, с которой у меня проблемы.
Я занимаюсь разработкой под Linux (Ubuntu 18.04) в Qt 5.12.2.
Обновление:
StoryTeller предлагает рабочее решение, которое использует функцию C ++ 17 "if constexpr" , чтобы остановить рекурсию:
template <unsigned int Value>
constexpr unsigned int sumAllValues()
{
if constexpr (Value > 0)
return Value + sumAllValues<Value - 1>()
return 0;
}