Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Что мы тут видим? А видим мы боль. Компилятор проигнорировал тот факт, что конструкторы и деструкторы M имеют глобальные побочные эффекты. Умный компилятор выбросил из нашей логической цепочки:
— создание копии m1 для передачи копии локального объекта как результата работы функции;
— вызов деструктора локального объекта m1 (что логично после выполнения первого пункта).
Деструктор локального объекта был вызван только после вызова конструктора копирования m2/m3.
В итоге — I изменилась не на 10, а на 6.
M(100) M(const M &obj) ~M() M(-100) M(const M &obj) ~M() 6 ~M() ~M()
M &operator=(const M &obj)
{
/* Копирующий оператор присваивания */
i = obj.i;
cout << "M &operator=(const M &obj)" << endl;
}M func( int Value )
{
if ( Value > 0 ) {
return M{100};
}
else {
return M{-100};
}
}M(100) M(-100) 2 ~M() ~M()
if (this != &obj)
{...}
return *this;Деструкторы объектов m2 и m3 вызываются уже при выходе из main().
Деструкторы объектов m2 и m3 вызываются уже при выходе из main(). А вот что действительно вызывает вопрос: почему в вашем выводе не присутствует вывода из этих деструкторов. Корректный результат, который получаю я, должен выглядеть так:
По логике произойти должно следующее:
Without NRVO (cl /Od /EHsc sample3.cpp), the expected output would be:
В результате чего — программа функционирует абсолютно неправильно, а потенциальное количество проблем в крупном проекте увеличивается до неприличия.
На самом деле, есть еще несколько интересных моментов, которые вытекают из такого поведения. Тем, кому интересно — рекомендую поиграться с разными настройками компиляции и перемещающими конструкторами.
Sleep(-1);
system("PAUSE.EXE"); Мое приложение не создавалось для прохождения тестов.
И у меня не линукс.
# 15
When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization.111) This elision of copy operations is permitted in the following circumstances (which may be combined to eliminate multiple copies):
— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type, the copy operation can be omitted by constructing the automatic object directly into the function’s return value
— when a temporary class object that has not been bound to a reference (12.2) would be copied to a class object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy
Accessing an object designated by a volatile glvalue (3.10), modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.
Copy elision, или как отстрелить ногу по самую шею…