C++ 模板函数:将一个嵌套的向量压平为一个向量

4

我编写了一个模板函数,用于展开两级嵌套的vector。然而,第二级向量可以是另一个向量、指向向量的unique_ptr或shared_ptr。

例如:

std::vector<std::unique_ptr<std::vector<int>>> f1;
std::vector<std::shared_ptr<std::vector<int>>> f2;
std::vector<std::vector<int>> f3;
std::vector<std::unique_ptr<std::vector<std::string>>> f4;
std::vector<std::shared_ptr<std::vector<std::string>>> f5;
std::vector<std::vector<std::string>> f6;

我写了这段代码,它在coliru上运行良好。

#include <vector>
#include <string>
#include <algorithm>
#include <memory>
#include <iostream>
#include <sstream>

template<typename T>
const T* to_pointer(const T& e) {
    return &e;
}

template<typename T>
const T* to_pointer(const std::unique_ptr<T>& e) {
    return e.get();
}

template<typename T>
const T* to_pointer(const std::shared_ptr<T>& e) {
    return e.get();
}

template <typename T, typename K,
    typename = typename std::enable_if<
        std::is_same<K, std::unique_ptr<std::vector<T>>>::value or
        std::is_same<K, std::shared_ptr<std::vector<T>>>::value or
        std::is_same<K, std::vector<T>>::value
    >::type
>
std::vector<T> flatten(std::vector<K>& source) {
    std::vector<T> result;
    size_t size = 0;
    for (const auto& e : source) {
        size += to_pointer(e)->size();
    }
    result.reserve(size);

    for (const auto& e : source) {
        auto ptr   = to_pointer(e);
        auto begin = ptr->begin();
        auto end   = ptr->end();
        std::copy(begin, end, std::back_inserter(result));
    }
    return result;
}

然而,我想检查是否有更好的方法来编写相同的代码。感谢您的时间和精力。


2
可能更适合于代码审查堆栈交换 - Thomas
@Thomas 确定,谢谢,我不知道。谢谢。 - Prasad Joshi
1个回答

5
如果您希望简化代码,可以使用std::accumulate与自定义操作,如下所示:
#include <vector>
#include <numeric>

int main() {
    std::vector<std::vector<int>> foo {
        { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }
    };

    auto bar = std::accumulate(foo.begin(), foo.end(), decltype(foo)::value_type{},
            [](auto& dest, auto& src) {
        dest.insert(dest.end(), src.begin(), src.end());
        return dest;
    });
}

我的例子的缺点是它不会为新元素保留空间,而是在必要时重新分配空间。

我的第一个例子仅适用于具有成员类型value_type并具有成员函数insert的类型。这意味着foo不能是例如std::vector<std::unique_ptr<std::vector<int>>>

可以通过在两个函数模板上使用SFINAE来解决上述问题,具体如下:

template<typename T, typename = typename T::value_type>
T flatten(const std::vector<T>& v) {
    return std::accumulate(v.begin(), v.end(), T{}, [](auto& dest, auto& src) {
        dest.insert(dest.end(), src.begin(), src.end());
        return dest;
    });
}

template<typename T, typename = typename T::element_type::value_type>
typename T::element_type flatten(const std::vector<T>& v) {
    using E = typename T::element_type;

    return std::accumulate(v.begin(), v.end(), E{}, [](auto& dest, auto& src) {
        dest.insert(dest.end(), src->begin(), src->end());
        return dest;
    });
}

非常感谢您的建议。了解到std::accumulate很好,因为我以前从未使用过它。然而,这段代码无法与std::vector<std::unique_ptr<std::vector<int>>>或者shared_ptr一起使用。 - Prasad Joshi
不客气!我还用像std::vector <std :: unique_ptr <std :: vector <int >>>这样支持了类型的答案进行了扩展。 - Akira

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接