2014-08-29 17 views
19

Ich verstehe, dass wenn ein temporäres an ein Referenzelement in der Initialisierungsliste des Konstruktors gebunden wird, das Objekt zerstört wird, wie der Konstruktor zurückgibt.Falsche Warnung über temporäre Bindung an Referenzelement im Konstruktor

jedoch Betrachten Sie den folgenden Code:

#include <functional> 
#include <iostream> 

using callback_func = std::function<int(void)>; 

int 
func(const callback_func& callback) 
{ 
    struct wrapper 
    { 
    const callback_func& w_cb; 
    wrapper(const callback_func& cb) : w_cb {cb} { } 
    int call() { return this->w_cb() + this->w_cb(); } 
    }; 
    wrapper wrp {callback}; 
    return wrp.call(); 
} 

int 
main() 
{ 
    std::cout << func([](){ return 21; }) << std::endl; 
    return 0; 
} 

Das sieht mir vollkommen gültig. Das callback-Objekt wird während der gesamten Ausführung der func-Funktion und keine temporäre Kopie sollte für wrapper-Konstruktor vorgenommen werden.

In der Tat, GCC 4.9.0 kompiliert in Ordnung mit allen Warnungen aktiviert.

jedoch GCC 4.8.2 Compiler gibt mir die folgende Warnung:

$ g++ -std=c++11 -W main.cpp 
main.cpp: In constructor ‘func(const callback_func&)::wrapper::wrapper(const callback_func&)’: 
main.cpp:12:48: warning: a temporary bound to ‘func(const callback_func&)::wrapper::w_cb’ only persists until the constructor exits [-Wextra] 
    wrapper(const callback_func& cb) : w_cb {cb} { } 
              ^

Ist das eine falsch positive oder bin ich Missverständnis die Objektlebensdauern?

Hier sind meine genaue Compiler-Versionen getestet:

$ g++ --version 
g++ (GCC) 4.8.2 
Copyright (C) 2013 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
$ g++ --version 
g++ (GCC) 4.9.0 20140604 (prerelease) 
Copyright (C) 2014 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+0

Deaktivieren von Optimierungen gibt mir einen Seg Fehler. Valgrind weist darauf hin, dass das Problem irgendwo bei 'func (std :: function const &) :: wrapper :: call()' liegt. –

+2

Die Verwendung von 'w_cb {cb}' führt zu einer Segmentierungsverletzung für mich. Die Verwendung von 'w_cb (cb)' leidet nicht unter dem gleichen Problem. Getestet in g ++ 4.8.3. –

+0

Ich könnte den Valgrind-Fehler mit GCC 4.8.2 reproduzieren. (Kein segfault, zäh, das Programm gibt 42 aus und wird wie erwartet erfolgreich beendet.) Die von GCC 4.9.0 erzeugte ausführbare Datei ist Valgrind-clean. Diese Beobachtungen ändern sich nicht mit verschiedenen Optimierungsstufen. – 5gon12eder

Antwort

12

Dies ist ein Fehler in gcc 4.8, die in 4.9 behoben wurde. Hier ist der Bug-Report:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=50025

+1

Die Tatsache, dass der Fehler nicht die * Warnung * ist, sondern eher, worüber Sie die * Warnung * warnt, wäre nützlich. Ich musste zu dem Link gehen, um festzustellen, dass ... Dh, eine temporäre ist tatsächlich in gcc 4.8 erstellt und es dauert nicht lange genug. Optimierungen können dazu führen, dass das Temporäre nicht mehr existiert. – Yakk

+0

Ja, das erklärt, was vor sich geht. Danke für den Link. Ich habe weitere Recherchen dazu gemacht und werde sie als Antwort veröffentlichen, aber ich werde deine annehmen. – 5gon12eder

+4

Es war ein Fehler in der Norm, und GCC war die Umsetzung der genauen Formulierung des Standards, die ein temporary dort erstellt werden muss. DR 1288 hat den Standard festgelegt. –

5

Wie von Howard Hinnant und bereits von R Sahu Kommentar angegeben, ist das ein Fehler (die durch die dann gebrochen Norm gefordert werden verwendet, dank Tony D um dies zu verdeutlichen) in der Art, wie GCC 4.8 Initialisierungslisten behandelt.

Ändern der Konstruktor in meinem ursprünglichen Beispiel von

wrapper(const callback_func& cb) : w_cb {cb} { } 

zu

wrapper(const callback_func& cb) : w_cb (cb) { } 

macht die Warnung mit GCC 4.8.3 gehen weg und die erstellte ausführbare Valgrind sauber. Das diff der beiden Assembly-Dateien ist riesig, daher poste ich es hier nicht. GCC 4.9.0 erstellt für beide Versionen einen identischen Assemblercode.

Als nächstes ersetzte ich die std::function durch eine benutzerdefinierte Struktur und gelöschte Kopie und Verschieben Konstruktoren und Zuweisungsoperatoren. Mit GCC 4.8.3 behält dies zwar die Warnung bei, aber jetzt gibt es auch einen (etwas hilfreicheren) Fehler, dass die obige Codezeile den Konstruktor der gelöschten Kopie der Struktur aufruft. Wie erwartet gibt es keinen Unterschied zu GCC 4.9.0.