2009-08-29 4 views
1

templated Supposons que j'ai une fonction qui ressemble à ceci:façon de déterminer prédicat approprié pour les types

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

Je voudrais cette fonction pour appeler une autre fonction qui accepte un prédicat. Mon instinct initial était de faire quelque chose comme ceci:

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>()); 
} 

Mais il y a un problème, si In et In2 sont itérateurs à différents types? Par exemple, char* par rapport à int*. Selon ce qui est In et In2, le prédicat peut tronquer des valeurs lors de sa comparaison. Par exemple, si In est char*, alors std::less<char> sera appelé même si In2 est int*.

Lorsque ::operator< reçoit deux paramètres, le compilateur peut déduire le type correct et les règles de promotion de type standard s'appliquent. Cependant, lors de la sélection d'un prédicat à passer à une fonction, il n'y a aucune possibilité de le faire. Y at-il un moyen intelligent de comprendre quelle version de std::less<> je veux passer en fonction de In et In2?

EDIT:

L'exemple suivant illustre le problème:

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; 

Affichera:

1 
0 

EDIT:

Après avoir réfléchi à ce sujet, ce que je voudrais vraiment est d'être capable de faire quelque chose comme ceci:

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

Je suppose que je pourrais utiliser __typeof__ extension gcc ..., mais je n'aime pas cette idée non plus. Un moyen d'obtenir cet effet net d'une manière conforme conforme?

Répondre

2

J'ai semblé me ​​rappeler qu'il y avait un trait pour ceci dans boost, mais je ne peux pas le trouver après une recherche rapide. Si vous n'êtes pas plus de succès que moi, vous pouvez construire vous-même,

template <typename T1, typename T2> 
struct least_common_promotion; 

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

mais vous devrez spécifier un bon nombre de spécialisations explicites. La bibliothèque de boost type traits peut peut-être vous aider à réduire leur nombre. Editer: Je me sens stupide, ce genre de choses est nécessaire pour le fonctionnement (où le type de résultat dépend des types d'opérandes), mais pas pour les prédicats (où le type de résultat est bool). Vous pouvez simplement écrire:

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

oui, jusqu'à présent, un modèle moins homogène semble être la solution. En fait, je me demande pourquoi 'std :: less' (ou tous les comparateurs de fonctions binaires) ne sont pas faits pour prendre 2 paramètres de modèle. On dirait que c'est la seule façon de faire que 'std :: less' se comporte comme un opérateur

0

Si vos exigences sur l'algorithme sont telles que In « s value_type ne doit pas être le même que In2 » type de valeur s, alors je laisserais les paramètres du modèle que vous les avez; sinon, ils devraient être les mêmes.

Qu'ils soient identiques ou différents, il appartient au client de votre routine de répondre aux conditions préalables de l'algorithme, que vous êtes autorisé à spécifier. Par exemple, ici vous pouvez exiger que le value_type de In soit le même que le value_type de In2. Si cela est vrai, alors la fonction devrait compiler et être correcte comme le client l'attend.

Dans un tel cas, vous pouvez passer une instance std::less<T> du type value_type de l'un ou l'autre type de gabarit, et cela devrait fonctionner.

Cependant, si le client viole cette condition préalable (comme dans l'exemple que vous fournissez ci-dessus où char n'est pas la même chose que int), alors il appartiendra au client, pas, pour corriger l'erreur de compilation.

Assurez-vous que votre algorithme est bien documenté, pour le moins :)

+0

Mais il est raisonnable qu'un algorithme accepte les itérateurs à des types différents mais compatibles (comme dans mon exemple, un char peut être comparé à un int). Mais 'std :: less (some_int, some_char);' est une erreur, mais ':: operator <(some_int, some_char)' ne l'est pas. –

+0

Vous pouvez utiliser des outils comme la bibliothèque assert statique de Boost pour affirmer à la compilation que les types sont identiques. – fbrereto

+0

Je veux leur permettre ** pas ** d'être la même chose. Fondamentalement, je voudrais un moyen approprié de choisir un prédicat tel qu'il agit comme le ':: operator <' (qui favorisera le plus petit type si possible). –

0

Prendre ancienne mise en œuvre de SGI de std::equal à titre d'exemple, les algorithmes de STL gérer ce genre de situation en ayant deux versions du même algorithme: un qui utilise l'opérateur < intrinsèque que le compilateur déduit au moment de la compilation, et qui prend un prédicat binaire défini par l'utilisateur afin que l'utilisateur peut utiliser tous les types qu'ils préféreriez:

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; 
} 

(note: Old SGI code STL prise de here.)

Questions connexes