Frage Konvertierungen in C ++ 0x einschränken. Ist es nur ich, oder klingt das wie eine bahnbrechende Veränderung?


C ++ 0x wird den folgenden Code und ähnlichen Code schlecht formatiert machen, da es ein sog. Benötigt Einschränkung der Conversion von einem double zu einem int.

int a[] = { 1.0 };

Ich frage mich, ob diese Art von Initialisierung viel in realem Code verwendet wird. Wie viele Codes werden durch diese Änderung beschädigt? Ist es viel Aufwand, das in Ihrem Code zu beheben, wenn Ihr Code überhaupt betroffen ist?


Siehe 8.5.4 / 6 von n3225

Eine einschränkende Konvertierung ist eine implizite Konvertierung

  • von einem Fließkommatyp zu einem Integer-Typ oder
  • von Long Double zu Double oder Float oder von Double zu Float, außer wenn die Quelle ein konstanter Ausdruck ist und der tatsächliche Wert nach der Konvertierung innerhalb des Wertebereichs liegt, der dargestellt werden kann (auch wenn er nicht genau dargestellt werden kann), oder
  • von einem Integer-Typ oder Unscoped-Enumeration-Typ zu einem Floating-Point-Typ, außer wenn die Quelle ein konstanter Ausdruck ist und der tatsächliche Wert nach der Konvertierung in den Zieltyp passt und den ursprünglichen Wert erzeugt, wenn zurück in den ursprünglichen Typ konvertiert wird
  • von einem Integer-Typ oder nicht gekürzten Aufzählungstyp in einen Integer-Typ, der nicht alle Werte des ursprünglichen Typs darstellen kann, außer wenn die Quelle ein konstanter Ausdruck ist und der tatsächliche Wert nach der Konvertierung in den Zieltyp passt und den ursprünglichen Wert erzeugt zurück zum ursprünglichen Typ konvertiert.

76
2017-12-13 22:33


Ursprung


Antworten:


Ich habe diese bahnbrechende Veränderung erlebt, als ich GCC benutzt habe. Der Compiler hat einen Fehler für folgenden Code ausgegeben:

void foo(const unsigned long long &i)
{
    unsigned int a[2] = {i & 0xFFFFFFFF, i >> 32};
}

In Funktion void foo(const long long unsigned int&):

Fehler: Einschränkung der Konvertierung von (((long long unsigned int)i) & 4294967295ull) von long long unsigned int zu unsigned int Innerhalb { }

Fehler: Einschränkung der Konvertierung von (((long long unsigned int)i) >> 32) von long long unsigned int zu unsigned int Innerhalb { }

Glücklicherweise waren die Fehlermeldungen einfach und die Fehlerbehebung war einfach:

void foo(const unsigned long long &i)
{
    unsigned int a[2] = {static_cast<unsigned int>(i & 0xFFFFFFFF),
            static_cast<unsigned int>(i >> 32)};
}

Der Code befand sich in einer externen Bibliothek mit nur zwei Vorkommen in einer Datei. Ich denke nicht, dass die brechende Änderung viel Code beeinflussen wird. Anfänger könnten bekommen  verwirrt, obwohl.


41
2018-01-14 12:25



Ich wäre überrascht und enttäuscht von mir selbst zu erfahren, dass jeder C ++ Code, den ich in den letzten 12 Jahren geschrieben habe, ein solches Problem hatte. Aber die meisten Compiler hätten Warnungen über Kompilierungszeit-Engpässe ausgegeben, solange ich nichts verpasst habe.

Schrumpfen diese auch die Conversions?

unsigned short b[] = { -1, INT_MAX };

Wenn dem so ist, denke ich, dass sie etwas häufiger auftauchen können als Ihr Beispiel vom Floating-Typ zum Integral-Typ.


9
2017-12-13 22:49



Ich wäre nicht so überrascht, wenn jemand von etwas wie:

float ra[] = {0, CHAR_MAX, SHORT_MAX, INT_MAX, LONG_MAX};

(Bei meiner Implementierung erzeugen die letzten beiden nicht das gleiche Ergebnis, wenn sie zurück in int / long konvertiert werden, daher werden sie schmaler)

