Nous avons vu comment paramètrer la fonction simple_sort
par un opérateur de comparaison. Cependant, l'opérateur de comparaison est un opérateur qui expose une méthode statique
. Nous ne pouvons pas choisir un opérateur de conversion dynamique, ie. dont nous pouvons au moment de l'exécution décider de l'ordre, si c'est un tri ascendant ou descendant. Nous pourrions imagine une telle classe.
template<typename T>
struct greater_or_lower_traits
{
private:
bool m_isAscending;
public:
greater_or_lower_traits():
m_isAscending(false) {}
explicit greater_or_lower_traits(bool isAscending):
m_isAscending(isAscending) {}
set(bool isAscending) { m_isAscending = isAscending; }
get() const { return m_isAscending ; }
bool is_greater(T aValue, T anotherValue)
{
if(!m_isAscending)
return aValue < anotherValue;
return aValue > anotherValue;
}
};
Sauf que dans ce cas, il est nécessaire d'avoir un champ. Donc la méthode is_greater
ne peut pas être une méthode de classe.
Pour ce faire, nous modifions le contrat relatif à la classe traits
définissant l'ordre sur les valeurs de type traits
comme suit :
la méthode is_greater
n'est plus une méthode de classe.
l'objet expose un constructeur par défaut.
l'objet supporte la recopie.
Nous devons modifier en conséquence les classes greater_traits
et lower_traits
pour transformer la méthode statique
en méthode standard :
template<T>
struct greater_traits
{
public bool is_greater(T aValue, T anotherValue) const
{
return aValue > anotherValue;
}
};
template<T>
struct lower_traits
{
public bool is_lower(T aValue, T anotherValue) const
{
return aValue < anotherValue;
}
};
Avec ces deux conditions, il est possibles de redéfinir la définition de la fonction simple_sort
comme suit :
namespace generic
{
...
template<typename T, typename sortTraits = greater_traits<T>>
void simple_sort(std::vector<T>& theValues,
sortTraits traits = sortTraits())
{
int length = theValues.size();
for(int i = 0; i < length-1; i ++)
{
for(int j= i+1; j < length; j++)
{
if(traits.is_greater(theValues[i], theValues[j]))
std::swap(theValues[i], theValues[j]);
}
}
}
...
}
Nous ajoutons un paramètre supplémentaire à la fonction simple_sort
qui est le paramètre traits
. Ce paramètre a pour type sortTraits
et correspond normalement à une instance d'une classe traits qui fournit la méthode is_greater
tel que définit précédemment. Cependant, il n'est pas nécessaire de passer ce paramètre quand nous utilisons les classes traits greater_traits
ou lower_traits
. Dans ce cas, nous affectons à ce paramètre l'objet de type sortTraits
qui est initialisé par le constructeur par défaut.
std::vector<int> vector;
...
simple_sort(vector);
Dans ce cas, quand le compilateur compile le code simple_sort
, il peut inférer que :
T dénote le type entier int
,
aucun argument n'est passé pour le paramètre traits
, le type n'est pas résolu.
Il reste donc à résoudre le type sortTraits
.
aucun type n'est passé en paramètre à la fonction simple_sort
un type par défaut est spécifié pour le type sortTraits
. Dans ce cas, le compilateur affecte à sortTraits
le type par défaut, c'est-à-dire greater_traits<T>
avec T==int
qui se transforme en greater_traits<int>
.
Une fois le type sortTraits
inféré, il ne reste plus qu'à affecter à traits
la valeur par défaut. Cette valeur par défaut est défini comme étant égal une instance d'objet de type sortTraits
initialisé par son constructeur par défaut : sortTraits()
. Ce qui dans le cas correspond à greater_traits<int>()
. Le compilateur génère donc l'appel suivant :
std::vector<int> vector;
...
simple_sort<int, greater_traits<int>)(vector, greater_traits<int>());
Considérons désormais l'appel suivant:
std::vector<int> vector;
...
simple_sort<int, lower_traits<int>>(vector);
Dans ce cas, quand le compilateur compile le code simple_sort
, il peut inférer que :
T dénote le type entier int
,
aucun argument n'est passé pour le paramètre traits
, le type n'est pas résolu.
Il reste donc à résoudre le type sortTraits
.
Une fois le type sortTraits
inféré, il ne reste plus qu'à affecter à traits
la valeur par défaut. Cette valeur par défaut est défini comme étant égal une instance d'objet de type sortTraits
initialisé par son constructeur par défaut : sortTraits()
. Ce qui dans le cas correspond à lower_traits<int>()
. Le compilateur génère donc l'appel suivant :
std::vector<int> vector;
...
simple_sort<int, lower_traits<int>>(vector, lower_traits<int>());
Enfin, si je souhaite effectuer un appel en passant une instance de l'objet greater_or_lower_traits
, il suffit d'écrire le code suivant :
std::vector<int> vector;
greater_or_lower_traits traits(true);
simple_sort(vector, traits);
traits.set(false);
simple_sort(vector, traits);
Mais il est tout à fait possible d'écrire aussi :
std::vector<int> vector;
simple_sort<int, greater_or_lower_traits <int>>(vector);
qui deviendra au moment de la compilation :
std::vector<int> vector;
simple_sort<int, greater_or_lower_traits <int>>(vector,
greater_or_lower_traits<int>());