【发布时间】:2017-09-11 15:37:45
【问题描述】:
我正在尝试编写一个通用的erase_if 方法,该方法可用于任何容器类型以删除给定谓词的元素。如果容器允许,它应该使用erase-remove-idiom,或者循环通过容器并调用erase 方法。我也只想提供容器本身,而不是单独提供 begin 和 end 迭代器。由方法处理。
但是,我无法让元模板通过 SFINAE 区分这两种情况。我正在尝试检查方法std::remove_if(或std::remove)是否可以为给定类型很好地定义,但vector 和map 的值都是true(在这种情况下代码不会编译) 或false 为他们两个。我对模板元编程很陌生,所以我缺少什么吗?或者也许还有其他更好的解决方案?
下面是我的示例代码:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <map>
#include <type_traits>
#include <vector>
namespace my_std
{
using std::begin;
using std::end;
namespace detail
{
template <typename T>
// this is false for both vector and map
auto is_remove_compatible(int) -> decltype(std::remove_if(begin(std::declval<T&>()), end(std::declval<T&>()),
[](auto&&) { return false; }),
std::true_type{});
// this is true for both vector and map
// auto is_remove_compatible(int)
// -> decltype(std::remove(begin(std::declval<T&>()), end(std::declval<T&>()), std::declval<T::value_type&>()),
// std::true_type{});
template <typename T>
auto is_remove_compatible(...) -> std::false_type;
}
template <typename T>
using is_remove_compatible = decltype(detail::is_remove_compatible<T>(0));
template <typename T>
constexpr bool is_remove_compatible_v = is_remove_compatible<T>::value;
template <typename Cont, typename Pred>
std::enable_if_t<is_remove_compatible_v<Cont>> erase_if(Cont& container, Pred pred)
{
std::cout << "Using erase-remove\n";
container.erase(std::remove_if(begin(container), end(container), pred), end(container));
}
template <typename Cont, typename Pred>
std::enable_if_t<!is_remove_compatible_v<Cont>> erase_if(Cont& container, Pred pred)
{
std::cout << "Using loop\n";
for (auto it = begin(container); it != end(container);)
{
if (pred(*it))
it = container.erase(it);
else
++it;
}
}
}
template <typename T>
std::ostream& operator<<(std::ostream& out, std::vector<T> const& v)
{
if (!v.empty())
{
out << '[';
std::copy(v.begin(), v.end(), std::ostream_iterator<T>(out, ", "));
out << "\b\b]";
}
return out;
}
template <typename K, typename V>
std::ostream& operator<<(std::ostream& out, std::map<K, V> const& v)
{
out << '[';
for (auto const& p : v)
out << '{' << p.first << ", " << p.second << "}, ";
out << "\b\b]";
return out;
}
int main(int argc, int argv[])
{
auto vp = my_std::is_remove_compatible_v<std::vector<int>>;
auto mp = my_std::is_remove_compatible_v<std::map<int, int>>;
std::cout << vp << ' ' << mp << '\n';
std::vector<int> v = {1, 2, 3, 4, 5};
auto v2 = v;
my_std::erase_if(v2, [](auto&& x) { return x % 2 == 0; });
std::map<int, int> m{{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}};
auto m2 = m;
my_std::erase_if(m2, [](auto&& x) { return x.first % 2 == 0; });
std::cout << v << " || " << v2 << '\n';
std::cout << m << " || " << m2 << '\n';
std::cin.ignore();
}
【问题讨论】:
-
[Off Topic] 如果您不希望它与所有标准容器一起使用,那么您需要为
std::forward_list设置一个特殊的重载,因为它没有erase。相反,它有erase_after。 -
使用的方法对 SFINAE 不友好 :-(,但会引发硬错误。
-
更容易(部分)将
is_remove_compatible<container<T>>直接用于不同的支持容器。 -
你用的是什么编译器? gcc & clang 无法编译您的示例,因为在
decltype表达式和int argv[]中使用了 lambda。无论如何,std 库中没有太多容器,因此您可以为每个容器创建一个erase_if重载。 -
@Praetorian 我正在使用 VC2015,它编译得很好。我不会为每种容器类型创建重载,因为我还希望它适用于不属于标准库的自定义容器。
标签: c++ c++14 template-meta-programming