Как устранить ошибку "делить на 0" в коде шаблона
Я использую пару целочисленных параметров шаблона для указания отношения, так как я не могу использовать double в качестве параметра шаблона. Преобразование в double защищено от деления на ноль тройным. Это работало в более ранней версии компилятора, но Visual Studio 2013 дает ошибку:
error C2124: divide or mod by zero
Здесь приведена упрощенная версия кода:
template<int B1, int B2>
class MyClass
{
const double B = (B2 == 0) ? 0.0 : (double) B1 / (double) B2;
// ...
};
MyClass<0, 0> myobj;
Я действительно хочу, чтобы B
был оптимизирован из выражений, которые используют его, когда он равен нулю, поэтому мне нужно однострочное определение. Я знаю, что могу просто использовать параметры шаблона <0, 1>
, чтобы обойти его, но мне интересно, есть ли способ убедить компилятор в том, что мое выражение безопасно?
Ответы
Ответ 1
Мне сказали:
const double B = (B2 == 0 ? 0.0 : (double) B1) /
(B2 == 0 ? 1.0 : (double) B2);
Это позволяет избежать зависимости от оценки короткого замыкания, предотвращающей деление на 0; с условными выборами до деления.
Оригинальная идея/Возможно, что-то вроде этого...? (Я думаю, что B
должен быть static const
или constexpr
, но я уверен, что вы можете сортировать это...)
template<int B1, int B2>
struct MyClass
{
const double B = (double) B1 / (double) B2;
};
template <int B1>
struct MyClass<B1, 0>
{
const double B = 0.0;
};
Если в MyClass
есть много других вещей, которые вы не хотите дублировать или помещать в базу и т.д., вы можете переместить вычисление B
в поддерживающий шаблон, используя вышеприведенный подход специализации.
Ответ 2
Visual Studio не может отображать тип B1, B2 в тройной работе во время компиляции, но явно работает кастинг.
template<int B1, int B2>
class MyClass
{
double d1 = (double)B1;
double d2 = (double)B2;
const double B = (B2 == 0) ? 0.0 : d1/d2;
// ...
};
MyClass<0, 0> myobj;
Ответ 3
Для любопытных - вот код, который я, наконец, закончил. Вероятно, это помогает увидеть его в реальном мире.
template<int B1, int B2, int C1, int C2>
class BicubicFilter
{
// Based on the formula published by Don Mitchell and Arun Netravali at
// http://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf
public:
BicubicFilter() : m_dMultiplier(1.0) {}
double m_dMultiplier;
double k(double x) const
{
const double B = (double) B1 / ((B2 == 0) ? 1.0 : (double) B2);
const double C = (double) C1 / ((C2 == 0) ? 1.0 : (double) C2);
x = fabs(x) * m_dMultiplier;
if (x < 1.0)
return ((2.0 - 1.5*B - C) * x*x*x) + ((-3.0 + 2.0*B + C) * x*x) + (1.0 - (2.0/6.0)*B);
if (x < 2.0)
return (((-1.0/6.0)*B - C) * x*x*x) + ((B + 5.0*C) * x*x) + ((-2.0*B - 8.0*C) * x) + ((8.0/6.0)*B + 4.0*C);
return 0.0;
}
};
Функция k
выполняется как минимум 4 раза на пиксель для операции изменения размера изображения, поэтому эффективность имеет решающее значение. Я хочу, чтобы все константы были известны во время компиляции, поэтому компилятор может максимально упростить выражения.
Основываясь на принятом ответе, я надеялся создать класс шаблона Ratio
, который просто вывел бы соотношение двух int
как constexpr double
и специализировал его для параметров 0, 0
. Visual Studio 2013 еще не реализует constexpr
, поэтому я не был уверен, что компилятор будет рассматривать его как константу времени компиляции. К счастью, вариация исходного тернарного выражения устранила ошибку.