Im Moment sind die Überladungen mehrdeutig, weil Sie sie nicht eingeschränkt haben. Wie soll der Compiler wissen, dass der erste nicht für Iteratoren verwendet werden sollte?
Die Antwort ist einfach, Sie müssen es sagen. Zum Glück definiert der Standard std::iterator_traits
, die Sie für alle Arten von Informationen über den Iterator-Typ abfragen können, den Sie ihm übergeben haben - solange es ein Iterator ist.
Der letzte Punkt hier ist der Schlüssel: Wenn std::iterator_tags<T>
keine verschachtelte typedef iterator_category
hat, dann ist T
kein Iterator. Daher können wir member detection using void_t
verwenden, um eine benutzerdefinierte Eigenschaft zu definieren, die überprüft, ob ein bestimmter Typ ein Iterator ist.
Im Code sieht es wie folgt aus:
// 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;
Hier habe ich auch die constexpr für Bequemlichkeit variable is_iterator_v
definiert.
Wir können diese Eigenschaft in Kombination mit std::enable_if
verwenden, um selektiv eine der beiden zuvor mehrdeutigen Überladungen basierend darauf, ob ein Typ T
ein Iterator ist oder nicht, zu deaktivieren. Um die Template-Deklaration template <typename T>
einfach durch template <typename T, std::enable_if_t<is_iterator_v<T>, int> = 0>
bzw. template <typename T, std::enable_if<!is_iterator_v<T>, int> = 0>
zu ersetzen.
Putting alles zusammen, sollte der Code wie folgt aussehen dies:
// 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;
}
Sie können auch das Arbeitsbeispiel auf wandbox finden.
Ja, es ist möglich. Hast du es versucht? Wo sind Sie in Schwierigkeiten geraten? – aschepler
Solange die Funktionsaufrufe nicht mit Funktionsaufrufen mit dem gleichen Namen interferieren, sollten Sie keine Probleme mit dem Überladen haben. –
Sie können keine Funktion implementieren, die folgendermaßen aussieht: int max (const int * p); es gibt keine Möglichkeit zu sagen, wie viele Ints 'p' auf etwas verweisen, also würden Sie etwas wie 'int max (const int * begin, const int * end);' und ähnlich für Iteratoren haben wollen. –