Égalité-comparer std:: faible ptr

je veux comparer deux std::weak ou un std::weak et un std::shared_ptr pour l'égalité.

ce que je veux savoir, c'est si l'objet sur lequel porte le point de weak_ptr/shared_ptr est le même. La comparaison devrait donner des résultats négatifs non seulement si les adresses ne concordent pas, mais aussi si l'objet sous-jacent a été supprimé puis reconstruit avec la même adresse par hasard.

donc en gros, je veux que cette affirmation tienne même si l'allocateur les réserves de la même adresse:

auto s1 = std::make_shared<int>(43);
std::weak_ptr<int> w1(s1);

s1.reset();

auto s2 = std::make_shared<int>(41);
std::weak_ptr<int> w2(s2);

assert(!equals(w1,w2));

les modèles weak_ptr ne fournissent pas d'opérateurs d'égalité, et comme j'ai compris c'est pour une bonne raison.

donc une implémentation naïve ressemblerait à ceci:

template <typename T, typename U>
inline bool naive_equals(const std::weak_ptr<T>& t, const std::weak_ptr<U>& u)
{
    return !t.expired() && t.lock() == u.lock();
}

template <typename T, typename U>
inline bool naive_equals(const std::weak_ptr<T>& t, const std::shared_ptr<U>& u)
{
    return !t.expired() && t.lock() == u;
}

si le premier weak_ptr expire entre - temps, il donne 0. Si ce n'est pas le cas, je mets à jour weak_ptr en shared_ptr et je compare les adresses.

le problème avec ça c'est que je dois verrouiller les deux (une) fois de weak_ptr! Je suis peur que prend trop de temps.

j'ai trouvé ceci:

template <typename T, typename U>
inline bool equals(const std::weak_ptr<T>& t, const std::weak_ptr<U>& u)
{
    return !t.owner_before(u) && !u.owner_before(t);
}


template <typename T, typename U>
inline bool equals(const std::weak_ptr<T>& t, const std::shared_ptr<U>& u)
{
    return !t.owner_before(u) && !u.owner_before(t);
}

Qui vérifie si le propriétaire bloc de u n'est pas "avant" t et t n'est pas avant u, de sorte que t == u.

est-ce que cela fonctionne comme je l'ai prévu? Est-ce que deux weak_ptr créés à partir de shared_ptr se comparent toujours comme non égaux de cette façon? Ou ai-je raté quelque chose?

Edit: Pourquoi est-ce que je veux faire cela en premier lieu? Je veux avoir un container avec des pointeurs partagés, et je distribuer des références à des objets qu'il contient. Je ne peux pas utiliser d'itérateurs, car ils peuvent être invalidés. Je pourrais distribuer des ID (entiers), mais cela conduirait à des problèmes d'unicité et exigerait un type de carte et compliquerait les opérations de recherche/insertion/suppression. L'idée est d'utiliser un std::set et de donner les pointeurs eux-mêmes (capsuled dans une classe wrapper) comme clés, de sorte que les clients puissent utiliser les weak_ptr pour accéder aux objets de l'ensemble.

25
demandé sur fat-lobyte 2012-09-06 18:04:54
la source

1 ответов

réécrire complètement cette réponse parce que j'ai totalement mal compris. C'est une chose la plus délicate à obtenir le droit!

L'habituelle mise en œuvre de std::weak_ptr et std::shared_ptr qui est compatible avec la norme est d'avoir deux objets tas: l'objet géré, et un bloc de contrôle. Chaque pointeur partagé qui se réfère au même objet contient un pointeur à l'objet et au bloc de contrôle, et chaque pointeur faible de même. Le bloc de contrôle conserve un enregistrement du nombre des pointeurs partagés et le nombre de points faibles, et désalloque l'objet géré lorsque le nombre de pointeurs partagés atteint 0; le bloc de contrôle lui-même est désalloué lorsque le nombre de points faibles atteint également 0.

ceci est compliqué par le fait que le pointeur d'objet dans un pointeur partagé ou faible peut pointer vers un sous-objet de l'objet géré réel, par exemple une classe de base, un membre, ou même un autre objet tas qui est la propriété de l'objet géré objet.

S0 ----------______       MO <------+
   \__             `----> BC        |
      \_ _______--------> m1        |
     ___X__               m2 --> H  |
S1 -/      \__ __----------------^  |
    \___ _____X__                   |
    ____X________\__                |
W0 /----------------`---> CB -------+  
                          s = 2 
                          w = 1 

ici nous avons deux pointeurs partagés pointant respectivement vers une classe de base de l'objet géré et Vers un membre, et un pointeur faible pointant vers un objet tas appartenant à l'objet géré; le bloc de contrôle enregistre que deux pointeurs partagés et un pointeur faible existent. Le bloc de contrôle a également un pointeur vers l'objet géré, qu'il utilise pour supprimer l'objet géré quand il expire.

owner_before/owner_less la sémantique est de comparer les et les pointeurs faibles par l'adresse de leur bloc de contrôle, qui est garanti de ne pas changer à moins que le pointeur lui-même est modifié; même si un pointeur faible expire parce que tous les pointeurs partagés ont été détruits, son bloc de contrôle existe toujours jusqu'à ce que tous les pointeurs faibles aient aussi été détruits.

equals le code est absolument correct et le thread sûr.

Le problème est qu'il n'est pas compatible avec shared_ptr::operator== parce qu'il compare l'objet les pointeurs, et deux pointeurs partagés avec le même bloc de contrôle peuvent pointer vers des objets différents (comme ci-dessus).

pour la cohérence avec shared_ptr::operator==, écrit t.lock() == u sera tout à fait bien; note toutefois que, si elle retourne true alors il n'est pas encore certain que le pointeur faible soit un pointeur faible de l'autre pointeur partagé; il être un pointeur d'alias et donc peut toujours expirer dans le code suivant.

cependant, la comparaison des blocs de contrôle a moins de frais généraux (car il n'a pas besoin de regarder le bloc de contrôle) et vous donnera les mêmes résultats que == si vous n'utilisez pas de pointeurs d'alias.


je pense qu'il y a quelque chose d'une faille dans la norme ici; l'ajout d'un owner_equals et owner_hash permettrait d'utiliser les weak_ptr dans des contenants non classés, et donnés owner_equals il devient en fait raisonnable de comparer des pointeurs faibles pour l'égalité, car vous pouvez comparer en toute sécurité le pointeur de bloc de contrôle la pointeur d'objet, puisque si deux pointeurs faibles ont le même bloc de contrôle alors vous savez que les deux ou ni l'un ni l'autre sont expirés. Quelque chose pour la prochaine version de la norme, peut-être.

23
répondu ecatmur 2016-02-12 16:50:23
la source

Autres questions sur c++ c++11 shared-ptr weak-ptr