Ich kann mich nicht daran erinnern, dass ich das je geschrieben habe. Es ist nur nützlich, wenn eine Annäherung an die Grenzen für etwas nützlich ist.

Dies erscheint zumindest vage auch plausibel:

void some_function(int val1, int val2) {
    float asfloat[] = {val1, val2};    // not in C++0x
    double asdouble[] = {val1, val2};  // not in C++0x
    int asint[] = {val1, val2};        // OK
    // now do something with the arrays
}

aber es ist nicht ganz überzeugend, denn wenn ich weiß, dass ich genau zwei Werte habe, warum setze ich sie lieber in Arrays als einfach nur ein float floatval1 = val1, floatval1 = val2;? Was ist jedoch die Motivation, warum das kompilieren (und funktionieren sollte, vorausgesetzt, der Genauigkeitsverlust liegt innerhalb der akzeptablen Genauigkeit für das Programm), während float asfloat[] = {val1, val2};sollte nicht? Wie auch immer, ich initialisiere zwei Floats aus zwei Ints, nur dass in einem Fall die beiden Floats Mitglieder eines Aggregats sind.

Dies erscheint besonders hart in Fällen, in denen ein nicht konstanter Ausdruck zu einer engeren Konvertierung führt, obwohl (in einer bestimmten Implementierung) alle Werte des Quelltyps im Zieltyp darstellbar sind und auf ihre ursprünglichen Werte zurückkonvertiert werden können:

char i = something();
static_assert(CHAR_BIT == 8);
double ra[] = {i}; // how is this worse than using a constant value?

Unter der Annahme, dass es keinen Bug gibt, ist es vermutlich immer das Problem, die Konvertierung explizit zu machen. Es sei denn, Sie machen etwas seltsam mit Makros, ich denke, ein Array-Initialisierer erscheint nur in der Nähe des Typs des Arrays oder zumindest auf etwas, das den Typ repräsentiert, der von einem Template-Parameter abhängig sein könnte. So sollte eine Besetzung einfach sein, wenn sie ausführlich ist.


7
2017-11-03 19:13



Eine praktische Instanz, der ich begegnet bin:

float x = 4.2; // an input argument
float a[2] = {x-0.5, x+0.5};

Das numerische Literal ist implizit double was zur Beförderung führt.


5
2018-03-23 10:02



Einschränkende Konvertierungsfehler interagieren schlecht mit impliziten Integer-Promotion-Regeln.

Ich hatte einen Fehler mit Code, der aussah

struct char_t {
    char a;
}

void function(char c, char d) {
    char_t a = { c+d };
}

Dies führt zu einem sich verengenden Konvertierungsfehler (der gemäß dem Standard korrekt ist). Der Grund ist, dass c und d wird implizit zu int und das Ergebnis int Es ist nicht erlaubt, in einer Initialisierungsliste auf Char zu reduzieren.

OTOH

void function(char c, char d) {
    char a = c+d;
}

ist natürlich noch in Ordnung (sonst würde die Hölle los). Aber überraschenderweise sogar

template<char c, char d>
void function() {
    char_t a = { c+d };
}

ist in Ordnung und kompiliert ohne Warnung, wenn die Summe von c und d kleiner als CHAR_MAX ist. Ich denke immer noch, dass dies ein Fehler in C ++ 11 ist, aber die Leute dort denken anders - möglicherweise, weil es nicht einfach zu beheben ist, ohne implizite Ganzzahlkonversion loszuwerden (was ein Relikt aus der Vergangenheit ist, als Leute Code geschrieben haben mögen char a=b*c/d und erwarte, dass es funktioniert, auch wenn (b * c)> CHAR_MAX) oder die Konvertierungsfehler verringern (was möglicherweise eine gute Sache ist).


4
2018-05-20 14:56



Fügen Sie Ihren CFLAGs beispielsweise -Wennerweiterung hinzu:

CFLAGS += -std=c++0x -Wno-narrowing

2
2018-05-29 08:49



Es sieht so aus, als ob GCC-4.7 nicht mehr Fehler bei der Reduzierung von Conversions gibt, sondern stattdessen Warnungen.


1