Czytaj warningi czyli o obiektach tymczasowych

Popatrzmy na następujący kawałek kodu:

#include <iostream.h>
#include <string>

void foo (unsigned long& x)
{
    x = 1;
}

int main()
{
    unsigned int p = 0;
    foo(p);
    cout << p << endl;
}

Ku zdumieniu autora, program ten po uruchomieniu wypisywał 0 a nie 1 (no, troszkę kłamię, program, w którym faktycznie problem wystąpił, był spory i zagmatwany, a objawem problemu było niedeterministyczne zachowanie się kodu bazującego na przypadkowej wartości nie zainicjowanej zmiennej).

Przyczyna? Tłumacząc wywołanie foo(p) kompilator stwierdził niezgodność typów, w związku z czym - aby funkcja foo otrzymała parametr oczekiwanego typu - stworzył zmienną tymczasową typu unsigned long, inicjując ją dotychczasową wartością p - czyli zerem. Funkcja foo otrzymała referencję do tej zmiennej tymczasowej i właśnie tymczasowy obiekt zmodyfikowała. Po powrocie z foo zmienna tymczasowa - jako już niepotrzebna - została usunięta. A wartość p pozostała bez zmian.

Problem nie jest zresztą w żaden sposób mistyczny, kompilator grzecznie poinformował o nim stwierdzając coś w stylu:

ble.cxx:13: initializing non-const `long unsigned int &' with `unsigned int' will use a temporary
ble.cxx:5: in passing argument 1 of `foo(long unsigned int &)'

Trzeba było jednak to ostrzeżenie przeczytać i zrozumieć. Autor warningi ignorował a ja mam kolejny argument za pisaniem programów kompilujących się bez jakichkolwiek ostrzeżeń...

Sytuacje tego typu można spotkać oczywiście nie tylko przy konwersjach numerycznych, także gdy wywoływana funkcja oczekuje referencji do obiektu a otrzymuje zmienną czy obiekt niezgodnego typu, z którego jednak można odpowiednim konstruktorem właściwy obiekt utworzyć.

Narzuca się pytanie, po co właściwie istnieje ten kłopotliwy mechanizm generowania obiektów tymczasowych przy przekazywaniu parametrów przez referencję? Ano, jest bardzo użyteczny gdy korzystamy z stałych referencji. Tworząc funkcję

void writeSomewhere(const string& s);

możemy ją swobodnie wywoływać także dla parametrów const char *:

writeSomewhere("some text");
komentarze obsługiwane przez Disqus