Получить представление всех T в std :: vector <std :: tuple <Ts ... >> - PullRequest
3 голосов
/ 22 апреля 2019

Представьте, что у меня есть контейнер, похожий на std::vector<std::tuple<Ts...>>.

Исходя из этого, я хотел бы получить "view" (без копирования) для всех T, чтобы я мог работать с этим видом, как если бы он был стандартным * Контейнер 1007 *.

Итак, что бы я хотел иметь:

using tuple_vector = std::vector<std::tuple<int,float,double>>;
tuple_vector tuple_vec = {{1, 4.f, 8.},
                          {2, 5.f, 9.},
                          {3, 6.f, 10.},
                          {4, 7.f, 11.}}

auto int_view = view<int>(tuple_vec);
^^^
type should be some kind of non-owning reference 

// what I would like to do
int_view[0] = 10; // modify
assert(int_view[0] == std::get<int>(tuple_vec[0])); // modification should modify tuple_vec as well

Я попытался std::transform, но затем я получил копию всех int.

std::vector<int> int_vec(tuple_vec.size());
std::transform(tuple_vec.begin(), tuple_vec.end(), int_vec.begin(), [&](const auto& elem) {
    return std::get<int>(elem);
});

Я не уверен, возможно ли это вообще, но если это так, я был бы признателен за любые советы или указания.

Ответы [ 2 ]

3 голосов
/ 22 апреля 2019

Вы можете создать вектор std::reference_wrapper:

template <typename T, typename ContainerOfTuples>
auto make_refs_to(ContainerOfTuples& tuples) {
    using RefType = std::reference_wrapper<T>;

    std::vector<RefType> refs;
    refs.reserve(std::size(tuples));
    std::transform(std::begin(tuples), std::end(tuples), std::back_inserter(refs),
                   [](auto& tup) -> RefType { return {std::get<T>(tup)}; });
    return refs;
}

auto double_view = make_refs_to<double>(tuple_vec);
double_view[1].get() += 3.14;  // Caveat: must access through .get().

Живой пример


Идем дальше ... вот как мы можем получить ссылки на многие типы:

namespace detail {

// When many types are asked for, return a tuple of references.
template <typename... T> struct RefTypeImpl {
    using type = std::tuple<std::reference_wrapper<T>...>;
};

// When a single type is asked for, return a single reference.
template <typename T> struct RefTypeImpl<T> {
    using type = std::reference_wrapper<T>;
};

// When two types are asked for, return a pair for more convenient access.
template <typename T, typename U> struct RefTypeImpl<T, U> {
    using type = std::pair<std::reference_wrapper<T>, std::reference_wrapper<U>>;
};

}  // namespace detail

template <typename... Ts, typename ContainerOfTuples>
auto make_refs_to(ContainerOfTuples& tuples) {
    using RefType = typename detail::RefTypeImpl<Ts...>::type;

    std::vector<RefType> refs;
    refs.reserve(std::size(tuples));
    std::transform(std::begin(tuples), std::end(tuples), std::back_inserter(refs),
                   [](auto& tup) -> RefType { return {std::get<Ts>(tup)...}; });
    return refs;
}

auto int_float_view = make_refs_to<int, float>(tuple_vec);
std::cout << (int_float_view[2].first.get() == 3) << '\n';

Живой пример

3 голосов
/ 22 апреля 2019

Что ж, если вы используете библиотеку Eric-Niebler range-v3 (которая находится в процессе принятия в стандарт - теперь есть Ranges TS ), вы можете применить интуиция использовать что-то вроде std::transform, но с целью:

#include <range/v3/view/transform.hpp>

// ... etc. ...

auto int_view = tuple_vector | ranges::view::transform(
    [](auto& t)->auto&{ return std::get<int>(t);} );

См. Это в action (Coliru), с модификацией даже одного из элементов.

Примечание: это представление становится неизменяемым, если мы удалим -> decltype(auto); изменение связано с ответом @ дедупликатора на на этот вопрос .

...