C ++ constexpr для полиномиальной оценки по методу Хорнера - PullRequest
0 голосов
/ 08 мая 2018

Я хочу иметь возможность оценить производную полинома по методу Хорнера и использовать результат как constexpr. Это кажется невероятно обыденным, но я упускаю что-то очевидное, потому что компилятор говорит, что я превышаю максимальную глубину рекурсии. Основная рекурсия происходит здесь:

template<size_t d, size_t i, typename C, typename X>
constexpr X evalImpl(const C &c, const X &x) {
    return i >= (C::SizeAtCompileTime - 1 - d) ? 1 : evalImpl<d, i + 1, C, X>(c, x);
}

Вам не нужно даже знать метод Хорнера, чтобы знать, что здесь происходит, поэтому я максимально расширил код, даже удалив, как используется x, потому что он не имеет отношения к проблеме. У меня есть.

Идея состоит в том, что когда индекс i равен степени многочлена Degree<C>::value минус порядок производной d, тогда рекурсия должна прекратиться. В противном случае следует увеличить индекс i и повторить попытку.

Я называю вышеупомянутую рекурсию вызовом вида

 eval<derivative, 0>(c, x)

, где c - собственная матрица типа Eigen::Matrix<double,1,7>, а x - двойная. Идея состоит в том, чтобы начать с 0 и считать до степени многочлена.

Сообщение об ошибке компилятора имеет вид

In file included from /mnt/c/proj/src/main.cpp:11:0:
/mnt/c/proj/src/polynomial.h: In instantiation of 'constexpr X {anonymous}::evalImpl(const C&, const X&) [with long unsigned int d = 1ul; long unsigned int i = 898ul; C = Eigen::Matrix<double, 1, 7>; X = double]':
/mnt/c/proj/src/polynomial.h:74:108:   recursively required from 'constexpr X {anonymous}::evalImpl(const C&, const X&) [with long unsigned int d = 1ul; long unsigned int i = 1ul; C = Eigen::Matrix<double, 1, 7>; X = double]'
/mnt/c/proj/src/polynomial.h:74:108:   required from 'constexpr X {anonymous}::evalImpl(const C&, const X&) [with long unsigned int d = 1ul; long unsigned int i = 0ul; C = Eigen::Matrix<double, 1, 7>; X = double]'
/mnt/c/proj/src/polynomial.h:109:39:   required from 'constexpr X Polynomial::eval(const C&, const X&) [with long unsigned int d = 1ul; C = Eigen::Matrix<double, 1, 7>; X = double]'
/mnt/c/proj/src/main.cpp:306:66:   required from here
/mnt/c/proj/src/polynomial.h:74:108: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
         return i >= (C::SizeAtCompileTime - 1 - d) ? 1 : evalImpl<d, i + 1, C, X>(c, x);

1 Ответ

0 голосов
/ 08 мая 2018

Состояние здесь:

return i >= (C::SizeAtCompileTime - 1 - d) ? 1 : evalImpl<d, i + 1, C, X>(c, x);

не является if constexpr. Таким образом, независимо от того, является ли i >= (C::SizeAtCompileTime - 1 - d) true или false, оставшийся всегда будет создан. Таким образом, рекурсия не остановится, как вы хотите.

Изменить на это:

if constexpr (i >= (C::SizeAtCompileTime - 1 - d)) {
    return 1;
} else { 
    return evalImpl<d, i + 1, C, X>(c, x);  
}

EDIT:

Если у вас нет доступа к C ++ 17, используйте диспетчеризацию тегов:

template<size_t d, size_t i, typename C, typename X>
constexpr X evalImpl_impl(const C &c, const X &x, std::true_type) {
    return 1;
}

template<size_t d, size_t i, typename C, typename X>
constexpr X evalImpl_impl(const C &c, const X &x, std::false_type) {
    return evalImpl_impl<d, i + 1, C, X>(c, x, std::integral_constant<bool, C::SizeAtCompileTime-1-d <= i+1>{});
}

template<size_t d, size_t i, typename C, typename X>
constexpr X evalImpl(const C &c, const X &x) {
    return evalImpl_impl(c, x, std::integral_constant<bool, C::SizeAtCompileTime-1-d <= i>{});
}
...