Je lis un extrait de code d'un livre et trouve ceci:
const char* const & a = "hello"; //can compile
const char*& a = "hello"; //cannot
Tout ce que je sais, c'est que lors de l'initialisation d'une référence, le conversion de tableau en pointeur n'aurait pas lieu.
const char* const &
, une référence à un const pointer
, le pointeur pointe sur const char
.
const char*&
, une référence à un pointer
, le pointeur pointe sur const char
.
Alors pourquoi ajouter un const
supplémentaire, indiquant que le pointeur est un const
, lui permet de se compiler?
C'est essentiellement adhérer à cette formule
T const & a = something_convertible_to_T;
Où T est const char*
. Dans le premier cas, un pointeur temporaire peut être matérialisé, attribué à l'adresse du littéral, puis être lié à la référence. Dans le second cas, puisque la référence de lvalue n'est pas const, cela ne peut pas arriver. Un autre exemple de plus de la même chose
const char* && a = "hello"; // rvalue ref makes a no into a yes.
Maintenant, le pointeur temporaire est lié à une référence rvalue.
Quelques phrases supplémentaires pour ceux qui s'ennuient, après avoir lu la superbe réponse de @StoryTeller, car j'ai dû mener une réflexion différente à ce sujet.
Donc syntaxiquement, dans les deux lignes nous définissons un référencea
, et dans les deux nous aurons un [matérialisation] d'un temporaire pointeur qui prend l'adresse du chaîne littérale. La seule différence entre les deux est le 2nd const
n'apparaissant qu'ici:
const char* const & a = "hello";
et pas ici:
const char*& a = "hello";
Ce 2nd const
indique que l'objet étant référencé ici, n pointeur dans ce cas est lui-même const , car il ne peut pas être modifié à l'aide de cette référence.
Par conséquent, parce que le type de ce littéral de chaîne est const char[6]
(et pas const char *
_ par exemple), notre lvalue
référence au type const char*
dans la deuxième ligne ne peut pas être lié - mais la référence dans la première ligne, référence au type const char* const
pourrait. Pourquoi? En raison de règles de initialisation de la référence :
(5) Une référence au type "cv1 T1" est initialisée par une expression de type "cv2 T2" comme suit:
(5.1) Si la référence est une référence lvalue et l'expression de l'initialiseur
- (5.1.1) est une valeur lvalue (mais n'est pas un champ de bits), et "cv1 T1" est compatible avec la référence avec "cv2 T2", [...]
- (5.1.2) a un type de classe (c'est-à-dire que T2 est un type de classe) [...]
Les deux expressions sont lvalue s, mais notre "cv1 T1" n’est pas [compatible avec la référence avec nos “cv2 T2” et “T2” n'est pas un type de classe.
- (5.2) Sinon, si la référence est une référence lvalue à un type qui n'est pas qualifié de type const ou est qualifié de volatile, le programme est mal formé
La référence est en effet not const-qualifiée: notre “T1” est const char*
, qui est un pointeur sur const, par opposition à un pointeur const. Le type réel ici est un type de pointeur, c'est donc ce qui compte.
L'erreur Clang pour la deuxième ligne, lue dans cet esprit, nous dit exactement ceci:
error: non-const lvalue reference to type 'const char *' cannot bind to a value of unrelated type 'const char [6]'
const char*& a = "hello";
^ ~~~~~~~
La partie sur l'être référence de non-const lvalue est exactement ¶5.2 - la lvalue dans notre cas est un pointeur sur const
, mais n'est pas en soi const! Contrairement à la première ligne. La partie sur la liaison à un type sans rapport] est exactement ¶5.1 - notre const char*
n'est pas compatible avec le RHS étant const char[6]
, ou const char* const
après tableau en pointeur conversion.
Pour cette raison exacte, ou son absence, cela peut compiler sans erreur:
char* const & a = "hello";
ISO C++ 11 mis à part, un compilateur laisse passer celui-ci (ce n'est pas le cas, car le littéral de chaîne est un 'const char 6 ' et nous ne devrions pas laisser tomber ce premier const
), car la référence est maintenant const
en ce qui concerne son objet, le pointeur.
Une autre chose intéressante est que An rvalue
référence const char* && a
_ (no "2nd const
") pourrait se lier au pointeur temporaire qui s'est matérialisé à partir du chaîne littérale, comme l'a fourni @StoryTeller. Pourquoi donc? En raison des règles de conversion de tableau en pointeur:
Une lvalue ou une rvalue de type "tableau de NT" ou "tableau de borne inconnue de T " peut être converti en un prvalue de type" pointeur sur T ".
Aucune mention de const
ou autre cv-qualification dans la formulation, mais ceci n’est valable que tant que nous initialisons une référence rvalue.