c++ 如何消除`erase_all_if`函数模板重载的歧义?

hc2pp10m  于 2023-05-23  发布在  其他
关注(0)|答案(2)|浏览(126)

我有两个erase_all_if函数模板的实现。

template <typename Container, typename Pred>
typename Container::size_type erase_all_if(Container& c, Pred&& pred)
{
    auto newend = std::remove_if(c.begin(), c.end(), std::forward<Pred>(pred));
    auto ret = c.end() - newend;
    c.erase(newend, c.end());
    return ret;
}

template <typename Container, typename Pred>
auto erase_all_if(Container& c, Pred&& pred) {
    typename Container::size_type removed = 0;
    for (auto it = c.begin(); it != c.end();) {
        if (pred(*it)) {
            it = c.erase(it);
            ++removed;
        }
        else {
            ++it;
        }
    }
    return removed;
}

第一个只适用于std::vector这样的容器,因为它需要一个随机访问迭代器,第二个适用于所有容器,但对于连续容器来说效率更低,适合基于节点的容器。如何消除这两个版本的歧义?我目前使用的C标准是C17。

dgsult0t

dgsult0t1#

你不需要SFINAE在这里。我会把这两个功能合二为一,然后

if constexpr (std::is_base_of_v<std::random_access_iterator_tag,
    typename std::iterator_traits<decltype(c.begin())>::iterator_category>)
{
    // ...
}
else
{
    // ...
}

也可以使用标记分派:(注意迭代器类别是相互继承的,所以这很好地处理了类别之间的回退)

template <typename Container, typename Pred>
typename Container::size_type erase_all_if_low(std::random_access_iterator_tag, Container& c, Pred&& pred)
{
    // ...
}

template <typename Container, typename Pred>
typename Container::size_type erase_all_if_low(std::forward_iterator_tag, Container& c, Pred&& pred)
{
    // ...
}

template <typename Container, typename Pred>
auto erase_all_if(Container& c, Pred &&pred)
{
    using category = typename std::iterator_traits<decltype(c.begin())>::iterator_category;
    return erase_all_if_low(category{}, c, std::forward<Pred>(pred));
}
xdyibdwo

xdyibdwo2#

这里不需要SFINAE。你可以在这里使用添加一个辅助模板来接收interator类别作为附加参数。你甚至可以用C++11做一些调整。

// helper templates with overloads taking a parameter indicating info about the iterator category
namespace impl
{

    template<class T>
    constexpr typename std::iterator_traits<decltype(std::declval<T>().begin())>::iterator_category ContainerIteratorCategory_v {};

    template <typename Container, typename Pred>
    typename Container::size_type erase_all_if(std::random_access_iterator_tag, Container& c, Pred&& pred)
    {
        auto newend = std::remove_if(c.begin(), c.end(), std::forward<Pred>(pred));
        auto ret = c.end() - newend;
        c.erase(newend, c.end());
        return ret;
    }

    template <typename Container, typename Pred>
    auto erase_all_if(std::input_iterator_tag, Container& c, Pred&& pred) {
        typename Container::size_type removed = 0;
        for (auto it = c.begin(); it != c.end();) {
            if (pred(*it)) {
                it = c.erase(it);
                ++removed;
            }
            else {
                ++it;
            }
        }
        return removed;
    }
}

template <typename Container, typename Pred>
auto erase_all_if(Container& c, Pred&& pred) {
    return impl::erase_all_if(impl::ContainerIteratorCategory_v<Container>, c, std::forward<Pred>(pred));
}

template<class Container>
void Print(Container const& c)
{
    for (auto& e : c)
    {
        std::cout << e << '\n';
    }
}

int main()
{
    {
        std::vector<int> v{ 1, 2, 3, 4, 5 };
        std::cout << "std::vector<int>: " << erase_all_if(v, [](int i) { return (i % 2) == 0; }) << '\n';
        Print(v);
    }
    {
        std::list<int> l{6, 7, 8, 9, 10};
        std::cout << "std::list<int>: " << erase_all_if(l, [](int i) { return (i % 2) == 0; }) << '\n';
        Print(l);
    }
}

相关问题