2016-12-30 17 views
7

Poniższy kod jest kompilowany bez żadnych błędów/ostrzeżeń za pomocą gcc 6.3 (https://godbolt.org/g/sVZ8OH), ale zawiera niebezpieczne niezdefiniowane zachowanie z powodu nieprawidłowego dostępu do pamięci zaznaczonego poniżej. Główną przyczyną jest niejawna konwersja wykonana w emplace_back. Czy ktokolwiek może zaproponować dobry sposób lub najlepsze praktyki, aby uniknąć takich błędów w kodzie?Niebezpieczna niejawna konwersja w emule

#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

„Czy ktoś może sugerować dobry sposób lub najlepsze praktyki aby uniknąć takich błędów w kodzie? " - unikać używania referencji jako członków? –

+0

Foo {d} zostanie odrzucone przez kompilator, ponieważ inicjalizacja {} zapobiega niejawnym konwersjom. – user2736667

+0

@ user2736667 oops, thankyou, comment deleted – Oktalist

Odpowiedz

4

Czy ktoś może sugerować dobry sposób lub najlepszych praktyk, aby uniknąć takich błędów w kodzie?

Gdy klasa przechowuje const odniesienie do innego obiektu, ty, jako programista, biorą na siebie odpowiedzialność za upewnienie się, że nie skończyć przechowywania dangling odniesienia.

Jeśli nie masz silnego powodu do przechowywania odniesienia const, zaleca się przechowywanie wartości.

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

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

Nie tylko odwołanie do stałej, z pewnością wszelkie odniesienia (lub wskaźnik). –

+2

@latedeveloper: Tak, ale znacznie łatwiej jest utworzyć odniesienie do stałej (która szybko staje się wiszącym referencją). Niezawarte odniesienie musi odnosić się do lwartości (która * może * zwisać) - ale jest mniej prawdopodobne). –

+0

@latedeveloper, True. Chodzi tylko o to, że gdy 'double' jest niejawnie konwertowane na' int', tymczasowe 'int' nie może być użyte w wywołaniu funkcji, które oczekuje odniesienia innego niż' const '. –

11

Jeśli zamierzasz przyjąć const odniesienia, ale ty nie chcą odniesienie do tymczasowej, zadeklarować dodatkową konstruktor z argumentem odniesienia RValue - i go usunąć.

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; 
}; 

(jestem zakładając, że rzeczywisty problem jest bardziej złożony niż tylko wew. Dla int, trzymając go przez wartość jest właściwa odpowiedź.)

+0

Czy moje założenie jest słuszne, że parametr typu 'double' z funkcji' bar' jest niejawnie rzutowany na int, a następnie przekazywany do domyślnego konstruktora ruchu (który jest albo usuwany, albo tworzony domyślnie przez kompilator, w odniesieniu do podanego kodu w tym wątku ..)? – SebNag

+1

Niezupełnie. "Podwójny" jest rzeczywiście niejawnie rzucony na "int". Wynik tej obsady jest tymczasowy. Możesz powiązać odwołanie do stałej wartości tymczasowej (co było problemem OP), ale referencja rvalue jest jeszcze lepiej dopasowana, więc usuwając ją otrzymujesz żądany błąd. Nie ma zaangażowanego konstruktora ruchu. –

Powiązane problemy