Compte tenu du programme suivant:
#include <iostream> #include <string> using namespace std; struct GenericType{ operator string(){ return "Hello World"; } operator int(){ return 111; } operator double(){ return 123.4; } }; int main(){ int i = GenericType(); string s = GenericType(); double d = GenericType(); cout << i << s << d << endl; i = GenericType(); s = GenericType(); //This is the troublesome line d = GenericType(); cout << i << s << d << endl; }
4 Réponses :
Je crois que GCC et SLIG sont corrects.
Il y a deux Tous deux de ces < Code> Opérateur = Code> Les surcharges nécessitent une conversion définie par l'utilisateur de votre argument de type P> P> P> L'important chose est que les deux surcharges nécessitent une conversion définie par l'utilisateur. Déterminer si l'une de ces conversions est meilleure que l'autre, nous pouvons rechercher les règles de résolution de la surcharge, en particulier la règle suivante de C ++ 11 §13.3.3.2 / 3 (reformaté pour la clarté): P> séquence de conversion définie par l'utilisateur Ils contiennent la même fonction de conversion définie par l'utilisateur ou la même initialisation du constructeur ou de l'agrégat et p>
li>
La deuxième séquence de conversion standard de Notez qu'un et em> joint les deux parties de la règle, de sorte que les deux parties doivent être satisfaites. La première partie de la règle n'est pas satisfaite: les deux séquences de conversion définies par l'utilisateur utilisent différentes fonctions de conversion définies par l'utilisateur. P> Par conséquent, aucune conversion n'est meilleure, et l'appel est ambigu. P> < P> [Je n'ai pas de bonne suggestion sur la manière de résoudre le problème sans modifier la définition de Il y avait un rapport de bogue GCC dans lequel ce problème a été décrit et résolu comme par conception: < Un href = "http://gcc.gnu.org/bugzilla/show_bug.cgi?id=14830" rel = "noreferrer"> compilateur diagnostite de manière incorrecte la surcharge de l'opérateur ambiguë. p> p> opérateur = code> surcharges en lecture: p>
généricype code>. (1) strong> nécessite l'utilisation de la conversion sur
chaîne code>. (2) strong> nécessite l'utilisation de la conversion sur
int code>, suivie d'une conversion standard sur
char code>. p>
u1 code> est une meilleure séquence de conversion qu'une autre séquence de conversion définie par l'utilisateur
u2 code> si p>
u1 code> est meilleure que la seconde séquence de conversion standard de
U2 code>. P>
li>
ol>
blockQuote>
principal () code>. Les conversions implicites ne sont généralement pas une bonne idée; Ils sont parfois très utiles, mais plus souvent, ils risquent de causer des ambiguïtés de surcharge ou un autre comportement étrange de surcharge étrange.] p>
J'aimerais indiquer que l'en quelque sorte Visual Studio parvient à le traiter correctement: String Val; val = 65; COUT << (VAL == 'A'); Je me demande si c'est une différence de résolution du compilateur. Je suppose que vous avez raison que GCC et Scrang sont corrects, je suppose que je vais suivre un bogue avec Microsoft et voir si c'est le cas. J'espérais vraiment qu'une sorte d'astuce indirecte pour gérer correctement la surcharge dans l'attribution directe à un int sans avoir besoin d'une distribution, mais pour déshonver la conversion implicite si les types ne correspondent pas précisément.
Connect.Microsoft.com/visualstudio/feedback/Détails/743685/...
Microsoft a eu l'un de leurs développeurs confirmer ce défaut. Je pense que c'est une honte personnellement, je pense que la définition de la résolution de la surcharge de la norme pourrait probablement faire un meilleur travail de résolution de ce type d'ambiguïté personnellement. Dans le cas de deux chaînes d'appels impliquant un utilisateur spécifié Cast, celui qui entraîne le résultat précis pourrait facilement être considéré comme le meilleur (sur un qui nécessite une nouvelle distribution entre types de pod).
Je crois que GCC est faux. Dans le livre "Le langage de programmation C ++" de Bjarne Stroustrup, il y a tout un chapitre consacré à la surcharge de l'opérateur. Dans la section 11.4.1, le premier paragraphe dit que:
"Une affectation d'une valeur de type V à un objet de classe X est légale s'il y a un opérateur d'attribution x :: opérateur = (z) de sorte que v est z ou une conversion unique de V sur Z. L'initialisation est traitée de manière équivalente. " p>
dans votre exemple, GCC accepte" String S = généricype (); " Pourtant, rejette "s = généraliste ();", donc il ne traite évidemment pas l'affectation de la même manière que l'initialisation. C'était mon premier indice que quelque chose est absent dans GCC. P>
GCC rapporte 3 candidats à la conversion de généraliste de la cordes dans la mission, tous dans basic_string.h. L'une est la conversion correcte, l'un des rapports informatiques n'est pas valide et la troisième provoque l'ambiguïté. Il s'agit de la surcharge de l'opérateur dans basic_string.h qui provoque l'ambiguïté: p> Ce n'est pas une conversion valide EDIT: Je ne savais pas que l'attribution d'un entier à une chaîne était en fait légal, comme un entier Peut être converti en un char, lequel peut être affecté à une chaîne (bien qu'une chaîne ne puisse pas être initialisée à un caractère!). Généricype définit une conversion vers un INT, ce qui rend ce membre un candidat valide. Cependant, je maintiens toujours que ce n'est pas une conversion valable, la raison étant que l'utilisation de cette conversion entraînerait deux conversions implicites définies par l'utilisateur pour la mission, d'abord du générateur à int, puis de Int à la chaîne. Comme indiqué dans le même livre 11.4.1, "Un seul niveau de conversion implicite définie par l'utilisateur est légal." P> p> car elle accepte un opérande qui ne correspond pas à un opérande qui ne correspond pas à la type d'objet qui lui est transmis. Dans aucun endroit, il n'ya pas d'affectation à une tentative de charcuterie, cette conversion ne devrait donc pas être un candidat beaucoup moins cher qui provoque une ambiguïté. GCC semble mélanger le type de modèle avec le type d'opérande dans son membre. Grève> p>
Je crois que la raison pour laquelle il n'échoue pas la construction de GCC n'est pas à cause de l'incohérence interne avec le compilateur, mais que la classe de base_string n'a pas de constructeur qui prend _chart (seul un opérateur d'affectation qui). Je suppose que si cela faisait alors cela échouerait également sur la construction.
Ce serait parce que l'initialisation d'un caractère à une chaîne est une exception à cette règle. Initialiser une chaîne avec un caractère n'est pas valide, mais l'attribution d'un caractère à une chaîne est valide, comme indiqué dans le même livre de la section 20.3.7.
L'étant comparé est de type chaîne de type, mais l'opérande de cette surcharge prend un char. Pour que cela soit un candidat acceptable, un caractère devrait être initialisé avec la valeur d'une chaîne. Comme: Char c = string (v). Cela n'a aucun sens! Bien que vous puissiez attribuer un caractère à une chaîne, vous ne pouvez pas attribuer une chaîne à un caractère, qui est la conversion nécessaire pour ce candidat. Comme cette conversion n'est pas valide, cela devrait donc être ce candidat et il ne devrait donc pas y avoir d'ambiguïté.
J'ai regardé les chapitres. Je vois ce que vous voulez dire, mais le générateur peut être implicitement converti en int, et INT peut être converti en un caractère (comme un caractère peut être converti en int). Pour clarifier, je crois que l'ambiguïté est entre les deux conversions: 1) String :: Opérateur = (static_cast
Si mon explication ci-dessus est correcte, cela signifie qu'il existe une divergence de compilateur où Visual Studio reconnaît qu'il faut une inconvénalité de l'option 1 que l'option 2 et résout ainsi l'ambiguïté de cette manière, mais GCC et Clang n'utilisent pas cette information à résoudre l'ambiguïté. Si tel est le cas (et je crois que c'est), l'un ou l'autre est incorrect (ou cela peut être indéfini, mais je doute que c'est).
Si c'est GCC / CLANG qui gère de manière incorrecte la substitution, la réponse est que j'ai écrit ce qui précède correctement et qu'ils ont besoin de réparer leur compilateur, si Visual Studio a le bogue puis je suis intéressé par un travail intelligent / académique qui existe si l'on existe. , ou une explication de la raison pour laquelle il est réellement impossible (plutôt que pour pourquoi ce n'est pas une bonne idée.)
"Le généricype peut être implicitement converti en int, et int peut être converti en un char" qui peut ensuite être converti en une chaîne, vous êtes juste là. Cependant, à ce stade, nous sommes à 2 conversions implicites définies par l'utilisateur (génératrice générale-> Int-> String). En se référant à 11.4.1 "Un seul niveau de conversion implicite défini par l'utilisateur est légal", toujours invalidant ce candidat.
Non, il n'y a toujours qu'une conversion définie par l'utilisateur dans ce cas. N'oubliez pas que le type cible n'est pas chaîne code>, il est
char code>, car c'est le type du paramètre de fonction. Par conséquent, il y a une conversion définie par l'utilisateur (
généricype code> sur
int code>) suivie d'une conversion standard (
int code> sur
char code> ). J'ai ajouté à ma réponse à la langue exacte de C ++ 11 qui exige que l'appel est ambigu.
Ma question est la suivante: Comment puis-je résoudre cette ambiguïté sans modifier le contenu de la principale? p> blockQuote>
Créez votre propre classe nommée
String code> qui n'a pas d'opérateur code> ambigu = code>, puis ne
en utilisant code> le
std code> un. p>
Évidemment, ce n'est pas une très bonne solution, mais cela fonctionne et
principal code> ne doit pas changer. P>
Je ne pense pas que vous puissiez obtenir le comportement que vous voulez d'autre manière. P>
Ceci est précis, mais pas aussi utile que la question que j'ai finalement attribuée à une prime. Merci, cependant, vous avez fourni une réponse correcte et raisonnable.
Cette solution fonctionne et une solution plus générale. Je pense que vous n'avez pas besoin de créer des opérateurs pour chaque type arithmétique, car les conversions implicites feront le tour. P> // ...
template <typename T, typename = std::enable_if_t
<std::is_arithmetic<T>::value && !std::is_same<T, char>::value>>
operator T() const
{
return std::stod("123.4");
}
//...
Merci beaucoup pour cette réponse! En fait, c'est la seule solution qui a fonctionné pour moi. Je crois que cela est dû au modèle introduisant un autre niveau d'indirection, non?
Les conversions implicites sont une bonne source de problèmes. Reconsidérer si vous voulez vraiment cela ...
Je fais. Je suis surtout intéressé par cela pour les curiosités à ce stade.
Vous pouvez également utiliser
opérateur explicite int () code> etc. en C ++ 11. Cela empêche les erreurs aussi bien que l'utilisation d'une fonction
gettype () code>, car l'utilisateur doit faire explicitement.
Dans une version antérieure de votre commentaire, vous prétendez que vous ne voulez que cela pour la tâche et l'initialisation et que vous avez demandé si des conversions pourraient être limitées à ces deux opérations. Ils ne peuvent pas. Cela fait partie du problème des conversions, ils lancent dans des circonstances que vous pourriez ne pas vouloir qu'ils. Vous avez également inclus le modèle
alternatif t get (); code>, eh bien, si vous voulez affectation i> considérer
modèle VOID assigno (T &) code> comme cela rendra la syntaxe utilisateur plus sympathique (le compilateur déduira le type)
David, j'apprécie votre préoccupation, j'ai édité mon commentaire après avoir examiné exactement ce que vous venez d'essayer de clarifier. J'ai envie d'investir beaucoup d'efforts pour tenter de rejeter une question valable. Qu'est-ce qui compte ce que l'application est pour vous quand même? Je demande une question concise et je cherche une réponse concise.
@Mooing Duck: C'est une esquive concise. Découvrir l'incohérence du compilateur avec quelque chose qui ne devrait pas être un comportement indéfini (la résolution de la surcharge) est suffisante d'une raison à laquelle cela doit être répondu correctement. Déterminer s'il est possible de faire à travers les fournisseurs et de la compréhension de la langue. Dans tous les droits, il s'agit d'une question valable et des commentaires offerts comme les vôtres ne sont tout simplement pas appréciés ni recherchés.
@ M2TM: Ce n'est pas un comportement non défini, cela ne parvient pas à compiler. La façon d'obtenir ce que vous voulez est (comme David a dit) d'utiliser des opérateurs de conversion nommés ou explicites. Je ne sais pas pourquoi vous jeûnez simplement ce qu'il a dit, puisqu'il est i> la bonne réponse. (J'admets mon commentaire est né d'une irritation)
Je ne comprends pas pourquoi vous êtes irrité. Je n'ai ni essayé d'offenser, ni essayé d'ignorer les conseils. Je suis légitimement curieux et j'ai posé une belle question. S'il n'est tout simplement pas possible d'atteindre cette syntaxe, c'est une chose (je crois qu'il peut y avoir un moyen avec une combinaison de modèles et de type d'emballage si je ne le sais pas), mais ce que je suis dit est "Don ' t dérange de demander "qui en est un autre. La distinction de bullhed est pire que d'essayer de faire la mauvaise chose en premier lieu et franchement plus insultant parce que cela implique de parler à l'astucieux.
@Mooing Duck: Je vois que vous avez supprimé le commentaire original qui a commencé cette chaîne. Je suis prêt à supprimer mes commentaires si vous le souhaitez, tout cela a moins de sens avec les commentaires manquants.
Je pense que la chaîne tient assez bien sans ma remarque désinvolte. La question est bonne, et Chris et David vous ont dit la réponse dans les 20 minutes. Je suis (à tort) devenu irrité lorsque vous avez accusé David de «rejeter une question valide», mais j'ai maintenant refroidi. Résumé de base (confirmé par des réponses ci-dessous) est que cela ne peut pas être fait. Les solutions de contournement sont dans les commentaires de David et Chris.
Le problème de votre question est que vous soumettez une solution C ++ particulière (une classe avec de nombreux opérateurs de conversion), puis demandez comment le réparer. La réponse technique a été fournie et la question "Comment résoudre cette question" ne peut pas être répondue à une question avec une classe nommée
génératrice code>: Si vous expliquez votre conception, pourquoi vous pensez avoir besoin de son, peut-être Nous pouvons fournir une "solution".
Il y aurait de la valeur pour pouvoir emballer et décompresser facilement les valeurs via l'opérateur d'accès à un type de conteneur générique pouvant sérialiser et lire dans les valeurs JSON (y compris les chaînes). Cela fonctionne pour tous les types autres que STD :: String en raison de l'opérateur de trutation de caractère. Il n'y a pas de solution pour cela, sauf une extension non officielle que Microsoft a mis en œuvre dans Visual Studio en raison du libellé de résolution de l'ambiguïté dans la norme. Connect.Microsoft.com/visualstudio/feedback/Détails/743685/...
Exemple: Jsondict ["Âge"] = 5; jsondict ["nom"] = "Howdy Doody"; std :: nom de string = jsondict ["nom"]; int Âge = JSondict ["Âge"]; Cela fonctionnera réellement, mais si vous utilisiez l'opérateur d'affectation et que STD :: Nom de la chaîne; Nom = jsondict ["nom"]; Il se casse. En renvoyant un "générateur" qui peut alors lancer correctement (ou jeter s'il s'agit du mauvais type), vous pouvez écrire un code de charge JSON plus gracieux et plus gracieux. Sinon, vous devez nommer les types deux fois: STD :: String Name = JSONCT.GET ("Nom"); J'espère que vous pourrez voir le désir ici.