Как суммировать по массиву вариантов с помощью посетителя? - PullRequest
0 голосов
/ 29 ноября 2018

Я пытаюсь найти способ суммирования по std::array из std::variant с помощью посетителя.Я дошел до этого далеко, но я не могу понять, как определить тип посетителей, не включая запись void в начало моего лямбда-списка посетителей.

Кто-нибудь знает, как я могу определить тип возврата лямбд в посетителе, чтобы мне не пришлось полагаться на это?

Вот что у меня сейчас есть:

#include <array>
#include <iostream>
#include <string_view>
#include <type_traits>
#include <variant>

using namespace std::literals::string_view_literals;

template<typename... Base>
struct Visitor: Base ... {
    using Base::operator()...;
};

template<typename... T>
Visitor(T...) -> Visitor<T...>;

// There has to be a better way to deduce Result than what I'm doing...
template<typename... T, typename S, typename... Ss, size_t N, typename Result = typename std::result_of_t<S()>>
constexpr std::enable_if_t<std::is_arithmetic_v<Result>, Result>
summation(const Visitor<S, Ss...> &visitor, const std::array<std::variant<T...>, N> &array) {
    Result sum{};
    for (const auto &a: array)
        sum += std::visit(visitor, a);
    return sum;
}

int main() {
    constexpr Visitor visitor {
            // This first entry should be unnecessary, I would think:
            []()                   -> double { return 0; },
            [](double d)           -> double { return d + 3.4; },
            [](int i)              -> double { return i - 2; },
            [](std::string_view s) -> double { return s.size(); }
    };

    constexpr std::array<std::variant<int, double, std::string_view>, 5> arr{9.0, 9, 3, 5.2, "hello world"sv};
    constexpr auto val = summation(visitor, arr);
    std::cout << val << '\n';
}

Редактировать: Я хотел бы, чтобы результат был constexpr.

Спасибо за любую помощь.

Ответы [ 2 ]

0 голосов
/ 29 ноября 2018

Упрощение (я надеюсь) решения Фрэнка decltype() / std::declval().

Используя decltype() / std::declval(), вам не нужно знать S, Ss...и T...;вам просто нужен тип шаблона V для visitor и тип шаблона для array.

Вы также можете избежать static_assert(), если хотите, повторно включив SFINAE, просто написав

template <typename V, typename A,
          typename R = decltype(std::visit(std::declval<V>(), std::declval<A>().at(0)))>
constexpr std::enable_if_t<std::is_arithmetic_v<R>, R>
   summation(V const & visitor, A const &array)
{
    R sum{};
    for (const auto &a: array)
        sum += std::visit(visitor, a);
    return sum;
}
0 голосов
/ 29 ноября 2018

Вы слишком явно выражаете вывод своего типа, когда auto существует, чтобы позволить компилятору позаботиться об этом за вас.

Как только вы попадаете в область действия функций decltype() и std::declval() сделать вывод (необходимый для создания инициализируемой по умолчанию цели) легко, поскольку вы можете просто высмеять фактический вызов посетителя.

template<typename... T, typename S, typename... Ss, size_t N>
constexpr auto summation(const Visitor<S, Ss...> &visitor, const std::array<std::variant<T...>, N> &array) {

    using Result = decltype(std::visit(visitor, std::declval<std::variant<T...>>()));
    static_assert(std::is_arithmetic_v<Result>);

    Result sum{};
    for (const auto &a: array)
        sum += std::visit(visitor, a);
    return sum;
}

На самом деле я очень предпочитаю этот стиль, поскольку ошибочный вызов будетфактически выдает разумное сообщение об ошибке вместо «функция не найдена».Это если у вас не будет арифметических версий accumulate(), против которых вы пытаетесь сфинкаться (что было бы странно).

...