Que sont les comparateurs transparents?

En C ++ 14, les conteneurs associatifs semblent avoir changé depuis C ++ 11 – [associative.reqmts] / 13 indique:

Les modèles de fonction membre find , count , lower_bound , upper_bound et equal_range ne doivent pas participer à la résolution de la surcharge, sauf si le type Compare::is_transparent existe.

Quel est le but de rendre un comparateur “transparent”?

C ++ 14 fournit également des modèles de bibliothèque comme ceci:

 template  struct less { constexpr bool operator()(const T& x, const T& y) const; typedef T first_argument_type; typedef T second_argument_type; typedef bool result_type; }; template  struct less { template  auto operator()(T&& t, U&& u) const -> decltype(std::forward(t) < std::forward(u)); typedef *unspecified* is_transparent; }; 

Ainsi, par exemple, std::set<T, std::less> n’aurait pas de comparateur transparent, mais std::set<T, std::less> aurait un.

Quel problème cela résout-il et cela change-t-il le fonctionnement des conteneurs standard? Par exemple, les parameters de modèle de std::set sont toujours Key, Compare = std::less, ... , ainsi le jeu par défaut perd-il ses membres find , count , etc.?

Quel problème cela résout-il?

Voir la réponse de Dietmar et Remyabel.

et cela change-t-il le fonctionnement des conteneurs standard?

Non, pas par défaut

Les surcharges du nouveau modèle de fonction membre de find vous permettent d’utiliser un type comparable à la clé du conteneur, au lieu d’utiliser le type de clé lui-même. Voir N3465 par Joaquín Mª López Muñoz pour la justification et une proposition détaillée, soigneusement écrite pour append cette fonctionnalité.

Lors de la réunion de Bristol, le LWG a convenu que la fonction de recherche hétérogène était utile et souhaitable, mais nous ne pouvions pas être sûrs que la proposition de Joaquín serait sûre dans tous les cas. La proposition N3465 aurait causé de sérieux problèmes à certains programmes (voir la section Impact sur le code existant ). Joaquín a préparé un projet de proposition mis à jour avec quelques implémentations différentes avec des compromis différents, ce qui a été très utile pour aider le LWG à comprendre les avantages et les inconvénients, mais tous risquaient de casser certains programmes. Nous avons décidé que, même s’il n’était pas prudent d’append la fonctionnalité sans condition, il serait sûr qu’elle soit désactivée par défaut et qu’elle soit activée uniquement.

La principale différence entre la proposition N3657 (qui était une révision de dernière minute et celle de la STL basée sur N3465 et une version non publiée ultérieure de Joaquín) consistait à append le type is_transparent comme protocole permettant d’ is_transparent la nouvelle fonctionnalité. .

Si vous n’utilisez pas de “foncteur transparent” (c’est-à-dire un foncteur qui définit un type is_transparent ), les conteneurs se comportent comme ils l’ont toujours fait, et c’est toujours la valeur par défaut.

Si vous choisissez d’utiliser std::less<> (ce qui est nouveau pour C ++ 14) ou un autre type “foncteur transparent”, vous obtenez la nouvelle fonctionnalité.

Utiliser std::less<> est facile avec les modèles d’alias:

 template, typename Alloc = std::allocator> using set = std::set; 

