web-dev-qa-db-fra.com

Est-il utile de passer std :: faible_ptr aux fonctions?

Je lisais cet article de Herb Sutter sur le fait de passer des pointeurs intelligents à des fonctions. Il ne mentionne pas std::weak_ptr et honnêtement, je ne trouve pas un bon scénario où il est utile de passer un pointeur intelligent.

Est-ce que la fonction devient propriétaire? Passez std::shared_ptr. La fonction doit-elle simplement fonctionner sur l'objet sous-jacent? Passez un pointeur brut ou une référence. 

Donc, passer std::weak_ptr aux fonctions est-il 100% inutile? 

6
Ignorant

Donc, passer std::weak_ptr aux fonctions est-il 100% inutile?

Non.

Considérons cet exemple de jouet.

struct PointerObserver
{
    std::weak_ptr<int> held_pointer;

    void observe( std::weak_ptr<int> p )
    {
        held_pointer = std::move(p);
    }

    void report() const
    {
        if ( auto sp = held_pointer.lock() )
        {
            std::cout << "Pointer points to " << *sp << "\n";
        }
        else
        {
            std::cout << "Pointer has expired.\n";
        }
    }
};

Dans cet exemple, une fonction observe maintient l'état.

Son paramètre weak_ptr indique que ce pointeur transmis ne lui appartient pas, mais se réserve le droit de le posséder ultérieurement, en détectant en toute sécurité si le pointeur a expiré.

Une fonction qui ne conserve pas l'état pour plus tard pourrait également recevoir un paramètre weak_ptr dans un contexte multithread où les données associées pourraient expirer pendant que la fonction fonctionne.

9
Drew Dormann

Si vos clients ont un weak_ptr et que votre logique peut la verrouiller ou non, et qu'elle soit valide quelle qu’elle soit, passez un weak_ptr.

À titre d'exemple concret et trivial:

mutable std::mutex m_mutex;
mutable std::vector<std::weak_ptr<std::function<void(int)>>> m_callbacks;

void clean_callbacks(int x) {
  auto l = std::unique_lock<std::mutex>(m_mutex);
  auto it = std::remove_if( begin(m_callbacks), end(m_callbacks), [](auto w){ return !w.lock(); } );
  m_callbacks.erase( it, end(m_callbacks) );
}
void call_callbacks() {
  clean_callbacks();
  auto tmp = [&]{
    auto l = std::unique_lock<std::mutex>(m_mutex);
    return m_callbacks;
  }();
  for (auto&& wf:tmp) {
    if(auto sf = wf.lock()) {
      (*sf)(x);
    }
  }
}

clean_callbacks a un lambda qui prend un weak_ptr. Il est utilisé pour supprimer tout m_callbacks dont la durée de vie est terminée.

Ce code est utilisé dans un simple radiodiffuseur où les émissions sont diffusées beaucoup plus souvent que les auditeurs sont invalidés. Il est donc judicieux d'attendre la prochaine émission pour éliminer un auditeur mort.

3

Les pointeurs faibles sont utiles pour conserver des objets qui pourraient ne plus être disponibles ultérieurement (sans prolonger leur durée de vie). Cela signifie qu'ils sont généralement utilisés pour le stockage dans des conteneurs (ou variables). On passe généralement un pointeur partagé jusqu'à ce que l'objet soit stocké puis converti en pointeurs faibles. Ensuite, lorsqu'ils sont utilisés, ils doivent d'abord être convertis en pointeurs partagés afin de vérifier s'ils sont toujours valides. Il est donc peu probable que vous passiez un pointeur faible en dehors du processus de stockage et de récupération, peut-être dans les fonctions d'assistance. 

1
Motti