web-dev-qa-db-fra.com

Pourquoi les tableaux de références sont-ils illégaux?

Le code suivant ne compile pas.

int a = 1, b = 2, c = 3;
int& arr[] = {a,b,c,8};

Qu'est-ce que la norme C++ dit à ce sujet?

Je sais que je pourrais déclarer une classe contenant une référence, puis créer un tableau de cette classe, comme indiqué ci-dessous. Mais je veux vraiment savoir pourquoi le code ci-dessus ne compile pas.

struct cintref
{
    cintref(const int & ref) : ref(ref) {}
    operator const int &() { return ref; }
private:
    const int & ref;
    void operator=(const cintref &);
};

int main() 
{
  int a=1,b=2,c=3;
  //typedef const int &  cintref;
  cintref arr[] = {a,b,c,8};
}

Il est possible d'utiliser struct cintref au lieu de const int & pour simuler un tableau de références.

121
Alexey Malistov

En réponse à votre question sur le standard, je peux citer le Standard C++ §8.3.2/4 :

Il ne doit y avoir aucune référence à des références, aucun tableau de références , et aucun pointeur vers des références. 

132

Les références ne sont pas des objets. Ils n'ont pas de stockage propre, ils font simplement référence à des objets existants. Pour cette raison, il n’a pas de sens d’avoir des tableaux de références.

Si vous voulez un object léger faisant référence à un autre objet, vous pouvez utiliser un pointeur. Vous ne pourrez utiliser un struct avec un membre de référence en tant qu'objets dans des tableaux si vous fournissez une initialisation explicite pour tous les membres de référence pour toutes les instances struct. Les références ne peuvent pas être initialisées par défaut.

Edit: Comme le note jia3ep, la section standard des déclarations contient une interdiction explicite des tableaux de références.

53
CB Bailey

C'est une discussion intéressante. Clairement, les tableaux de références sont carrément illégaux, mais la raison pour laquelle IMHO n’est pas aussi simple que de dire «ils ne sont pas des objets» ou «ils n’ont pas de taille». Je ferais remarquer que les tableaux eux-mêmes ne sont pas des objets à part entière en C/C++. Si vous vous objectez à cela, essayez d'instancier certaines classes de modèle stl en utilisant un tableau comme paramètre de modèle 'classe' et voyez ce qui se passe. Vous ne pouvez pas les retourner, les assigner, les passer comme paramètres. (un paramètre de tableau est traité comme un pointeur). Mais il est légal de créer des tableaux de tableaux . Les références ont une taille que le compilateur peut et doit calculer - vous ne pouvez pas sizeof () une référence, mais vous pouvez créer une structure ne contenant que des références. Il aura une taille suffisante pour contenir tous les pointeurs qui implémentent les références. Vous ne pouvez pas instancier une telle structure sans initialiser tous les membres:

struct mys {
 int & a;
 int & b;
 int & c;
};
...
int ivar1, ivar2, arr[200];
mys my_refs = { ivar1, ivar2, arr[12] };

my_refs.a += 3  ;  // add 3 to ivar1

En fait, vous pouvez ajouter cette ligne à la définition de la structure

struct mys {
 ...
 int & operator[]( int i ) { return i==0?a : i==1? b : c; }
};

... et maintenant j'ai quelque chose qui ressemble beaucoup à un tableau de références:

int ivar1, ivar2, arr[200];
mys my_refs = { ivar1, ivar2, arr[12] };

my_refs[1] = my_refs[2]  ;  // copy arr[12] to ivar2
&my_refs[0];               // gives &my_refs.a == &ivar1

Maintenant, ce n'est pas un vrai tableau, c'est une surcharge d'opérateur; il ne fera pas les choses que les tableaux font normalement comme sizeof (arr)/sizeof (arr [0]), par exemple. Mais cela correspond exactement à ce que je veux faire avec un tableau de références, avec C++ parfaitement légal . Sauf que (a) il est difficile de configurer plus de 3 ou 4 éléments et (b) qu’il effectue un calcul avec tas de?: ce qui pourrait être fait en utilisant l'indexation (pas avec l'indexation normale du C-pointeur-calcul-sémantique, mais néanmoins avec l'indexation). J'aimerais voir un type de "matrice de références" très limité qui peut réellement le faire. C'est à dire. un tableau de références ne serait pas traité comme un tableau général de choses qui sont des références, mais plutôt comme un nouveau "tableau de références" mappant efficacement vers une classe générée en interne similaire à celle ci-dessus (mais malheureusement ne peux pas faire avec des modèles).

cela fonctionnerait probablement, si cela ne vous dérangeait pas ce genre de méchant: reformulez '* this' sous la forme d'un tableau d'int * et renverrez une référence faite depuis un: (non recommandé, mais cela montre comment le bon 'tableau' travaillerait):

 int & operator[]( int i ) { return *(reinterpret_cast<int**>(this)[i]); }
27
greggo

Commentaire à votre édition:

La meilleure solution est std::reference_wrapper.

Détails: http://www.cplusplus.com/reference/functional/reference_wrapper/

Exemple:

#include <iostream>
#include <functional>
using namespace std;

int main() {
    int a=1,b=2,c=3,d=4;
    using intlink = std::reference_wrapper<int>;
    intlink arr[] = {a,b,c,d};
    return 0;
}
24
Youw

