Как написать шаблон c ++, который работает как для карты, так и для вектора пары> - PullRequest
0 голосов
/ 13 июня 2018

Я хотел бы написать шаблонную функцию, которая перебирает контейнер std::pair и возвращает шаблонное значение с обоими типами в паре.Я получил это, чтобы работать для std::map следующим образом:

template <typename T1, typename T2>
std::pair<std::vector<T1>, std::vector<T2>> unzip(const std::map<T1,T2>& zipped)
{
    auto unzipped = std::make_pair(std::vector<T1>(), std::vector<T2>());
    for (auto& one_two : zipped)
    {
        unzipped.first.push_back(one_two.first);
        unzipped.second.push_back(one_two.second);
    }
    return unzipped;
}

Это отлично работает, но ограничивает контейнер до std::map.Я хотел бы, чтобы это работало для чего-то вроде std::vector<std::pair<T1,T2>>, поскольку итерация для обоих контейнеров работает одинаково.

Я пытался сделать контейнер шаблоном, изменивАргументы шаблона:

template <typename T1, typename T2, template<typename ... Types> class Container>
std::pair<std::vector<T1>, std::vector<T2>> unzip(const Container<T1,T2>& zipped)
{
//do stuff and return
}

, но в этом случае вычет не выполняется, если не используется std::map, поскольку std::vector зависит только от одного типа.Затем я попытался стать более креативным, но компилятор жаловался еще больше:

template <typename PairContainerType>
std::pair<std::vector<typename PairContainerType::value_type::first_type>,
          std::vector<typename PairContainerType::value_type::second_type>>
unzip(const PairContainerType& zipped)
{
typedef typename PairContainerType::value_type::first_type T1;
typedef typename PairContainerType::value_type::second_type T2;
//do the same stuff and return
}

Я думаю, что то, что я пытаюсь сделать, должно быть возможным, но я в растерянности.Я использую c++11, если это имеет значение, хотя, если то, что я хочу, доступно в будущих версиях, я все еще заинтересован в этих решениях.Спасибо.


Обновление: благодаря RiaD я получил следующее для работы с c ++ 11:

template <typename PairContainerType,
          typename T1 = typename std::remove_const<typename PairContainerType::value_type::first_type>::type,
          typename T2 = typename std::remove_const<typename PairContainerType::value_type::second_type>::type>
std::pair<std::vector<T1>, std::vector<T2>> unzip(const PairContainerType& zipped)
{
//do stuff and return
}

Обратите внимание, что он немного отличается от принятого ответа, так как ониспользует std::remove_const вместо std::remove_const_t и в конце необходимо добавить ::type.

RiaD также указывает, что типы шаблонов T1 и T2 могут быть переопределены любым, кто делаетвызов.Как предполагает Jarod42, это может быть смягчено с помощью некоторой дополнительной типизации, что оставляет меня в окончательном решении c ++ 11:

template <typename PairContainerType>
std::pair<std::vector<typename std::remove_const<typename PairContainerType::value_type::first_type>::type>, 
          std::vector<typename std::remove_const<typename PairContainerType::value_type::second_type>::type>>
unzip(const PairContainerType& zipped)
{
    auto unzipped = std::make_pair(
        std::vector<typename std::remove_const<typename PairContainerType::value_type::first_type>::type>(), 
        std::vector<typename std::remove_const<typename PairContainerType::value_type::second_type>::type>());

    for (const auto& one_two : zipped)
    {
        unzipped.first.push_back(one_two.first);
        unzipped.second.push_back(one_two.second);
    }
    return unzipped;
}

В целом, c ++ 14 и c ++ 17 выглядят довольно привлекательно,Я мог бы сэкономить время с помощью функции auto return!

Ответы [ 2 ]

0 голосов
/ 13 июня 2018

Обычно с c ++ вы просто разрешаете любой тип и допускаете сбой, если он не правильный.Чтобы получить T1 и T2, вы можете получить value_type коллекции (существует как в std::map и std::vector<std::pair<>>, так и в других, например, std::set<std::pair<>>)

template <typename T, typename T1 = std::remove_const_t<typename T::value_type::first_type>, typename T2 = std::remove_const_t<typename T ::value_type::second_type>>
std::pair<std::vector<T1>, std::vector<T2>> unzip(const T& zipped)
{
    auto unzipped = std::make_pair(std::vector<T1>(), std::vector<T2>());
    for (auto& one_two : zipped)
    {
        unzipped.first.push_back(one_two.first);
        unzipped.second.push_back(one_two.second);
    }
    return unzipped;
}

У него (небольшой) недостаток, что кто-то может форсировать T1 иТ2, как они подходят.Вы можете удалить их из списка аргументов шаблона.

template <typename T>
auto /* or return type (but it will require copy-pasting) before c++14*/ 
unzip(const T& zipped)
{
    using T1 = std::remove_const_t<typename T::value_type::first_type>; //may need to remove const
    using T2 = std::remove_const_t<typename T::value_type::second_type>; 
    // impl
}

Удаление const необходимо, поскольку тип_значения std::map равен std::pair<const K, V>.это может быть сделано без std::remove_const_t, если вам нужна более старая стандартная поддержка, например, для c ++ 11 вам нужно typename std::remove_const<>::type

0 голосов
/ 13 июня 2018
template<class T>
    struct zipper {
    using first_t = remove_cv_t<typename T::value_type::first_type>;
    using second_t = typename T::value_type::second_type;

    template <class container>
    static std::pair<std::vector<first_t>, std::vector<second_t>> unzip(const container& zipped)
    {
        auto unzipped = std::make_pair(std::vector<first_t>(), std::vector<second_t>());
        for (auto& one_two : zipped)
        {
            unzipped.first.emplace_back(one_two.first);
            unzipped.second.emplace_back(one_two.second);
        }
        return unzipped;
    }
};


template<class container>
auto unzip(const container& cont)  {
    return zipper<container>::unzip(cont);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...