web-dev-qa-db-fra.com

Pourquoi l'utilisation de tuples en C ++ n'est-elle pas plus courante?

Pourquoi personne ne semble-t-il utiliser des tuples en C++, que ce soit Boost Tuple Library ou la bibliothèque standard pour TR1? J'ai lu beaucoup de code C++, et très rarement je vois l'utilisation de tuples, mais je vois souvent beaucoup d'endroits où les tuples résoudraient de nombreux problèmes (retournant généralement plusieurs valeurs à partir de fonctions).

Les tuples vous permettent de faire toutes sortes de choses sympas comme ceci:

tie(a,b) = make_Tuple(b,a); //swap a and b

C'est certainement mieux que ça:

temp=a;
a=b;
b=temp;

Bien sûr, vous pouvez toujours faire ceci:

swap(a,b);

Mais que faire si vous souhaitez faire pivoter trois valeurs? Vous pouvez le faire avec des tuples:

tie(a,b,c) = make_Tuple(b,c,a);

Les tuples facilitent également le retour de plusieurs variables à partir d'une fonction, ce qui est probablement un cas beaucoup plus courant que l'échange de valeurs. L'utilisation de références pour renvoyer des valeurs n'est certainement pas très élégante.

Y a-t-il de gros inconvénients aux tuples auxquels je ne pense pas? Sinon, pourquoi sont-ils rarement utilisés? Sont-ils plus lents? Ou est-ce juste que les gens n'y sont pas habitués? Est-ce une bonne idée d'utiliser des tuples?

120
Zifre

Parce que ce n'est pas encore standard. Tout ce qui n'est pas standard a un obstacle beaucoup plus élevé. Pieces of Boost est devenu populaire parce que les programmeurs les réclament. (hash_map saute à l'esprit). Mais bien que Tuple soit pratique, ce n'est pas une victoire si écrasante et claire que les gens s'en soucient.

40
Alan De Smet

Une réponse cynique est que de nombreuses personnes programment en C++, mais ne comprennent pas et/ou n'utilisent pas la fonctionnalité de niveau supérieur. Parfois, c'est parce qu'ils ne sont pas autorisés, mais beaucoup n'essaient tout simplement pas (ou même ne comprennent pas).

Comme exemple sans boost: combien de personnes utilisent les fonctionnalités trouvées dans <algorithm>?

En d'autres termes, de nombreux programmeurs C++ sont simplement des programmeurs C utilisant des compilateurs C++, et peut-être std::vector et std::list. C'est une des raisons pour lesquelles l'utilisation de boost::Tuple n'est pas plus courant.

122
Trey Jackson

La syntaxe de Tuple C++ peut être un peu plus verbeuse que la plupart des gens ne le souhaiteraient.

Considérer:

typedef boost::Tuple<MyClass1,MyClass2,MyClass3> MyTuple;

Donc, si vous souhaitez faire un usage intensif des tuples, vous obtenez soit des Tuple typedefs partout, soit des noms de type ennuyeux partout. J'aime les tuples. Je les utilise quand c'est nécessaire. Mais il est généralement limité à quelques situations, comme un index à N éléments ou lors de l'utilisation de plusieurs cartes pour lier les paires d'itérateurs de plage. Et c'est généralement dans une portée très limitée.

Tout cela est très laid et hacky comparé à quelque chose comme Haskell ou Python. Lorsque C++ 0x arrive ici et que nous obtenons le mot-clé "auto", les tuples commenceront à paraître beaucoup plus attrayants.

L'utilité des tuples est inversement proportionnelle au nombre de touches nécessaires pour les déclarer, les emballer et les décompresser.

23
user21714

Pour moi, c'est l'habitude, haut la main: les tuples ne résolvent pas de nouveaux problèmes pour moi, juste quelques-uns que je peux déjà gérer très bien. L'échange de valeurs semble toujours plus facile à l'ancienne - et, plus important encore, je ne pense pas vraiment à la façon d'échanger "mieux". C'est assez bon tel quel.

Personnellement, je ne pense pas que les tuples soient une excellente solution pour renvoyer plusieurs valeurs - cela ressemble à un travail pour structs.

9
ojrac

Mais que faire si vous souhaitez faire pivoter trois valeurs?

swap(a,b);
swap(b,c);  // I knew those permutation theory lectures would come in handy.

OK, donc avec 4 valeurs etc., le n-Tuple devient finalement moins de code que les n-1 swaps. Et avec l'échange par défaut, cela fait 6 affectations au lieu des 4 que vous auriez si vous implémentiez vous-même un modèle à trois cycles, bien que j'espère que le compilateur résoudra cela pour les types simples.

Vous pouvez proposer des scénarios où les échanges sont difficiles à manier ou inappropriés, par exemple:

tie(a,b,c) = make_Tuple(b*c,a*c,a*b);

est un peu gênant à déballer.

Le fait est, cependant, qu'il existe des moyens connus de faire face aux situations les plus courantes pour lesquelles les tuples sont bons, et donc pas de grande urgence à prendre des tuples. Si rien d'autre, je ne suis pas convaincu que:

tie(a,b,c) = make_Tuple(b,c,a);

ne fait pas 6 copies, ce qui le rend tout à fait inapproprié pour certains types (les collections étant les plus évidentes). N'hésitez pas à me persuader que les tuples sont une bonne idée pour les "grands" types, en disant que ce n'est pas le cas :-)

