web-dev-qa-db-fra.com

Accéder aux éléments dans std :: string où la position de la chaîne est supérieure à sa taille

Dans le cas de std :: string, si nous accédons à un élément où (element position) == (size of string) La norme dit qu'elle renvoie une référence à un objet de type charT avec la valeur charT().

const_reference operator[](size_type pos) const;
reference       operator[](size_type pos);

Attend: pos <= size ().

Renvoie: * (begin () + pos) si pos <size (). Sinon, renvoie une référence à un objet de type charT avec la valeur charT (), où la modification de l'objet à une valeur autre que charT () conduit à un comportement non défini.

http://eel.is/c++draft/strings#string.access-1

Malheureusement, je ne pouvais pas raisonner à ce sujet, il aurait été préférable que ce soit un comportement indéfini.

Quelqu'un peut-il expliquer la raison derrière cela?

36
cpp_enthusiast

Vous devez considérer les spécifications complètes.

Tout d'abord:

Attend: pos <= size ().

Si vous ne respectez pas la condition préalable, vous avez de toute façon un comportement indéfini. Maintenant...

Renvoie: * (begin () + pos) si pos <size (). Sinon, renvoie une référence à un objet de type charT avec la valeur charT (), où la modification de l'objet à une valeur autre que charT () conduit à un comportement non défini.

Le seul cas (valide) auquel "sinon" se réfère est lorsque pos == size(). Et c'est probablement pour émuler le comportement d'une chaîne c qui a un élément some_string[size] Accessible. Notez que charT() est généralement juste '\0'.

PS: On pourrait penser que pour implémenter la spécification, operator[] Devrait vérifier si pos == size. Cependant, si le tableau de caractères sous-jacent a une charT() à la fin de la chaîne, vous obtenez le comportement décrit essentiellement gratuitement. Par conséquent, ce qui semble un peu différent de l'accès "habituel" dans un tableau est en fait juste cela.

39
idclev 463035818

La déclaration 1 est la condition préalable à la déclaration 2:

  1. Attend: pos <= size().

  2. Renvoie: *(begin() + pos) if pos < size().

    Sinon ( donc ici la seule possibilité viable est pos == size()), renvoie une référence à un objet de tapez charT avec la valeur charT() (ie '\0'), où la modification de l'objet à une valeur autre que charT() conduit à un comportement indéfini.

str[str.size()] pointe essentiellement vers le caractère de terminaison nulle. Vous pouvez le lire et l'écrire, mais vous ne pouvez y écrire qu'un '\0'.

22
rustyx

L'opérateur s'attend à ce que pos soit inférieur ou égal à size(), donc s'il n'est pas inférieur, il devrait être égal.

15
Yola

En plus des réponses précédentes, veuillez jeter un œil à libcxx (l'implémentation llvm) définit std::string::operator[] Comme:

template <class _CharT, class _Traits, class _Allocator>
inline
typename basic_string<_CharT, _Traits, _Allocator>::const_reference
basic_string<_CharT, _Traits, _Allocator>::operator[](size_type __pos) const _NOEXCEPT
{
    _LIBCPP_ASSERT(__pos <= size(), "string index out of bounds");
     return *(data() + __pos);
}

template <class _CharT, class _Traits, class _Allocator>
inline
typename basic_string<_CharT, _Traits, _Allocator>::reference
basic_string<_CharT, _Traits, _Allocator>::operator[](size_type __pos) _NOEXCEPT
{
    _LIBCPP_ASSERT(__pos <= size(), "string index out of bounds");
    return *(__get_pointer() + __pos);
}

Jetez un oeil à la .at() qui jette correctement à la place.

template <class _CharT, class _Traits, class _Allocator>
typename basic_string<_CharT, _Traits, _Allocator>::const_reference
basic_string<_CharT, _Traits, _Allocator>::at(size_type __n) const
{
    if (__n >= size())
        this->__throw_out_of_range();
    return (*this)[__n];
}

Comme vous pouvez, dans le premier cas, il y a une assertion d'exécution (merci t.niese pour l'avoir souligné) qui est déclenchée uniquement en mode débogage alors que la seconde lancera toujours, quelles que soient les options de construction de la bibliothèque.

2
KostasRim