Maintenant que nous avons std :: array, quelles utilisations reste-t-il des tableaux de style C?

88

std::arrayest largement supérieur aux tableaux C. Et même si je veux interagir avec le code hérité, je peux simplement utiliser std::array::data(). Y a-t-il une raison pour laquelle je voudrais un jour un tableau à l'ancienne?

R. Martinho Fernandes
la source

Réponses:

60

À moins d'avoir manqué quelque chose (je n'ai pas suivi de trop près les changements les plus récents de la norme), la plupart des utilisations des tableaux de style C subsistent. std::arrayautorise l'initialisation statique, mais ne compte toujours pas les initialiseurs à votre place. Et comme la seule véritable utilisation des tableaux de style C auparavant std::arrayétait pour les tables initialisées statiquement comme:

MyStruct const table[] =
{
    { something1, otherthing1 },
    //  ...
};

en utilisant les fonctions habituelles beginet de endmodèle (adoptées en C ++ 11) pour les parcourir. Sans jamais parler de la taille, que le compilateur détermine à partir du nombre d'initialiseurs.

EDIT: Une autre chose que j'ai oubliée: les chaînes littérales sont toujours des tableaux de style C; c'est à dire avec le type char[]. Je ne pense pas que quiconque exclurait l'utilisation de chaînes littérales simplement parce que nous l'avons fait std::array.

James Kanze
la source
7
Vous pouvez écrire un modèle de fonction variadique qui construit des tableaux sans que vous ayez à spécifier la longueur.
rightfold
2
Avec la déduction de modèle de classe C ++ 17, la déduction automatique du nombre d'initialiseurs est prise en charge. Par exemple, "auto a = std :: array {1, 2, 3};"
Ricky65
Nitpick: le type de littéraux de chaîne estconst char[]
Bulletmagnet
30

Non, euh, dis-le franchement. Et en 30 caractères.

Bien sûr, vous avez besoin de tableaux C pour implémenter std::array , mais ce n'est pas vraiment une raison pour laquelle un utilisateur voudrait un tableau C. De plus, non, std::arrayn'est pas moins performant qu'un tableau C et dispose d'une option pour un accès vérifié par les limites. Et enfin, il est tout à fait raisonnable pour tout programme C ++ de dépendre de la bibliothèque Standard - c'est en quelque sorte le point d'être Standard - et si vous n'avez pas accès à une bibliothèque Standard, alors votre compilateur est non conforme et le La question est étiquetée "C ++", pas "C ++ et les choses non-C ++ qui manquent la moitié de la spécification parce qu'elles la jugent inappropriée.".

Chiot
la source
1
Hm. Que faire si vous écrivez du code C ++ qui est appelé depuis un autre langage et a besoin de quelque chose à passer en tant que paramètre?
asveikau
3
Les implémentations autonomes sont autorisées à omettre la quasi-totalité de la bibliothèque standard et à rester conformes. J'aurais cependant de sérieux doutes sur un compilateur qui ne pourrait pas être implémenté std::arraydans une implémentation autonome C ++ 11.
Dennis Zickefoose
11
C ++ 0x Final Draft (Document N3092) § 1.4.7 "Deux types d'implémentations sont définis: hébergée et autonome. Pour une implémentation hébergée, la présente Norme internationale définit l'ensemble des bibliothèques disponibles. Une implémentation autonome est une implémentation dans laquelle l'exécution peut ont lieu sans l’avantage d’un système d’exploitation et ont un ensemble de bibliothèques défini par l’implémentation qui inclut certaines bibliothèques de prise en charge des langues. »La STL n’est pas incluse en tant que bibliothèque« de prise en charge des langues »dans un compilateur autonome
Earlz
24

On dirait que l'utilisation de tableaux multidimensionnels est plus facile avec les tableaux C que std::array. Par exemple,

char c_arr[5][6][7];

par opposition à

std::array<std::array<std::array<char, 7>, 6>, 5> cpp_arr;

