Compilation du code suivant et erreur de type illegal
.
int main()
{
// Compilation error - switch expression of type illegal
switch(std::string("raj"))
{
case"sda":
}
}
Vous ne pouvez pas utiliser la chaîne dans les deux switch
ou case
. Pourquoi? Existe-t-il une solution qui fonctionne bien pour prendre en charge une logique similaire à l'activation des chaînes?
c++
string
switch-statement
yesraaj
la source
la source
QMetaEnum
Réponses:
La raison en est liée au système de type. C / C ++ ne prend pas vraiment en charge les chaînes en tant que type. Il prend en charge l'idée d'un tableau de caractères constants mais il ne comprend pas vraiment la notion de chaîne.
Pour générer le code d'une instruction switch, le compilateur doit comprendre ce que cela signifie pour deux valeurs égales. Pour des éléments comme les entiers et les énumérations, il s'agit d'une comparaison de bits triviale. Mais comment le compilateur doit-il comparer 2 valeurs de chaîne? Sensible à la casse, insensible, sensible à la culture, etc.
En outre, les instructions de commutateur C / C ++ sont généralement générées sous forme de tables de branche . Il n'est pas aussi facile de générer une table de branche pour un commutateur de style de chaîne.
la source
std::string
littéraux ont été ajoutés. C'est surtout historique. Mais un problème qui ne vient à l' esprit est que , avec la façon dontswitch
fonctionne actuellement, en doublecase
s doit être détectée lors de la compilation; cependant, cela peut ne pas être aussi facile pour les chaînes (compte tenu de la sélection des paramètres régionaux au moment de l'exécution, etc.). Je suppose qu'une telle chose devrait nécessiter desconstexpr
cas, ou ajouter un comportement non spécifié (jamais quelque chose que nous voulons faire).std::string
valeurs ou même unstd::string
avec un tableau de caractères const (à savoir en utilisant l'opérateur ==), il n'y a aucune raison technique qui empêcherait le compilateur de générer une instruction switch pour tout type qui fournit cet opérateur. Cela ouvrirait quelques questions sur des choses comme la durée de vie des étiquettes, mais dans l'ensemble, c'est principalement une décision de conception de la langue, pas une difficulté technique.Comme mentionné précédemment, les compilateurs aiment construire des tables de recherche qui optimisent les
switch
instructions pour se rapprocher de O (1) dans la mesure du possible. Combinez cela avec le fait que le langage C ++ n'a pas de type chaîne -std::string
fait partie de la bibliothèque standard qui ne fait pas partie du langage en soi.Je vais vous proposer une alternative que vous voudrez peut-être envisager, je l'ai utilisée dans le passé à bon escient. Au lieu de basculer sur la chaîne elle-même, basculez sur le résultat d'une fonction de hachage qui utilise la chaîne en entrée. Votre code sera presque aussi clair que de basculer sur la chaîne si vous utilisez un ensemble de chaînes prédéterminé:
Il y a un tas d'optimisations évidentes qui suivent à peu près ce que le compilateur C ferait avec une instruction switch ... drôle comment cela se passe.
la source
C ++
fonction de hachage constexpr:
la source
operator ""
pour rendre le code plus beau.constexpr inline unsigned int operator "" _(char const * p, size_t) { return hash(p); }
Et utilisez-le commecase "Peter"_: break;
DémoMise à jour C ++ 11 apparemment pas @MarmouCorp ci-dessus mais http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htm
Utilise deux mappes pour convertir entre les chaînes et l'énumération de classe (mieux que l'énumération simple parce que ses valeurs sont limitées à l'intérieur, et recherche inversée pour de beaux messages d'erreur).
L'utilisation de statique dans le code codeguru est possible avec le support du compilateur pour les listes d'initialisation, ce qui signifie VS 2013 plus. gcc 4.8.1 était d'accord, je ne sais pas combien de temps il serait compatible.
...
la source
Le problème est que, pour des raisons d'optimisation, l'instruction switch en C ++ ne fonctionne que sur les types primitifs et vous ne pouvez les comparer qu'avec les constantes de temps de compilation.
Vraisemblablement, la raison de la restriction est que le compilateur est capable d'appliquer une certaine forme d'optimisation en compilant le code vers une instruction cmp et un goto où l'adresse est calculée en fonction de la valeur de l'argument au moment de l'exécution. Étant donné que les branchements et les boucles et ne fonctionnent pas bien avec les processeurs modernes, cela peut être une optimisation importante.
Pour contourner cela, je crains que vous n'ayez à recourir à des déclarations if.
la source
std::string
et les autres premiers citoyens dans le langage et les soutenir dans l'instruction switch avec un algorithme efficace.std::map
+ Modèle lambdas C ++ 11 sans énumérationsunordered_map
pour le potentiel amortiO(1)
: Quelle est la meilleure façon d'utiliser un HashMap en C ++?Production:
Utilisation à l'intérieur de méthodes avec
static
Pour utiliser ce modèle efficacement à l'intérieur des classes, initialisez la carte lambda de manière statique, sinon vous payez à
O(n)
chaque fois pour la construire à partir de zéro.Ici, nous pouvons nous en sortir avec l'
{}
initialisation d'unestatic
variable de méthode: Variables statiques dans les méthodes de classe , mais nous pourrions également utiliser les méthodes décrites dans: constructeurs statiques en C ++? J'ai besoin d'initialiser des objets statiques privésIl était nécessaire de transformer la capture du contexte lambda
[&]
en argument, ou cela n'aurait pas été défini: const statique lambda automatique utilisé avec capture par référenceExemple qui produit la même sortie que ci-dessus:
la source
switch
déclaration. La duplication des valeurs de cas dans uneswitch
instruction est un échec de compilation. L'utilisationstd::unordered_map
silencieuse accepte les valeurs en double.En C ++ et C, les commutateurs ne fonctionnent que sur les types entiers. Utilisez plutôt une échelle if else. C ++ aurait évidemment pu implémenter une sorte de déclaration swich pour les chaînes - je suppose que personne n'en a pensé la peine, et je suis d'accord avec elles.
la source
Pourquoi pas? Vous pouvez utiliser une implémentation de commutateur avec une syntaxe équivalente et la même sémantique. Le
C
langage n'a pas du tout d'objets et de chaînes d'objets, mais les chaînes enC
sont des chaînes terminées par des valeurs nulles référencées par un pointeur. LeC++
langage a la possibilité de faire des fonctions de surcharge pour la comparaison d'objets ou la vérification des égalités d'objets. CommeC
enC++
est suffisamment souple pour avoir un tel interrupteur pour les chaînes deC
langue et pour les objets de tout type que le soutien ou vérifier l' égalité comparaison de laC++
langue. Et moderneC++11
permet d'avoir cette implémentation de commutateur suffisamment efficace.Votre code sera comme ceci:
Il est possible d'utiliser des types plus compliqués par exemple
std::pairs
ou des structures ou classes qui prennent en charge les opérations d'égalité (ou des comarisions pour le mode rapide ).Caractéristiques
Les différences de Sintax avec le changement de langue sont
Pour la
C++97
langue utilisée la recherche linéaire. PourC++11
et plus moderne possible d'utiliser lequick
mode de recherche d'arbre wuth où la déclaration de retour dans CASE n'est plus autorisée. L'C
implémentation du langage existe lorsquechar*
des comparaisons de types et de chaînes terminées par zéro sont utilisées.En savoir plus sur cette implémentation de commutateur.
la source
Pour ajouter une variante en utilisant le conteneur le plus simple possible (pas besoin d'une carte ordonnée) ... Je ne me dérangerais pas avec une énumération - mettez simplement la définition du conteneur juste avant le commutateur pour qu'il soit facile de voir quel nombre représente quel cas.
Cela effectue une recherche hachée dans le
unordered_map
et utilise l'associéint
pour piloter l'instruction switch. Devrait être assez rapide. Notez qu'ilat
est utilisé à la place de[]
, car j'ai créé ce conteneurconst
. L'utilisation[]
peut être dangereuse - si la chaîne n'est pas dans la carte, vous créerez un nouveau mappage et vous pourriez vous retrouver avec des résultats indéfinis ou une carte en croissance continue.Notez que la
at()
fonction lèvera une exception si la chaîne n'est pas dans la carte. Donc, vous voudrez peut-être d'abord tester en utilisantcount()
.La version avec un test pour une chaîne non définie suit:
la source
Je pense que la raison en est qu'en C, les chaînes ne sont pas des types primitifs, comme l'a dit tomjen, pensez dans une chaîne comme un tableau de caractères, donc vous ne pouvez pas faire des choses comme:
la source
En c ++, les chaînes ne sont pas des citoyens de première classe. Les opérations de chaîne sont effectuées via une bibliothèque standard. Je pense que c'est la raison. En outre, C ++ utilise l'optimisation de table de branche pour optimiser les instructions de casse de commutateur. Jetez un œil au lien.
http://en.wikipedia.org/wiki/Switch_statement
la source
En C ++, vous ne pouvez utiliser qu'une instruction switch sur int et char
la source
long
etlong long
, qui ne se transformera pas enint
. Il n'y a aucun risque de troncature là-bas.la source
dans de nombreux cas, vous pouvez faire un travail supplémentaire en tirant le premier caractère de la chaîne et en l'activant. peut finir par devoir faire un switch imbriqué sur charat (1) si vos cas commencent avec la même valeur. toute personne lisant votre code apprécierait un indice, car la plupart d'entre eux chercheraient juste si-sinon-si
la source
Solution de contournement plus fonctionnelle au problème du commutateur:
la source
Vous ne pouvez pas utiliser de chaîne dans le cas du commutateur. Seuls les caractères int et char sont autorisés. Au lieu de cela, vous pouvez essayer enum pour représenter la chaîne et l'utiliser dans le bloc de cas de commutateur comme
Utilisez-le dans l'instruction swich case.
la source
Les commutateurs ne fonctionnent qu'avec des types intégraux (int, char, bool, etc.). Pourquoi ne pas utiliser une carte pour associer une chaîne à un nombre, puis utiliser ce numéro avec le commutateur?
la source
C'est parce que C ++ transforme les commutateurs en tables de saut. Il effectue une opération triviale sur les données d'entrée et saute à la bonne adresse sans comparer. Puisqu'une chaîne n'est pas un nombre, mais un tableau de nombres, C ++ ne peut pas en créer une table de saut.
(code de wikipedia https://en.wikipedia.org/wiki/Branch_table )
la source
cmp
/jcc
mise en œuvre peut être tout aussi valable selon la norme C ++.