繁体   English   中英

将 n 个向量组合成一个 n 元组向量

[英]Combining n vectors into one vector of n-tuples

我正在考虑带有签名的函数

template<typename ...Ts>
std::vector<std::tuple<Ts...>> join_vectors(std::vector<Ts>&&...) {
    //...
};

但可能更通用的接受任何可迭代的而不是只接受std::vector会更好。 大概会有这样的签名吧?

template<template<typename> typename C, typename ...Ts>
C<std::tuple<Ts...>> join_vectors(C<Ts>&&...) {
    // ...
};

但是,我在 C++ 中还没有达到这个水平(尽管在 Haskell 中做同样的事情会相对容易),因此我寻求帮助。

不幸的是,在这种情况下,我无法使用 Range-v3 的zip 我标记它是因为我认为那些对它感兴趣的人可以更好地帮助我。

对于任何具有以下size可索引容器,都是可能的:

#include <tuple>
#include <vector>
#include <algorithm>

// Copy from lvalue containers, move from rvalue containers.
template<typename ...Cs>
auto zip(Cs... vecs) {
    std::vector<std::tuple<typename std::decay_t<Cs>::value_type...>> vec;

    auto len = std::min({vecs.size()...});
    vec.reserve(len);
    for(std::size_t i=0;i<len;++i){
        vec.emplace_back(std::move(vecs[i])...);
    }
    return vec;
};

//Return vector of tuples with & for non-const vecs and const& if const.
template<typename ...Cs>
auto zip_view(Cs&... vecs) {
    std::vector<std::tuple<decltype(vecs[0])...>> vec;
    auto len = std::min({vecs.size()...});
    vec.reserve(len);
    for(std::size_t i=0;i<len;++i){
        vec.emplace_back(vecs[i]...);
    }
    return vec;
};

如果容器已正确实现移动构造函数,则此解决方案将复制作为左值传递的容器并从右值移动。 非常小的缺点是左值容器首先被整个复制,而不仅仅是单个元素。

示例 [ Godbolt ]

#include <iostream>
#include <memory>
template<typename T, typename...Args>
void print_tuple(const T& first, const Args&... args){
    std::cout<<'('<<first;
    ((std::cout<<','<< args),...);
    std::cout<<')';
}

template<typename T>
struct helper{
using fnc_t = void;
};
template<typename...Args>
struct helper<std::tuple<Args...>>{
using fnc_t = void(*)(const Args&... args);
};
template<typename...Args>
struct helper<std::tuple<Args&...>>{
using fnc_t = void(*)(const Args&... args);
};

template<typename T>
using fnc_t2 = typename helper<T>::fnc_t;

template<typename T>
void template_apply(fnc_t2<T> f, const T& tuple){
    std::apply(f, tuple);
}

template<typename T>
void print_vec(const std::vector<T>& vec){
    for(const auto&e:vec){
        template_apply(print_tuple,e);
        std::cout<<'\n';
    }
}
struct MoveOnlyFoo{
    MoveOnlyFoo(int i):m_i(i){}

    int m_i;
    std::unique_ptr<int> ptr = nullptr;
};
    std::ostream& operator<<(std::ostream& o, const MoveOnlyFoo& foo){
        return o<<foo.m_i;
    }


int main(){
    std::vector v1{1,2,3,4,5,6};
    std::vector v2{'a','b','c','d','e'};
    std::vector v3{1.5,3.5,7.5};
    std::vector<MoveOnlyFoo> vmove;
    vmove.emplace_back(45);
    vmove.emplace_back(46);
    vmove.emplace_back(47);
    const std::vector v4{-1,-2,-3,-4,-5};

    //Move rvalues, copy lvalue.
    print_vec(zip(v1,v2,v3, v4, std::move(vmove)));
    // This won't work since the elements from the last vector cannot be copied.
    //print_vec(zip(v1,v2,v3, v4, vmove));
    std::cout<<"View:\n";
    //View, provides const& for const inputs, & for non-const
    print_vec(zip_view(v1,v2,v3,v4));
    std::cout<<"Modify and print:\n";
    for(auto& [x,y]: zip_view(v1,v2)){
        ++x,++y;
    }
    // Note the view can work with const containers, returns tuple of `const T&`.
    print_vec(zip_view(std::as_const(v1),std::as_const(v2)));
}

输出

(1,a,1.5,-1,45)
(2,b,3.5,-2,46)
(3,c,7.5,-3,47)
View:
(1,a,1.5,-1)
(2,b,3.5,-2)
(3,c,7.5,-3)
Modify and print:
(2,b)
(3,c)
(4,d)
(5,e)
(6,f)

请忽略打印代码的可读性;)

我在 python zip功能之后对其进行了建模。 请注意,您的初始提议复制了向量,因此输出是一个向量,其中的值从参数中移出。

返回可迭代的Cs更难,因为您必须指定如何将元素插入其中,迭代器不能自己完成。

让它与迭代器一起工作(但仍然返回一个向量)是一件苦差事,但理论上也是可能的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM