IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Cast et parenthèses


Sujet :

Langage C++

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Septembre 2013
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Septembre 2013
    Messages : 4
    Points : 6
    Points
    6
    Par défaut Cast et parenthèses
    Bonjour,

    Voici un petit bout de programme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int i = 5;
    double l = double(int(i)) / double(int(i));
    Ce code compile parfaitement (avec gcc 4.6.3).

    Ajoutons des parenthèses :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    double l = (double(int(i))) / double(int(i));
    Ce code compile toujours.

    Maintenant, changeons le / en * :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    double l = (double(int(i))) * double(int(i));
    Cette fois, on obtient une erreur :
    error: invalid type argument of unary '*' (have 'double')

    Changeons le * en + :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    double l = (double(int(i))) + double(int(i));
    On obtient une autre erreur :
    error: invalid cast to function type 'double(int)'

    Si on enlève les parenthèses :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    double l = double(int(i)) + double(int(i));
    Alors le code compile (et c'est vrai aussi avec le *).

    J'avoue que je ne comprends pas la différence entre l'expression double(int(i)) sans parenthèse, et (double(int(i))) avec parenthèses.

    Quelqu'un peut-il mexpliquer ?

    Merci d'avance.

  2. #2
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    En gros, des ambiguïtés bizarres au niveau du function-style cast? Ça promet d'être intéressant.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    Par défaut
    J'avoue que j'ai du mal à comprendre. Il n'y a rien d'explicite dans la norme :
    5.2.3 Explicit type conversion (functional notation) [expr.type.conv]

    1) A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4). If the type specified is a class type, the class type shall be complete. If the expression list specifies more than a single value, the type shall be a class with a suitably declared constructor (8.5, 12.1), and the expression T(x1, x2, ...) is equivalent in effect to the declaration T t(x1, x2, ...); for some invented temporary variable t, with the result being the value of t as a prvalue.

    2) The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates a prvalue of the specified type, which is value-initialized (8.5; no initialization is done for the void() case). [ Note: if T is a non-class type that is cv-qualified, the cv-qualifiers are ignored when determining the type of the resulting prvalue (3.10). — end note ]

    3) Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized (8.5.4) with the specified braced-init-list, and its value is that temporary object as a prvalue.
    Pour référence, les erreurs données par clang :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    error: indirection requires pointer operand ('double' invalid)
        double l = (double(int(i))) * double(int(i));
                                    ^ ~~~~~~~~~~~~~~
    error: C-style cast from 'double' to 'double (int)' is not allowed
        double l = (double(int(i))) + double(int(i));
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Dans le premier cas, il essaye de dé-référencer un pointeur dans le second terme, et dans le deuxième cas il voit le premier terme comme la signature d'une fonction. Le code suivant produit la même erreur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    double l = (double(int i)) + double(int(i));
    Dans les deux cas, l’ambiguïté semble provenir du fait que * et + sont aussi des opérateurs unaires, qui semblent prendre le dessus sur l'interprétation en opérateur binaires.

    Edit: En fait si, il y a plusieurs passages dans la norme sur les ambiguités liées au cast.
    6.8 Ambiguity resolution [stmt.ambig]

    1) There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion (5.2.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration. [ Note: To disambiguate, the whole statement might have to be examined to determine if it is an expression-statement or a declaration. This disambiguates many examples. [ Example: assuming T is a simple-type-specifier (7.1.6),
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    T(a)->m = 7; // expression-statement
    T(a)++; // expression-statement
    T(a,5)<<c; // expression-statement
    T(*d)(int); // declaration
    T(e)[5]; // declaration
    T(f) = { 1, 2 }; // declaration 
    T(*g)(double(3)); // declaration
    ...

    3) The disambiguation is purely syntactic; that is, the meaning of the names occurring in such a statement, beyond whether they are type-names or not, is not generally used in or changed by the disambiguation. Class templates are instantiated as necessary to determine if a qualified name is a type-name. Disambiguation precedes parsing, and a statement disambiguated as a declaration may be an ill-formed declaration. If, during parsing, a name in a template parameter is bound differently than it would be bound during a trial parse, the program is ill-formed. No diagnostic is required. [ Note: This can occur only when the name is declared earlier in the declaration. — end note ]
    Le passage en rouge est important. Si j'ai bien compris, peu importe que i soit déclaré comme une variable : la "désambigüisation" a lieu avant que soit attribué une sémantique à un nom particulier. Donc au moment où il lit cette ligne, le compilateur voit une ambiguité d'interprétation qui vient à la base du fait que tu aies entouré ton membre de gauche dans des parenthèses : à première vue (si on ne sais pas que i est une variable et pas un type) on peut comprendre ça de deux manières différentes : si i est une variable, alors c'est juste une chaine de conversion, si i est un type, c'est un opérateur de conversion "C-style" qui cherche à convertir en fonction qui retourne un double et prend en paramètre l'adresse d'une fonction qui retourne un int et prend en paramètre un i.

    Autre passage de la norme sur cette ambiguïté dans un autre contexte :
    8.2 Ambiguity resolution [dcl.ambig.res]

    1) The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 6.8 can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 6.8, the resolution is to consider any construct that could possibly be a declaration a declaration. [ Note: a declaration can be explicitly disambiguated by a nonfunction-style cast, by an = to indicate initialization or by removing the redundant parentheses around the parameter name. — end note ]
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct S {
        S(int);
    };
     
    void foo(double a) {
        S w(int(a)); // function declaration
        S x(int()); // function declaration
        S y((int)a); // object declaration
        S z = int(a); // object declaration
    }
    2) The ambiguity arising from the similarity between a function-style cast and a type-id can occur in different contexts. The ambiguity appears as a choice between a function-style cast expression and a declaration of a type. The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a type-id.
    Dans ce contexte, s'il a le choix entre un cast et un type, le compilateur doit considérer qu'une expression est un type.

  4. #4
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    Par défaut
    En fait je pense que je me suis trompé sur l'interprétation de i.

    Par exemple, le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <iostream>
     
    int main() {
        using type = float(int);
     
        type t; // NB: déclaration anticipée de la fonction 't' ci-dessous !
        t(1);
     
        return 0;
    }
     
    float t(int i) {
        std::cout << "affiche: " << i << std::endl;
        return float(i);
    }
    ... compile aussi avec :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    using type = float(int(pouet));
    J'ai donc l'impression qu'on peut écrire les paramètres d'une fonction sous la forme type(nom) (alors qu'on fait normalement type nom). Ça me paraît étrange...

  5. #5
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Septembre 2013
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Septembre 2013
    Messages : 4
    Points : 6
    Points
    6
    Par défaut
    Merci à Kalith pour toutes ses recherches et ses explications.

    Donc si je comprends bien, l'expression avec parenthèses peut être considérée soit comme un cast à la C++ de int vers double mis entre parenthèses, soit comme un cast à la C vers un type-fonction.

    Dans le cas de opérateur +, le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    double l = (double(int(i))) + double(int(i));
    est en fait équivalent à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    double l = (double(int(i)))  ( + double(int(i)) );
    L'opérateur + est unaire, d'où l'erreur du compilateur (on ne peut pas caster un double en type-fonction).

    De même, avec l'opérateur *, le code est équivalent à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    double l = (double(int(i)))  ( * double(int(i)) );
    L'opérateur * est l'opérateur unaire de déréférencement, d'où l'erreur du compilateur (on ne peut pas déréférencer un double).

    Par contre, je n'ai pas compris pourquoi le compilateur a choisi le cast vers un type-fonction avec les opérateurs + et *, alors qu'il a choisi les cast vers double avec l'opérateur /.

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Probablement parce qu'il n'existe pas d'opérateur / unaire.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Septembre 2013
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Septembre 2013
    Messages : 4
    Points : 6
    Points
    6
    Par défaut
    Probablement parce qu'il n'existe pas d'opérateur / unaire.
    Certes. Mais dans le cas des opérateurs + et *, pourquoi le compilateur choisit-il les opérateurs unaires et le cast vers le type-fonction plutôt que les opérateurs binaires et le cast vers double entre parenthèses ?

  8. #8
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    Par défaut
    Pour simplifier, disons que la lecture du code par le compilateur se fait en deux étapes :
    1. la lecture des caractères du code, et la construction d'un arbre d'expression
    2. l'analyse de l'arbre et sa conversion en code machine


    L'étape 1) s'assure juste que le code respecte la grammaire du langage. C'est ici que tu vas te faire jeter si tu écris des choses du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int i = 5 %% 6;
    = 5 + 6;
    int j = 5 +;
    // ...
    L'étape 2) vérifie que les expressions qui ont été comprises dans l'étape 1) sont valides, c'est à dire que telle variable est convertible en tel type, que telle fonction prend tant de paramètres avec tels types, que telle classe possède bien une fonction membre avec tel nom, etc. C'est là que tu vas te faire jeter avec du code comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    std::vector<double> v; v.push_back(5, 6, 7);
    int j = std::string("coucou");
    std::string s = std::string("coucou").sort();
    Tout ce code là est du C++ valide, syntaxiquement. Le fait qu'il compilera ou pas dépend du contexte, i.e. de ce que contiennent les classes std::vector et std::string.

    Pour en revenir à ta question : l’ambiguïté est détectée à l'étape 1), donc avant que l'étape 2) n'ai été effectuée. Si l'on se réfère à cette clause :
    2) The ambiguity arising from the similarity between a function-style cast and a type-id can occur in different contexts. The ambiguity appears as a choice between a function-style cast expression and a declaration of a type. The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a type-id.
    ... quand il y a ambiguïté, la priorité est donnée à l'interprétation d'un type, pas d'un cast. Dans le cas de /, l'interprétation en type ne serait pas syntaxiquement correcte (dans le sens où elle ne passerait pas à l'étape 1)), donc il n'y a pas de problème. Dans les autres cas, il y a ambiguïté, donc l'interprétation comme un type est choisie, et malheureusement ce choix ne passe pas à l'étape 2) (on ne peut pas dé-référencer un double, et on ne peut pas convertir un double en pointeur sur fonction).

  9. #9
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Septembre 2013
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Septembre 2013
    Messages : 4
    Points : 6
    Points
    6
    Par défaut
    Kalith, merci beaucoup pour toutes ces explications.

Discussions similaires

  1. Probleme CAST
    Par cedric31 dans le forum SQL
    Réponses: 2
    Dernier message: 16/02/2004, 10h46
  2. CAST ou autre ?
    Par 74160 dans le forum Requêtes
    Réponses: 2
    Dernier message: 10/07/2003, 15h00
  3. CAST DATETIME ----> SMALLDATETIME
    Par Guizz dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 26/06/2003, 12h07
  4. traduction en delphi "reinterpreted cast"
    Par Chupakabra dans le forum Langage
    Réponses: 3
    Dernier message: 13/02/2003, 15h49
  5. Appeler une fonction avec/sans parenthèses
    Par haypo dans le forum Algorithmes et structures de données
    Réponses: 8
    Dernier message: 29/12/2002, 18h48

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo