I'd like to write a template function that iterates over a container of std::pair
and returns a templated value with both types in the pair. I've gotten this to work for std::map
as follows:
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;
}
This works fine, but it restricts the container to be std::map
. What I'd like to accomplish is to have this also work for something like std::vector<std::pair<T1,T2>>
, since the iteration over both containers works the same way.
I've tried to make the container a template by changing the template arguments:
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
}
but in this case, the deduction fails if not using std::map
because std::vector
depends on only a single type. Then I tried getting more creative, but the compiler just complained even more:
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
}
I think what I'm trying to do should be possible, but I'm at a loss. I'm using c++11
if that matters, though if what I want is available in future versions I'm still interested in those solutions. Thank you.
Update: Thanks to RiaD I got the following to work using 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
}
Note that it's slightly different than the accepted answer, since it uses std::remove_const
instead of std::remove_const_t
and needs to have ::type
added at the end.
RiaD also points out that the template types T1
and T2
can be overridden by whoever is making the call. As suggested by Jarod42, this can be mitigated with some extra typing, which leaves me in the final c++11 solution:
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;
}
Overall, c++14 and c++17 are looking pretty appealing. I could have saved myself time with the auto
return feature!