如何在C++中组合两个或多个任意类型的向量

mwp*_*htx 7 c++ tuples vector cartesian-product

我有以下代码将两个任意类型的向量组合成一个组合,即std::vector<std::tuple<A, B>>.

template<class A, class B>
std::vector<std::tuple<A, B>> combine(const std::vector<A>& a, const std::vector<B>& b) {

    const auto combine_parts_ = [](const A& x, const B& y) {
        auto result = std::tuple_cat(std::make_tuple(x), std::make_tuple(y));
        return result;
    };

    std::vector<std::tuple<A, B>> results;

    for (const auto& x : a) {
        for (const auto& y : b) {
            results.push_back(combine_parts_(x, y));
        }
    }

    return results;
}
Run Code Online (Sandbox Code Playgroud)

但是,我不清楚如何将其扩展到任意数量的类型/向量.我不关心重复类型; 实际上可能涉及两组或更多组相同类型.没关系.

一些示例用例,例如:

const auto combinations = combine(
    std::vector<int>({1,2,3})
    , std::vector<int>({1,2,3})
);
const auto combinations2 = combine(
    std::vector<int>({1,2,3})
    , std::vector<int>({1,2,3})
    , std::vector<bool>({true,false})
);
const auto combinations3 = combine(
    std::vector<int>({1,2,3})
    , std::vector<int>({1,2,3})
    , std::vector<bool>({true,false})
    , std::vector<char>({'a','b','c','d','e'})
);
Run Code Online (Sandbox Code Playgroud)

首先,我想要做的是避免丑陋的嵌套for循环.同时,我想结合一些单元测试组合用例,以便将结果std::tuple<...>作为测试用例.

注意,我不是在谈论同类集的排列.这与以前的问题混淆了.

我认为它可能与模板,可变参数有关std::tuple_cat,在某个地方,但我不知道.

思考?建议?

Jar*_*d42 4

如果你想计算异构向量的笛卡尔积,你可以这样做:

template <std::size_t N>
bool increase(const std::array<std::size_t, N>& sizes, std::array<std::size_t, N>& it)
{
    for (std::size_t i = 0; i != N; ++i) {
        const std::size_t index = N - 1 - i;
        ++it[index];
        if (it[index] >= sizes[index]) {
            it[index] = 0;
        } else {
            return true;
        }
    }
    return false;
}

template <typename F, std::size_t ... Is, std::size_t N, typename Tuple>
void apply_impl(F&& f,
                std::index_sequence<Is...>,
                const std::array<std::size_t, N>& it,
                const Tuple& tuple)
{
    f(std::get<Is>(tuple)[it[Is]]...);
}

template <typename F, typename ... Ts>
void cartesian_product_apply(F&& f, const std::vector<Ts>&... vs)
{
    constexpr std::size_t N = sizeof...(Ts);
    std::array<std::size_t, N> sizes{{vs.size()...}};
    std::array<std::size_t, N> it{};

    do {
        apply_impl(f, std::index_sequence_for<Ts...>(), it, std::tie(vs...));
    } while (increase(sizes, it));
}
Run Code Online (Sandbox Code Playgroud)

最后:

template <typename ... Ts>
std::vector<std::tuple<Ts...>> cartesian_product(const std::vector<Ts>&... vs)
{
    std::vector<std::tuple<Ts...>> res;

    cartesian_product_apply([&res](const auto&... args) { res.emplace_back(args...); },
                            vs...);
    return res;
}
Run Code Online (Sandbox Code Playgroud)

用法类似于:

std::vector<int> v1 = {1, 2, 3};
std::vector<std::string> v2 = {" A "," B "};
std::vector<int> v3 = {4, 5};

const auto res = cartesian_product(v1, v2, v3);
for (const auto& t : res) {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

演示