【问题标题】:How to write a c++ template that works for both a map and vector of pair>如何编写一个适用于一对映射和向量的 C++ 模板>
【发布时间】:2018-11-22 09:43:02
【问题描述】:

我想编写一个模板函数,它遍历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&lt;std::pair&lt;T1,T2&gt;&gt; 之类的东西,因为两个容器的迭代工作方式相同。

我尝试通过更改模板参数使容器成为模板:

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 返回功能节省自己的时间!

【问题讨论】:

    标签: c++ c++11 templates variadic-templates template-templates


    【解决方案1】:

    通常使用 c++ 你只允许任何类型,如果它不正确就让它失败。要获得 T1 和 T2,您可能会获得 value_type 的集合(存在于 std::mapstd::vector&lt;std::pair&lt;&gt;&gt; 以及其他,例如 std::set&lt;std::pair&lt;&gt;&gt;

    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&lt;const K, V&gt;。如果您需要较旧的标准支持,例如,它可以在没有 std::remove_const_t 的情况下完成。对于 c++11 你需要typename std::remove_const&lt;&gt;::type

    【讨论】:

    • 这不会为我编译,因为 T1 和 T2 的推导类型是 const
    • “您可以将它们从模板参数列表中删除(但这需要来自 c++11 或 c++14 的 auto 返回类型)”auto 不是必需的,它只是更方便(至少是 C++14)。
    • @Jarod42 c++14 是不需要的,是的(这就是为什么我写 c++11 or c++14 并留下评论在哪里为 c 写返回类型++11)。但我不知道如何在 c++98 中做到这一点(可能有一个,但与 c++98 的兼容性通常对我来说不是问题,所以我从来没有真的想过在 c++98 下修复此问题)
    • 通过使用详细的std::pair&lt;std::vector&lt;typename remove_const&lt;typename T::value_type::first_type&gt;::type&gt;, std::vector&lt;typename remove_const&lt;typename T::value_type::second_type&gt;::type&gt;&gt;(也重写remove_const)两次(在返回类型和正文中)。
    • std::remove_const_t 是 c++14,对,所以你需要使用typename std::remove_const&lt;T::value_type&gt;::typestd::remove_const 是(不相关的)类型本身
    【解决方案2】:
    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 作为一个模板参数
    • 您要求一种接受std::container of type std::pair&lt;T,U&gt; 的方法,我发布的代码适用于std::vector&lt;std::pair&lt;T,U&gt;&gt;
    • 您是否要求一种可以同时处理这两种情况的方法?
    • 问的不是我。 (我认为是的,要求的是单一方法(但我可能是错的,ofc)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-21
    • 1970-01-01
    • 1970-01-01
    • 2021-11-23
    • 2012-03-04
    • 2014-01-28
    • 1970-01-01
    相关资源
    最近更新 更多