Бесконечная рекурсия из-за непреднамеренного вывода типа аргумента шаблона - PullRequest
1 голос
/ 16 апреля 2020

Пожалуйста, рассмотрите следующую boost::math::pdf -подобную реализацию обобщенной c оценки функции плотности вероятности:

template<typename T, class Distribution>
inline typename Distribution::result_type pdf(Distribution const& d, T const& x) {
    return pdf(d, static_cast<typename Distribution::result_type>(x));
}

template<typename RealType>
RealType pdf(std::uniform_real_distribution<RealType> const& d, RealType const& x)
{
    if (d.a() <= x && x <= d.b())
        return 1 / (d.b() - d.a());
    return 0;
}

Я бы хотел определить другую pdf функцию, которая принимает распределение и вектор и оценивает плотность вероятности каждого компонента вектора. Функция должна быть активна и для вложенных векторов. Я пробовал что-то вроде этого:

template<typename T, class Distribution>
inline typename Distribution::result_type pdf(Distribution const& d, std::vector<T> const& x)
{
    return std::reduce(x.begin(), x.end(), typename Distribution::result_type{ 1 },
        [&](auto const& p, auto const& x) { return p * pdf(d, x); });
}

Пример кода:

std::vector<std::vector<double>> x = { {1}, {2, 3}, {4} };
std::vector<double> y = { 1, 2, 3, 4 };
std::uniform_real_distribution<> d;
std::cout << pdf(d, x) << std::endl;
std::cout << pdf(d, y) << std::endl;

Это работает так, как должно быть. Тем не менее, если я изменю std::uniform_real_distribution<> d; на std::uniform_real_distribution<float> d;, то я получу бесконечную рекурсию для вызова pdf(d, x) (по очевидным причинам). Итак, как мне нужно изменить код? Может быть, так, что он работает и для других контейнеров.

1 Ответ

1 голос
/ 16 апреля 2020

Вместо двух перегрузок для первого случая (которые в основном делают одно и то же):

template<typename T, class Distribution>
inline typename Distribution::result_type pdf(Distribution const& d, T const& x) {
    return pdf(d, static_cast<typename Distribution::result_type>(x));
}

template<typename RealType>
RealType pdf(std::uniform_real_distribution<RealType> const& d, RealType const& x)
{
    if (d.a() <= x && x <= d.b())
        return 1 / (d.b() - d.a());
    return 0;
}

сворачивает их в один:

template <typename R, typename T, std::enable_if_t<std::is_convertible_v<T, R>, int> = 0>
auto pdf(std::uniform_real_distribution<R> const& d, T x) -> R
{
    if (d.a() <= x && x <= d.b()) {
        return 1 / (d.b() - d.a());
    }
    return 0;
}

Или проще просто заблокировать тип вычет по второму аргументу полностью (std::type_identity - это C ++ 20, но его тривиально реализовать):

template <typename R>
auto pdf(std::uniform_real_distribution<R> const& d, std::type_identity_t<R> x) -> R
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...