87 votes

Pourquoi les références ne sont pas « const » en C++ ?

Nous savons qu'une "variable const" indique qu'une fois attribué, vous ne pouvez pas modifier la variable, comme ceci:

int const i = 1;
i = 2;

Le programme ci-dessus échoue à compiler; gcc invites avec une erreur:

assignment of read-only variable 'i'

Pas de problème, je peux le comprendre, mais l'exemple suivant est au-delà de ma compréhension:

#include<iostream>
using namespace std;
int main()
{
    boolalpha(cout);
    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;
    int const &ri = i;
    cout << is_const<decltype(ri)>::value << endl;
    return 0;
}

C'sorties

true
false

Bizarre. Nous savons qu'une fois qu'une référence est lié à un nom de variable, on ne peut pas changer cette liaison, nous changeons de son objet lié. Donc je suppose que le type d' ri devrait être le même que i: lors de l' i est int const, pourquoi est - ri pas const?

56voto

Galik Points 522

Cela peut sembler contre-intuitif, mais je pense que la façon de comprendre c'est de réaliser que, à certains égards, les références sont traités du point de vue syntaxique comme des pointeurs.

Cela semble logique pour un pointeur:

int main()
{
    boolalpha(cout);

    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;

    int const* ri = &i;
    cout << is_const<decltype(ri)>::value << endl;
}

Sortie:

true
false

Ce qui est logique, car nous savons que ce n'est pas le pointeur de l'objet qui est const (il peut être fait à point d'ailleurs) c'est l'objet qui est en train d'être souligné.

Nous avons donc de voir correctement la constness de le pointeur lui-même retourné comme false.

Si nous voulons rendre le pointeur lui-même const nous avons à dire:

int main()
{
    boolalpha(cout);

    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;

    int const* const ri = &i;
    cout << is_const<decltype(ri)>::value << endl;
}

Sortie:

true
true

Et donc, je pense que nous avons un syntaxiques analogie avec la référence.

Cependant, les références sont sémantiquement différents à des pointeurs en particulier dans un sens, nous ne sommes pas autorisés à relier une référence à un autre objet, une fois lié.

Ainsi, même si les références de partager la même syntaxe que les pointeurs les règles sont différentes et donc la langue nous empêche de déclarer la référence elle-même const comme ceci:

int main()
{
    boolalpha(cout);

    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;

    int const& const ri = i; // COMPILE TIME ERROR!
    cout << is_const<decltype(ri)>::value << endl;
}

Je suppose que nous ne sommes pas autorisés à le faire, car il ne semble pas être nécessaire lorsque les règles de la langue empêcher la référence de rebond de la même façon qu'un pointeur peut(si elle n'est pas déclarée const).

Donc pour répondre à la question:

Q) Pourquoi "de référence" n'est pas un "const en C++?

Dans votre exemple, la syntaxe rend la chose visée const de la même façon que si vous étiez à la déclaration d'un pointeur.

A tort ou à raison, nous ne sommes pas autorisés à faire de la référence elle-même const mais si nous étions il devrait ressembler à ceci:

int const& const ri = i; // not allowed

Q) nous savoir une fois qu'une référence est lié à un nom de variable, on ne peut pas changer cette liaison, nous allons changer sa bindings objet. Donc je suppose que le type d' ri doit être la même qu' i: lors de l' i est int const, pourquoi ri n'est const?

Pourquoi l' decltype() ne sont pas transférés à l'objet de la referece est lié?

Je suppose que c'est pour l'équivalence sémantique avec les pointeurs et peut-être aussi la fonction d' decltype() (déclarée type) est de regarder en arrière sur ce qui a été déclaré avant de la liaison a eu lieu.

55voto

songyuanyao Points 2265

pourquoi le "ri", pas de "const"?

std::is_const vérifie si le type est const-qualifiés ou non.

Si T est un const qualifiés de type (const, ou const volatile), fournit le membre de la valeur constante égale à true. Pour tout autre type, la valeur est false.

Mais la référence ne peut pas être const-qualifiés. $8.3.2/1 Références [dcl.ref]

Cv qualifiés références sont mal formés, sauf lorsque le cv-qualificatifs sont introduites par l'utilisation d'une définition de type-nom ([dcl.typedef], [temp.param]) ou decltype spécificateur ([dcl.type.simple]), auquel cas le cv-qualificatifs sont ignorés.

Donc, is_const<decltype(ri)>::value sera de retour false car ri (la référence) n'est pas un const qualifiés de type. Comme vous l'avez dit, nous ne pouvons pas relier une référence après l'initialisation, ce qui implique de référence est toujours "const", d'autre part, const qualifiés de référence ou de const-non qualifiés de référence, n'aurait pas de sens en fait.

18voto

chema989 Points 1964

Vous devez utiliser `` pour obtenir la valeur que vous recherchez.

Pour plus d’informations, voir ce post.

9voto

Pourquoi sont des macros non const? Fonctions? Les littéraux? Les noms de types?

const choses ne sont qu'un sous-ensemble de immuable des choses.

Depuis les types de référence, ne sont que des types — il peut avoir fait un certain sens à exiger de l' const-qualificatif sur eux tous pour la symétrie avec les autres types (en particulier avec les types de pointeur), mais cela pourrait devenir très ennuyeux très rapidement.

Si C++ a des objets immuables par défaut, nécessitant l' mutable mot-clé sur quelque chose que vous ne voulez être const, alors cela aurait été facile: il suffit de ne pas permettre aux programmeurs d'ajouter mutable pour les types référence.

Comme il est, ils sont immuables sans qualification.

Et, puisqu'ils ne sont pas const-qualifiés, il serait probablement plus déroutant pour is_const sur un type de référence à rendement vrai.

Je trouve cela être un compromis raisonnable, surtout depuis l'immutabilité est de toute façon forcée par le simple fait qu'aucune syntaxe existe pour muter une référence.

6voto

Kaz Points 18072

C'est un caprice/fonction en C++. Bien que nous ne pensons pas de références que de types, en fait, elles "s'asseoir" dans le système de type. Bien que cela semble maladroit (étant donné que lorsque les références sont utilisées, la référence sémantique se produit automatiquement et la référence "sort de la voie"), il y a quelques défendable raisons pour lesquelles les références sont modélisés dans le système de type plutôt que comme un attribut distinct à l'extérieur de type.

Tout d'abord, permettez-nous de considérer que chaque attribut d'une déclaration de nom doit être dans le type de système. À partir du langage C, nous ont "avec la classe de stockage", et "lien". Un nom peut être présenté en tant que extern const int ri, où l' extern indique statique de la classe de stockage et de la présence de la liaison. Le type est juste const int.

C++ évidemment embrasse la notion que les expressions ont des attributs qui sont en dehors du système de type. La langue a maintenant un concept de "classe", qui est une tentative d'organiser le nombre croissant de non-type des attributs qu'une expression peut les exposer.

Pourtant, les références sont des types. Pourquoi?

Il sert à être expliqué en C++ tutoriels qu'une déclaration telle que celle - const int &ri introduite ri de type const int, mais la référence à la sémantique. Qui font référence à la sémantique n'était pas un type, c'était tout simplement une sorte d'attribut indiquant une relation inhabituelle entre le nom et l'emplacement de stockage. En outre, le fait que les références ne sont pas les types a été utilisé pour expliquer pourquoi vous ne pouvez pas construire des types de références, même si le type de construction de la syntaxe permet. Par exemple, des tableaux ou des pointeurs vers des références n'étant pas possible: const int &ari[5] et const int &*pri.

Mais en fait, les références sont les types de decltype(ri) récupère une référence de type de nœud qui est sans réserve. Vous devez descendre passé ce nœud dans le type de l'arbre pour obtenir le type sous-jacent avec remove_reference.

Lorsque vous utilisez ri, la référence est résolu de manière transparente, de sorte qu' ri "ressemble i" et peut être appelé un "alias" pour elle. Dans le système de type, même si, ri a, en fait, un type qui est "laréférence à l' const int".

Pourquoi sont des références types?

Considérer que, si les références étaient pas les types, ces fonctions seraient considérées comme ayant le même type:

void foo(int);
void foo(int &);

Qui simplement ne peut pas être pour des raisons qui sont à peu près de soi. S'ils avaient le même type, cela signifie que la déclaration serait approprié pour la définition, et donc chaque (int) fonction devrait être soupçonnées de prendre une référence.

De même, si les références n'étaient pas des types, alors ces deux déclarations de classe serait l'équivalent:

class foo {
  int m;
};

class foo {
  int &m;
};

Il serait correct pour une unité de traduction à utiliser une déclaration, et une autre unité de traduction dans le même programme de déclaration.

Le fait est qu' une référence implique une différence dans la mise en œuvre et il est impossible de séparer ce de type, parce que le type en C++ a à voir avec la mise en œuvre d'une entité: sa "mise en page" en bits pour ainsi dire. Si les deux fonctions ont le même type, elles peuvent être invoquées avec le même binaire conventions d'appel: l'ABI est le même. Si deux structures ou classes ont le même type, leur mise en page est la même, ainsi que la sémantique de l'accès à tous les membres. La présence de références à des changements de ces aspects de types, et c'est donc une conception simple décision de les intégrer dans le système de type. (Notez toutefois un contre-argument ici: struct/membre de la classe peuvent être static, ce qui modifie également la représentation; or, ce n'est pas le type!)

Ainsi, les références sont dans le système de type "citoyens de seconde classe" (pas à la différence des fonctions et des tableaux en C ISO). Il y a certaines choses que nous ne pouvons pas "faire" avec des références telles que de déclarer des pointeurs vers des références, ou des tableaux. Mais cela ne veut pas dire qu'ils ne sont pas les types. Ils ne sont pas seulement des types d'une manière qui fait sens.

Pas tous ces de deuxième classe,-les restrictions sont essentiels. Étant donné qu'il existe des structures de références, il pourrait y avoir des tableaux de références! E. g.

// fantasy syntax
int x = 0, y = 0;
int &ar[2] = { x, y };

// ar[0] is now an alias for x: could be useful!

Ce n'est pas implémentée en C++, c'est tout. Des pointeurs vers les références n'ont pas de sens, bien que, parce qu'un pointeur levé à partir d'une référence va juste à l'objet référencé. Probablement la raison pour laquelle il n'existe pas de tableaux de références, c'est que le C++ les gens considèrent les tableaux à une sorte de bas niveau de fonctionnalité héritée de C qui est cassé dans de nombreuses façons qui sont irréparables, et ils ne veulent pas toucher les tableaux en tant que base pour quelque chose de nouveau. L'existence de tableaux de références, cependant, serait de faire un exemple clair de la façon dont les références doivent être de types différents.

Non-const-qualifiable types: trouvé dans la norme ISO C90, trop!

Certaines réponses sont allusion au fait que les références ne pas prendre un const de qualification. C'est plutôt un hareng rouge, parce que la déclaration const int &ri = i n'est même pas tenter de faire un const-qualifiés de référence: c'est une référence à un const qualifiés de type (qui lui, n'est pas const). Tout comme const in *ri déclare un pointeur vers quelque chose d' const, mais que le pointeur n'est pas const.

Cela dit, il est vrai que les références ne peuvent pas prendre en const qualifier eux-mêmes.

Pourtant, ce n'est pas si bizarre. Même dans la norme ISO C 90 langue, pas tous les types peuvent être const. À savoir, les tableaux ne peuvent pas être.

Tout d'abord, la syntaxe n'existe pas de déclaration d'une const tableau: int a const [42] est erronée.

Cependant, ce que la déclaration ci-dessus est d'essayer de faire peut être exprimé par le biais d'un intermédiaire, typedef:

typedef int array_t[42];
const array_t a;

Mais ce n'est pas faire ce à quoi il ressemble, il ne. Dans cette déclaration, il n'est pas a qui reçoit const qualifiés, mais les éléments! C'est-à-dire, a[0] est const int, mais a est juste "tableau de int". Par conséquent, cela ne nécessite pas un diagnostic:

int *p = a; /* surprise! */

Ce n':

a[0] = 1;

Encore une fois, ce qui souligne l'idée que les références sont en quelque sorte de "seconde classe" dans le système de type, comme des tableaux.

Notez comment l'analogie est encore plus profondément, comme les tableaux ont également un "invisible comportement de conversion", comme des références. Sans le programmeur d'avoir à utiliser de manière explicite de l'opérateur, l'identificateur a se transforme automatiquement en une int * pointeur, comme si l'expression &a[0] a été utilisé. Ceci est analogue à la façon dont une référence ri, lorsque nous l'utilisons comme une expression primaire, comme par magie, désigne l'objet i auquel il est lié. C'est juste un autre "décroissance" comme le "tableau de pointeur de la décroissance".

Et tout comme nous ne devons pas devenir confus par le "tableau de pointeur de la" désintégration de penser à tort que "les tableaux sont juste des pointeurs en C et C++", de même, nous ne devons pas penser que les références sont des alias qui n'ont pas de type de leur propre.

Lors de l' decltype(ri) supprime l'habitude de conversion de la référence à son référent de l'objet, ce n'est pas si différent de sizeof a suppression de la pile-à-pointeur de conversion et d'exploitation sur le type de tableau lui-même pour calculer sa taille.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X