Le nom is_transparent vient du is_transparent de STL qui a ajouté les “opérateurs de diamants” à C ++ 14. Un “foncteur transparent” est un “foncteur transparent” qui accepte tous les types d’arguments (qui ne doivent pas nécessairement être identiques) et transmet simplement ces arguments à un autre opérateur. Un tel foncteur se trouve être exactement ce que vous voulez pour une recherche hétérogène dans des conteneurs associatifs, de sorte que le type is_transparent été ajouté à tous les opérateurs de diamant et utilisé comme type de balise pour indiquer que la nouvelle fonctionnalité doit être activée dans des conteneurs associatifs. Techniquement, les conteneurs n’ont pas besoin d’un “foncteur transparent”, juste celui qui prend en charge l’appel avec des types hétérogènes (par exemple, le type pointer_comp dans https://stackoverflow.com/a/18940595/981959 n’est pas transparent selon la définition de la STL, mais définir pointer_comp::is_transparent permet d’être utilisé pour résoudre le problème). Si vous ne faites que chercher dans votre std::set avec des clés de type T ou int alors C doit seulement être appelable avec des arguments de type T et int (dans l’un ou l’autre ordre), il n’a pas besoin d’être vraiment transparent. Nous avons utilisé ce nom en partie parce que nous ne pouvions pas trouver de meilleur nom (j’aurais préféré is_polymorphic car de tels foncteurs utilisent un polymorphism statique, mais il existe déjà un trait de type std::is_polymorphic qui fait référence au polymorphism dynamic).

En C ++ 11, il n’y a pas de modèles de membres find() , lower_bound() , etc. Autrement dit, rien n’est perdu par ce changement. Les modèles de membre ont été introduits avec n3657 pour permettre l’utilisation de clés hétérogènes avec les conteneurs associatifs. Je ne vois aucun exemple concret où cela soit utile, sauf pour l’exemple qui est bon ou mauvais!

L’utilisation de is_transparent est destinée à éviter les conversions indésirables. Si les modèles de membre étaient libres de contraintes, le code existant peut passer directement par des objects qui auraient été convertis sans les modèles de membre. L’exemple de cas d’utilisation de n3657 consiste à localiser un object dans un std::set aide d’un littéral de chaîne: avec la définition C ++ 11, un object std::ssortingng est construit lors du passage d’une chaîne littérale au fonction membre correspondante. Avec le changement, il est possible d’utiliser directement le littéral de chaîne. Si l’object de la fonction de comparaison sous-jacente est implémenté exclusivement en termes de std::ssortingng cela est mauvais car maintenant une std::ssortingng sera créée pour chaque comparaison. D’autre part, si l’object de la fonction de comparaison sous-jacente peut prendre un std::ssortingng et un littéral de chaîne, cela peut éviter la construction d’un object temporaire.

Le type is_transparent nested dans l’object fonction de comparaison permet de spécifier si la fonction membre modélisée doit être utilisée: si l’object fonction de comparaison peut traiter des arguments hétérogènes, il définit ce type pour indiquer qu’il peut traiter efficacement différents arguments. Par exemple, les nouveaux objects de fonction opérateur délèguent simplement à l’ operator<() et prétendent être transparents. Cela, au moins, fonctionne pour std::ssortingng qui a surchargé moins que les opérateurs qui prennent en argument char const* . Étant donné que ces objects de fonction sont également nouveaux, même s'ils ne font pas la bonne chose (c.-à-d. Qu'ils nécessitent une conversion pour un certain type), ils ne seraient au moins pas un changement silencieux entraînant une dégradation des performances.

Ce qui suit est tous des copies de n3657 .

Q. Quel est le but de rendre un comparateur “transparent”?

A. Les fonctions de recherche de conteneur associatif (find, lower_bound, upper_bound, equal_range) ne prennent qu’un argument de key_type, obligeant les utilisateurs à construire (implicitement ou explicitement) un object key_type pour effectuer la recherche. Cela peut être coûteux, par exemple en construisant un object de grande taille à rechercher dans un ensemble lorsque la fonction de comparaison ne regarde qu’un seul champ de l’object. Les utilisateurs souhaitent vivement pouvoir effectuer des recherches à l’aide d’autres types comparables à key_type.

Q. Quel problème cela résout

A. Le LWG était préoccupé par le code comme suit:

 std::set s = /* ... */; s.find("key"); 

En C ++ 11, cela créera un simple std :: ssortingng temporaire et le comparera ensuite aux éléments pour trouver la clé.

Avec le changement proposé par N3465, la fonction std :: set :: find () serait un modèle non contraint qui passerait le caractère const * à la fonction de comparaison, std :: less, ce qui créerait un std :: ssortingng temporaire pour chaque comparaison. Le LWG a estimé que ce problème de performance était un problème grave. La fonction template find () empêche également de trouver NULL dans un conteneur de pointeurs, ce qui empêche la compilation d’un code précédemment valide, mais cela est considéré comme un problème moins grave que la régression des performances en mode silencieux.

Q. cela change-t-il le fonctionnement des conteneurs standard?

A. Cette proposition modifie les conteneurs associatifs dans et en surchargeant les fonctions de membre de recherche avec les modèles de fonction de membre. Il n’y a pas de changement de langue.

Q. Ainsi, l’ensemble par défaut perd ses membres find, count, etc.

A. Presque tout le code C ++ 11 existant n’est pas affecté car les fonctions membres ne sont pas présentes à moins que de nouvelles fonctionnalités de bibliothèque C ++ 14 ne soient utilisées comme fonctions de comparaison.

Pour citer Yakk ,

En C ++ 14, std :: set :: find est une fonction de modèle si Compare :: is_transparent existe. Le type que vous transmettez n’a pas besoin d’être clé, juste l’équivalent sous votre comparateur.

et n3657,

Ajoutez le paragraphe 13 dans 23.2.4 [associative.reqmts]: Les modèles de fonction membres find, lower_bound, upper_bound et equal_range ne doivent pas participer à la résolution de la surcharge à moins que le type Compare :: is_transparent n’existe pas .

n3421 fournit un exemple de “foncteurs opérateurs transparents” .

Le code complet est ici .

Stephan T Lavavej parle des problèmes où le compilateur continue à créer des provisoires, et comment sa proposition de foncteurs opérateurs transparents résoudra ce problème en c ++ 1y

GoingNative 2013 – Ne pas aider le compilateur (vers l’heure)