Un tableau est implicitement convertible en un pointeur et le pointeur vers la référence est illégal en C++

11
EFraim

Parce que, comme beaucoup l'ont dit ici, les références ne sont pas des objets. ce sont simplement des alias. Il est vrai que certains compilateurs peuvent les implémenter en tant que pointeurs, mais la norme ne force pas/ne spécifie pas cela. Et comme les références ne sont pas des objets, vous ne pouvez pas les pointer. Stocker des éléments dans un tableau signifie qu’il existe une sorte d’adresse d’index (c’est-à-dire pointant sur des éléments à un certain index); C'est pourquoi vous ne pouvez pas avoir de tableaux de références, car vous ne pouvez pas les indiquer.

Utilisez boost :: reference_wrapper ou boost :: Tuple à la place; ou juste des pointeurs.

9
navigator

Étant donné int& arr[] = {a,b,c,8};, quelle est sizeof(*arr)?

Partout ailleurs, une référence est traitée comme étant simplement la chose elle-même, donc sizeof(*arr) devrait simplement être sizeof(int). Mais cela rendrait l'arithmétique de pointeur de tableau sur ce tableau incorrect (en supposant que les références ne sont pas les mêmes largeurs, c'est ints) Pour éliminer l'ambiguïté, c'est interdit. 

9
PaulMurrayCbr

Vous pouvez vous rapprocher de ce modèle avec la structure struct . Cependant, vous devez initialiser avec des expressions qui sont des pointeurs sur T plutôt que sur T; ainsi, bien que vous puissiez facilement créer un 'fake_constref_array' de la même manière, vous ne pourrez pas le lier à des valeurs comme dans l'exemple du PO ('8');

#include <stdio.h>

template<class T, int N> 
struct fake_ref_array {
   T * ptrs[N];
  T & operator [] ( int i ){ return *ptrs[i]; }
};

int A,B,X[3];

void func( int j, int k)
{
  fake_ref_array<int,3> refarr = { &A, &B, &X[1] };
  refarr[j] = k;  // :-) 
   // You could probably make the following work using an overload of + that returns
   // a proxy that overloads *. Still not a real array though, so it would just be
   // stunt programming at that point.
   // *(refarr + j) = k  
}

int
main()
{
    func(1,7);  //B = 7
    func(2,8);     // X[1] = 8
    printf("A=%d B=%d X = {%d,%d,%d}\n", A,B,X[0],X[1],X[2]);
        return 0;
}

-> A = 0 B = 7 X = {0,8,0}

4
greggo

Lorsque vous stockez quelque chose dans un tableau, sa taille doit être connue (car l'indexation d'un tableau dépend de la taille). Selon la norme C++ Il n'est pas précisé si une référence nécessite ou non un stockage. Par conséquent, l'indexation d'un tableau de références ne serait pas possible.

2
Pradyot

Un objet de référence n'a pas de taille. Si vous écrivez sizeof(referenceVariable), cela vous donnera la taille de l'objet référencé par referenceVariable, pas celle de la référence elle-même. Il n'a pas de taille propre, raison pour laquelle le compilateur ne peut pas calculer la taille requise par le tableau.

2
Carl Seleborg

Juste pour ajouter à toute la conversation . Comme les tableaux ont besoin d'emplacements de mémoire consécutifs pour stocker l'élément, donc si nous créons un tableau de références, il n'est pas garanti qu'ils se trouveront dans un emplacement de mémoire consécutif, ce qui rend l'accès difficile. nous ne pouvons même pas appliquer toutes les opérations mathématiques sur array.

1
Amit Kumar

Considérez un tableau de pointeurs. Un pointeur est vraiment une adresse. ainsi, lorsque vous initialisez le tableau, vous indiquez de manière analogue à l'ordinateur: "allouez ce bloc de mémoire pour contenir ces numéros X (qui sont les adresses d'autres éléments)". Ensuite, si vous modifiez l’un des pointeurs, vous modifiez simplement son objet; c'est toujours une adresse numérique qui est elle-même assise au même endroit.

Une référence est analogue à un alias. Si vous deviez déclarer un tableau de références, vous diriez en gros à l'ordinateur: "allouez cette goutte de mémoire amorphe constituée de tous ces différents éléments éparpillés".

0
Dan Tao

Je pense que la réponse est très simple et concerne les règles de référence sémantiques et la gestion des tableaux en C++.

En bref: Les références peuvent être considérées comme des structures qui n'ont pas de constructeur par défaut, donc toutes les mêmes règles s'appliquent.

1) Sémantiquement, les références n’ont pas de valeur par défaut. Les références ne peuvent être créées qu'en référençant quelque chose. Les références n'ont pas de valeur pour représenter l'absence de référence.

2) Lors de l'allocation d'un tableau de taille X, le programme crée une collection d'objets initialisés par défaut. Puisque reference n'a pas de valeur par défaut, la création d'un tel tableau est sémantiquement illégale.

Cette règle s'applique également aux structures/classes qui n'ont pas de constructeur par défaut. L'exemple de code suivant ne compile pas:

struct Object
{
    Object(int value) { }
};

Object objects[1]; // Error: no appropriate default constructor available
0
Kristupas A.