2009-08-29 4 views
1

Angenommen, ich habe eine Funktion, um zu bestimmen, die wie folgt aussieht:Way richtige Prädikat für Templat-Typen

template <class In, class In2> 
void func(In first, In last, In2 first2); 

ich diese Funktion möchte eine andere Funktion aufrufen, die ein Prädikat akzeptiert.

template <class In, class In2> 
void func(In first, In last, In2 first2) { 
    typedef typename std::iterator_traits<In>::value_type T; 
    other_func(first, last, first2, std::less<T>()); 
} 

Aber es gibt ein Problem, was passiert, wenn In und In2 sind Iteratoren auf verschiedene Arten: Mein erster Instinkt, so etwas zu tun war? Zum Beispiel char* vs int*. Abhängig davon, welches In ist und welches In2 ist, kann das Prädikat Werte während seines Vergleichs abschneiden. Zum Beispiel, wenn In ist char* dann wird std::less<char> auch genannt werden, wenn In2 ein int* ist.

Wenn ::operator< zwei Parameter angegeben wird, ist der Compiler in der Lage die richtige Art und die Standardtyp Förderung Regeln gelten abzuleiten. Wenn Sie jedoch ein Prädikat auswählen, das an eine Funktion übergeben werden soll, besteht keine Möglichkeit, dass dies geschieht. Gibt es eine kluge Art und Weise, um herauszufinden, welche Version von std::less<> ich auf In passieren will basierte und In2?

EDIT:

Das folgende Beispiel veranschaulicht das Problem:

unsigned int x = 0x80000000; 
unsigned char y = 1; 
std::cout << std::less<unsigned char>()(x, y) << std::endl; 
std::cout << std::less<unsigned int>()(x, y) << std::endl; 

AUSGABE:

1 
0 

EDIT:

Nachdem darüber nachzudenken, was würde Ich mag in der Lage sein, so etwas zu tun:

typedef typeof(T1() < T2()) T; 
other_func(first, last, first2, std::less<T>()); 

Ich glaube, ich __typeof__ Erweiterung des gcc verwenden könnte ..., aber ich weiß nicht, dass die Idee entweder lieben. Gibt es eine Möglichkeit, diesen Nettoeffekt standardkonform zu erhalten?

Antwort

2

Ich schien mich daran zu erinnern, dass es in boost eine Eigenschaft dafür gab, aber ich kann es nach einer schnellen Suche nicht finden. Wenn Sie nicht erfolgreicher als ich sind, können Sie es selbst bauen,

template <typename T1, typename T2> 
struct least_common_promotion; 

template <> 
struct least_common_promotion<short, int> 
{ 
    typedef int type; 
}; 

aber Sie werden schon einige explizite Spezialisierungen angeben müssen. Die Boost-Bibliothek type traits kann Ihnen vielleicht dabei helfen, ihre Anzahl zu reduzieren.

Edit: Ich fühle mich dumm, solche Art von Dingen sind für den Betrieb benötigt (wo der Ergebnistyp von den Operanden Typen abhängt), aber nicht für Prädikate (wo der Ergebnistyp ist bool). Sie können einfach schreiben:

template <class T1, T2> 
struct unhomogenous_less : public std::binary_function<T1, T2, bool> 
{ 
    bool operator()(T1 const& l, T2 const& r) const 
    { return l < r; } 
}; 

... 

typedef typename std::iterator_traits<In>::value_type value_type_1; 
typedef typename std::iterator_traits<In2>::value_type value_type_2; 
other_func(first, last, first2, unhomogenous_less<value_type_1, value_type_2>()); 
+0

ja, bisher scheint eine inhomogene weniger Vorlage die Lösung zu sein. In der Tat frage ich mich, warum 'std :: less' (oder alle binary_function-Komparatoren) nicht gemacht werden, um 2 Template-Parameter zu nehmen. Scheint, dass dies die einzige Möglichkeit ist, 'std :: less' in allen Situationen wie 'operator

