web-dev-qa-db-fra.com

Pourquoi std :: queue :: pop ne renvoie-t-il pas de valeur?

Je suis passé par ceci page mais je ne peux pas obtenir la raison de la même chose. Il est mentionné que

"Il est plus judicieux qu'il ne renvoie aucune valeur et oblige les clients à utiliser front () pour inspecter la valeur située au début de la file d'attente"

Mais inspecter un élément de front () nécessite également de copier cet élément dans lvalue. Par exemple dans ce segment de code

std::queue<int> myqueue;
int myint;
int result;
std::cin >> myint;
myqueue.Push (myint);

/ * ici temporaire sera créé sur le RHS qui sera assigné au résultat, et dans le cas où les renvois par référence, le résultat sera rendu invalide après l'opération pop * /

result = myqueue.front();  //result.
std::cout << ' ' << result;
myqueue.pop();

sur la cinquième ligne cout , l'objet crée d'abord une copie de myqueue.front (), puis l'assigne à son résultat. Alors, quelle est la différence, la fonction pop aurait pu faire la même chose.

95
cbinder

Alors, quelle est la différence, la fonction pop aurait pu faire la même chose.

Il aurait en effet pu faire la même chose. Cela s’explique par le fait qu’une pop qui a retourné l’élément popped n’est pas sécurisée en présence d’exceptions (avoir à retourner par valeur et donc à créer une copie).

Considérez ce scénario (avec une implémentation pop naïve/inventée pour illustrer mon propos):

template<class T>
class queue {
    T* elements;
    std::size_t top_position;
    // stuff here
    T pop()
    {
        auto x = elements[top_position];
        // TODO: call destructor for elements[top_position] here
        --top_position;  // alter queue state here
        return x;        // calls T(const T&) which may throw
    }

Si le constructeur de copie de T renvoie la valeur renvoyée, vous avez déjà modifié l'état de la file d'attente (top_position Dans mon implémentation naïve) et l'élément est supprimé de la file d'attente (et n'est pas renvoyé). À toutes fins utiles (quelle que soit la manière dont vous interceptez l'exception dans le code client), l'élément situé en haut de la file d'attente est perdu.

Cette implémentation est également inefficace dans le cas où vous n’avez pas besoin de la valeur sautée (c’est-à-dire qu’elle crée une copie de l’élément que personne n’utilisera).

Cela peut être mis en œuvre de manière sûre et efficace, avec deux opérations distinctes (void pop Et const T& front()).

88
utnapistim

La page que vous avez liée à répond à votre question.

Pour citer toute la section pertinente:

On pourrait se demander pourquoi pop () renvoie void au lieu de value_type. Autrement dit, pourquoi faut-il utiliser front () et pop () pour examiner et supprimer l'élément situé en tête de la file d'attente, au lieu de combiner les deux dans une fonction membre unique? En fait, il y a une bonne raison pour cette conception. Si pop () renvoyait l'élément de front, il devrait renvoyer par valeur plutôt que par référence: retour par référence créerait un pointeur suspendu. Le retour par valeur est cependant inefficace: il implique au moins un appel du constructeur de la copie redondante. Dans la mesure où il est impossible pour pop () de renvoyer une valeur de manière à être efficace et correcte, il est plus judicieux de ne renvoyer aucune valeur et d’obliger les clients à utiliser front () pour inspecter la valeur à le devant de la file d'attente.

Le C++ est conçu pour être efficace, en fonction du nombre de lignes de code que le programmeur doit écrire.

30
BPF2010

pop ne peut pas renvoyer de référence à la valeur supprimée, car elle est supprimée de la structure de données. À quoi la référence doit-elle faire référence? Il pourrait retourner par valeur, mais que se passe-t-il si le résultat de pop n'est pas stocké nulle part? Ensuite, on perd du temps à copier inutilement la valeur.

5
Neil Kirk

À partir de C++ 11, il serait possible d'archiver le comportement souhaité à l'aide de la sémantique de déplacement. Comme pop_and_move. Ainsi, le constructeur de copie ne sera pas appelé et les performances ne dépendront que du constructeur de déplacement.

1
Slavik Putsenko

Avec l'implémentation actuelle, ceci est valide:

int &result = myqueue.front();
std::cout << result;
myqueue.pop();

Si pop renvoie une référence, comme ceci:

value_type& pop();

Le code suivant pourrait alors se bloquer, car la référence n'est plus valide:

int &result = myqueue.pop();
std::cout << result;

Par contre, si cela retourne directement une valeur:

value_type pop();

Ensuite, vous devrez faire une copie pour que ce code fonctionne, ce qui est moins efficace:

int result = myqueue.pop();
std::cout << result;
1
Jan Rüegg

Vous pouvez totalement faire cela:

std::cout << ' ' << myqueue.front();

Ou, si vous voulez la valeur dans une variable, utilisez une référence:

const auto &result = myqueue.front();
if (result > whatever) do_whatever();
std::cout << ' ' << result;

À côté de cela: le terme "plus sensible" est une forme subjective de "nous avons examiné les schémas d'utilisation et avons jugé plus nécessaire de scinder". (Rassurez-vous, le langage C++ n'évolue pas à la légère ...)

0
xtofl

Je pense que la meilleure solution serait d’ajouter quelque chose comme

std::queue::pop_and_store(value_type& value);

où valeur recevra la valeur éclatée.

L'avantage est qu'il peut être implémenté à l'aide d'un opérateur d'assignation de mouvement, tandis que front + pop en fera une copie.

0
Masse Nicolas