Получить общее количество элементов во вложенном STL-подобном контейнере - PullRequest
1 голос
/ 07 февраля 2020

Я хотел бы написать функцию C ++, которая может подсчитывать общее количество элементов "atomi c" в обобщенном c вложенном STL-подобном контейнере со следующими условиями:

  1. Каждый уровень может быть контейнером любого типа.

  2. Число уровней не задано априори.

Я написал следующая рекурсивная функция (используя код из здесь ):

template <typename T>
size_t get_total_size(const T & var)
{
    if ( is_container<typeof(var)>::value ) {  // From https://stackoverflow.com/a/9407521/2707864
        typename T::const_iterator iter;
        size_t sumsize = 0;
        for ( iter = var.begin() ; iter!= var.end() ; iter++ ) {
            sumsize += get_total_size(*iter);
        }
        return sumsize;
    } else {
        return 1;
    }
};

Компиляция / компоновка этого может работать. Проблема заключается в том, что при его использовании (в противном случае не было бы никакого смысла в его написании!) Не компилируется, так как экземпляр получает на уровне «atomi c» тип, который не имеет итераторов, например, в этом коде

typedef vector<int> vint;
typedef vector<vint> vvint;
vvint vec_heap;
for (int i=0; i < 12; i++) {
    vec_heap.push_back(vint(2, i));
}
cout << get_total_size(vec_heap) << endl;  // Instantiation leads to compilation errors 

Возможно ли это?

EDIT : согласно одному комментарию это можно сделать с помощью c ++ 17 ... Является ли рекурсивная функция, которую я написал, излишним для цели?

Ответы [ 2 ]

1 голос
/ 07 февраля 2020

Если вы не можете использовать C ++ 17 или просто хотите открыть, какой стандарт можно использовать с вашей функцией, тогда вы можете переключиться на использование двух перегрузок и использовать SFINAE , чтобы определить, когда вызывать каждая перегрузка. Использование

// this will be called when T is not a container (it is the "atomic" type)
template <typename T, typename std::enable_if<!is_container<T>::value, bool>::type = true>
size_t get_total_size(const T & var)
{
    return 1;
}

// forward declare of pair function for associative containers
template <typename T, typename U>
size_t get_total_size(const std::pair<T, U> & var);

// this will be called for all container types
template <typename T, typename std::enable_if<is_container<T>::value, bool>::type = true>
size_t get_total_size(const T & var)
{
    size_t sumsize = 0;
    for ( auto iter = var.begin() ; iter != var.end() ; ++iter ) {
        sumsize += get_total_size(*iter);
    }
    return sumsize;
}

// this will be called for pair to handle associative containers
template <typename T, typename U>
size_t get_total_size(const std::pair<T, U> & var)
{
    return get_total_size(var.first) + get_total_size(var.second);
}

Это будет работать с C ++ 11 и выше, что вы можете увидеть в этом живом примере

1 голос
/ 07 февраля 2020

С C ++ 17 вы можете использовать if constexpr:

template <typename T>
size_t get_total_size(const T& var)
{
    if constexpr (is_container<T>::value) {
        return std::accumulate(var.begin(),
                               var.end(),
                               0u,
                               [](int acc, const auto& e){ return acc + get_total_size(e); });
    } else {
        return 1u;
    }
};

До этого вы могли использовать перегрузки и SFINAE:

// this will be called when T is not a container (it is the "atomic" type)
template <typename T, std::enable_if_t<!is_container<T>::value, int> = 0>
size_t get_total_size(const T& var)
{
    return 1u;
};
// this will be called for all container types, except for maps
template <typename T, std::enable_if_t<is_container<T>::value, int> = 0>
size_t get_total_size(const T& var)
{
    return std::accumulate(var.begin(),
                           var.end(),
                           0u,
                           [](int acc, const auto& e){ return acc + get_total_size(e); });
};
// this will be called for maps
template <typename Key, typename T>
size_t get_total_size(const std::map<Key, T> & var)
{
    return std::accumulate(var.begin(),
                           var.end(),
                           0u,
                           [](int acc, const auto& e){ return acc + get_total_size_sfinae(e.second); });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...