C ++ рекурсивное разрешение шаблонов: элегантное выравнивание векторов векторов - PullRequest
5 голосов
/ 16 апреля 2019

Рассмотрим следующий (наивный) фрагмент кода C ++ для переноса объектов из пользовательского типа списка в std::vector

template<class A> void transfer(std::vector<A>& target, const custom_list_type& source){
  for(const A& elem:source){
    target.push_back(elem);
  }
}

Теперь представьте, что у каждого было std::vector таких пользовательских списков, и он хотел бы сгладить структуру, или std::vector таких векторов. Наивно, я бы сейчас пошел писать такие функции.

template<class A> void flatten_transfer(std::vector<A>& target, const std::vector<custom_list_type>& source){
  for(const auto& elem:source){
    flat_transfer(target,elem);
  }
}

template<class A> void flatten_transfer(std::vector<A>& target, const std::vector<std::vector<custom_list_type> >& source){
  for(const auto& elem:source){
    flat_transfer(target,elem);
  }
}

И так далее, и тому подобное. Но я вижу, что это не очень элегантно, потому что мне нужна версия этой функции каждого уровня глубины. Я бы предположил, что есть более элегантный способ решить эту проблему с помощью некоторой магии шаблонов, но я не достаточно осведомлен, чтобы придумать решение, которое действительно лучше.

Каким был бы «рекомендуемый» способ абстрагирования уровня глубины вектора с помощью шаблонов, так что нужно было бы написать только один экземпляр flatten_transfer?

1 Ответ

6 голосов
/ 16 апреля 2019

Предполагая, что я правильно понимаю проблему, следующий шаблон функции хорошо работает для этой цели в C ++ 17 и более поздних версиях. Эта функция создает target.push_back(elem) тогда и только тогда, когда A совпадает с B. Иначе, это идет к следующей глубине:

Live DEMO

template<
    class A,
    class B,
    template <class...> class Container,
    class... extras>
void flat_transfer(std::vector<A>& target, const Container<B, extras...>& source)
{
    for(const auto& elem : source)
    {
        if constexpr(std::is_same<A, B>::value){
            target.push_back(elem);
        }
        else{
            flat_transfer(target, elem);
        }
    }
}

Это пример использования:

std::vector<int> v{1,2,3};
std::set<std::vector<std::deque<int>>, std::greater<>> vv{{{4,5}, {6,7}, {8,9}}, {{10,11,12}, {13,14}, {15}}};

flat_transfer(v, vv);

// prints "1 2 3 10 11 12 13 14 15 4 5 6 7 8 9" 
for(const auto& i : v){
    std::cout << i << " ";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...