2016-07-07 13 views
3

Ich war der in this earlier question, da Code mit Blick auf die Code enthalten, die im Wesentlichen die folgende ist:Warum gibt g ++ keine Warnungen aus, wenn uninitalisierte Werte durch const-Referenz in Funktionen übergeben werden?

bool (*uninitializedFunctionPointer)(int, int); 
typedef std::multimap<int, std::string, bool(*)(int, int)> MultiMapType; 
MultiMapType myMap(uninitializedFunctionPointer); 

Beachten Sie, dass (wie der Name schon sagt) uninitializedFunctionPointer ist ein uninitlized Funktionszeiger, der in den Konstruktor von myMap übergeben wird. Seltsamerweise, wenn ich diesen Code mit g ++ 4.8.4 mit -Wall -Werror kompilierte, kompilierte es diesen Code, ohne irgendwelche Warnungen zu melden. Aber es tat Bericht ein Fehler für diesen ähnlichen Code:

bool (*uninitializedFunctionPointer)(int, int); 
uninitializedFunctionPointer(137, 42); 

Da die Funktionszeiger ruft eine Warnung ausgelöst, aber es in den multimap Konstruktor nicht, ich dachte, dass g ++ einfach nicht über die Weitergabe scherte nicht initialisierte Werte als Parameter für Funktionen. Allerdings ist dieser Code in der Tat eine Warnung auslösen:

void doSomething(bool (*function)(int, int)) { 
    function(137, 42); // Problem if 'function' is uninitialized 
} 

bool (*uninitializedFunctionPointer)(int, int); 
doSomething(uninitializedFunctionPointer); // Warning! 

ich zum cppreference documentation for multimap ging und sah, dass der multimap Konstruktor in seinem Komparator durch konstante Referenz nimmt, so habe ich versucht, diesen Code zu schreiben:

typedef bool (*FunctionType)(int, int); 
void doSomething(const FunctionType &function) { 
    function(137, 42); // Problem if 'function' is uninitialized 
} 

bool (*uninitializedFunctionPointer)(int, int); 
doSomething(uninitializedFunctionPointer); 

Und, überraschenderweise, dieser Code kompiliert vollständig gut ohne Warnungen überhaupt. Ich dachte, dass dies etwas mit Funktionszeigern zu tun haben könnte, aber es sieht so aus, als wäre das nicht der Fall! Hier ist im Zusammenhang Code, der einfach nur alte ganzen Zahlen verwendet:

void doSomething(const int &value) { 
    std::cout << value << std::endl; // Problem if value is uninitialized 
} 

int uninitializedInt; 
doSomething(uninitializedInt); 

Dieser Code kompiliert ohne Warnungen an alle, auch mit -Wall aktiviert.

Ich verstehe, dass der Compiler nicht Warnungen für alle Arten von Programmierfehlern ausgeben muss, aber es scheint sehr ungewöhnlich, dass g ++ eine direkte Verwendung einer nicht initialisierten Variable und einen Versuch, eine nicht initialisierte Variable in eine Funktion übergeben würde nach Wert, würde jedoch kein Problem melden, wenn eine nicht initialisierte Variable in eine Funktion by const reference übergeben wird.

Gibt es einen zwingenden Grund, warum g ++ hier keine Warnung meldet? Wie in, gibt es vernünftigen Code, wo eine nicht initialisierte Variable in eine Funktion durch const Referenz übergeben werden könnte, ohne irgendeine Art von undefiniertem Verhalten auszulösen? Oder ist das nur ein Versehen im Compiler?

+1

Die Funktion konnte einen Verweis oder Zeiger auf die nicht initialisierte Variable * speichern * und dann ihren Wert später lesen, nachdem sie initialisiert wurde. Ich würde dies zu einer Antwort machen, außer dass ich mir kein spezifisches Beispiel von oben vorstellen kann. Aber ich vermute, es ist üblich genug, so dass Warnung in diesem Fall Lärm wäre. – Brian

+1

@Brian: Der gesamte CRTP-Code hängt von dieser Funktionalität ab. –

+0

@MooingDuck Verwendet CRTP das tatsächlich? Es verwendet Vorwärtsdeklarationen von Typen, aber das ist im Typsystem und nicht in Laufzeitvariablen. – templatetypedef

Antwort

5

Das Problem tritt auf, weil es bei einer Referenz möglich ist, dass die Funktion einen Zeiger oder eine Referenz speichert.

typedef bool (*FunctionType)(int, int); 

FunctionType *stored_function; 

void doSomething(const FunctionType &function) 
{ 
    stored_function = const_cast<FunctionType *>(&function); 
} 

void doSomethingElse(int a, int b) 
{ 
    (*stored_function)(a, b); 
} 

bool a_function(int, int) 
{ 
    // do something 
    return false; 
} 


int main() 
{ 
    FunctionType func; 
    doSomething(func); 

    func = a_function; 

    doSomethingElse(1,2); 
} 

führt Dies wird in doSomethingElse()a_function() Aufruf, ob die Zuordnung zu func vor oder nach dem Aufruf von doSomething() auftritt. Wenn sich die Funktionsdefinitionen in verschiedenen Kompilierungseinheiten befinden und der Compiler vor solchen Dingen warnt, würde Code wie oben in einigen Fällen falsche Warnungen geben.

Es gibt ähnliche Techniken, bei denen die Funktion den Verweis oder Zeiger als Mitglied eines zurückgegebenen Objekts speichert, das später vom Aufrufer verwendet wird. Wenn der Konstruktor eines solchen Objekts eine const-Referenz oder einen Zeiger unter Verwendung der übergebenen Referenz initialisiert, wäre die const_cast, die ich hier verwendet habe, nicht erforderlich.

Ob es eine gute Idee für einen Entwickler ist, solche Techniken zu verwenden, ist eine andere Geschichte - ich denke, dass das obige eine schlechte Technik ist. Aber Entwickler, die solche Techniken verwenden, neigen dazu, in ihren Beschwerden über "falsche" Warnungen - einschließlich in einigen kommerziellen Bibliotheken - lauthals zu sein, so dass Compiler-Anbieter es vorziehen werden, keine Warnungen auszugeben.

+0

Ich bin mir nicht sicher, ob ich das kaufe. doSomething verletzt const-Regeln und erzeugt einen Kompilierungsfehler unter g ++: error: ungültige Konvertierung von 'bool (* const *) (int, int)' nach 'bool (**) (int, int)' [-fpermissiv]. – SCFrench

+0

Oh, ok, ich denke ich bekomme es, aber wie beschrieben wird der Beispielcode aus mehreren Gründen nicht kompiliert. Zumindest muss stored_function ein Zeiger auf const FunctionType sein. (Ich würde es reparieren, aber alles, was ich gerade habe, ist ein iPad und die Bearbeitung dieser Dinge ist Mord.) – SCFrench