2017-02-16 2 views
3

Je ne comprends pas cette logique de conversion de l'opérateur ternaire (voici un exemple):C++ compréhension de conversion de l'opérateur ternaire

#include <iostream> 
#include <typeinfo> 
#include <unistd.h> 
#include <cxxabi.h> 
#include <climits> 

template<typename T> 
struct singletime 
{ 
private: 
    T    value; 
public: 
    T& operator()() {return this->value;} 

    operator const T&() const {return value;} 
    unsigned char flag_needed_for_all_types; 
}; 

static void getvalue1 (uint64_t value, const char *call) 
{ 
    std::cout << call << ": \t" << value << std::endl << std::endl; 
} 

#define getvalue(x, str) \ 
std::cout << typeid(x).name() << std::endl; \ 
getvalue1(x, str); 

int main (int argc, char *argv[]) 
{ 
    bool flag = true; 
    singletime<uint64_t> singletime_64; 
    singletime_64() = INT_MAX+1lu; 

    uint64_t value_64 = singletime_64; 

    getvalue (flag ? singletime_64 : 0, "Ternary with singletime, > INT_MAX"); 
    getvalue (singletime_64, "singletime w/o ternary, > INT_MAX"); 

    getvalue (flag ? value_64 : 0, "Ternary with uint64_t, > INT_MAX"); 
    getvalue (value_64, "uint64_t w/o ternary, > INT_MAX"); 


    singletime_64() = INT_MAX; 

    uint64_t value_64_l = singletime_64; 

    getvalue (flag ? singletime_64 : 0, "Ternary with singletime, <= INT_MAX"); 
    getvalue (singletime_64, "singletime w/o ternary, <= INT_MAX"); 

    getvalue (flag ? value_64_l : 0, "Ternary with uint64_t, <= INT_MAX"); 
    getvalue (value_64_l, "uint64_t w/o ternary, <= INT_MAX"); 

    return 0; 
} 

J'ai une classe de modèle singletime<T>, qui est un emballage de tout type, utilisé pour les cas, non lié à cette question et a un opérateur de conversion à T. Le problème est lorsque singletime<uint64_t> est utilisé dans une expression d'opérateur ternaire.

Ceci est la ligne problématique:

getvalue (flag ? singletime_64 : 0, "Ternary with singletime, > INT_MAX"); 

La valeur 64 bits est converti en int et si la valeur est au-dessus INT_MAX, il devient incorrect.

L'exemple imprime certains types d'utilisation de l'opérateur ternaire - avec le type résultant de l'expression et la valeur résultante.
est ici la sortie de l'exemple:

int 
Ternary with singletime, > INT_MAX:  18446744071562067968 

singletime<unsigned long> 
singletime w/o ternary, > INT_MAX: 2147483648 

unsigned long 
Ternary with uint64_t, > INT_MAX: 2147483648 

unsigned long 
uint64_t w/o ternary, > INT_MAX: 2147483648 

int 
Ternary with singletime, <= INT_MAX: 2147483647 

singletime<unsigned long> 
singletime w/o ternary, <= INT_MAX:  2147483647 

unsigned long 
Ternary with uint64_t, <= INT_MAX: 2147483647 

unsigned long 
uint64_t w/o ternary, <= INT_MAX: 2147483647 

Le seul problème est lorsque l'opérateur ternaire est utilisé avec singletime<uint64_t> - il obtient la valeur 18446744071562067968

Si je comprends bien, il essaie de convertir différents types de un type.

Comme il y a un opérateur de conversion de singletime<uint64_t> à uint64_t, il l'utilise peut-être, mais après je ne comprends pas pourquoi il convertit les deux valeurs en int, au lieu de uint64_t? Dans les exemples où uint64_t est utilisé au lieu de singletime<uint64_t>, l'int est converti en uint64_t et qu'aucune valeur ne perd

Dans le cas de singletime<uint64_t> et int, il n'y a pas non plus compilateur d'avertissement au sujet de CAST pour petits caractères et la perte potentielle de données.

Essayé avec gcc 4.8.2 et gcc 5.2.0

Répondre

6

De standard, 5,16.

si le deuxième et troisième opérandes ont différents types, et soit a (peut-être cv-qualifié) type de classe, une tentative de convertir chaque de ces opérandes au type de l'autre. Procédé pour déterminer si une expression de l'opérande E1 de type T1 peut être converti en correspondre à une expression de l'opérande E2 de type T2 est définie comme suit:

Si E2 est un rvalue, ou si la conversion ci-dessus ne peut se faire:

Sinon (si E1 ou E2 a un type de classe non-classe ou si les deux classes ont des classes mais que les classes sous-jacentes ne sont pas identiques ou une classe de base de l'autre): E1 peut être converti E2 si E1 peut être implicitement converti au type que l'expression E2 aurait si E2 a été converti en une valeur (ou le type qu'il a, si E2 est un rvalue).

Si les deuxième et troisième opérande n'ont pas le même type, et soit a (peut-être cv-qualifié) type de classe, la résolution de surcharge est utilisé pour déterminer les conversions (le cas échéant) à appliquer aux opérandes (13.3.1.2, 13.6).

Donc, ici 0 est rvalue et il a le type int. Le compilateur va essayer de convertir le premier argument en int et il le fera, parce que cela peut être fait, grâce à votre opérateur de conversion.