web-dev-qa-db-fra.com

C ++ 11 permet-il le vecteur <const T>?

Les exigences relatives aux conteneurs sont passées de C++ 03 à C++ 11. Alors que C++ 03 avait des exigences générales (par exemple, la constructibilité de la copie et l'attribution pour le vecteur), C++ 11 définit des exigences précises pour chaque opération de conteneur (section 23.2).

En conséquence, vous pouvez par exemple stocker un type qui est constructible par copie mais non assignable - comme une structure avec un membre const - dans un vecteur tant que vous n'effectuez que certaines opérations qui ne nécessitent pas d'affectation (construction et Push_back sont de telles opérations; insert ne l'est pas).

Ce que je me demande, c'est: est-ce que cela signifie que la norme permet désormais vector<const T>? Je ne vois aucune raison pour laquelle cela ne devrait pas - const T, tout comme une structure avec un membre const, est un type qui est constructible en copie mais non assignable - mais j'ai peut-être manqué quelque chose.

(Une partie de ce qui me fait penser que j'ai peut-être manqué quelque chose, c'est que le tronc gcc se bloque et brûle si vous essayez d'instancier vector<const T>, mais ça va avec vector<T> où T a un membre const).

73
HighCommander4

Non, je crois que les exigences d'allocateur disent que T peut être un "type d'objet non const, non référence".

Vous ne pourriez pas faire grand-chose avec un vecteur d'objets constants. Et un const vector<T> Serait presque le même de toute façon.


Plusieurs années plus tard, cette réponse rapide et sale semble toujours attirer des commentaires et des votes. Pas toujours debout. : -)

Donc, pour ajouter quelques références appropriées:

Pour la norme C++ 03, que j'ai sur papier, le tableau 31 dans la section [lib.allocator.requirements] dit:

T, U any type

Ce n'est pas que tout type ait réellement fonctionné.

Ainsi, la norme suivante, C++ 11, dit dans un brouillon proche dans [allocator.requirements] et maintenant le tableau 27:

T, U, C any non-const, non-reference object type

ce qui est extrêmement proche de ce que j'ai écrit à l'origine ci-dessus de mémoire. C'est aussi de cela qu'il s'agissait.

Cependant, en C++ 14 ( projet N4296 ) le tableau 27 dit maintenant:

T, U, C any non-const object type

Peut-être parce qu'une référence n'est peut-être pas un type d'objet après tout?

Et maintenant en C++ 17 ( draft N4659 ) c'est le tableau 30 qui dit:

T, U, C any cv-unqualified object type (6.9)

Ainsi, non seulement const est exclu, mais aussi volatile. Probablement de vieilles nouvelles de toute façon, et juste une clarification.


Veuillez également consulter informations de première main d'Howard Hinnant , actuellement juste en dessous.

41
Bo Persson

Mise à jour

Sous la réponse acceptée (et correcte), j'ai commenté en 2011:

Conclusion: nous n'avons pas conçu de conteneurs pour contenir const T. Bien que j'y ai réfléchi. Et nous avons failli le faire par accident. À ma connaissance, le point de blocage actuel est la paire de fonctions membres address surchargées dans l'allocateur par défaut: lorsque T est const, ces deux surcharges ont le même Signature. Un moyen simple de corriger cela serait de se spécialiser std::allocator<const T> et supprimez l'une des surcharges.

Avec le prochain projet de C++ 17, il me semble que nous avons maintenant légalisé vector<const T>, et je crois aussi que nous l'avons fait accidentellement. :-)

P0174R supprime les surcharges address de std::allocator<T>. P0174R ne fait aucune mention du support std::allocator<const T> dans le cadre de sa justification.

Correction

Dans les commentaires ci-dessous T.C. note correctement que les surcharges address sont obsolètes, non supprimées. Ma faute. Les membres déconseillés n'apparaissent pas au 20.10.9 où le std::allocator est défini, mais est plutôt relégué à la section D.9. J'ai négligé de scanner le chapitre D pour cette possibilité lorsque j'ai posté cela.

Merci T.C. pour la correction. J'envisageais de supprimer cette réponse trompeuse, mais peut-être est-il préférable de laisser cette correction de côté afin que peut-être cela empêche quelqu'un d'autre de mal interpréter la spécification de la même manière que moi.

26
Howard Hinnant

Même si nous avons déjà de très bonnes réponses à ce sujet, j'ai décidé d'apporter une réponse plus pratique pour montrer ce qui peut et ce qui ne peut pas être fait.

Donc ça ne marche pas:

vector<const T> vec; 

Lisez simplement les autres réponses pour comprendre pourquoi. Et, comme vous l'avez peut-être deviné, cela ne fonctionnera pas non plus:

vector<const shared_ptr<T>> vec;

T n'est plus const, mais vector contient shared_ptrs, pas Ts.

D'un autre côté, cela fonctionne fonctionne:

vector<const T *> vec;
vector<T const *> vec;  // the same as above

Mais dans ce cas, const est l'objet pointé, pas le pointeur lui-même (qui est ce que le vecteur stocke). Cela équivaudrait à:

vector<shared_ptr<const T>> vec;

Ce qui est bien.

Mais si nous mettons const à la fin de l'expression, cela transforme maintenant le pointeur en const, donc ce qui suit ne se compile pas:

vector<T * const> vec;

Un peu déroutant, je suis d'accord, mais on s'y habitue.

4
Lucio Paiva

En complément des autres réponses, une autre approche consiste à utiliser:

vector<unique_ptr<const T>> vec;

Si c'est le cas où vous souhaitez appliquer que seul vec est propriétaire de ses éléments. Ou si vous voulez une dynamique de déplacement des éléments dans vec et à un moment donné les déplacer.

Comme indiqué, la sémantique du pointeur const peut prêter à confusion, mais shared_ptr et unique_ptr ne le sont pas. const unique_ptr<T> est un pointeur const et unique_ptr<const T> est une pointe constante comme on peut s'y attendre.

2
Daniel Gouvêa