Bonjour à tous,

J'ai écrit un foncteur générique qui permet de créer facilement un foncteur de comparaison à utiliser dans std::set ou std::map en écrivant le moins de code possible :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
template<typename T, typename I, I (T::*F)() const>
struct mem_fun_comp_t {
    bool operator () (const T& t1, const T& t2) const {
        return (t1.*F)() < (t2.*F)();
    }
};
On l'utilise alors comme suit :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
// classe de test
struct test {
    const std::string name_;
    const int         id_;
 
    const std::string& name() const { return name_; }
    int                id()   const { return id_; }
};
 
// 's1' est trié selon 'name'
std::set<test, mem_fun_comp_t<test, const std::string&, &test::name>> s1;
// 's2' est trié selon 'id'
std::set<test, mem_fun_comp_t<test, int, &test::id>> s2;
Le problème est qu'on doit fournir deux informations redondantes en paramètres de mem_fun_comp_t : la classe test d'une part, et le type de la valeur comparée (const std::string& ou int) d'autre part, qui font toute deux partie de la signature de la fonction membre passée en troisième paramètre.

Je cherche donc un moyen d'éviter cette lourdeur. La seule solution que j'ai trouvée jusqu'à présent, et que je n'aime pas trop, est de recourir à une macro :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
// extraire la classe qui possède la fonction membre
template<typename T, typename I>
T mem_fun_owner(I (T::*f)() const) { return std::declval<T>(); }
 
// extraire le type de la valeur comparée
template<typename T, typename I>
I mem_fun_return_type(I (T::*f)() const) { return std::declval<I>(); }
 
// la macro
#define mem_fun_comp(f) mem_fun_comp_t<decltype(mem_fun_owner(f)), decltype(mem_fun_return_type(f)), f>
On l'utilise alors comme ceci :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
// 's1' est trié selon 'name'
std::set<test, mem_fun_comp(&test::name)> s1;
// 's2' est trié selon 'id'
std::set<test, mem_fun_comp(&test::id)> s2;
Voyez-vous une autre solution qui n'utilise pas de macro ?

HS : ce qui est sympa, c'est qu'on peut écrire quelque chose de très similaire pour trier selon une variable membre, sans avoir besoin de définir un accesseur (à condition que cette variable soit publique, bien entendu) :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template<typename T, typename I, I T::*V>
struct mem_var_comp_t {
    bool operator () (const T& t1, const T& t2) const {
        return t1.*V < t2.*V;
    }
};
 
template<typename T, typename I>
T mem_var_owner(I T::* v) { return std::declval<T>(); }
 
template<typename T, typename I>
I mem_var_type(I T::* v) { return std::declval<I>(); }
 
#define mem_var_comp(v) mem_var_comp_t<decltype(mem_var_owner(v)), decltype(mem_var_type(v)), v>
 
// 's1' est trié selon 'name'
std::set<test, mem_var_comp(&test::name_)> s1;
// 's2' est trié selon 'id'
std::set<test, mem_var_comp(&test::id_)> s2;
Ça m'a fait découvrir pour la première fois une utilité aux pointeurs sur variables membres