*** ВНИМАНИЕ: Блог переехал на другой адрес - demin.ws ***

пятница, 29 октября 2010 г.

Ссылка на временный объект в списке инициализации конструктора

Имеем два исходника:

#include <iostream>

struct A {
  A();
  const int& i;
};

A::A() : i(123) {}

int main() {
  A a;
  std::cout << a.i << std::endl;
}
и

#include <iostream>

struct A {
  A() : i(123) {}
  const int& i;
};

int main() {
  A a;
  std::cout << a.i << std::endl;
}

Будучи скомпилированными компилятором от Sun или GCC, эти два примера печатают разные результаты. И первый - неправильный. Студия же 2010, с настройками по умолчанию, дает предупреждение и генерирует код, работающий правильно (точнее, как ожидает программист) в обоих случаях.

Понятно, что код сам по себе несколько странный, так как сложно представить себе, кому может понадобиться инициализировать ссылку константой, которая передается в конструктор не извне, а создается временно во время обработки списка инициализации. Но вот на ошибку-опечатку вполне себе потянет.

Ко мне пример попал как результат анализа реального бага.

4 комментария:

  1. gcc - unix-вариант? Или mingw?

    В windows оба компилятора (и gcc, и студия) дали 123 во обоих примерах.

    ОтветитьУдалить
  2. gcc 4.1.2, 4.4.5, 4.5.1 обработали нормально, правда ворнинга не дали.

    ОтветитьУдалить
  3. Я пробовал на codepad.org. Там gcc под unix'ом.

    ОтветитьУдалить
  4. В обоих случаях программа некорректна. Согласно стандарту, "A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits". (12.2p5) Это значит что как только A::A() завершается, ссылка начинает указывать на мусор.

    Далее всё зависит от того, где компилятор берёт память под временные знчения и как он потом её использует. Sun'овский компилятор, похоже, агрессивнее всего в повторной утилизации памяти. Остальные же позволяют старому значению посидеть в ней подольше.

    ОтветитьУдалить