Pourquoi std :: faible_ptr :: expired est-il optimisé?

Dans le code suivant, while ( !Ref.expired() ); est joyeusement optimisé en une boucle infinie. Si la ligne de code est modifiée en while ( !Ref.lock() ); . tout fonctionne comme prévu. Donc deux questions vraiment:

1) Comment le compilateur peut-il optimiser l’expiration lorsque std::weak_ptr::expired() accède à un compteur avec mémoire std::weak_ptr::expired() ?

2) Est-ce que Ref.lock() réellement sans danger, ou est-ce que cela pourrait aussi être optimisé?

Exemple de code ci-dessous.

 #include  #include  #include  #include  class A { public: A() { m_SomePtr = std::make_shared( false ); } virtual ~A() { std::weak_ptr Ref = m_SomePtr; m_SomePtr.reset(); // Spin (will be optimised into an infinite loop in release builds) while ( !Ref.expired() ); } std::shared_ptr GetPtr() const { return m_SomePtr; } private: std::shared_ptr m_SomePtr; }; class B { public: B( std::shared_ptr SomePtr ) : m_Ref( SomePtr ) {} void LockPtr() { m_SomePtr = m_Ref.lock(); } void UnLockPtr() { m_SomePtr.reset(); } private: std::shared_ptr m_SomePtr; std::weak_ptr m_Ref; }; int main() { std::unique_ptr a( new A() ); std::unique_ptr b( new B( a->GetPtr() ) ); b->LockPtr(); std::cout << "Starting " <UnLockPtr(); } ); std::thread second( [&]() { a.reset( nullptr ); } ); first.join(); second.join(); std::cout << "Complete" << std::endl; return 0; } 

Votre programme est incorrect. les fonctionnalités de pointeur de propriété partagée ne sont pas destinées à être utilisées pour la synchronisation.

[intro.multithread] / 24:

L’implémentation peut supposer que n’importe quel fil finira par faire l’une des choses suivantes:
– résilier,
– appeler une fonction d’E / S de bibliothèque,
– accéder ou modifier un object volatile, ou
– effectuer une opération de synchronisation ou une opération atomique.

std::weak_ptr::expired() n’est pas une opération de synchronisation ou une opération atomique; tout ce que dit la norme, c’est qu’elle n’introduit pas de course aux données. Depuis la résolution du défaut de la bibliothèque 2316 , std::weak_ptr::lock() est considéré comme une opération atomique, donc pour répondre à cette question 2) votre code utilisant Ref.lock() est valide à partir de C ++ 14.

Il est vrai que si vous weak_ptr de créer votre propre implémentation de bibliothèque de weak_ptr à l’aide des outils de langage et de bibliothèque, elle utiliserait nécessairement les fonctions de synchronisation et / ou d’exploitation atomique. Ainsi, un utilisateur weak_ptr::expired() être OK pour tourner (la mise en œuvre serait obligée de s’assurer que le fil a finalement progressé, par [intro.multithread] / 2 et / 25). Mais une implémentation n’est pas obligée de restreindre sa propre bibliothèque à la langue et aux installations de la bibliothèque.

Je ne suis pas tout à fait sûr de savoir comment le compilateur optimise l’access à expired() . J’imagine que la bibliothèque MSVC exploite des aspects du modèle de mémoire x86 que le compilateur / optimiseur observe ne sont pas garantis par le modèle de mémoire C ++.