2010-12-17 1 views
5

Le modèle vecteur STL définit accesseurs des éléments à la fois comme const et variantes non const, par exemple:const et non-const dans des conteneurs stl

reference operator[](size_type __n) 
    {return *(this->_M_impl._M_start + __n);} 
const_reference operator[](size_type __n) const 
    {return *(this->_M_impl._M_start + __n);} 

Quand le compilateur décide d'utiliser une seule version sur l'autre? Le vecteur lui-même n'est pas défini comme const, ni les éléments y sont stockés. Donc, étant donné deux fonctions:

A f(int i) const 
    { return myVector[i]; } 
A f(int i) 
    { return myVector[i]; } 

Ma compréhension est que la première appelleraient la version const de l'opérateur [] et retourner un const A. La deuxième appelle la version non-const et retourne un non-const A?

Pour moi, la première version de f() semble être la "correcte" à écrire puisque la fonction ne modifie rien, mais il est peut-être surprenant pour l'appelant qu'il renvoie un const A. Certainement si Je voulais un const un retour, je besoin de définir f() comme:

const A f(int i) const //Note the extra const at the start 
    { return myVector[i]; } 

Ce qui raconterait celui qui est en train d'écrire l'appelant d'attendre un retour const.

Donc le bonus additionnel apparaît par magie? Et à quoi sert le const supplémentaire si j'utilise boost :: ptr_vector plutôt que std :: vector? Les données? Le pointeur? tous les deux?

+3

Vous feriez mieux de poser une question générale sur 'const'. Mieux encore, étudiez ceci dans votre manuel C++ et essayez des exemples de programmes, puis posez des questions sur des problèmes concrets. Quel manuel utilisez-vous? –

Répondre

7

Ceci tire parti d'une partie délicate des règles de surcharge de fonction. Tout d'abord, les qualificatifs cv après la parenthèse fermante d'un prototype de méthode sont comme les qualificatifs cv sur les arguments, mais ils s'appliquent à l'argument implicite this.Dans une variante C++ hypothétique où vous deviez déclarer this, les prototypes seraient comme ceci:

reference operator[](this_type this, size_type n); 
const_reference operator[](const this_type this, size_type n); 

Par conséquent, si l'objet que vous appelez la méthode sur est const, la deuxième surcharge est une meilleure adéquation et sera appelé. Si ce n'est pas le cas, la première surcharge sera appelée. Donc, vous obtenez un const_reference de retour si vous indexez un conteneur const, et vous obtenez un reference de retour si vous indexez un conteneur non const. L'objet const_reference applique ensuite la nature en lecture seule du conteneur vers lequel il pointe.

Parfois const_reference est le même que const reference et parfois ce n'est pas le cas; pour les conteneurs plus compliqués, un reference est une classe avec un code non trivial, et ce code doit être différent pour la variété en lecture seule. La norme utilise systématiquement const_reference afin que les développeurs aient la liberté de le faire lorsqu'ils en ont besoin.

+0

'const_reference' n'est presque jamais la même chose que' const reference', sauf quand 'value_type' est déjà' const'. Si 'reference' est' int & ',' const reference' serait aussi 'int &', pas 'const int &'. – aschepler

1

Le const à la fin d'une déclaration de fonction s'applique uniquement aux fonctions de membre non statiques et signifie que *this est const-qualifié.

struct A { 
    void f(int i) const; 
    void f(int i); 
}; 

void g(const A& x, A& y) { 
    x.f(); // calls const version 
    y.f(); // calls non-const version 
} 

En conteneurs STL, ce type de surcharge est utilisé pour déterminer si la référence retournée ou iterator peuvent être utilisés pour modifier les éléments du conteneur. Vous ne pouvez pas modifier un élément via un conteneur qualifié const.

0

Renvoyer une valeur de retour n'a pas de sens - il est parfaitement légal de construire une nouvelle valeur à partir d'une valeur constante, ce qui explique pourquoi la première forme est valide. Pensez-y, si vous voulez copier quelque part, cela n'a pas d'importance si vous ne pouvez pas écrire quelque part.

Si vous renvoyez une référence, le compilateur ne vous permettra pas de retourner un A & dans la version const.

1

Dans la fonction:

A f(int i) const 
    { return myVector[i]; } 

Vous retournez un A. non-const Puisque vous retournez en valeur, la valeur retournée par l'opérateur [] (une référence const) est copié dans un nouveau A. Vous "perdez" le const, mais ce n'est pas grave parce que vous avez un tout nouvel objet. Au contraire, si votre classe ressemble à ceci:

struct B { 
    const A& f(int i) const; 
    A& f(int i); 

private: 
    vector<A> myVector; 
}; 

et que vous appelez f() en utilisant un const B:

const B b; 
const A& a = b.f(); 

votre sera une référence const.

Questions connexes