Également en raison de la propriété de décroissance automatique des tableaux C, c_arr[i]dans l'exemple ci-dessus, la décomposition en un pointeur se fera et il vous suffit de passer les dimensions restantes comme deux paramètres supplémentaires. Mon point est que la c_arrcopie n'est pas coûteuse. Cependant, la cpp_arr[i]copie sera très coûteuse.

Sumant
la source
1
Cependant, vous pouvez passer un multidimensionnel arrayà une fonction sans perdre de dimensions. Et si vous le transmettez à un modèle de fonction, cette fonction peut déduire à la fois la dimension et la taille de chaque dimension, ou juste l'une des deux. Cela peut être intéressant pour les bibliothèques de modèles scientifiques qui travaillent principalement sur des dimensions arbitraires.
Sebastian Mach
29
un simple template <typename T, int M, int N> using array2d = std::array<std::array<T, N>, M>;devrait résoudre l'un de ces problèmes.
Miles Rout
6
Votre exemple c_arrest très coûteux à copier! Vous devez fournir le code pour le faire vous-même. Le pointeur vers lequel il se désintègre est un équivalent plus proche d'une référence qu'une copie et vous pouvez l'utiliser std::arraypour passer une référence si c'est ce que vous voulez.
ChetS
1
@MilesRout techniquement , cela ne devrait-il pas être à la std::size_tplace int? désolé pour pinailler, mais cela le rendrait universel.
robbie
1
@ robbie0630 Oui, vous pouvez le faire size_tsi vous le souhaitez, même si je ne peux pas imaginer qu'il existe de nombreux scénarios où des tableaux de plus de 4 milliards de lignes ou de colonnes sont nécessaires.
Miles Rout
13

Comme Sumant l'a dit, les tableaux multidimensionnels sont beaucoup plus faciles à utiliser avec les tableaux C intégrés qu'avec std::array.

Lorsqu'elles sont imbriquées, elles std::arraypeuvent devenir très difficiles à lire et inutilement verbeuses.

Par exemple:

std::array<std::array<int, 3>, 3> arr1; 

par rapport à

char c_arr[3][3]; 

Notez également que begin(), end()et size()tous renvoient des valeurs sans signification lorsque vous imbriquez std::array.

Pour ces raisons, j'ai créé mes propres conteneurs de tableaux multidimensionnels de taille fixe, array_2det array_3d. Ils sont analogues àstd::array mais pour des tableaux multidimensionnels de 2 et 3 dimensions. Ils sont plus sûrs et n'ont pas de pires performances que les tableaux multidimensionnels intégrés. Je n'ai pas inclus de conteneur pour les tableaux multidimensionnels avec des dimensions supérieures à 3 car ils sont rares. En C ++ 0x, une version de modèle variadique peut être créée qui prend en charge un nombre arbitraire de dimensions.

Un exemple de la variante bidimensionnelle:

//Create an array 3 x 5 (Notice the extra pair of braces) 

fsma::array_2d <double, 3, 5> my2darr = {{
    { 32.19, 47.29, 31.99, 19.11, 11.19},
    { 11.29, 22.49, 33.47, 17.29, 5.01 },
    { 41.97, 22.09, 9.76, 22.55, 6.22 }
}};

La documentation complète est disponible ici:

http://fsma.googlecode.com/files/fsma.html

Vous pouvez télécharger la bibliothèque ici:

http://fsma.googlecode.com/files/fsma.zip

Ricky65
la source
4
Les tableaux de style C de taille fixe sont faciles, mais si vous voulez varier les dimensions, les choses se compliquent. Par exemple, étant donné arr[x][y], vous ne pouvez pas dire s'il arrs'agit d'un tableau de tableaux, d'un tableau de pointeurs, d'un pointeur vers un tableau ou d'un pointeur vers un pointeur; tout pour les implémentations sont légitimes, en fonction de vos besoins. Et probablement la plupart des cas d'utilisation réels pour les tableaux multidimensionnels nécessitent que la taille soit déterminée au moment de l'exécution.
Keith Thompson
J'aimerais voir cette implémentation de modèle variadique de tableaux à n dimensions! La méta-programmation à son meilleur!
steffen
3
@steffen J'ai fait une tentative il y a quelques années. Vous pouvez le consulter ici: code.google.com/p/fsma/source/browse/trunk/… . Cas de test ici: code.google.com/p/fsma/source/browse/trunk/… . Je suis sûr que cela peut être fait beaucoup mieux.
Ricky65
5

