【问题标题】:Get a view to all T's in std::vector<std::tuple<Ts...>>查看 std::vector<std::tuple<Ts...>> 中的所有 T
【发布时间】:2019-09-11 05:02:29
【问题描述】:

假设我有一个类似于std::vector&lt;std::tuple&lt;Ts...&gt;&gt; 的容器。

由此,我想为所有T 获得一个“view”(非复制),这样我就可以对该视图进行操作,就好像它是一个类似标准的 容器。

所以我想要什么:

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);
});

我不确定这是否可行,但如果可以,我将不胜感激任何提示或指示。

【问题讨论】:

    标签: c++ vector tuples c++14 c++17


    【解决方案1】:

    你可以创建一个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().
    

    Live Example


    更进一步……下面是我们如何获得对多种类型的引用:

    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';
    

    Live Example

    【讨论】:

    • 我喜欢std::reference_wrapper 的想法!但是,正如您已经指出的那样,使用.get() 来检索数据并不理想。我还没有接受答案,也许有人想出了一个更好的主意:)
    • @kanstar 是的,我能理解。就我个人而言,我觉得需要使用.get() 是一件好事,因为它提醒人们这是一个“视图”,而不是一个实际的对象。
    • 如果我想查看多个T's,如auto int_float_view = make_refs_to&lt;int,float&gt;(tuple_vec),您会如何处理?
    • @kanstar 我想在这种情况下向量可以包含一个引用元组(将代码添加到答案中)。但是,经历如此多的复杂性让我认为您实际上应该使用完全不同的策略来组织数据。
    【解决方案2】:

    好吧,如果您使用Eric Niebler's ranges-v3 library(它正在被标准采用——现在有一个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) 中查看此内容,甚至对其中一个元素进行了修改。

    注意:如果我们删除-&gt; decltype(auto),此视图将变为不可修改;更改是由于@deduplicator 对this question 的回答。

    【讨论】:

    • int_view 在这种情况下不是 const 视图 吗?如果我需要一个可修改视图怎么办?
    • @kanstar:嗯 - 这将是可修改的!
    • 谢谢!看起来很棒!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-02
    • 2019-06-15
    • 2017-09-01
    • 2019-10-20
    • 2016-06-15
    • 1970-01-01
    • 2016-03-31
    相关资源
    最近更新 更多