web-dev-qa-db-fra.com

Que sont les comparateurs transparents?

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

Les modèles de fonction membre find, count, lower_bound, upper_bound, et equal_range ne doit pas participer à la résolution de surcharge à moins que 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 celui-ci:

template <class T = void> 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<void> {
    template <class T, class U> auto operator()(T&& t, U&& u) const
    -> decltype(std::forward<T>(t) < std::forward<U>(u));
    typedef *unspecified* is_transparent;
};

Ainsi, par exemple, std::set<T, std::less<T>> aurait pas un comparateur transparent, mais std::set<T, std::less<>>serait en avoir un.

Quel problème cela résout-il et cela change-t-il le fonctionnement des conteneurs standard? Par exemple, les paramètres de modèle de std::set sont encore Key, Compare = std::less<Key>, ..., l'ensemble par défaut perd donc ses membres find, count, etc.?

99
Kerrek SB

Quel problème cela résout-il,

Voir réponse de Dietmar et réponse de remyabel .

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

Non, pas par défaut.

Les nouvelles surcharges de modèle de fonction membre de find etc. 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 et soigneusement écrite pour ajouter 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 graves problèmes pour 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 alternatives avec différents compromis, ce qui était très utile pour aider le LWG à comprendre les avantages et les inconvénients, mais ils risquaient tous de casser certains programmes d'une manière ou d'une autre, il n'y avait donc pas de consensus pour ajouter la fonctionnalité. Nous avons décidé que bien qu'il ne soit pas sûr d'ajouter la fonctionnalité sans condition, ce serait sûr si elle était désactivée par défaut et seulement "opt in".

La principale différence de la proposition N3657 (qui était une révision de dernière minute par moi-même et STL basée sur N3465 et un brouillon non publié plus tard par Joaquín) était d'ajouter le is_transparent tapez comme protocole pouvant être utilisé pour activer la nouvelle fonctionnalité.

Si vous n'utilisez pas de "foncteur transparent" (c'est-à-dire celui qui définit un is_transparent type), 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<> (qui est nouveau pour C++ 14) ou un autre type de "foncteur transparent", vous obtenez la nouvelle fonctionnalité.

En utilisant std::less<> est facile avec les modèles d'alias:

template<typename T, typename Cmp = std::less<>, typename Alloc = std::allocator<T>>
  using set = std::set<T, Cmp, Alloc>;

Le nom is_transparent provient de STL N3421 qui a ajouté les "opérateurs diamant" à C++ 14. Un "foncteur transparent" est celui qui accepte tous les types d'arguments (qui ne doivent pas nécessairement être les mêmes) 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, donc le type is_transparent a été ajouté à tous les opérateurs diamant et utilisé comme type de balise pour indiquer que la nouvelle fonctionnalité doit être activée dans les conteneurs associatifs. Techniquement, les conteneurs n'ont pas besoin d'un "foncteur transparent", juste un qui supporte l'appel avec des types hétérogènes (par exemple le pointer_comp tapez https://stackoverflow.com/a/18940595/981959 n'est pas transparent selon la définition de STL, mais définit pointer_comp::is_transparent permet de l'utiliser pour résoudre le problème). Si vous ne recherchez que dans votre std::set<T, C> avec des clés de type T ou int alors C n'a besoin d'être appelable qu'avec des arguments de type T et int (dans ne doit pas être vraiment transparent. Nous avons utilisé ce nom en partie parce que nous ne pouvions pas trouver un meilleur nom (j'aurais préféré is_polymorphic parce que ces foncteurs utilisent le polymorphisme statique, mais il y a déjà un std::is_polymorphic trait de type qui fait référence au polymorphisme dynamique).

56
Jonathan Wakely

Dans C++ 11, il n'y a pas de modèles de membres find(), lower_bound(), etc. Autrement dit, rien n'est perdu par cette modification. Les modèles de membres 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 est utile à l'exception de l'exemple qui est bon et mauvais!

L'utilisation de is_transparent Est destinée à éviter les conversions indésirables. Si les modèles de membre n'étaient pas contraints, le code existant peut passer directement par des objets qui auraient été convertis sans les modèles de membre. L'exemple d'utilisation de n3657 consiste à localiser un objet dans un std::set<std::string> À l'aide d'un littéral de chaîne: avec la définition C++ 11, un objet std::string Est construit lors du passage d'un littéral de chaîne au membre correspondant une fonction. Avec le changement, il est possible d'utiliser directement le littéral de chaîne. Si l'objet de fonction de comparaison sous-jacent est implémenté exclusivement en termes de std::string, C'est mauvais car maintenant un std::string Serait créé pour chaque comparaison. D'un autre côté, si l'objet de la fonction de comparaison sous-jacente peut prendre un std::string Et un littéral de chaîne, cela peut éviter la construction d'un objet temporaire.

Le type is_transparent Imbriqué dans l'objet fonction de comparaison fournit un moyen de spécifier si la fonction membre modèle doit être utilisée: si l'objet fonction de comparaison peut traiter des arguments hétérogènes, il définit ce type pour indiquer qu'il peut traiter différents arguments efficacement. Par exemple, les nouveaux objets de fonction opérateur délèguent simplement à operator<() et prétendent être transparents. Cela, au moins, fonctionne pour std::string Qui a surchargé moins que les opérateurs prenant char const* Comme argument. Étant donné que ces objets fonction sont également nouveaux, même s'ils font la mauvaise chose (c'est-à-dire nécessitent une conversion pour un certain type), ce ne serait pas, au moins, un changement silencieux entraînant une dégradation des performances.

32
Dietmar Kühl

Ce qui suit est toutes les pâtes à copier 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 type_clé, obligeant les utilisateurs à construire (implicitement ou explicitement) un objet du type_clé pour effectuer la recherche. Cela peut être coûteux, par exemple construire un grand objet pour rechercher dans un ensemble lorsque la fonction de comparateur ne regarde qu'un seul champ de l'objet. Les utilisateurs souhaitent fortement pouvoir effectuer des recherches à l'aide d'autres types comparables au type_clé.

Q. Quel problème cela résout-il

A. Le LWG avait des préoccupations au sujet du code comme celui-ci:

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

En C++ 11, cela construira une seule chaîne std :: string temporaire, puis la comparera 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 transmettrait le caractère const * à la fonction de comparaison, std :: less, qui construirait une chaîne std :: temporaire pour chaque comparaison. Le LWG a considéré ce problème de performances comme un problème sérieux. La fonction find () du modèle empêcherait également de trouver NULL dans un conteneur de pointeurs, ce qui empêche la compilation de code précédemment valide, mais cela a été considéré comme un problème moins grave que la régression silencieuse des performances.

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

A. Cette proposition modifie les conteneurs associatifs dans et en surchargeant les fonctions membres de recherche avec des modèles de fonctions membres. Il n'y a aucun 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 présentes que si de nouvelles fonctionnalités de bibliothèque C++ 14 sont utilisées comme fonctions de comparaison.

Pour citer Yakk ,

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

et n3657,

Ajouter le paragraphe 13 dans 23.2.4 [associative.reqmts]: les modèles de fonction membre find, lower_bound, upper_bound et equal_range ne doivent pas participer à la résolution de surcharge sauf si le type Compare :: is_transparent n'existe pas existe.

n3421 fournit un exemple de "Transparent Operator Functors" .

Le le code complet est ici .

18
user1508519

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

GoingNative 2013 - N'aidez pas le compilateur (à environ la marque de l'heure)

6
woolstar