Les tableaux de style C qui sont disponibles en C ++ sont en fait beaucoup moins polyvalents que les véritables tableaux C. La différence est qu'en C, les types de tableaux peuvent avoir des tailles d' exécution . Ce qui suit est du code C valide, mais il ne peut être exprimé ni avec des tableaux de style C C ++ ni avec les array<>types C ++ :

void foo(int bar) {
    double tempArray[bar];
    //Do something with the bar elements in tempArray.
}

En C ++, vous devrez allouer le tableau temporaire sur le tas:

void foo(int bar) {
    double* tempArray = new double[bar];
    //Do something with the bar elements behind tempArray.
    delete[] tempArray;
}

Cela ne peut pas être réalisé avec std::array<>, car ce barn'est pas connu au moment de la compilation, cela nécessite l'utilisation de tableaux de style C en C ++ ou de std::vector<>.


Alors que le premier exemple pourrait être relativement facilement exprimé en C ++ (bien que nécessitant new[]et delete[]), ce qui suit ne peut pas être réalisé en C ++ sans std::vector<>:

void smoothImage(int width, int height, int (*pixels)[width]) {
    int (*copy)[width] = malloc(height*sizeof(*copy));
    memcpy(copy, pixels, height*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y][x] = //compute smoothed value based on data around copy[y][x]
        }
    }
    free(copy);
}

Le fait est que les pointeurs vers les tableaux de lignes int (*)[width]ne peuvent pas utiliser une largeur d'exécution en C ++, ce qui rend tout code de manipulation d'image beaucoup plus compliqué en C ++ qu'en C.Une implémentation C ++ typique de l'exemple de manipulation d'image ressemblerait à ceci:

void smoothImage(int width, int height, int* pixels) {
    int* copy = new int[height*width];
    memcpy(copy, pixels, height*width*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y*width + x] = //compute smoothed value based on data around copy[y*width + x]
        }
    }
    delete[] copy;
}

Ce code fait exactement les mêmes calculs que le code C ci-dessus, mais il doit effectuer le calcul d'index à la main partout où les indices sont utilisés . Pour le cas 2D, cela est toujours faisable (même si cela offre de nombreuses possibilités de se tromper dans le calcul de l'indice). Cela devient vraiment méchant dans le cas de la 3D, cependant.

J'aime écrire du code en C ++. Mais chaque fois que j'ai besoin de manipuler des données multidimensionnelles, je me demande vraiment si je dois déplacer cette partie du code vers C.

cmaster - réintégrer monica
la source
7
Il convient de noter qu'au moins Clang et GCC supportent les VLA en C ++.
Janus Troelsen
@JanusTroelsen et aussi qu'ils sont horriblement limités dans les types d'éléments qu'ils prennent en charge.
rightfold
C11 ne rend-il pas VLA facultatif? Si tel est le cas, je pense que votre réponse est trompeuse. Ce serait correct quand C99 était la norme mais pas C11.
Z boson
1
@Zboson C99 est un standard C, et il existe des compilateurs qui implémentent ses fonctionnalités VLA ( gccpar exemple). C11 a rendu facultatifs un certain nombre de choses intéressantes, et je ne pense pas que ce soit parce qu'ils veulent interdire cette fonctionnalité. J'ai tendance à voir cela comme un signe qu'ils voulaient abaisser le niveau d'écriture d'un compilateur entièrement conforme aux normes: les VLA sont assez difficiles à implémenter, et beaucoup de code peut s'en passer, il est donc logique pour un nouveau compilateur sur un nouveau plate-forme pour ne pas avoir à mettre en œuvre les VLA tout de suite.
cmaster - réintégrer monica le
-1

