Pull to refresh

Comments 12

В этом случае можно считать, что объект S() начнет разрушаться после вызова функции f()

Звучит как в буддизме: «мы начинаем умирать сразу после рождения» :) Ну т.е. я имею ввиду, что, имхо, это предложение нужно перефразировать.
Я недавно этот фокус использовал для удобного конвертирования между различными кодировками строк — UTF8/UTF16/UTF32, когда вынужденно портировал большой кусок кода на Linux, при том, что еще использовалась библиотека нуждающаяся в UTF16 аргументах (сам wchar_t в Linux — 4х байтный). Сделал себе классы-врапперы «U8», «U16», и «U32», скрывающие всю грязную работу, и все конвертирование меж кодировками в самом коде выглядило примерно так:

functionCall ( U8( lpSomeUTF32String ).get() );

Где соответственно метод .get() отдавал уже c-string обычный. Проверял, такая например лесенка:

functionCall ( U8( U8( U8( U8( U8( arg ).get() ).get() ).get() ).get() ).get() );

Прекрасно живет, и вся эта пачка «вложенных» объектов сначала строго последовательно создается, и потом строго последовательно удаляется, после выхода из «functionCall».

Почему вывод у msvc2012 такой? т.е. два раза вызван деструктор в последнем примере?
Создаётся первый объект класса foo конструктором без аргументов, создаётся временный объект копирующим конструктором, первый объект уничтожается, заканчивается main(), временный объект уничтожается. Если добавить явный конструктор (пусть даже пустой)
#include <iostream>
struct foo {
    foo() {}
    ~foo() {
        std::cout << "~foo()\n";
    }
};

struct foo_holder {
    const foo &f;
};

int main() {
    foo_holder holder = { foo() };
    std::cout << "done!\n";
    return 0;
}

то вывод будет как и у gcc (видимо, срабатывает оптимизация RVO/NRVO?)
int& r = 1; // не компилируется

test.cpp:3:11: ошибка: invalid initialization of non-const reference of type «int&» from an rvalue of type «int»

компилятор как бы намекает почему нельзя. Из чего сразу понятно почему
const int& r = 1;

можно

Когда время жизни объекта определяется временем жизни ссылки на него

Я бы перефразировал
Вау, можно создавать константный ссылки на rvalue объекты (и это работает если у них одна область видимости)


Вводит в небольшой ступор последний пример. Мне кажется что это из за того, что синтаксис { }
foo_holder holder = { foo() };

это именно синтаксис и он не связан с ограничением области видимости как в ( ) и что объект { foo() }; имеет туже области видимости что и holder.
осознал никчемность и неправоту своего комментария :( ссылка действительно продлевает время жизни объекта.
осознал никчемность и неправоту своего комментария :( ссылка действительно продлевает время жизни объекта.

#include <iostream>

class Temp
{
public:
	Temp(int i):i_(i){std::cout << "Temp(" << i_ << ")\n";}
	~Temp(){std::cout << "~Temp(" << i_ << ")\n";}
private:
	int i_;
};

int main()
{
	std::cout << "Start main\n";
	Temp(1);
	const Temp& ref = Temp(2);
	std::cout << "Finish main\n";
}


Start main
Temp(1)
~Temp(1)
Temp(2)
Finish main
~Temp(2)


Опечатка после первого немаленького листига:


создастся o5
деструкторы будут вызваны в обратном порядке: o5, временный объект и o1


Вместо o5 должно быть o6.
Несколько лет назад в нашу компанию как-то тоже -то откуда-то пришла лихорадка к «константным ссылкам, продлевающим жизнь объекта». Вместо привычного сердцу программиста кода:
SomeClass object = SomeFunction();

стало принято писать:
const SomeClass& object = SomeFunction();

Сам грешил этим и оправдывал сие действие оптимизацией кода, «чтобы не было лишнего копирования». Сразу возвражу этому аргументу возможностью современных оптимизаторов уметь конструировать результат сразу в фрейме вызывающей функции, так что эта оптимизация сомнительна. С другой стороны данная нотация добавляет нечитабельности коду и, что еще хуже, дает возможность жестоко покрашиться:
struct Computing {
  Computing() : Current() {}

  Computing& Plus(int argument) {
    Current += argument;
    return *this;
  }

  int GetResult() const {
    return Current;
  }

private:
  int Current;
};

Computing Zero() {
  return Computing();
}

int main() {
  const Computing& result = Zero().Plus(1); // ВНИМАНИЕ: Баг!
  std::cout << result.GetResult(); // Неопределенное поведение
}

Баг заключается в том, что в коде const Computing& result = Zero().Plus(1); лексически на временный объект, возвращаемый из Zero() нет ни одной константной ссылки, и то, что эта ссылка возвращается из Plus, всего лишь фактическое положение вещей и компилятором учитываться не может. Поэтому временный объект уничтожится, как ему и полагается, по достижении точки с запятой и константная ссылка будет ссылаться на мусор.
Заключение: используйте константные ссылки только в качестве аргументов, и ваш код будет надежнее и читабельнее.
Решил проверить, и да, все верно вы сказали. для тех кто сомневается, подробный пример:
#include <iostream>

struct Computing {
	Computing() :
			Current() {
		std::cout << "build Computing\n";
	}
	~Computing() {
		Current = 0;
		std::cout << "destroy Computing\n";
	}

	Computing& Plus(int argument) {
		std::cout << "plus argument\n";
		Current += argument;
		return *this;
	}

	int GetResult() const {
		std::cout << "getting result\n";
		return Current;
	}

	Computing(const Computing& other) {
		std::cout << "copy constructor Computing\n";
		Current = other.Current;
	}

private:
	int Current;
};

Computing Zero() {
	return Computing();
}

int main() {
	std::cout << "start correct code\n";
	{
		Computing result = Zero().Plus(1);
		std::cout << "result is " << result.GetResult() << "\n";
	}
	std::cout << "finish correct code\n";
	std::cout << "\nstart incorrect code\n";
	{
		const Computing& result = Zero().Plus(1); // ВНИМАНИЕ: Баг!
		std::cout << "result is " << result.GetResult() << "\n"; // Неопределенное поведение
	}
	std::cout << "finish incorrect code\n";
}
Полностью поддерживаю, такой подход не для продакшена, покрашиться очень легко. Именно поэтому топик в хабе «ненормальное программирование».
Основная идея поста — показать неочевидную особенность ссылок, чтобы предотвратить подобные ошибки и лучше различать r-value и l-value ссылки.
Sign up to leave a comment.

Articles