如何编写一个适用于pair<vector<int>,string>的map和vector的C++模板?

10

我想编写一个模板函数,用于迭代包含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还指出,调用者可以重写模板类型T1T2。如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返回特性来节省时间!

2个回答

10

通常在C++中,您只允许任何类型,并在其不正确时使其失败。要获取T1和T2,您可以获取集合的value_type(存在于std::mapstd::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和T2设置为它们所适合的值。您可以将它们从模板参数列表中移除。

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的value_type是std::pair<const K, V>。如果您需要旧标准支持,例如c++11,则可以在不使用std::remove_const_t的情况下完成,需要使用typename std::remove_const<>::type


这对我来说无法编译,因为T1和T2的推导类型是const。 - jwimberley
你可以从模板参数列表中删除它们(但这将需要C++11或C++14的auto返回类型)。auto并非必需,只是更加方便(至少对于C++14而言)。 - Jarod42
@Jarod42 不需要使用c++14,是的(这就是为什么我写了c++11 c++14,并在其中留下了关于c++11返回类型的注释)。但我不知道如何在c++98中实现这一点(可能有一种方法,但与c++98的兼容性通常不是我的问题,所以我从未真正考虑过在c++98下修复这个问题的方法)。 - RiaD
1
通过使用冗长的 std::pair<std::vector<typename remove_const<typename T::value_type::first_type>::type>, std::vector<typename remove_const<typename T::value_type::second_type>::type>>(同时重写 remove_const)两次(在返回类型和主体中)。 - Jarod42
1
std::remove_const_t 是 C++14 的,所以您需要使用 typename std::remove_const<T::value_type>::typestd::remove_const 本身是(无关的)类型。 - RiaD
显示剩余3条评论

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

std::map不能将std::pair作为一个模板参数。 - RiaD
你要求一个接受std::pair<T,U>类型的std::container的方法,我发布的代码适用于std::vector<std::pair<T,U>> - Joseph Franciscus
你是在问是否需要一个单一的方法来处理这两种情况吗? - Joseph Franciscus
不是我在问。 (我认为是的,单个方法就是所要求的(但我可能错了)) - RiaD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接