Peut-être que le std::arrayn'est pas lent. Mais j'ai fait des analyses comparatives en utilisant simple store et en lisant depuis le std :: array; Voir les résultats de référence ci-dessous (sur W8.1, VS2013 Update 4):

ARR_SIZE: 100 * 1000
Avrg = Tick / ARR_SIZE;

test_arr_without_init
==>VMem: 5.15Mb
==>PMem: 8.94Mb
==>Tick: 3132
==>Avrg: 0.03132
test_arr_with_init_array_at
==>VMem: 5.16Mb
==>PMem: 8.98Mb
==>Tick: 925
==>Avrg: 0.00925
test_arr_with_array_at
==>VMem: 5.16Mb
==>PMem: 8.97Mb
==>Tick: 769
==>Avrg: 0.00769
test_c_arr_without_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 358
==>Avrg: 0.00358
test_c_arr_with_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 305
==>Avrg: 0.00305

Selon les marques négatives, le code que j'ai utilisé est dans le pastebin ( lien )

Le code de classe de référence est ici ;

Je ne sais pas grand-chose sur les benchmarks ... Mon code est peut-être défectueux

K'Prime
la source
6
Benchmark des résultats sans code de benchmark, ni indicateurs de compilation? Allez, tu peux faire mieux.
R. Martinho Fernandes
FWIW, juste ce petit bout de code montre déjà que le benchmark est gravement défectueux. Un compilateur assez intelligent transformera le tout enlong test_arr_without_init() { return ARR_SIZE; }
R. Martinho Fernandes
C'était juste un exemple. Je pensais que ce n'était pas grave. J'ai changé le code pour renvoyer void, version utilisée dans VS 2013, avec / O2 / Ot / Gl.
K'Prime
La suppression de la valeur de retour signifie que le compilateur peut transformer le tout en void test_arr_without_init() {}maintenant. Vous devez vraiment sauter à travers des cercles pour vous assurer que le code que vous mesurez est le code que vous souhaitez mesurer.
R. Martinho Fernandes
-6
  1. pour implémenter quelque chose comme std::array
  2. si vous ne voulez pas utiliser la STL ou ne pouvez pas
  3. Pour la performance
Lou Franco
la source
27
Dites-moi comment std::arraysera moins performant qu'un tableau C.
Xeo
2
De wikipedia : "L'implémentation du tableau n'est pas requise pour effectuer une vérification liée. Cependant, l'implémentation dans boost le fait pour l'opérateur [], mais pas pour les itérateurs." - donc l'opérateur [] est plus lent. Je n'ai pas regardé les implémentations, mais n'importe quel code de l'implémentation pourrait gêner l'optimiseur.
Lou Franco
19
@Aaron McDaid: C'est seulement at()dedans, ce n'est pas dedans operator[], juste comme std::vector. Il n'y a pas de diminution des performances ou de gonflement du code std::array, le compilateur est conçu pour optimiser ce genre de chose. Et, bien sûr, l'ajout de la fonction vérifiée est un excellent outil de débogage et un grand avantage. @Lou Franco: Tout le code C ++ peut dépendre de la bibliothèque Standard - c'est un peu ce à quoi il sert. @Earlz: Si vous n'avez pas STL disponible, alors ce n'est pas C ++, et c'est la fin de cela.
Chiot
6
@Earlz: Le standard C ++ contient la bibliothèque Standard. Si vous ne pouvez pas utiliser la bibliothèque, elle n'est pas conforme. Et deuxièmement, vous devez avoir un sacré compilateur de merde pour que l'utilisation de std::arraysoit plus grande que l'utilisation du tableau C équivalent.
Chiot
5
@Earlz: Il y a une grande différence entre "pas tout à fait conforme" et "fonctionnalités manquantes qui représentent des centaines de pages dans les spécifications".
Chiot