cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
89 stars 0 forks source link

std::visit для std::tuple #389

Closed apolukhin closed 3 years ago

apolukhin commented 3 years ago

Перенос предложения: голоса +5, -1 Автор идеи: Олег Фатхиев

Предлагается добавить следующую функцию:

template <class Visitor, class Tuple>
constexpr /*see below*/ visit(Visitor&& v, Tuple&& t);

Где тип возвращаемого значения - это std::tuple со следующими подтипами:

std::invoke_result_t<Visitor&&, std::tuple_element<i, std::remove_cvref<Tuple>>>

Для i от 0 до std::tuple_size_v<std::remove_cvref> - 1.

Пример:

struct visiter {
    int operator()(int a) { return a * 2; }
    std::string operator(std::string b) { return b + b; }
};

std::tuple t{5, 6, 8, "abc"s, "def"s};

auto res = std::visit(visiter{}, t);

assert(res == std::tuple{10, 12, 16, "abcabc"s, "defdef"s};

Такое visit также удобно использовать с предлагаемым overloaded.

Возможная реализация:

template <class Visiter, class Tuple, std::size_t... Is>
constexpr auto visit(Visiter&& v, Tuple&& t, std::index_sequence<Ts...>) {
    return std::tuple{
        std::forward<Visiter>(v)(std::get<Is>(std::forward<Tuple>(t)))...
    };
}

template <class Visiter, class Tuple>
constexpr auto visit(Visiter&& v, Tuple&& t) {
    return visit_impl(std::forward<Visiter>(v), std::forward<Tuple>(t),
                      std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}

Из-за сложности работы с функцие в случае, если visiter имеет void в качестве возвращаемого значения для какого-то из типов, возможно, будет лучше добавить следующую функцию:

template <class Visitor, class Tuple>
constexpr void visit(Visitor&& v, Tuple&& t);

Возможная реализация:

template <class Visiter, class Tuple, std::size_t... Is>
constexpr auto visit(Visiter&& v, Tuple&& t, std::index_sequence<Ts...>) {
    (..., std::forward<Visiter>(v)(std::get<Is>(std::forward<Tuple>(t))));
}

template <class Visiter, class Tuple>
constexpr void visit(Visiter&& v, Tuple&& t) {
    visit_impl(std::forward<Visiter>(v), std::forward<Tuple>(t),
               std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}
apolukhin commented 3 years ago

Andrey Davydov, 12 декабря 2018, 20:31 Вот же ж круто! А зачем?

Олег Фатхиев, 12 декабря 2018, 22:48 Andrey Davydov, бывает нужно пройтись по таплу как по гетерогенному контейнеру. Если представлять, что tuple - это контейнер, то можно считать, что visit - это своеобразный for_each для тапла

Andrey Davydov, 12 декабря 2018, 23:59 Олег Фатхиев, это скорее transform а не for_each (в Boost.Hana так и называется), но я спрашивал, о каком-нибудь конкретном мотивирующем примере, потому что с tuple можно делать много разного (что и демонстрирует Boost.Hana) и не очень понятно, почему стандартизировать надо именно transform?

apolukhin commented 3 years ago

Дубликат #139