简体   繁体   中英

c++ Template function to flatten a vector of vector

I wrote a template function to flatten two level nested vector of vector. The second level vector however could be another vector, unique_ptr to vector, or shared_ptr to vector.

For example:

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;

I wrote this code which works view on 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;
}

However, would like check if there is better way to write the same code. Appreciate your time and effort.

If you wish to simplify your code, you can use the std::accumulate with custom operation as follows:

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

The disadvantage of my example is that it won't reserve the space for new elements but reallocates it when necessary.


My first example only applies to types which has member type value_type which has member function insert . This means that foo cannot be eg an std::vector<std::unique_ptr<std::vector<int>>> .

The mentioned problem can be solved by using SFINAE on two function templates as follows:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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