59 votes

La comparaison de chaînes de caractères STL que l'utilisation de différents allocateurs

Je voudrais comparer des chaînes de caractères STL qui sont attribuées à différents allocateurs, par exemple, un ordinaire std::string avec une chaîne à l'aide d'un custom STL allocateur. Malheureusement, il semble que d'habitude operator==() ne fonctionne pas dans ce cas:

// Custom STL allocator to allocate char's for string class
typedef MyAllocator<char> MyCharAllocator;

// Define an instance of this allocator
MyCharAllocator myAlloc;

// An STL string with custom allocator
typedef std::basic_string
<
    char, 
    std::char_traits<char>, 
    MyCharAllocator
> 
CustomAllocString;

std::string s1("Hello");
CustomAllocString s2("Hello", myAlloc);

if (s1 == s2)  // <--- ERROR: doesn't compile
   ...

En particulier, MSVC10 (VS2010 SP1) émet le message d'erreur suivant:

erreur C2678: binary '==' : l'opérateur n'a pas trouvé ce qui prend de la main gauche opérande de type 'std::string' (ou il n'est pas acceptable de conversion)

Ainsi, un niveau inférieur (moins lisible) code comme ceci:

if (strcmp(s1.c_str(), s2.c_str()) == 0)
   ...

doit être utilisé.

(Ceci est particulièrement gênant dans le cas où il y a par exemple std::vector's de l'différemment affectés chaînes, où l'habitude simple v[i] == w[j] de la syntaxe ne peut pas être utilisé.)

Cela ne semble pas très bon pour moi, depuis un allocateur personnalisé modifie la façon dont la chaîne de mémoire est requis, mais l' interface d'une classe de chaînes de caractères (y compris la comparaison avec operator==()) est indépendant de la façon particulière qu'a une chaîne alloue de la mémoire.

Est-il quelque chose qui me manque ici? Est-il possible de garder le C++ interface de haut niveau et la surcharge de l'opérateur dans ce cas?

37voto

Kerrek SB Points 194696

Utiliser std::lexicographical_compare de moins que la comparaison:

bool const lt = std::lexicographical_compare(s1.begin(), s1.end(),
                                             s2.begin(), s2.end());

Pour la comparaison d'égalité, vous pouvez utiliser std::equal:

bool const e = s1.length() == s2.length() &&
               std::equal(s1.begin(), s1.end(), s2.begin());

Alternativement, vous pouvez simplement tomber en arrière sur strcmp (ou réellement memcmp, puisque c'est la sémantique correcte; rappelez-vous que la chaîne C++ est plus général qu'une chaîne C), comme vous l'avez suggéré, ce qui peut potentiellement de recourir à un niveau inférieur de la magie comme comparer un ensemble de la machine parole à un moment (bien que l'algorithme ci-dessus peut également être spécialisé, donc). Mesurer et comparer, je dirais. Pour les chaînes courtes, les algorithmes de la bibliothèque standard sont au moins joliment auto-descriptif.


@Dietmar de l'idée ci-dessous, vous pouvez envelopper ces fonctions dans basé sur un modèle de surcharge:

#include <string>
#include <algorithm>

template <typename TChar,
          typename TTraits1, typename TAlloc1,
          typename TTraits2, typename TAlloc2>
bool operator==(std::basic_string<TChar, TTraits1, TAlloc1> const & s1,
                std::basic_string<TChar, TTraits2, TAlloc2> const & s2)
{
    return s1.length() == s2.length() &&
           std::equal(s1.begin(), s1.end(), s2.begin());
}

Exemple d'utilisation:

#include <ext/malloc_allocator.h>
int main()
{
    std::string a("hello");
    std::basic_string<char, std::char_traits<char>, __gnu_cxx::malloc_allocator<char>> b("hello");
    return a == b;
}

En fait, vous pouvez définir une surcharge pour la plupart des conteneurs standard. Vous pourriez même modèle sur un modèle, mais qui serait extrême.

19voto

Dietmar Kühl Points 70604

La norme définit seulement les opérateurs utilisant homogène types de chaînes, c'est à dire, tous les arguments de modèle doivent correspondre. Toutefois, vous pouvez définir un adapté opérateur d'égalité dans l'espace de noms où l'allocateur est défini: l'argument dépendante de recherche vont le trouver là. Si vous choisissez de mettre en place votre propre opérateur d'affectation, il ressemblerait à quelque chose comme ceci:

bool operator== (std::string const& s0,
                 std::basic_string<char, std::char_traits<char>, MyCharAllocator> const& s1) {
    return s0.size() == s1.size() && std::equal(s0.begin(), s0.end(), s1.begin()).first;
}

(plus quelques autres surcharges). Prendre cela au niveau suivant, il peut même être raisonnable de définir les versions les différents opérateurs relationnels en termes de conditions du conteneur et de ne pas restreindre les arguments de modèle:

namespace my_alloc {
    template <typename T> class allocator { ... };
    template <typename T0, typename T1>
    bool operator== (T0 const& c0, T1 const& c1) {
        return c0.size() == c1.size() && std::equal(c0.begin(), c0.end(), c1.end);
    }
    ...
}

Évidemment, les opérateurs peuvent être limitées à certains types de récipients, ne différant que par leurs allocateur de paramètres du modèle.

Concernant le pourquoi de la norme ne définit pas de mélange de type de comparaisons, la principale raison de ne pas soutenir mixte type de comparaison est probablement que vous avez réellement ne voulez pas mélanger les allocateurs dans votre programme, en premier lieu! C'est, si vous avez besoin d'utiliser un allocateur, vous pouvez utiliser un allocateur de type qui encapsule une manière dynamique polymorphes politique d'attribution et de toujours utiliser le programme d'allocation de type. Le raisonnement serait que sinon, vous obtiendrez soit incompatible interface ou vous auriez besoin de faire tout ce qu'un modèle, c'est à dire, vous souhaitez conserver un certain niveau de vocabulaire des types utilisés. Bien sûr, avec l'aide d'un seul supplémentaires allocateur de type, vous auriez deux vocabulaire types de chaînes: par défaut, l'instanciation et l'instanciation de votre allocation spéciale.

Cela dit, il y a une autre raison de ne pas soutenir mixte type de comparaison: Si operator==() devient vraiment une comparaison entre deux valeurs, comme c'est le cas si les allocateurs diffèrent, il peut susciter une beaucoup plus large de la définition de la valeur de l'égalité: faut - std::vector<T>() == std::deque<T> être pris en charge? Si non, pourquoi serait-comparaison entre les chaînes avec différents allocateurs être spécial? Bien sûr, l'allocateur est un non saillant, attribut de l' std::basic_string<C, T, A> qui pourrait être une bonne raison pour l'ignorer. Je ne suis pas sûr si elle est mélangée type de comparaison devrait être pris en charge. Il peut être raisonnable de soutenir les opérateurs (c'est probablement ce qui s'étend à d'autres opérateurs que l' operator==()) pour les types de conteneurs qui ne diffèrent que dans leur processus d'allocation de type.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X