Здравствуйте, remark, Вы писали:
E>>А чем это отличается от старого доброго: E>>В чем выгода?
R>Компилятору не обязательно уметь делать NRVO, что бы избавится от лишнего копирования. R>Шестая студия, например, должна делать на одно копирование меньше в его варианте, чем в твоём.
Не вижу смысла усложнять код из-за подозрений в ущербности компилятора.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: RFC: Польза от деструкторов локальных переменных
Здравствуйте, Не могу выдумать корректный ник АКА iZverg, Вы писали:
НМВ> value_type vpop() НМВ> { НМВ> deferred_pop p(*this) НМВ> return top(); НМВ> // вызов деструктора p НМВ> }
И так же учти, что тут идёт копирование в возвращаемое значение, а потом из возвращаемого значения в переменную пользователя. Исключение во время первого копирования ты можешь засечь таким образом. А во время второго — уже нет. Хотя, очевидно, что исключение во время второго копирования тоже можно считать, что "значение не дошло по пользователя" в том смысле, что у него ещё не было шанса его обработать. Поэтому его тоже надо как-то отлавливать и обрабатывать.
Решение с обёрткой над возвращаемым значением, я думаю, может решить и эту проблему.
remark'a
из потока "оптимизация возвращаемого значения" пересеклось с некоторыми моими мыслями.
Мысли старые, но все не хватало вермени как следует разобраться...Пытался поискать
аналоги, но поиск на RSDN не работает
Первая идея: оператор постфиксного инремента
class foo
{
private:
class deferred_increment
{
public:
typedef foo master_type;
explicit
deferred_increment(master_type& m): m_(m) {}
~deferred_increment() { ++m_; }
private:
master_type& m_;
};
public:
//prefix ++
foo& operator++()
{
/* ... do increment ... */return *this;
}
//postfix ++const foo operator++(int)
{
deferred_increment inc(*this);
return *this;
// увеличили *this в деструкторе
// ни одной временной переменной не надо
}
};
Если так можно (т.е. безопасно) делать, то почему так никто не делает? Я просмотрел
пару реализаций STL и весь Boost -- ничего такого...
Ещё брутальнее: безопасный по исключениям stack::pop(), возвращающий значение
Если исключение выброшено -- деструктор ничего не делает, если нет -- делает.
Вроде бы сильная (strong) гарантия безопасности по исключениям, семантика транзакции
налицо... Тестировал в MSVS 8 и gcc 4.1.1 -- работает правильно.
Но что-то меня здесь все-таки беспокоит... Может быть, просто я начитался Саттера,
который в "те времена" не знал ни одного хорошего использования (good use) функции
uncaught_exception()?
Буду благодарен за любой комментарий...
iZverg
Re: RFC: Польза от деструкторов локальных переменных
Здравствуйте, Не могу выдумать корректный ник АКА iZverg, Вы писали:
НМВ>Но что-то меня здесь все-таки беспокоит... Может быть, просто я начитался Саттера, НМВ>который в "те времена" не знал ни одного хорошего использования (good use) функции НМВ>uncaught_exception()?
std::uncaught_exception тут тоже не будет корректно работать.
Т.к. он выдаст true даже если исключение было выброшено не в этой функции, а было ещё при входе в функцию. Например, если это код выполняется внутри деструктора, который вызван в результате размотки стека, std::uncaught_exception выдаст true, даже если в этой функции не было исключений.
Есть подход запоминать результат std::uncaught_exception вначале функции, а потом стравнивать со значением в конце функции. Но он тоже ни к чему хорошему не приводит.
Тут единственный вариант делать явную переменную и в конце функции её вручную устанавливать в true, что значит, что исключений в функции не было. Тогда деструкторы смогут её проверять.
Здравствуйте, Не могу выдумать корректный ник АКА iZverg, Вы писали:
НМВ> //postfix ++ НМВ> const foo operator++(int) НМВ> { НМВ> deferred_increment inc(*this); НМВ> return *this; НМВ> // увеличили *this в деструкторе НМВ> // ни одной временной переменной не надо НМВ> }
По моим представлениям это должно работать, т.к. возвращаемое значение должно создаваться определённо перед разрушением локальных переменных. Но пусть выскажутся гуру.
НМВ>Если так можно (т.е. безопасно) делать, то почему так никто не делает? Я просмотрел НМВ>пару реализаций STL и весь Boost -- ничего такого...
Ну почему. Такие подходы встречаются иногда. Пару раз где-то видел аналогичные мысли.
А встречаются они редко, потомучто мало кто действительно знает С++, что бы делать такие вещи
Здравствуйте, eao197, Вы писали:
E>А чем это отличается от старого доброго: E>В чем выгода?
Компилятору не обязательно уметь делать NRVO, что бы избавится от лишнего копирования.
Шестая студия, например, должна делать на одно копирование меньше в его варианте, чем в твоём.
Здравствуйте, remark, Вы писали:
R>Тут единственный вариант делать явную переменную и в конце функции её вручную устанавливать в true, что значит, что исключений в функции не было. Тогда деструкторы смогут её проверять.
Но тут у тебя так просто не получится, т.к. тебе надо детектировать возникновение исключение не внутри функции, а во время неявного копирования.
Тут тогда надо делать обёртку над возвращаемым значением из pop(), что бы она контролировала, "дошло" ли значение до пользователя или нет. И только если все копирования прошли успешно, тогда снимать верхний элемент со стека.
Теоретически, я думаю, возможно сделать работающую схему.
R>
Re: RFC: Польза от деструкторов локальных переменных
От:
Аноним
Дата:
20.07.07 11:07
Оценка:
НМВ>
НМВ> //postfix ++
НМВ> const foo operator++(int)
НМВ> {
НМВ> deferred_increment inc(*this);
НМВ> return *this;
НМВ> // увеличили *this в деструкторе
НМВ> // ни одной временной переменной не надо
НМВ> }
НМВ>};
НМВ>
НМВ>Если так можно (т.е. безопасно) делать, то почему так никто не делает?
А зачем? В чем собственно заключается оптимизация? deferred_increment имеет размер sizeof(master_type&). Т.е. тот же временный объект создаваемый на стеке.
НМВ>Ещё брутальнее: безопасный по исключениям stack::pop(), возвращающий значение
НМВ>
А в чем суть этого кода? Насколько я понимаю top() не бросает исключений, он и так безопасен. А в деструкторе deferred_pop вызывается pop(), который может бросить исключение и нигде в деструкторе они не обрабатываются!
Re[2]: RFC: Польза от деструкторов локальных переменных
Здравствуйте, Аноним, Вы писали:
А>А зачем? В чем собственно заключается оптимизация? deferred_increment имеет размер sizeof(master_type&). Т.е. тот же временный объект создаваемый на стеке.
Ты хочешь сказать, что всегда sizeof(T) == sizeof(T*)?
А>А в чем суть этого кода? Насколько я понимаю top() не бросает исключений, он и так безопасен. А в деструкторе deferred_pop вызывается pop(), который может бросить исключение и нигде в деструкторе они не обрабатываются!
pop() тоже не бросает исключений. Исключения может бросать конструктор копирования T.
Здравствуйте, Аноним, Вы писали:
А>А зачем? В чем собственно заключается оптимизация? deferred_increment имеет размер sizeof(master_type&). Т.е. тот же временный объект создаваемый на стеке.
Обычно (sizeof(master_type&) == sizof(void*)), тогда как sizof(master_type) может быть любым + может
быть нетривиальный копирующий конструктор
А>А в чем суть этого кода? Насколько я понимаю top() не бросает исключений, он и так безопасен.
Их бросает копирующий конструктор в операторе return (обратите внимание: возврат по значению).
А pop() AFAIK не бросает исключений в большинстве реализаций, т.к. сводится к вызову
деструктора и (иногда) освобождению памяти, что бросать не должно
А> А в деструкторе deferred_pop вызывается pop(), который может бросить исключение и нигде в деструкторе они не обрабатываются!
Так и задумано По-моему, ничего плохого в этом нет.
Re[2]: RFC: Польза от деструкторов локальных переменных
Re[3]: RFC: Польза от деструкторов локальных переменных
От:
Аноним
Дата:
20.07.07 11:26
Оценка:
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Аноним, Вы писали:
А>>А зачем? В чем собственно заключается оптимизация? deferred_increment имеет размер sizeof(master_type&). Т.е. тот же временный объект создаваемый на стеке.
R>Ты хочешь сказать, что всегда sizeof(T) == sizeof(T*)? R>
Упс... Под sizeof(T&) я подразумевал размер, который компилятор использует для хранения ссылки
А>>А в чем суть этого кода? Насколько я понимаю top() не бросает исключений, он и так безопасен. А в деструкторе deferred_pop вызывается pop(), который может бросить исключение и нигде в деструкторе они не обрабатываются!
R>pop() тоже не бросает исключений. Исключения может бросать конструктор копирования T.
А, понял. Спасибо. А pop() точно не бросает? Это гарантированно или нет?
Re[3]: RFC: Польза от деструкторов локальных переменных
Здравствуйте, Не могу выдумать корректный ник АКА iZverg, Вы писали:
НМВ>Здравствуйте, remark, Вы писали...
НМВ>За что огромное спасибо Надо еще подумать, может выдумается что...
НМВ>Вопрос со вторым копированием -- очень интересный вопрос. Я так понял, это о том, что НМВ>происходит например в операторе= при НМВ>
НМВ>var = my_stack.vpop();
НМВ>
Да.
НМВ>Если исключение произошло в операторе=, то понятно, что технически значение потеряно. НМВ>Но вот такой пример НМВ>
НМВ>var = my_stack.vpop() + my_stack.vpop();
НМВ>
НМВ>заставляет меня усомниться в возможности написать не слишком громоздкую обертку...
А я и не говорил ничего по поводу, что она будет "не слишком громоздкая"
А при использовании top()/pop() пользователь сможет и в таком случае легко всё разрулить...
Здравствуйте, Не могу выдумать корректный ник АКА iZverg, Вы писали:
НМВ>Мысли старые, но все не хватало вермени как следует разобраться...Пытался поискать НМВ>аналоги, но поиск на RSDN не работает
НМВ>Первая идея: оператор постфиксного инремента
Здравствуйте, Programador, Вы писали:
P>Здравствуйте, Не могу выдумать корректный ник АКА iZverg, Вы писали:
НМВ>>Мысли старые, но все не хватало вермени как следует разобраться...Пытался поискать НМВ>>аналоги, но поиск на RSDN не работает
НМВ>>Первая идея: оператор постфиксного инремента
P> ~PPhelper(){++t;}
У тебя этот оператор может произвольное кол-во раз выполниться по прихоти компилятора.
Тут надо делать "передачу владения", что бы только один (последний) экземпляр PPhelper делал инкремент.
R>У тебя этот оператор может произвольное кол-во раз выполниться по прихоти компилятора. R>Тут надо делать "передачу владения", что бы только один (последний) экземпляр PPhelper делал инкремент.
Я так понял этот вопрос не относится к а=б++ + б++ поскольку это изначально UB.
По моим представлениям
Есть 2 случая
X foo()
{ X x;
return x;
}
Х не отводится в стеке foo а берется готовый, тоесть в момент обьявления он уже на месте. При наличии некоторой логики это не возможно. Не все компиляторы это поддерживают.
X foo()
{ Other other;
return other;
}
Это более простой случай. Нельзя ли его так понимать?
X foo()
{ Other other;
X x(other);
return x;
}
Поглядел 6.6.3 и 12.2.4
If many temporaries are created by the evaluation of the initializer, the temporaries are destroyed in reverse order of the completion of their construction.
Получается если возвращается временная, то разрушить ее нужно после возвращаемого значения, а обеспечить это никак невозможно. Тоесть такая интерпретация вроде не стандарту
Точки следования будут только ";" имхо
Re[4]: RFC: Польза от деструкторов локальных переменных
Здравствуйте, Programador, Вы писали:
P>Здравствуйте, remark, Вы писали:
R>>У тебя этот оператор может произвольное кол-во раз выполниться по прихоти компилятора. R>>Тут надо делать "передачу владения", что бы только один (последний) экземпляр PPhelper делал инкремент.
P>Я так понял этот вопрос не относится к а=б++ + б++ поскольку это изначально UB.
А при чём тут вообще это?
P>По моим представлениям P>Есть 2 случая P>
P>X foo()
P>{ X x;
P> return x;
P>}
P>
P>Х не отводится в стеке foo а берется готовый, тоесть в момент обьявления он уже на месте. При наличии некоторой логики это не возможно. Не все компиляторы это поддерживают.
Да. Это предельный случай того, что может сделать компилятор.
R>Дальше я ничего не понял...
Второй случай это когда возвращается значение, которое может быть приведено к типу функции. В стандарте говорится про какойто временный обьект, который возвращается, наверно его можно понимать так как в паскале , где он имеет имя совпадающее с именем функции