Как устранить ошибку "делить на 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, поэтому я не был уверен, что компилятор будет рассматривать его как константу времени компиляции. К счастью, вариация исходного тернарного выражения устранила ошибку.