Pourquoi C ++ prend-il en charge l’affectation des tableaux par membre dans les structures, mais pas généralement?

Je comprends que l’atsortingbution des tableaux par les membres n’est pas prise en charge, de sorte que les éléments suivants ne fonctionneront pas:

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

Je viens d’accepter cela comme un fait, pensant que le but du langage est de fournir un cadre ouvert, et laisser l’utilisateur décider comment implémenter quelque chose comme la copie d’un tableau.

Cependant, les éléments suivants fonctionnent:

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

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

Pourquoi l’atsortingbution par les membres des tableaux est-elle prise en charge pour les structures, mais pas en général?

edit : Le commentaire de Roger Pate dans le thread std :: ssortingng in struct – Problèmes de copie / affectation? semble indiquer la direction générale de la réponse, mais je ne sais pas assez pour le confirmer moi-même.

edit 2 : Beaucoup d’excellentes réponses. J’ai choisi Luther Blissett parce que je m’interrogeais surtout sur la justification philosophique ou historique du comportement, mais la référence de James McNellis à la documentation relative aux spécifications était également utile.

Voici mon sharepoint vue:

Le développement du langage C donne un aperçu de l’évolution du type de tableau en C:

Je vais essayer de décrire le tableau:

Les précurseurs B et BCPL de C n’avaient aucun type de tableau distinct, une déclaration comme:

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

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

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

Mais avec le concept B, BCPL de tableaux en tant que pointeurs, il aurait fallu que le champ name contienne un pointeur qui devait être initialisé à l’exécution dans une région mémoire de 14 octets dans la structure. Le problème d’initialisation / mise en page a finalement été résolu en accordant un traitement spécial aux tableaux: le compilateur suivrait l’emplacement des tableaux dans les structures, la stack, etc., sans obliger le pointeur à se matérialiser, sauf dans les expressions impliquant les tableaux. Ce traitement a permis à presque tous les codes B de continuer à fonctionner et est la source des “tableaux convertis en pointeurs si vous les regardez” . C’est un hack de compatibilité, qui s’est avéré très pratique, car il permettait des tableaux de taille ouverte, etc.

Et voici ma raison pour laquelle le tableau ne peut pas être atsortingbué: les tableaux étant des pointeurs dans B, vous pouvez simplement écrire:

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

rebaser un “tableau”. Cela n’avait plus de sens, car la base d’une variable de tableau n’était plus une valeur. Donc, cet assigment a été interdit, ce qui a permis d’attraper les quelques programmes qui ont fait cela sur les baies déclarées . Et puis cette notion est restée bloquée: comme les tableaux n’ont jamais été conçus pour être des citations de première classe du système de type C, ils étaient principalement traités comme des bêtes spéciales qui deviennent des pointeurs si vous les utilisez. Et d’un certain sharepoint vue (qui ignore que les tableaux C sont un hack raté), le rejet de l’affectation de tableaux a encore du sens: un paramètre de tableau ouvert ou de fonction tableau est traité comme un pointeur sans informations de taille. Le compilateur n’a pas les informations pour générer une affectation de tableau pour eux et l’affectation du pointeur était nécessaire pour des raisons de compatibilité. L’introduction de l’affectation de tableaux pour les tableaux déclarés aurait introduit des bogues à travers des assertions erronées (est-ce qu’une assignation de pointeur ba = ou une copie élémentaire?) Et d’autres problèmes (comment passer un tableau par valeur?) Sans résoudre un problème – 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 affectation de structure ( http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf ). Même si les enregistrements étaient des types distincts en C, il n’était pas possible de les atsortingbuer au début de K & R C. Vous deviez les copier en tant que membre avec memcpy et vous ne pouviez leur transmettre que des pointeurs comme parameters de fonction. L’assignation (et le passage des parameters) était maintenant simplement défini comme le memcpy de la mémoire brute de la structure et comme cela ne pouvait pas casser le code existant, il était facilement adapté. En tant qu’effet secondaire involontaire, cela impliquait implicitement une sorte d’atsortingbution de tableau, mais cela se produisait quelque part dans une structure, donc cela ne pouvait pas vraiment poser de problèmes avec la façon dont les tableaux étaient utilisés.

Concernant les opérateurs d’affectation, le standard C ++ dit ceci (C ++ 03 §5.17 / 1):

Il y a plusieurs opérateurs d’affectation … tous nécessitent une lvalue modifiable comme opérande gauche

Un tableau n’est pas une lvalue modifiable.

Cependant, l’affectation à un object de type de classe est définie spécialement (§5.17 / 4):

L’affectation à des objects d’une classe est définie par l’opérateur d’atsortingbution de copie.

Donc, nous cherchons à voir ce que l’opérateur d’affectation de copie implicitement déclaré pour une classe fait (§12.8 / 13):

L’opérateur d’affectation de copie implicitement défini pour la classe X effectue une affectation par membres de ses sous-objects. … Chaque sous-object est affecté de la manière appropriée à son type:

– si le sous-object est un tableau, chaque élément est affecté, de la manière appropriée au type d’élément

Ainsi, pour un object de type classe, les tableaux sont copiés correctement. Notez que si vous fournissez un opérateur d’affectation de copie déclaré par l’utilisateur, vous ne pouvez pas en tirer parti et vous devrez copier le tableau element-by-element.


Le raisonnement est similaire en C (C99 §6.5.16 / 2):

Un opérateur d’affectation doit avoir une valeur modifiable comme opérande gauche.

Et §6.3.2.1 / 1:

Une lvalue modifiable est une lvalue qui n’a pas de type de tableau … [d’autres contraintes suivent]

En C, l’affectation est beaucoup plus simple qu’en C ++ (§ 6.5.16.1 / 2):

Dans l’affectation simple (=), la valeur de l’opérande droite est convertie dans le type de l’expression d’affectation et remplace la valeur stockée dans l’object désigné par l’opérande gauche.

Pour atsortingbuer des objects de type struct, les opérandes gauche et droit 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.

Dans ce lien: http://www2.research.att.com/~bs/bs_faq2.html, il y a une section sur l’affectation des tableaux:

Les deux problèmes fondamentaux avec les tableaux sont que

  • un tableau ne connaît pas sa propre taille
  • le nom d’un tableau se convertit en un pointeur vers son premier élément à la moindre provocation

Et je pense que c’est la différence fondamentale 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 un moyen de l’indexer.

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

Les structures n’ont toutefois pas la même ambiguïté.

Je sais, tous ceux qui ont répondu sont des experts en C / C ++. Mais je pensais que c’était la raison principale.

num2 = num1;

Ici, vous essayez de modifier l’adresse de base du tableau, ce qui n’est pas autorisé.

et bien sûr, struct2 = struct1;

Ici, l’object struct1 est assigné à un autre object.