46 votes

Y a-t-il une raison pour que la taille des tableaux std::array soit nulle en C++11 ?

Considérons le morceau de code suivant, qui est parfaitement acceptable par un compilateur C++11 :

#include <array>
#include <iostream>

auto main() -> int {
  std::array<double, 0> A;

  for(auto i : A) std::cout << i << std::endl;

  return 0;
}

Selon la norme § 23.3.2.8 [ Tableaux de taille zéro ] :

1 Array doit fournir un support pour le cas particulier N == 0 .

2 Dans le cas où N == 0 , begin() == end() == valeur unique. La valeur de retour de
data() n'est pas spécifié.

3 L'effet de l'appel front() o back() pour un tableau de taille nulle est indénié.

4 Fonction de membre swap() doit avoir une spécification noexcept qui est équivalente à noexcept(true) .

Comme indiqué ci-dessus, la taille zéro std::array sont parfaitement autorisés en C++11, contrairement aux tableaux de taille nulle (par ex, int A[0]; ) où ils sont explicitement interdits, mais ils sont autorisés par certains compilateurs (par exemple, GCC) au prix d'un comportement indéfini.

Compte tenu de cette "contradiction", je me pose les questions suivantes :

  • Pourquoi le comité C++ a décidé d'autoriser la taille zéro ? std::array s ?

  • Y a-t-il des utilisations utiles ?

20 votes

Probablement pour être cohérent avec les conteneurs et faciliter certaines choses génériques.

1 votes

Une considération pourrait être que même si des implémentations ont des difficultés avec les tableaux de taille zéro, elles ne devraient pas avoir de difficultés avec une spécialisation partielle pour les tableaux de taille zéro. std::array<T, 0> .

2 votes

Les méta-programmes de modèles sont souvent récursifs, et il est souvent plus simple de faire en sorte que la récursion se termine à 0 plutôt qu'à 1... Mais j'admets que j'ai du mal à construire un exemple réaliste dans ce cas...

44voto

sth Points 91594

Si vous avez une fonction générique, il n'est pas bon que cette fonction s'interrompe de manière aléatoire pour des paramètres spéciaux. Par exemple, disons que vous pourriez avoir une fonction modèle qui prendrait N les éléments aléatoires forment un vecteur :

template<typename T, size_t N>
std::array<T, N> choose(const std::vector<T> &v) {
   ...
}

Rien n'est gagné si cela provoque un comportement non défini ou des erreurs de compilation si N pour une raison quelconque, s'avère être zéro.

Pour les tableaux bruts, cette restriction s'explique par le fait que vous ne voulez pas que les types avec sizeof T == 0 cela conduit à des effets étranges en combinaison avec l'arithmétique des pointeurs. Un tableau avec zéro élément aura une taille de zéro, si vous n'ajoutez pas de règles spéciales pour cela.

Mais std::array<> est une classe, et les classes ont toujours une taille > 0. Vous ne rencontrez donc pas ces problèmes avec std::array<> Il est préférable d'avoir une interface cohérente sans restriction arbitraire du paramètre de modèle.

4voto

Ben Points 4733

Une utilisation à laquelle je peux penser est le retour de tableaux de longueur nulle est possible et a une fonctionnalité à vérifier spécifiquement.

Par exemple, voir la documentation sur le std::array fonction empty() . Il a la valeur de retour suivante :

true if the array size is 0, false otherwise.

http://www.cplusplus.com/reference/array/array/empty/

Je pense que la possibilité de retourner et de vérifier les tableaux de longueur 0 est conforme à la norme pour d'autres implémentations de types stl, par exemple les vecteurs et les cartes, et est donc utile.

13 votes

Veuillez ne pas référencer cplusplus.com. La plupart des entrées sont incomplètes ou erronées. Utilisez fr.cppreference.com/w/cpp/container/array à la place.

4 votes

Mais ce n'est pas le cas dans cette affaire ? Quand est-ce que c'est incomplet ou faux ?

0 votes

@Kay Je le vois occasionnellement incomplet sur des choses plus obscures-- mais faux ? Quand ?

1voto

Dronz Points 387

Comme avec d'autres classes de conteneurs, il est utile de pouvoir disposer d'un objet qui représente un tableau de choses, et de pouvoir faire en sorte que ce tableau soit ou devienne vide. Si cela n'était pas possible, il faudrait alors créer un autre objet, ou une classe de gestion, pour représenter cet état de manière légale. Le fait que cette capacité soit déjà contenue dans toutes les classes de conteneurs est très utile. En l'utilisant, il suffit de prendre l'habitude de considérer le tableau comme un conteneur qui peut être vide, et de vérifier la taille ou l'index avant de faire référence à un membre du tableau dans les cas où il peut ne rien indiquer.

1voto

Tim B Points 19851

Il y a en fait un certain nombre de cas où vous voulez être en mesure de le faire. C'est également le cas dans de nombreux autres langages. Par exemple, Java a Collections.emptyList() qui renvoie une liste dont la taille est non seulement nulle mais qui ne peut pas être étendue, redimensionnée ou modifiée.

Un exemple d'utilisation pourrait être si vous aviez une classe représentant un bus et une liste de passagers dans cette classe. La liste pourrait être initialisée paresseusement, et n'être créée que lorsque les passagers montent à bord. Si quelqu'un appelle getPassengers() mais alors une liste vide peut être retournée plutôt que de créer une nouvelle liste à chaque fois juste pour signaler qu'elle est vide.

Renvoyer null serait aussi une bonne chose pour l'efficacité interne de la classe - mais cela rendrait la vie beaucoup plus compliquée pour tous ceux qui utilisent la classe, car chaque fois que vous appelez getPassengers() vous devrez vérifier le résultat par un contrôle nul. Au contraire, si vous obtenez une liste vide en retour, tant que votre code ne suppose pas que la liste n'est pas vide, vous n'avez pas besoin de code spécial pour gérer le fait qu'elle soit nulle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X