现在重载是模棱两可的,因为你没有限制它们。编译器应该如何知道第一个不应该用于迭代器?
答案很简单,你必须告诉它。幸运的是,该标准定义了std::iterator_traits,您可以查询有关您传递给它的迭代器类型的各种信息——只要它是一个迭代器。
最后一点很关键:如果std::iterator_tags<T> 没有嵌套的typedef iterator_category,那么T 就不是迭代器。因此我们可以使用member detection using void_t 来定义一个自定义特征来检查给定类型是否是一个迭代器。
在代码中,它看起来像这样:
// C++17 void_t
template <typename...>
using void_t = void;
template <typename T, typename = void>
struct is_iterator : std::false_type {
};
template <typename T>
struct is_iterator<T, void_t<typename std::iterator_traits<T>::iterator_category>> : std::true_type {
};
template <typename T>
constexpr auto is_iterator_v = is_iterator<T>::value;
为方便起见,这里我还定义了 constexpr 变量 is_iterator_v。
我们可以将此特征与std::enable_if 结合使用,根据T 类型是否为迭代器,有选择地禁用两个先前模棱两可的重载之一。为此,只需将模板声明 template <typename T> 分别替换为 template <typename T, std::enable_if_t<is_iterator_v<T>, int> = 0> 和 template <typename T, std::enable_if<!is_iterator_v<T>, int> = 0>。
将所有内容放在一起,代码应该如下所示:
// C++17 void_t
template <typename...>
using void_t = void;
template <typename T, typename = void>
struct is_iterator : std::false_type {
};
template <typename T>
struct is_iterator<T, void_t<typename std::iterator_traits<T>::iterator_category>> : std::true_type {
};
template <typename T>
constexpr auto is_iterator_v = is_iterator<T>::value;
template<typename T, std::enable_if_t<!is_iterator_v<T>, int> = 0>
constexpr T maximum(T a, T b)
{
return (a > b) ? a : b;
}
template<typename Iter, std::enable_if_t<is_iterator_v<Iter>, int> = 0>
constexpr Iter maximum(Iter begin, Iter end)
{
if(begin == end) {
return end;
}
auto max = begin;
for (; begin != end; ++begin) {
if(*begin > *max) {
max = begin;
}
}
return max;
}
您还可以在 wandbox 上找到工作示例。