Pour retourner plusieurs valeurs, les tuples sont parfaits si les valeurs sont de types incompatibles, mais certaines personnes ne les aiment pas s'il est possible pour l'appelant de les obtenir dans le mauvais ordre. Certaines personnes n'aiment pas du tout les valeurs de retour multiples et ne veulent pas encourager leur utilisation en les rendant plus faciles. Certaines personnes préfèrent simplement les structures nommées pour les paramètres d'entrée et de sortie, et ne pourraient probablement pas être persuadées avec une batte de baseball d'utiliser des tuples. Pas de goût.

8
Steve Jessop

Comme beaucoup de gens l'ont souligné, les tuples ne sont tout simplement pas aussi utiles que d'autres fonctionnalités.

  1. Les trucs de permutation et de rotation ne sont que des trucs. Ils sont complètement déroutants pour ceux qui ne les ont jamais vus auparavant, et comme c'est à peu près tout le monde, ces gadgets ne sont que de mauvaises pratiques en génie logiciel.

  2. Renvoyer plusieurs valeurs à l'aide de tuples est beaucoup moins auto-documenté que les alternatives - renvoyer des types nommés ou utiliser des références nommées. Sans cette auto-documentation, il est facile de confondre l'ordre des valeurs retournées, si elles sont mutuellement convertibles, et pas plus sage.

7
igorlord

Tout le monde ne peut pas utiliser boost, et TR1 n'est pas encore largement disponible.

6
Brian Neal

Lors de l'utilisation de C++ sur des systèmes embarqués, l'extraction des bibliothèques Boost devient complexe. Ils se couplent les uns aux autres, donc la taille de la bibliothèque augmente. Vous renvoyez des structures de données ou utilisez la transmission de paramètres au lieu de tuples. Lorsque vous retournez des tuples en Python la structure de données est dans l'ordre et le type des valeurs retournées, ce n'est tout simplement pas explicite.

5
DanM

Vous les voyez rarement car un code bien conçu n'en a généralement pas besoin - il n'y a pas trop de cas dans la nature où l'utilisation d'une structure anonyme est supérieure à l'utilisation d'une structure nommée. Comme tout Tuple représente vraiment une structure anonyme, la plupart des codeurs dans la plupart des situations vont avec la vraie chose.

Supposons que nous ayons une fonction "f" où un retour Tuple pourrait avoir un sens. En règle générale, ces fonctions sont généralement suffisamment compliquées pour pouvoir échouer.

Si "f" PEUT échouer, vous avez besoin d'un retour d'état - après tout, vous ne voulez pas que les appelants doivent inspecter chaque paramètre pour détecter l'échec. "f" s'inscrit probablement dans le modèle:

struct ReturnInts ( int y,z; }
bool f(int x, ReturnInts& vals);

int x = 0;
ReturnInts vals;
if(!f(x, vals)) {
    ..report error..
    ..error handling/return...
}

Ce n'est pas joli, mais regardez à quel point l'alternative est moche. Notez que j'ai toujours besoin d'une valeur d'état, mais le code n'est plus lisible et n'est pas plus court. C'est probablement aussi plus lent, car j'encourage le coût d'une copie avec le tuple.

std::Tuple<int, int, bool> f(int x);
int x = 0;
std::Tuple<int, int, bool> result = f(x); // or "auto result = f(x)"
if(!result.get<2>()) {
    ... report error, error handling ...
}

Un autre inconvénient important est caché ici: avec "ReturnInts", je peux ajouter le retour d'alter "f" en modifiant "ReturnInts" SANS MODIFIER L'INTERFACE "f". La solution Tuple n'offre pas cette fonctionnalité critique, ce qui en fait la réponse inférieure pour tout code de bibliothèque.

4
Zack Yezek

Certes, les tuples peuvent être utiles, mais comme mentionné, il y a un peu de surcharge et un obstacle ou deux que vous devez franchir avant de pouvoir vraiment les utiliser.

Si votre programme trouve constamment des endroits où vous devez retourner plusieurs valeurs ou échanger plusieurs valeurs, il peut être intéressant de suivre la route Tuple, mais sinon, il est parfois plus facile de faire les choses de manière classique.

De manière générale, tout le monde n'a pas déjà installé Boost, et je ne passerais certainement pas par les tracas de le télécharger et de configurer mes répertoires d'inclusion pour qu'ils fonctionnent uniquement pour ses installations Tuple. Je pense que vous constaterez que les personnes qui utilisent déjà Boost sont plus susceptibles de trouver des utilisations de Tuple dans leurs programmes que les utilisateurs non-Boost, et les migrants d'autres langues (Python me vient à l'esprit) sont plus susceptibles d'être simplement contrariés par le manque de tuples en C++ que d'explorer les méthodes d'ajout de la prise en charge de Tuple.

3
Zac

Comme un magasin de données std::Tuple A les pires caractéristiques à la fois d'un struct et d'un tableau; tout accès est basé sur la nième position mais on ne peut pas parcourir une Tuple en utilisant une boucle for.

Donc, si les éléments du Tuple sont conceptuellement un tableau, j'utiliserai un tableau et si les éléments ne sont pas conceptuellement un tableau, une structure (qui a nommé des éléments) est plus maintenable. (a.lastname Est plus explicatif que std::get<1>(a)).

Cela laisse la transformation mentionnée par l'OP comme le seul cas d'utilisation viable pour les tuples.

1
doron

J'ai le sentiment que beaucoup utilisent Boost.Any et Boost.Variant (avec une certaine ingénierie) au lieu de Boost.Tuple.

0
dirkgently