【问题标题】:Iterating over vectors in a parameter pack迭代参数包中的向量
【发布时间】:2021-04-03 17:29:55
【问题描述】:

我有一个函数,它接收一个向量参数包,这些向量的长度相同,我想用每个索引处这两个向量的值调用一个函数。

例如,我传入一个函数,该函数打印出我传入的两个整数。我会将两个整数向量放入该函数,它会为每个索引调用我传入的函数,代码可能帮助解释我想做什么

#include <iostream>
#include <thread>
#include <functional>
#include <vector>

template<
    typename RETURN,
    typename ... INPUTS
>
std::vector<RETURN> thread_transform(std::function<RETURN(INPUTS ...)> function, std::vector<INPUTS>& ... inputs)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    /* Code to loop over the vectors in 'inputs' and call 'function' with */
}

int func(int a, int b)
{
    std::cout << a << ' ' << b << std::endl;
}

int main(int argc, char** argv)
{
    std::vector<int> a;
    std::vector<int> b;
    std::vector<int> ret = thread_transform(std::function<int(int, int)>(func), a, b);
    return 0;
}

【问题讨论】:

    标签: c++ function templates vector variadic-templates


    【解决方案1】:

    大概是这样的:

    template<typename RETURN, typename ... INPUTS>
    std::vector<RETURN> thread_transform(
        std::function<RETURN(INPUTS ...)> function,
        std::vector<INPUTS>& ... inputs)
    {
        std::vector<RETURN> ret;
        auto size = std::min({inputs.size()...});
        for (int i = 0; i < size; ++i) {
            ret.push_back(function(inputs[i]...));
        }
        return ret;
    }
    

    非常简单。 Demo

    【讨论】:

    • 啊,这很聪明,我自己不会想到的。谢谢!
    • 只有一个问题,我很好奇您执行 std::min 函数的方式。那只是将初始化列表传递给 std::min 函数,并且正在调用适用于集合的重载吗?当我查看 std::min 的函数定义时,它看起来只能比较两个值。
    • std::min 有一个采用初始化列表的重载。重载(3)here
    • 考虑调整结果向量的大小(假设这些值是可初始化的或默认可构造的):std::vector&lt;RETURN&gt; ret(size);;
    • 最好确保 no 输入向量不会把事情搞砸:auto size = std::min({std::size_t(-1), inputs.size()...}); 同样,ret.reserve(size); 也不会出错。最后,i 也应该是 std::size_t 类型。
    【解决方案2】:

    我建议简化函数的签名,而不是对vectorsstd::function 进行硬编码。

    由于您已经定义了一个函数模板,您不妨接受任何范围和任何可调用对象:

    Live On Compiler Explorer

    template <typename F, typename... Rs>
    auto zip(F f, Rs const&... args) {
        using R = std::decay_t<
            std::invoke_result_t<F, typename Rs::value_type...>
        >;
        auto const n = std::min({args.size()...});
        std::vector<R> r(n);
        for (size_t i = 0; i<n; ++i)
            r[i] = f(args[i]...);
        return r;
    }
    

    其实,当你泛化的时候,你最终会得到 RangeV3 的zip_with

    template <typename F, typename... Rs>
    auto my_zip(F&& f, Rs const&... args) {
        return r::to_vector(v::zip_with(f, args...));
    }
    

    我认为默认情况下你可以不用to_vector

    Live On Compiler Explorer

    #include <fmt/ranges.h>
    #include <functional>
    #include <iostream>
    #include <map>
    #include <range/v3/all.hpp>
    #include <vector>
    
    namespace r = ::ranges;
    namespace v = r::views;
    using namespace std::string_literals;
    
    template <typename F, typename... Rs>
    auto zip(F f, Rs const&... args) {
        return v::zip_with(std::move(f), args...);
    }
    
    template <typename F, typename... Rs>
    auto zip_vec(F const& f, Rs const&... args) {
        return r::to_vector(zip(f, args...));
    }
    
    auto foo(int x, std::string_view s) {
        std::string r(s.size() * x, '\0');
        while (x--) {
            std::copy(s.begin(), s.end(), r.begin() + x * s.size());
        }
        return r;
    }
    
    int main() {
        std::vector<int> a{ 1, 3 }, b{ 2, -2 };
    
        auto mul = std::multiplies<>{};
        fmt::print("{} x {} -> {}\n", a, b, zip(mul, a, b));
    
        auto ret = zip_vec(std::plus<>{}, a, b);
        fmt::print("ret as a vector: {}\n", ret);
        static_assert(std::is_same_v<decltype(ret), std::vector<int>>);
    
        std::map<std::string, int> m { {"one"s, 1}, {"three"s, 3} };
        fmt::print("But also using a non-vectors: {}\nor {}\n",
        zip(foo, a, m | v::keys),
        zip(mul, a, m | v::values));
    }
    

    打印

    {1, 3} x {2, -2} -> {2, -6}
    ret as a vector: {3, 1}
    But also using a non-vectors: {"one", "threethreethree"}
    or {1, 9}
    

    【讨论】:

    • 这有我的投票权,尽管它在本机阵列上窒息。真的不需要拒绝那些。
    • @Deduplicator WorksForMe™。仅使用标准库 works 并根据 range_value_t 进行微调。
    • @Deduplicator 如果你还没有“范围”的优点,我会稍微改写一下:godbolt.org/z/Ecn49K(但是请注意,libfmt 不处理原始数组,但这超出了范围)
    猜你喜欢
    • 2017-12-29
    • 2017-08-29
    • 2021-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    相关资源
    最近更新 更多