Pourquoi c++ supporte-t-il l'assignation des tableaux par memberwise dans les structures, mais pas en général?

je comprends que memberwise affectation de tableaux n'est pas pris en charge, tels que le code suivant ne fonctionnera pas:

int num1[3] = {1,2,3};
int num2[3];
num2 = num1; // "error: invalid array assignment"

j'ai juste accepté cela comme fait, en supposant que le but du langage est de fournir un cadre ouvert, et laisser l'utilisateur décider comment mettre en œuvre quelque chose comme la copie d'un tableau.

Toutefois, le code suivant fonctionne:

struct myStruct {int num[3];};
myStruct struct1={{1,2,3}};
myStruct struct2;
struct2 = struct1;

Le tableau num[3] est membre-sage assigné de son instance dans struct1 , à son instance dans struct2 .

pourquoi l'attribution de tableaux aux membres est-elle supportée pour les structures, mais pas en général?

modifier : Roger Pate commentaire dans le fil std::string struct - Copier/problèmes d'affectation? semble pointer dans la direction générale de la réponse, mais je ne sais pas assez pour confirmer moi-même.

edit 2 : Beaucoup d'excellentes réponses. Je choisis Luther Blissett ' s parce que je m'interrogeais principalement sur la raison philosophique ou historique derrière le comportement, mais James McNellis 's référence à la documentation spec connexe était également utile.

75
demandé sur Community 2010-08-09 07:10:49

4 réponses

Voici mon point de vue:

le développement du langage C offre un aperçu de l'évolution du type de tableau en C:

je vais essayer de décrire le tableau de chose:

C précurseurs B et BCPL avait pas de distinction de type tableau, une déclaration comme:

auto V[10] (B)
or 
let V = vec 10 (BCPL)

déclarerait que V est un pointeur (non typé) qui est initialisé pour pointer vers une région inutilisée de 10" mots " de mémoire. B a déjà utilisé * pour le déréférencement de pointeur et a eu la [] courte notation de main, *(V+i) signifie V[i] , tout comme dans C/C++ aujourd'hui. Cependant, V n'est pas un tableau, c'est toujours un pointeur qui a pour point de mémoire. Cela a causé des problèmes lorsque Dennis Ritchie a essayé d'étendre B avec les types de struct. Il voulait que les tableaux fassent partie des structures, comme en C aujourd'hui:

struct {
    int inumber;
    char name[14];
};

mais avec le concept B,BCPL de tableaux comme pointeurs, cela aurait exigé le champ name pour contenir un pointeur qui devait être initialisé à l'exécution à une région de mémoire de 14 octets dans la structure. Le problème d'initialisation/mise en page a finalement été résolu en donnant aux tableaux un traitement spécial: le compilateur traquerait l'emplacement des tableaux dans les structures, sur la pile, etc. sans réellement exigeant le pointeur vers les données de matérialiser, sauf dans les expressions qui impliquent les tableaux. Ce traitement a permis à presque tout le code B de fonctionner encore et est la source de la "tableaux convertir en pointeur si vous les regardez" règle. C'est un hack de compatibilité, qui s'est avéré être très pratique, parce qu'il permettait des tableaux de taille ouverte etc.

et voici ma conjecture pourquoi tableau ne peut pas être assigné: depuis tableaux ont été pointeurs en B, vous pouvez simplement écrire:

auto V[10];
V=V+5;

pour reformuler un"tableau". Cela n'avait plus de sens, car la base d'une variable de tableau n'était plus une valeur de l. Cette affectation a donc été rejetée, ce qui a aidé à attraper les quelques programmes qui ont fait ce rebasement sur les tableaux déclarés . Et puis cette notion est restée: comme les tableaux n'ont jamais été conçus pour être des citoyens de première classe du système de type C, ils ont été le plus souvent traités comme des bêtes spéciales qui devenir pointeur si vous les utilisez. Et d'un certain point de vue (qui ignore que les c-arrays sont un hack bâclé), le fait de ne pas accepter l'assignation de tableau a encore un certain sens: un tableau ouvert ou un paramètre de fonction de tableau est traité comme un pointeur sans information de taille. Le compilateur n'a pas l'information pour générer une assignation de tableau pour eux et l'assignation de pointeur était nécessaire pour des raisons de compatibilité. L'introduction de l'assignation des tableaux pour les tableaux déclarés aurait introduit des bogues bien que les affectations fictives (est-ce que a=b est une affectation de pointeur ou une copie élémentaire?) et d'autres problèmes (Comment passer un tableau par valeur?) sans réellement résoudre un problème - il suffit de tout rendre explicite avec memcpy!

