Ш>Нет. Возвращаемое значение должно быть rvalue.
Ш>Коряво.
Коряво? Криво? Легко использовать неверно в другом контексте?
Вот с этим можно согласится.
А то, что "возвращаемое значение должно быть rvalue" — это выдумка. Нет такого требования, конечно же
Почему нет? Ну функция должна вернуть вектор по значению. Ей приходит какая-то ссылка. Ну, по идее она смувает вектор из ссылки в возвращаемый адрес, но может сразу вектор по этой ссылке создавать. Короче, не вижу тут UB
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, Marty, Вы писали:
M>Здравствуйте!
M>
M>template<typename ItemType>
M>inline
M>std::vector<ItemType>& push_back_helper( std::vector<ItemType> &v, const ItemType &elem )
M>{
M> v.push_back(elem);
M> return v;
M>}
M>inline
M>std::vector<int> makeVec( int i )
M>{
M> return push_back_helper( std::vector<int>(), i );
M>}
M>int main()
M>{
M> auto v = makeVec( 3 );
M>}
M>
под майкрософт может работать без проблем. Под gcc будет сигфолт, после передачи инстанса вектора он умрет при свертке стека (а именно после возврата из push_back_helper(std::vector<int>(), ... ) )У майкрософта была какая-то "оптимизация" по продлению жизни ссылки
M>Вроде ничему не противоречит, но решил уточнить
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.
M>>template<typename ItemType>
M>>inline
M>>std::vector<ItemType>& push_back_helper( std::vector<ItemType> &v, const ItemType &elem )
M>>{
M>> v.push_back(elem);
M>> return v;
M>>}
M>>inline
M>>std::vector<int> makeVec( int i )
M>>{
M>> return push_back_helper( std::vector<int>(), i );
M>>}
M>>int main()
M>>{
M>> auto v = makeVec( 3 );
M>>}
M>>
O> Под gcc будет сигфолт, после передачи инстанса вектора он умрет при свертке стека (а именно после возврата из push_back_helper(std::vector<int>(), ... ) )У майкрософта была какая-то "оптимизация" по продлению жизни ссылки
Сигфолта не будет, т.к. не скомпилируется.
Но даже если добавить функцию:
Даже тогда Сигфолта не будет, т.к. std::vector<int>() должен дожить до конца выполнения выражения push_back_helper( std::vector<int>(), i );, которое включает в себя копирование вектора в возвращаемый результат
inline
std::vector<int> makeVec( int i )
{
return push_back_helper( std::vector<int>(), i );
}
Здравствуйте, B0FEE664, Вы писали: BFE>Здравствуйте, ollv, Вы писали:
Скрытый текст
M>>>
M>>>template<typename ItemType>
M>>>inline
M>>>std::vector<ItemType>& push_back_helper( std::vector<ItemType> &v, const ItemType &elem )
M>>>{
M>>> v.push_back(elem);
M>>> return v;
M>>>}
M>>>inline
M>>>std::vector<int> makeVec( int i )
M>>>{
M>>> return push_back_helper( std::vector<int>(), i );
M>>>}
M>>>int main()
M>>>{
M>>> auto v = makeVec( 3 );
M>>>}
M>>>
BFE> O>> Под gcc будет сигфолт, после передачи инстанса вектора он умрет при свертке стека (а именно после возврата из push_back_helper(std::vector<int>(), ... ) )У майкрософта была какая-то "оптимизация" по продлению жизни ссылки
BFE>Сигфолта не будет, т.к. не скомпилируется.
Да на gcc compile rvalue не ляжет в ссылку. просто майкрософт ведет себя вероломно. Т/к/ он мало того, что компилирует, так еще и без ворнингов (с настройками по умолчанию). Его конечно можно нагнуть, чтобы такое не компилировалось но это опасности не отменяет.
+ к тому есть продление жизни для ссылок — тоже опасное поведение, с такими трюками можно легко запутаться особенно присутствуют сложные вычисления типов
В общем, я могу сказать одно, я такие практики оч не поддерживаю.
такое скомпилируется и может даже имеет какой-то смысл. Но изначально — r& fun(type& v) { return v; }
я пытаюсь избегать, именно по причине того, что майкрософт ведет себя вероломно.
inline
std::vector<int> makeVec( int i )
{
std::vector<int> r;
return push_back_helper(r, i );
}
BFE>Даже тогда Сигфолта не будет, т.к. std::vector<int>() должен дожить до конца выполнения выражения push_back_helper( std::vector<int>(), i );, которое включает в себя копирование вектора в возвращаемый результат
BFE>
BFE>inline
BFE>std::vector<int> makeVec( int i )
BFE>{
BFE> return push_back_helper( std::vector<int>(), i );
BFE>}
BFE>
зачем тут мув семантика вообще непонятно. я вообще не понимаю зачем весь этот сырбор, если РВО помогает мейкать вектора и прочие объекты без потерь
все уже сделано в решениях вида std::make_shared_ptr и прочих bind. Параметры муваются, объекты создаются и возвращаются без потерь.
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.
Здравствуйте, ollv, Вы писали:
BFE>>Сигфолта не будет, т.к. не скомпилируется. O>Да на gcc compile rvalue не ляжет в ссылку. просто майкрософт ведет себя вероломно. Т/к/ он мало того, что компилирует, так еще и без ворнингов (с настройками по умолчанию). Его конечно можно нагнуть, чтобы такое не компилировалось но это опасности не отменяет.
В чём опасность? Такой код компилируется и работает у Майкрософта более 15-ти лет, ещё до принятия стандарта 2011-ого года, до move семантики. То, что это не по стандарту — это проблемы стандарта, а не кода.
O>+ к тому есть продление жизни для ссылок — тоже опасное поведение, с такими трюками можно легко запутаться особенно присутствуют сложные вычисления типов
Здесь нет продления жизни для ссылок.
O>В общем, я могу сказать одно, я такие практики оч не поддерживаю.
Никакой особой практики я тут не вижу. Это довольно редкий случай, как правило порождаемый при переиспользовании кода в ситуациях не предвиденных изначально.
O>такое скомпилируется и может даже имеет какой-то смысл. Но изначально — r& fun(type& v) { return v; } O>я пытаюсь избегать, именно по причине того, что майкрософт ведет себя вероломно.
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, ollv, Вы писали:
BFE>>>Сигфолта не будет, т.к. не скомпилируется. O>>Да на gcc compile rvalue не ляжет в ссылку. просто майкрософт ведет себя вероломно. Т/к/ он мало того, что компилирует, так еще и без ворнингов (с настройками по умолчанию). Его конечно можно нагнуть, чтобы такое не компилировалось но это опасности не отменяет.
BFE>В чём опасность? Такой код компилируется и работает у Майкрософта более 15-ти лет, ещё до принятия стандарта 2011-ого года, до move семантики. То, что это не по стандарту — это проблемы стандарта, а не кода.
Ну давай в политику такие вопросы )) Я считаю, что если ты пишешь на С++ такое typename calc_type<somw_arg>::type v = get_foo(); должно работать одинаково. На то он и стандарт. Кстати, сейчас майкрософт работает над этим. И по моему уже так просто в ссылку rvalue не пихнуть
O>>+ к тому есть продление жизни для ссылок — тоже опасное поведение, с такими трюками можно легко запутаться особенно присутствуют сложные вычисления типов BFE>Здесь нет продления жизни для ссылок.
есть но завуалированное. ссылка принимает rvalue и объект живет, в то время как rvalue уже умирает по стандарту после вызова конструктора. Потому оно и не компилируется в джисях
O>>В общем, я могу сказать одно, я такие практики оч не поддерживаю. BFE>Никакой особой практики я тут не вижу. Это довольно редкий случай, как правило порождаемый при переиспользовании кода в ситуациях не предвиденных изначально.
нет не редкий. встречается постоянно. тебя спасет только если ты пишешь только под мягких, с мультиплатформенностью таких практик (я лично) избегаю. просто потому как при вычислении типа легко можно получить такой эффект ..
и это ) ты считай как считаешь нужным. я не вижу даже смысла тут дискуссию заводить
O>>такое скомпилируется и может даже имеет какой-то смысл. Но изначально — r& fun(type& v) { return v; } O>>я пытаюсь избегать, именно по причине того, что майкрософт ведет себя вероломно.
BFE>Веру в что ломает майкрософт?
Ты собрался в этимологию слова вероломно уходить? смысл в том, что один и тот-же код может работать по разному в зависимости от платформы в ключевом (как я считаю) контексте. А должен работать одинаково
ну это мое мнение. Ты можешь считать, что это проблема стандарта, а не майкрософт С++, но я считаю иначе. Чисто математически это — верно. Но политически ты можешь с этим поспорить. У меня желания нет ))
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.
Здравствуйте, ollv, Вы писали:
BFE>>В чём опасность? Такой код компилируется и работает у Майкрософта более 15-ти лет, ещё до принятия стандарта 2011-ого года, до move семантики. То, что это не по стандарту — это проблемы стандарта, а не кода. O> Ну давай в политику такие вопросы )) Я считаю, что если ты пишешь на С++ такое typename calc_type<somw_arg>::type v = get_foo(); должно работать одинаково. На то он и стандарт. Кстати, сейчас майкрософт работает над этим. И по моему уже так просто в ссылку rvalue не пихнуть
Это не политика. Это та проблема стандарта, которую решили введением move семантики. Только вот Майкрософт эту проблему пытался решить до введения move семантики и из-за обратной совместимости отказаться от такого решения не может.
O>>>+ к тому есть продление жизни для ссылок — тоже опасное поведение, с такими трюками можно легко запутаться особенно присутствуют сложные вычисления типов BFE>>Здесь нет продления жизни для ссылок. O>есть но завуалированное. ссылка принимает rvalue и объект живет, в то время как rvalue уже умирает по стандарту после вызова конструктора. Потому оно и не компилируется в джисях
rvalue — это выражение, оно не может жить или умереть.
А вот временные объекты обязаны дожить (по стандарту) до конца выполнения выражения, так что вектор std::vector<int>() обязан дожить до конца выполнения функции push_back_helper.
Здравствуйте, T4r4sB, Вы писали:
TB>Почему нет? Ну функция должна вернуть вектор по значению. Ей приходит какая-то ссылка. Ну, по идее она смувает вектор из ссылки в возвращаемый адрес, но может сразу вектор по этой ссылке создавать. Короче, не вижу тут UB
Возможен сценарий получения битой ссылки. При нынешних-то тенденциях максимально широкого использования auto&&:
auto&& v = push_back_helper( std::vector<int>(), i );
В то время как при возврате по значению это полностью безопасный код.
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, ollv, Вы писали:
BFE>Это не политика. Это та проблема стандарта, которую решили введением move семантики. Только вот Майкрософт эту проблему пытался решить до введения move семантики и из-за обратной совместимости отказаться от такого решения не может.
это и есть политика. Неконстантная ссылка не должна продлевать жизнь объекта, по стандарту. У майкрософта это не так. Считать допустимо это, или нет — вопрос политический.
O>>>>+ к тому есть продление жизни для ссылок — тоже опасное поведение, с такими трюками можно легко запутаться особенно присутствуют сложные вычисления типов BFE>>>Здесь нет продления жизни для ссылок. O>>есть но завуалированное. ссылка принимает rvalue и объект живет, в то время как rvalue уже умирает по стандарту после вызова конструктора. Потому оно и не компилируется в джисях BFE>rvalue — это выражение, оно не может жить или умереть.
Не знаю о чем ты )) к rvalue вполне применим термин lifetime. Что касается выражения, синтаксически все, что попадает в конечном счете в АСТ — expression. такова селяви синтаксического анализа. Объект — это уже физика языка, так вот живет объект как rvalue, выражением в данном случае будет именно std::vector<int>() -> arg, вот здесь оно и умрет, после инициализации параметра.
BFE>А вот временные объекты обязаны дожить (по стандарту) до конца выполнения выражения,
Только вот конец выражения будет ровно после запятой (или скобки), std::vector<int>() -> arg. И если арг не проинициализируется от рвелью и не продлит жизнь объекту, он умрет.
BFE>так что вектор std::vector<int>() обязан дожить до конца выполнения функции push_back_helper.
Не совсем понимаю, что тут кто кому должен, если такая передача некорректна изначально. майкрософтовский вариант скорее попытка решить свои проблемы, за счет стандарта. И это — политика.
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.
Ш>Но, вообще, это плохо. Коряво. Ш>Лучше сделать что-то вроде
Я бы предложил еще один подход, основанный на максимально полном использовании perfect forwarding references. УПРОЩЕННЫЙ вариант, демонстрирующий идею, выглядит так:
Для тех, кто пока еще не понял, в чем фокус (а такие есть, я уверен): это функция-универсал — может возвращать как ссылку, так и значение — в зависимости от типа входного параметра. Когда входной параметр задается lvalue выражением, хоть константным, хоть неконстантным, функция возвращает lvalue ссылку — константную или неконстантную. Если же параметр задается rvalue выражением, функция вернет значение, применив при этом move. Преимущество такого подхода — одним выстрелом получается неплохой компромисс между эффективностью и надежностью. Код защищен от опасности возникновения висячих ссылок.
Ну а перегрузки и типовую безопасность придется достигать использованием SFINAE или концептов. И это слабая сторона такого подхода.
Здравствуйте, ollv, Вы писали:
O>>>>>+ к тому есть продление жизни для ссылок — тоже опасное поведение, с такими трюками можно легко запутаться особенно присутствуют сложные вычисления типов BFE>>>>Здесь нет продления жизни для ссылок. O>>>есть но завуалированное. ссылка принимает rvalue и объект живет, в то время как rvalue уже умирает по стандарту после вызова конструктора. Потому оно и не компилируется в джисях BFE>>rvalue — это выражение, оно не может жить или умереть. O> Не знаю о чем ты )) к rvalue вполне применим термин lifetime.
Что обозначает lifetime для rvalue?
O>Что касается выражения, синтаксически все, что попадает в конечном счете в АСТ — expression. такова селяви синтаксического анализа.
И какое отношение это имеет ко времени жизни?
O>Объект — это уже физика языка, так вот живет объект как rvalue, выражением в данном случае будет именно std::vector<int>() -> arg, вот здесь оно и умрет, после инициализации параметра.
Выражение умрёт? Или умрёт объект?
BFE>>А вот временные объекты обязаны дожить (по стандарту) до конца выполнения выражения, O> Только вот конец выражения будет ровно после запятой (или скобки), std::vector<int>() -> arg.
Конец full-expression'а?
O> И если арг не проинициализируется от рвелью и не продлит жизнь объекту, он умрет.
Если арг не проинициализируется, то и функция вызвана не будет. Причём тут продление жизни для ссылок?
Здравствуйте, T4r4sB, Вы писали:
TB>Превращается в возврат 1 TB>((( TB>По коду ссылка не битая, но она указывает на мёртвый объект, у которого уже вызван деструктор
Так это и есть битая ссылка, раз объект мертвый. Вот эта единица — это просто стековый мусор, значение которого по случайности совпало с оригиналом. Попробуй использовать, например, std::string вместо int — спецэффекты будут поинтереснее.
Здравствуйте, rg45, Вы писали:
R>Так это и есть битая ссылка, раз объект мертвый.
Скажем так, ссылка ведёт на объект, находящийся на стеке в вызывающей функции. То есть формально она не битая. И если бы у объекта не было деструктора, то ссылка бы вообще делала вид, что всё ништяк.
R>Попробуй использовать, например, std::string вместо int — спецэффекты будут поинтереснее.
То есть ссылка возвращается нормальная. Дальше по коду строка разрушается сразу после вызова этой функции, если вывести что-то на экран, то выводится пустая строка.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
TB>Скажем так, ссылка ведёт на объект, находящийся на стеке в вызывающей функции. То есть формально она не битая. И если бы у объекта не было деструктора, то ссылка бы вообще делала вид, что всё ништяк.
Давай по порядочку.
Во-первых, деструктор формально есть у любого типа данных, даже у int. И его даже можно явно позвать: http://coliru.stacked-crooked.com/a/8360b8eef0155e2c. Просто деструктор такая хитрая штука, что он может быть определен пользователем, а может быть сгенерирован компилятором. Деструктор может быть тривиальным и нетривиальным. Но он есть всегда. И время жизни объекта, согласно стандарту, закачивается, ПРИ ВХОДЕ, в деструктор (даже если он формальный — в этом случае просто где вход, там и выход).
Во-вторых, сразу же после инициализации auto&& i = test(IntWrapper(0)); время жизни объекта IntWrapper сразу же заканчивается. От него остается только область памяти, которой компилятор может распоряжаться по своему усмотрению. Например, он может разместить в ней объект(ы) какого-нибудь типа, массивы, вообще все, что угодно. Если ты начнешь постепенно усложнять свой пример, ты в этом убедишься рано или поздно. Поэтому любое использование объекта после окончания его времени жизни ведет к UB — согласно стандарту, ессно. Несмотря на то, что ссылка указывает на валидную область памяти, ее объекта в этой области уже нет и пользоваться такой ссылкой нельзя. Вот такие ссылки, которые пережили свои объекты и которыми нельзя пользоваться, я и называю "битыми".
Здравствуйте, rg45, Вы писали:
R>Давай по порядочку.
Да, согласен, пользоваться такой ссылкой нельзя.
Другой вопрос: а чего добились кресты, уничтожая временные объекты в конце текущего выражения, а не в конце операторного блока? По расходу памяти — копейки. Зато геморроя лишнего много.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
TB>Другой вопрос: а чего добились кресты, уничтожая временные объекты в конце текущего выражения, а не в конце операторного блока? По расходу памяти — копейки. Зато геморроя лишнего много.
Ну, я могу только порассуждать на эту тему. По расходу памяти — это ведь смотря как писать код. Размеры блоков не лимитированы и число временных объектов, создаваемых в подвыражениях, могло бы зашкаливать за разумные рамки. А с другой стороны, наверное, не смогли придумать сценариев, когда бы это создавало какие-то непреодолимые трудности.
Здравствуйте, rg45, Вы писали:
R>Ну, я могу только порассуждать на эту тему. По расходу памяти — это ведь смотря как писать код. Размеры блоков не лимитированы и число временных объектов, создаваемых в подвыражениях, могло бы зашкаливать за разумные рамки.
На практике я на некотором другом языке (тоже с RAII) писал, там по сути любое выражение вида
f(g())
означало что-то типа
tmp=g();
f(tmp);
И этот tmp жил до конца текущего блока, и что-то стек с кучей от этого не переполнялись.
R>А с другой стороны, наверное, не смогли придумать сценариев, когда бы это создавало какие-то непреодолимые трудности.
Даже на чистом асме нет непреодолимых трудностей.
В данном примере трудность была создана — удалось получить ссылку на объект, у которого уже вызвался деструктор.
Да, она преодолима. Но это трудность, на решение которой будут тратиться лишние человекочасы.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
R>Во-первых, деструктор формально есть у любого типа данных, даже у int. И его даже можно явно позвать: http://coliru.stacked-crooked.com/a/8360b8eef0155e2c. Просто деструктор такая хитрая штука, что он может быть определен пользователем, а может быть сгенерирован компилятором. Деструктор может быть тривиальным и нетривиальным. Но он есть всегда. И время жизни объекта, согласно стандарту, закачивается, ПРИ ВХОДЕ, в деструктор (даже если он формальный — в этом случае просто где вход, там и выход).
Итак, смотрим в C++17 [expr.pseudo]/1: > The use of a pseudo-destructor-name after a dot . or arrow -> operator represents the destructor for the non-class type denoted by type-name or decltype-specifier. The result shall only be used as the operand for the function call operator (), and the result of such a call has type void. The only effect is the evaluation of the postfix-expression before the dot or arrow.
Никаких вызовов деструкторов (для int) и окончания его жизни...
Да, в C++20 закрыли одно CWG issue и ЕМНИП применили его решение ретроактивно, но никаких вызовов деструкторов для `int` не появилось.
[expr.call]/5: > If the postfix-expression names a pseudo-destructor (in which case the postfix-expression is a possibly-parenthesized class member access), the function call destroys the object of scalar type denoted by the object expression of the class member access ([expr.ref], [basic.life]).
[basic.life]/1: > The lifetime of an object o of type T ends when: > — if T is a non-class type, the object is destroyed, or > — if T is a class type, the destructor call starts, or > — the storage which the object occupies is released, or is reused by an object that is not nested within o ([intro.object]).
Здравствуйте, σ, Вы писали:
σ>Итак, смотрим в C++17 [expr.pseudo]/1: >> The use of a pseudo-destructor-name after a dot . or arrow -> operator represents the destructor for the non-class type denoted by type-name or decltype-specifier. The result shall only be used as the operand for the function call operator (), and the result of such a call has type void. The only effect is the evaluation of the postfix-expression before the dot or arrow.
σ>Никаких вызовов деструкторов (для int) и окончания его жизни... σ>Да, в C++20 закрыли одно CWG issue и ЕМНИП применили его решение ретроактивно, но никаких вызовов деструкторов для `int` не появилось.
Я фигею, тетя Клава, с вашей телепередачи.
Во-первых, в данном случае рассматривается время жизни и вызов деструктора объекта типа IntWrapper, а не int.
А во-вторых, говорится же о допустимости выражений с использованием деструкторов в т.ч. для типа int. Ты же зацепился за некорректное использование слова "вызов", которое не делает погоды в данном случае от слова "совсем". Тем более, что сами же они называют это не иначе как "such a call". Да, этот самый CALL имеет ограниченный эффект, но я ничего такого и не утверждал, что этому бы противоречило.
Нельзя же так циклиться на мелочах. Ты за деревьями леса не видишь. И не в первый раз.
R>о времени жизни типа int в приведенном примере даже речи не шло, поскольку рассматривалось время жизни объекта типа IntWrapper
R> Во-первых, деструктор формально есть у любого типа данных, даже у int. И его даже можно явно позвать: http://coliru.stacked-crooked.com/a/8360b8eef0155e2c. Просто деструктор такая хитрая штука, что он может быть определен пользователем, а может быть сгенерирован компилятором. Деструктор может быть тривиальным и нетривиальным. Но он есть всегда. И время жизни объекта, согласно стандарту, закачивается, ПРИ ВХОДЕ, в деструктор (даже если он формальный — в этом случае просто где вход, там и выход).
Хочешь сказать, что это: R> деструктор формально есть у любого типа данных, даже у int. ... И время жизни объекта, согласно стандарту, закачивается, ПРИ ВХОДЕ, в деструктор (даже если он формальный — в этом случае просто где вход, там и выход).
не про то, что у int формально существует тривиальный сгенерированный компилятором деструктор при вызове которого заканчивается время жизни объекта типа int?
Здравствуйте, σ, Вы писали:
σ>[/ccode]Хочешь сказать, что это:
R> Во-первых, деструктор формально есть у любого типа данных, даже у int. И его даже можно явно позвать: http://coliru.stacked-crooked.com/a/8360b8eef0155e2c. Просто деструктор такая хитрая штука, что он может быть определен пользователем, а может быть сгенерирован компилятором. Деструктор может быть тривиальным и нетривиальным. Но он есть всегда. И время жизни объекта, согласно стандарту, закачивается, ПРИ ВХОДЕ, в деструктор (даже если он формальный — в этом случае просто где вход, там и выход).
σ>не про то, что у int формально существует тривиальный сгенерированный компилятором деструктор при вызове которого заканчивается время жизни объекта типа int?
Хочу сказать, что "формально есть" следует понимать, как "можно написать выражение". Или тебя удивляет, что я не цитирую стандарт по памяти слово в слово?
Ну а то, что после выполнения выражения i.~Int() время жизни i продолжается, для меня новость, признаюсь честно. Спасибо, конечно, но в данном случае это никак не влияет на выводы, поскольку рассматривается время жизни объекта типа IntWrapper, для которого все сказанное мной остается в силе.
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, ollv, Вы писали:
Так погоди. Давай я сначала попробую сказать точнее, почему я связываю кейсы
type& v = getrvalue();
call(getrvalue(), ...);
в обоих случаях происходит инициализация простой ссылки от рвелью. В первом случае это совсем критично, потому как в gcc (я тут даже версию не вспомню версию) есть кейсы, где это компиябильно, но продления жизни для темпорари object не происходит (и еще вопрос, происходит ли тут вообще материализация temporary object), во втором спасает только то, что оно не компилируется. т/к тоже вопрос, если вдруг произойдет кейс, где оно скомпилируется, произойдет ли там материализация temporary obj. И совершенно все равно каким рулом будет продлена жизнь темп объекта.
Видишь ли практик, я считаю, мыслит прецедентарно. Если прецедент был (а он был в практике) эта практика метится как ущербная. В этом случае совсем не тяжело просто перенести создание объекта выше.
O>> Не знаю о чем ты )) к rvalue вполне применим термин lifetime. BFE>Что обозначает lifetime для rvalue?
Да сплошь и рядом говорят lifetime of rvalue. lifetime of expression
Тут получается небольшой конфликт понятий если rvalue это выражение — то выражение суть понятия из синтаксического анализа. И мы тут перемешиваем понятия из синтаксиса и физики языка. А по сути rvalue — рантайм физика языка, а потому звать его выражением, как его понимать ? это не синтаксическое выражение ? ))
А так понятно, что касательно этого примера исключительно в С++ говорим о object lifetime.
O>>Что касается выражения, синтаксически все, что попадает в конечном счете в АСТ — expression. такова селяви синтаксического анализа. BFE>И какое отношение это имеет ко времени жизни?
а это и не о времени жизни. это о том, что "rvalue — это выражение". Хоть стандарт и показывает нам именно так, это имхо немного неровно.
O>>Объект — это уже физика языка, так вот живет объект как rvalue, выражением в данном случае будет именно std::vector<int>() -> arg, вот здесь оно и умрет, после инициализации параметра. BFE>Выражение умрёт? Или умрёт объект?
Давай так ) если считать, что мы принимаем связывание rvalue с простой ссылкой, то работает это правило
(6.9) — A temporary object bound to a reference parameter in a function call (7.6.1.2) persists until the
completion of the full-expression containing the call.
BFE>>>А вот временные объекты обязаны дожить (по стандарту) до конца выполнения выражения, O>> Только вот конец выражения будет ровно после запятой (или скобки), std::vector<int>() -> arg. BFE>Конец full-expression'а?
O>> И если арг не проинициализируется от рвелью и не продлит жизнь объекту, он умрет. BFE>Если арг не проинициализируется, то и функция вызвана не будет. Причём тут продление жизни для ссылок?
так именно. "если проинициализируется и продлит жизнь". А то может ведь и не продлить (получим битую ссылку в теле)
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.
Здравствуйте, ollv, Вы писали:
O> Так погоди. Давай я сначала попробую сказать точнее, почему я связываю кейсы O>
O> type& v = getrvalue();
O> call(getrvalue(), ...);
O>
O> в обоих случаях происходит инициализация простой ссылки от рвелью. В первом случае это совсем критично, потому как в gcc (я тут даже версию не вспомню версию) есть кейсы, где это компиябильно, но продления жизни для темпорари object не происходит (и еще вопрос, происходит ли тут вообще материализация temporary object), во втором спасает только то, что оно не компилируется. т/к тоже вопрос, если вдруг произойдет кейс, где оно скомпилируется, произойдет ли там материализация temporary obj. И совершенно все равно каким рулом будет продлена жизнь темп объекта.
По моим представлением эти два выражения ничем не отличаются с точки зрения жизни временных объектов. Оба временных объекта должны дожить, условно говоря, "до точки с запятой". Я не совсем понимаю зачем нужно расписывать в стандарте "продление жизни" если прямо сказано, что:
Temporary objects are destroyed as the last step in evaluating the full-expression
Можно же написать: getrvalue().print();. Тут тоже есть "продление жизни"?
O>>> Не знаю о чем ты )) к rvalue вполне применим термин lifetime. BFE>>Что обозначает lifetime для rvalue? O> Да сплошь и рядом говорят lifetime of rvalue. lifetime of expression O>Тут получается небольшой конфликт понятий если rvalue это выражение — то выражение суть понятия из синтаксического анализа. И мы тут перемешиваем понятия из синтаксиса и физики языка. А по сути rvalue — рантайм физика языка, а потому звать его выражением, как его понимать ? это не синтаксическое выражение ? )) O> А так понятно, что касательно этого примера исключительно в С++ говорим о object lifetime.
Видимо из-за этой путаницы терминологии я ничего не понимаю. Есть выражения разных категорий. Они (выражения) могут либо порождать временные объекты, либо нет. Если временные объекты были материализованы, то можно говорить о времени их жизни. Зачем при этом привязывать время жизни временного объекта к категории выражения мне совершенно не понятно. Это же независимые понятия.
O>>>Объект — это уже физика языка, так вот живет объект как rvalue, выражением в данном случае будет именно std::vector<int>() -> arg, вот здесь оно и умрет, после инициализации параметра. BFE>>Выражение умрёт? Или умрёт объект? O> Давай так ) если считать, что мы принимаем связывание rvalue с простой ссылкой, то работает это правило O>
(6.9) — A temporary object bound to a reference parameter in a function call (7.6.1.2) persists until the
O>completion of the full-expression containing the call.
Зачем нужна эта сентенция, если утверждение процитированное выше —
O>>> И если арг не проинициализируется от рвелью и не продлит жизнь объекту, он умрет. BFE>>Если арг не проинициализируется, то и функция вызвана не будет. Причём тут продление жизни для ссылок? O> так именно. "если проинициализируется и продлит жизнь". А то может ведь и не продлить (получим битую ссылку в теле)
Я не могу придумать конструкцию вызова функции без инициализации аргумента.
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, ollv, Вы писали:
O>> Так погоди. Давай я сначала попробую сказать точнее, почему я связываю кейсы O>>
O>> type& v = getrvalue();
O>> call(getrvalue(), ...);
O>>
BFE>По моим представлением эти два выражения ничем не отличаются с точки зрения жизни временных объектов. Оба временных объекта должны дожить, условно говоря, "до точки с запятой".
Таким образом простая ссылка в майкрософт, вела (сейчас они уже по умолчанию отключают такую возможность) себя как константная в стандарте.
Причем замечу
The word const was never present in this section. The rule has always been (from as long as I can remember) that a temporary used to initialize a reference has its lifetime extended to match that of the reference, regardless of the type of the reference.
И, кстати, говорят о продлении lifetime
BFE>Я не совсем понимаю зачем нужно расписывать в стандарте "продление жизни" если прямо сказано, что: BFE>
BFE>Temporary objects are destroyed as the last step in evaluating the full-expression
Меня вот это и смущает ) то, что мы то ссылаемся на стандарт, то "это проблемы стандарта" ))
BFE>Можно же написать: getrvalue().print();. Тут тоже есть "продление жизни"?
материализация временного объекта, и продление жизни до full-expression. без точки темпорари не создастся. только вот тут момент, опять таки из области синт анализа. Подозреваю, что само понятие full-expression идет именно из грамматики что-то типа
full-expression-body : expression | expression end-of-block full-expression-body
full-expression : full-expression-body end-of-scope
BFE>Видимо из-за этой путаницы терминологии я ничего не понимаю. Есть выражения разных категорий. Они (выражения) могут либо порождать временные объекты, либо нет. Если временные объекты были материализованы, то можно говорить о времени их жизни. Зачем при этом привязывать время жизни временного объекта к категории выражения мне совершенно не понятно. Это же независимые понятия.
Глядя на стандарт и диаграммку
Возникают смутные сомнения, что скопипащено оно было с некоей грамматики. Отсюда и это странное смешение — rvalue и выражения. Хотя вот эти ветвления скорее обоснованы структурами синтаксического разбора,
O>>>>Объект — это уже физика языка, так вот живет объект как rvalue, выражением в данном случае будет именно std::vector<int>() -> arg, вот здесь оно и умрет, после инициализации параметра. BFE>>>Выражение умрёт? Или умрёт объект? O>> Давай так ) если считать, что мы принимаем связывание rvalue с простой ссылкой, то работает это правило O>>
(6.9) — A temporary object bound to a reference parameter in a function call (7.6.1.2) persists until the
O>>completion of the full-expression containing the call.
BFE>Зачем нужна эта сентенция, если утверждение процитированное выше —
Так она с дополнением )) "если считать, что мы принимаем связывание rvalue с простой ссылкой, то работает это правило ...". И именно так оно и должно звучать (имхо) в практических руководствах.
на этом компиляторе видно как работает в данном случае студийный компилятор. тут простой референс ведет себя как константный. https://rextester.com/l/cpp_online_compiler_visual
O>>>> И если арг не проинициализируется от рвелью и не продлит жизнь объекту, он умрет. BFE>>>Если арг не проинициализируется, то и функция вызвана не будет. Причём тут продление жизни для ссылок? O>> так именно. "если проинициализируется и продлит жизнь". А то может ведь и не продлить (получим битую ссылку в теле) BFE>Я не могу придумать конструкцию вызова функции без инициализации аргумента.
Но я не про инициализацию говорил, я говорил про потенциальную вероятность того, что ссылка окажется битая, т/к каким-то образом не произойдет материализация временного объекта (т/к там просто референс)
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.
Здравствуйте, ollv, Вы писали:
O>>> Так погоди. Давай я сначала попробую сказать точнее, почему я связываю кейсы O>>>
O>>> type& v = getrvalue();
O>>> call(getrvalue(), ...);
O>>>
BFE>>По моим представлением эти два выражения ничем не отличаются с точки зрения жизни временных объектов. Оба временных объекта должны дожить, условно говоря, "до точки с запятой".
O>Вот этимологически как оно было. O>https://stackoverrun.com/ru/q/6489490
O> Таким образом простая ссылка в майкрософт, вела (сейчас они уже по умолчанию отключают такую возможность) себя как константная в стандарте. O>Причем замечу O>
The word const was never present in this section. The rule has always been (from as long as I can remember) that a temporary used to initialize a reference has its lifetime extended to match that of the reference, regardless of the type of the reference.
O> И, кстати, говорят о продлении lifetime
Говорят о продлении жизни объекта для ссылки v после выполнения: type& v = getrvalue();.
Это несколько отличается от времени жизни объекта созданного в call(getrvalue(), ...);. После выполнения этого выражения объект созданный в getrvalue() умирает в отличии от объекта на который ссылается v.
type& v = getrvalue(); // (1)
call(getrvalue(), ...); // (2)
// вот тут у MS обект созданный в (1) продолжает жить, а объект созданный в (2) уже разрушен.
Поэтому, на мой взгляд, говорить о продлении жизни для объекта созданного в call(getrvalue(), ...); смысла нет.
O> Глядя на стандарт и диаграммку O>expression O> | \ O>glvalue rvalue O> | | \ O>lvalue xvalue prvalue O> Возникают смутные сомнения, что скопипащено оно было с некоей грамматики. Отсюда и это странное смешение — rvalue и выражения. Хотя вот эти ветвления скорее обоснованы структурами синтаксического разбора,
Вот тут я согласен. У меня тоже сложилось впечатление, что вместо описания языка мы имеет описание работы парсера.
O>>>>> И если арг не проинициализируется от рвелью и не продлит жизнь объекту, он умрет. BFE>>>>Если арг не проинициализируется, то и функция вызвана не будет. Причём тут продление жизни для ссылок? O>>> так именно. "если проинициализируется и продлит жизнь". А то может ведь и не продлить (получим битую ссылку в теле) BFE>>Я не могу придумать конструкцию вызова функции без инициализации аргумента. O>Но я не про инициализацию говорил, я говорил про потенциальную вероятность того, что ссылка окажется битая, т/к каким-то образом не произойдет материализация временного объекта (т/к там просто референс)
Существование ссылки без инициализации запрещено, так что такой ситуации быть не может.
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, ollv, Вы писали:
BFE>
BFE> type& v = getrvalue(); // (1)
BFE> call(getrvalue(), ...); // (2)
BFE> // вот тут у MS обект созданный в (1) продолжает жить, а объект созданный в (2) уже разрушен.
BFE>
Я именно и пытался сказать, что ссылка обычная ссылка в МС ведет себя не по стандарту, и позволяет себя инициализировать от rvalue, что по сути и приводит к неоднозначности при переходе от одного компилятора к другому. Именно инициализация простой ссылки от rvalue и есть кривизна / опасность в данном подходе. Что собственно я и пытаюсь утверждать вот уже несколько постов. Разве что мне этимологически стало понятно откуда растут грабли. А именно потому, что майкрософт решила взять поведение ссылки, которое было еще до появления стандарта.
BFE>Поэтому, на мой взгляд, говорить о продлении жизни для объекта созданного в call(getrvalue(), ...); смысла нет.
Я думаю, что вопрос тут скорее о тех кейсах, в которых gcc таки соберет код, в котором идет инициализация ссылки от rvalue, но вот extend lifetime не произойдет (или материализации темп объекта). Имхо такие вещи лучше снабжать комментариями, и пытаться избегать инициализации простой ссылки rvalue. Т/е падение точно было type& r = getrvalue(); т/к здесь шло получение битой ссфлки.
O>> Глядя на стандарт и диаграммку O>>expression O>> | \ O>>glvalue rvalue O>> | | \ O>>lvalue xvalue prvalue O>> Возникают смутные сомнения, что скопипащено оно было с некоей грамматики. Отсюда и это странное смешение — rvalue и выражения. Хотя вот эти ветвления скорее обоснованы структурами синтаксического разбора,
BFE>Вот тут я согласен. У меня тоже сложилось впечатление, что вместо описания языка мы имеет описание работы парсера.
O>>>>>> И если арг не проинициализируется от рвелью и не продлит жизнь объекту, он умрет. BFE>>>>>Если арг не проинициализируется, то и функция вызвана не будет. Причём тут продление жизни для ссылок? O>>>> так именно. "если проинициализируется и продлит жизнь". А то может ведь и не продлить (получим битую ссылку в теле) BFE>>>Я не могу придумать конструкцию вызова функции без инициализации аргумента. O>>Но я не про инициализацию говорил, я говорил про потенциальную вероятность того, что ссылка окажется битая, т/к каким-то образом не произойдет материализация временного объекта (т/к там просто референс) BFE>Существование ссылки без инициализации запрещено, так что такой ситуации быть не может.
Вопрос чем? Речь идет о не безопасности такого подхода вообще.
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, rg45, Вы писали:
R>>Ну, я могу только порассуждать на эту тему. По расходу памяти — это ведь смотря как писать код. Размеры блоков не лимитированы и число временных объектов, создаваемых в подвыражениях, могло бы зашкаливать за разумные рамки.
TB>На практике я на некотором другом языке (тоже с RAII) писал, там по сути любое выражение вида TB>
TB>f(g())
TB>
TB>означало что-то типа TB>
TB>tmp=g();
TB>f(tmp);
TB>
TB>И этот tmp жил до конца текущего блока, и что-то стек с кучей от этого не переполнялись.
Ну о куче тут вообще нет разговора, а вот стек ) это до поры до времени. Вот внедрится эта хрень куда-то в рекурсивный разбор,
foo(v = g())
{
foo(g());
// и тут вычисления, что исключит оптимизацию в хвостовую рекурсию и вуаля, стек будет выеден мгновенно
}
R>>А с другой стороны, наверное, не смогли придумать сценариев, когда бы это создавало какие-то непреодолимые трудности.
TB>Даже на чистом асме нет непреодолимых трудностей.
есть, в реальных проектах вполне.
TB>В данном примере трудность была создана — удалось получить ссылку на объект, у которого уже вызвался деструктор. TB>Да, она преодолима. Но это трудность, на решение которой будут тратиться лишние человекочасы.
Лишние человекочасы это не снос проекта, когда логика изменений для простой задачи размазывается по всему 10*n-летнему коду. или при появлении новых технологий проект безнадежно устаревает, и сделать с ним ничего нельзя.
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.
Здравствуйте, ollv, Вы писали:
O>foo(v = g()) O>{ O> foo(g()); O> // и тут вычисления, что исключит оптимизацию в хвостовую рекурсию и вуаля, стек будет выеден мгновенно O>}
Такой случай элементарно обходится, и то мне такое не попадалось. А вот смерть объекта раньше ожидаемого — попадалась.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, ollv, Вы писали:
O>>foo(v = g()) O>>{ O>> foo(g()); O>> // и тут вычисления, что исключит оптимизацию в хвостовую рекурсию и вуаля, стек будет выеден мгновенно O>>}
TB>Такой случай элементарно обходится, и то мне такое не попадалось.
А вот я не сказал бы. Особенно, если на рекурсивном движке построены нетривиальные вычисления, это далеко не элементарная (кстати как там элементарная — не значит несложная) задача.
TB>А вот смерть объекта раньше ожидаемого — попадалась.
Да, и мне попадалось и попадается такое в очень разных языках, когда ресурс оказывается мертвым, или же наоборот ресурс висит. лайфтайм аднака ресурсы и технологии. И, знаешь, наверное это каким-то образом можно посчитать. Завести статистику, градации сложности. Старость / используемость / цитируемость ... куча параметров языков, и сравнивать. Главное о параметрах договориться, и можно сравнивать и прочее )) а так просто о чем говорить? Что на нете, хацкеле, и прочих нет bottle neck ? Просто, как бы мы обсуждаем язык со своей уже success story. А победителей не расстреливают ))
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.
Здравствуйте, ollv, Вы писали:
O> А вот я не сказал бы. Особенно, если на рекурсивном движке построены нетривиальные вычисления, это далеко не элементарная (кстати как там элементарная — не значит несложная) задача.
{} вокруг foo
O> Да, и мне попадалось и попадается такое в очень разных языках, когда ресурс оказывается мертвым, или же наоборот ресурс висит. лайфтайм аднака ресурсы и технологии.
Я про стековые объекты
O> Просто, как бы мы обсуждаем язык со своей уже success story. А победителей не расстреливают ))
Кобол, Паладин, С++...
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, watchmaker, Вы писали:
W>Здравствуйте, Шахтер, Вы писали:
Ш>>Нет. Возвращаемое значение должно быть rvalue.
Ш>>Коряво.
W>Коряво? Криво? Легко использовать неверно в другом контексте? W>Вот с этим можно согласится. W>А то, что "возвращаемое значение должно быть rvalue" — это выдумка. Нет такого требования, конечно же
ТС хотел сделать
inline
std::vector<int> makeVec( int i )
{
std::vector<int> ret;
ret.push_back(i);
return ret;
}
Только записать в одну строку. Здесь хочется move в return. Для этого нужно rvalue.
inline
std::vector<int> makeVec( int i )
{
returnpush_back_helper( std::vector<int>(), i );
}
Здравствуйте, Шахтер, Вы писали: Ш>Здравствуйте, watchmaker, Вы писали: W>>Здравствуйте, Шахтер, Вы писали:
Ш>>>Нет. Возвращаемое значение должно быть rvalue. Ш>>>Коряво. W>>Коряво? Криво? Легко использовать неверно в другом контексте? W>>Вот с этим можно согласится. W>>А то, что "возвращаемое значение должно быть rvalue" — это выдумка. Нет такого требования, конечно же Ш>ТС хотел сделать Ш>
Ш>inline
Ш>std::vector<int> makeVec( int i )
Ш>{
Ш> std::vector<int> ret;
Ш> ret.push_back(i);
Ш> return ret;
Ш>}
Ш>
Ш>Только записать в одну строку. Здесь хочется move в return. Для этого нужно rvalue. Ш>
Ш>inline
Ш>std::vector<int> makeVec( int i )
Ш>{
Ш> returnpush_back_helper( std::vector<int>(), i );
Ш>}
Ш>
Только получается все наоборот: копирование возникает как раз в makeVec2, тогда как в makeVec1 нет ни copy, ни move, благодаря Mandatory elision of copy/move operations:
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, ollv, Вы писали:
O>> А вот я не сказал бы. Особенно, если на рекурсивном движке построены нетривиальные вычисления, это далеко не элементарная (кстати как там элементарная — не значит несложная) задача.
TB>{} вокруг foo
это слишком примитивное представление о переделке рекурсивного
TB>Я про стековые объекты
А стековый объект не может держать ресурс ? Вопрос в том, что он будет делать ? Везде делать using ?
O>> Просто, как бы мы обсуждаем язык со своей уже success story. А победителей не расстреливают ))
TB>Кобол, Паладин, С++...
Да, заметь, работают
У С++ вполне устоявшийся рынок задач, куда всяким шарпам и джавам вход воспрещен (пока) . С развитием llvm технологий может еще территорий добавиться
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.