2016-12-30 2 views
7

Der folgende Code kompiliert ohne Fehler/Warnungen mit gcc 6.3 (https://godbolt.org/g/sVZ8OH), aber es enthält ein gefährliches undefiniertes Verhalten aufgrund ungültigen Speicherzugriffs unten markiert. Die Ursache ist die implizite Konvertierung in emplace_back. Kann jemand einen guten Weg oder Best Practices vorschlagen, um solche Fehler im Code zu vermeiden?Dangerous implizite Konvertierung in Emplace

#include <iostream> 
#include <vector> 

struct Foo 
{ 
    explicit Foo(const int& i) : i{i} {} 

    void foo() const { std::cout << i; } // invalid memory access, as i is an invalid ref!! 
    const int& i; 
}; 

void bar(const double& d) { 
    std::vector<Foo> fv; 
    fv.emplace_back(d); 
} 
+7

„jemand eine gute Art und Weise oder Best Practices vorschlagen kann, um solche Fehler im Code zu vermeiden? " - Vermeiden Sie Referenzen als Mitglieder? –

+0

Foo {d} wird vom Compiler zurückgewiesen, da die Initialisierung von {} implizite Konvertierungen verhindert. – user2736667

+0

@ user2736667 oops, danke, Kommentar gelöscht – Oktalist

Antwort

4

Kann mir jemand eine gute Art und Weise oder Best Practices vorschlagen solche Fehler im Code zu vermeiden?

Wenn Ihre Klasse einen const Verweis auf ein anderes Objekt speichert, Sie, als Programmierer, nehmen die Verantwortung, dafür zu sorgen, dass Sie am Ende nicht eine baumelnde Referenz speichern.

Sofern Sie keinen starken Grund haben, eine const Referenz zu speichern, würde ich empfehlen, einen Wert zu speichern.

struct Foo 
{ 
    explicit Foo(const int& i) : i{i} {} 

    void foo() const { std::cout << i; } 
    int i; 
}; 
+0

Nicht nur eine const Referenz, sicherlich jede Referenz (oder Zeiger). –

+2

@latedeveloper: Ja, aber es ist viel einfacher, eine const-Referenz auf ein temporäres Objekt zu erstellen (was schnell zu einer Referenz wird). Eine nicht-konstante Referenz muss zu einem L-Wert gehören (der * kann * baumeln - aber es ist weniger wahrscheinlich). –

+0

@latedeveloper, Wahr. Wenn ein 'double' implizit in ein' int' konvertiert wird, kann das temporäre 'int' nicht in einem Funktionsaufruf verwendet werden, der eine Nicht-'const'-Referenz erwartet. –

11

Wenn Sie eine konstante Referenz akzeptieren werden, aber Sie in einem temporären nicht wollen einen Verweis tun, einen zusätzlichen Konstruktor mit einem R-Wert Bezug Argumente erklären - und sie löschen.

struct Foo 
{ 
    explicit Foo(const int& i) : i{i} {} 
    explicit Foo(const int&& i) = delete; // This preferentially matches a reference to a 
             // temporary, and compilation fails. 

    void foo() const { std::cout << i; } // invalid memory access, as i is an invalid ref!! 
    const int& i; 
}; 

(nur ein int Ich gehe davon aus, dass das eigentliche Problem als komplexer ist. Für einen int, es von Wert hält, ist die richtige Antwort.)

+0

Ist meine Annahme richtig, dass der Parameter des Typs 'double' aus der Funktion' bar' implizit in ein int umgewandelt und dann an den Standard-Move-Konstruktor übergeben wird (der entweder gelöscht oder vom Compiler standardmäßig für den bereitgestellten Code erstellt wird) in diesem Thread ..)? – SebNag

+1

Nicht ganz. Das 'double' wird tatsächlich implizit auf ein' int' geworfen. Das Ergebnis dieser Besetzung ist vorübergehend. Sie können einen const-Verweis an ein temporäres binden (was das Problem des OP war), aber ein rvalue-Verweis ist eine noch bessere Übereinstimmung, so dass Sie den gewünschten Fehler erhalten, wenn Sie ihn löschen. Es ist kein Move-Konstruktor beteiligt. –