/* Example how array assignment void make things even weirder in C/C++, 
   if we don't want to break existing code.
   It's actually better to leave things as they are...
*/
typedef int vec[3];

void f(vec a, vec b) 
{
    vec x,y; 
    a=b; // pointer assignment
    x=y; // NEW! element-wise assignment
    a=x; // pointer assignment
    x=a; // NEW! element-wise assignment
}

cela n'a pas changé quand une révision de C en 1978 a ajouté une assignation de structure ( http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf ). Même si les enregistrements étaient des types distincts en C, ils étaient il N'était pas possible de les attribuer au début de K&R C. Vous deviez les copier par le membre avec memcpy et vous ne pouviez leur passer que des pointeurs comme paramètres de fonction. L'affectation (et le passage de paramètre) était Maintenant simplement défini comme la mémoire de la mémoire brute de la structure et comme cela ne pouvait pas briser le code exsisting, il était immédiatement affiché. Comme un effet secondaire non intentionnel, cela a implicitement introduit une sorte de tâche de tableau, mais cela s'est produit quelque part à l'intérieur d'une structure, donc cela ne pourrait pas vraiment introduire problèmes avec la façon dont les tableaux ont été utilisés.

38
répondu Nordic Mainframe 2010-08-09 08:22:54

en ce qui concerne les opérateurs d'affectation, la norme c++ dit ce qui suit (C++03 §5.17/1):

il y a plusieurs opérateurs... tous nécessitent une valeur modifiable comme opérande gauche

un tableau n'est pas une valeur l modifiable.

toutefois, l'affectation à un objet de type de classe est définie spécialement (§5.17/4):

L'affectation à des objets d'une classe est définie par l'opérateur d'assignation de copie.

ainsi, nous regardons ce que fait l'opérateur de cession de copie implicitement déclaré pour une classe (§12.8 / 13):

l'opérateur d'assignation de copie implicitement défini pour la classe X effectue l'assignation par membre de ses sous-objets. ... Chaque sous-objet est assigné de la manière appropriée à son type:

...

-- si le sous-objet est un tableau, chaque élément est assigné, de la manière appropriée au type d'élément

...

ainsi, pour un objet de type de classe, les tableaux sont copiés correctement. Notez que si vous fournissez un opérateur d'assignation de copie déclaré par l'utilisateur, vous ne pouvez pas en profiter, et vous devrez copier le tableau élément par élément.


le raisonnement est similaire dans C (C99 §6.5.16 / 2):

L'opérateur d'affectation doit avoir une valeur modifiable comme opérande de gauche.

et §6.3.2.1 / 1:

modifiable lvalue est une lvalue qui n'a pas de type tableau... [d'autres contraintes suivent]

en C, l'assignation est beaucoup plus simple qu'en C++ (§6.5.16.1/2):

en assignation simple (=), la valeur de l'opérande droite est convertie au type de expression d'affectation et remplace la valeur stockée dans l'objet désigné par la gauche opérande.

pour l'assignation d'objets de type struct, les opérandes gauche et droite doivent avoir le même type, de sorte que la valeur de l'opérande droite est simplement copiée dans l'opérande gauche.

25
répondu James McNellis 2010-08-09 03:53:58

dans ce lien: http://www2.research.att.com/~bs/bs_faq2.html il y a une section sur l'assignation des tableaux:

les deux problèmes fondamentaux avec les tableaux sont que

  • un tableau ne savez pas sa taille
  • le nom d'un tableau convertit un pointeur vers son premier élément à la moindre provocation

et je pense que c'est le principe différence entre les tableaux et les structures. Une variable de tableau est un élément de données de bas niveau avec une connaissance de soi limitée. Fondamentalement, c'est un morceau de mémoire et une façon de s'y indexer.

ainsi, le compilateur ne peut pas faire la différence entre int a[10] et int b[20].

les Structures, cependant, n'ont pas la même ambiguïté.

2
répondu Scott Turley 2010-08-09 16:10:40

je sais, tous ceux qui ont répondu sont des experts en C/C++. Mais j'ai pensé, c'est la raison principale.

num2 = num1;

ici vous essayez de changer l'adresse de base du tableau, ce qui n'est pas autorisé.

et bien sûr, struct2 = struct1;

ici, l'objet struct1 est assigné à un autre objet.

0
répondu user373215 2010-08-09 12:29:19