0

Wenn Ihre Anforderungen an den Algorithmus so sind, dass In s Werttyp ‚s value_type nicht das gleiche wie In2 sein muss‘, dann würde ich die Template-Parameter lassen, wie Sie sie haben; ansonsten sollten sie gleich sein.

Ob sie gleich oder verschieden sind, ist es bis zu dem Client Ihrer Routine der Voraussetzungen des Algorithmus zu erfüllen, die Sie berechtigt sind, zu spezifizieren. Zum Beispiel benötigen Sie könnten hier, dass die value_type von In als value_type von In2 gleich sein. Wenn das zutrifft, dann sollte die Funktion kompilieren und korrekt sein, wie der Client erwartet.

In solch einem Fall können Sie dann eine std::less<T> Instanz des value_type eines der beiden Vorlagentypen übergeben, und Sie sollten in Ordnung sein.

jedoch, wenn der Kunde diese Voraussetzung verletzt (wie im Beispiel Sie oben bieten, wo char ist nicht die gleiche wie int), dann wird es bis zu den Kunden sein, nicht wahr, die Fehler bei der Kompilierung zu korrigieren.

Achten Sie darauf, Ihr Algorithmus ist gut dokumentiert, die am wenigsten zu sagen :)

+0

Aber es ist vernünftig, dass ein Algorithmus Iteratoren zu verschiedenen, aber kompatiblen Typen akzeptieren (wie in meinem Beispiel kann ein Zeichen mit einem Int verglichen werden). Aber 'std :: less (some_int, some_char);' ist ein Fehler, aber ':: operator <(some_int, some_char)' ist nicht. –

+0

Sie können Tools wie die statische Assert-Bibliothek von Boost verwenden, um bei der Kompilierung zu bestätigen, dass die Typen identisch sind. – fbrereto

+0

Ich möchte ihnen ** nicht ** erlauben, gleich zu sein. Grundsätzlich würde ich gerne einen geeigneten Weg wählen, ein Prädikat so zu wählen, dass es sich wie der ':: operator <' verhält (was nach Möglichkeit den kleineren Typ fördert). –

0

Unter SGIs alte Implementierung von std::equal als Beispiel, STL-Algorithmen verarbeiten diese Art von Situation von zwei Versionen des gleichen Algorithmus mit: ein dass nutzt den intrinsischen < Operator, der Compiler bei der Kompilierung folgert, und eine, die ein benutzerdefiniertes binäres Prädikat nimmt, damit die Benutzer alle Typen verwenden können, würden sie bevorzugen:

template <class _InputIter1, class _InputIter2> 
inline bool equal(_InputIter1 __first1, _InputIter1 __last1, 
        _InputIter2 __first2) { 
    __STL_REQUIRES(_InputIter1, _InputIterator); 
    __STL_REQUIRES(_InputIter2, _InputIterator); 
    __STL_REQUIRES(typename iterator_traits<_InputIter1>::value_type, 
       _EqualityComparable); 
    __STL_REQUIRES(typename iterator_traits<_InputIter2>::value_type, 
       _EqualityComparable); 
    for (; __first1 != __last1; ++__first1, ++__first2) 
    if (*__first1 != *__first2) 
     return false; 
    return true; 
} 

template <class _InputIter1, class _InputIter2, class _BinaryPredicate> 
inline bool equal(_InputIter1 __first1, _InputIter1 __last1, 
        _InputIter2 __first2, _BinaryPredicate __binary_pred) { 
    __STL_REQUIRES(_InputIter1, _InputIterator); 
    __STL_REQUIRES(_InputIter2, _InputIterator); 
    for (; __first1 != __last1; ++__first1, ++__first2) 
    if (!__binary_pred(*__first1, *__first2)) 
     return false; 
    return true; 
} 

(Hinweis: Old SGI STL-Code genommen von here.)