提问人:bobeff 提问时间:5/23/2023 最后编辑:bobeff 更新时间:5/23/2023 访问量:74
如何消除“erase_all_if”函数模板的重载歧义?
How to disambiguate the overloads of the `erase_all_if` function template?
问:
我有两个函数模板的实现。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;
}
第一种仅适用于容器,因为它需要一个随机访问迭代器,第二个适用于所有容器,但对于连续容器来说效率较低,并且适用于基于节点的容器。如何消除这两个版本的歧义?我目前使用的 C++ 标准是 C++17。std::vector
答:
5赞
HolyBlackCat
5/23/2023
#1
您在这里不需要 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));
}
6赞
fabian
5/23/2023
#2
此处不需要 SFINAE。您可以在此处使用“添加帮助程序”模板,该模板接收中间器类别作为附加参数。您甚至可以通过一些调整使用 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);
}
}
评论