Комментарии 25
мало того, MS VS 10 ведёт себя по разному в Release и Debug,
в Release оно применяет NRVO и строка выводится нормально
в Release оно применяет NRVO и строка выводится нормально
Да уж, переход на c++11 для MSVS это очень далёкая перспектива, в отличие от g++ и clang.
Здесь, MSVC повел себя по стандарту, ничего запрещенного не сделал. В дебаге не включает NRVO, в релизе включает.
Поведение при включенном/отключенном NRVO может быть разное, т.к. при его включении теряется вызов конструктора копий и деструктора локального объекта, и это известная фича.
Тут проблема в новом стандарте.
Поведение при включенном/отключенном NRVO может быть разное, т.к. при его включении теряется вызов конструктора копий и деструктора локального объекта, и это известная фича.
Тут проблема в новом стандарте.
Не очень умное заявление, учитывая то, что у всех трех реализован разный набор функционала. И сказать, что в каком-то из них реализовано больше новшеств — нельзя.
g++ 4.6.3
$ g++ -std=c++0x return-string.cpp
$ ./a.out
Hi!
что и ожидалось
$ g++ -std=c++0x return-string.cpp
$ ./a.out
Hi!
что и ожидалось
Теоретически — всё верно описано. На практике: gcc-4.7 применяет NRVO, а не move semantics при возвращении временных объектов. MS VC 10 в Release так же использует NRVO. Если изменить пример на:
Тогда да, объект утянется.
std::string f()
{
std::string s = "Hi!";
Finalizer fin(s);
return std::move(s);
}
Тогда да, объект утянется.
+1
Спасибо! Ты меня опередил. Суть топика — рассмотреть особенность нового стандарта в отношении return и move semattics. Пример оказался слишком простым для оптимизатора. Когда в игру вступает NRVO, до rvalue reference и move дело не доходит, и это уже другая история.
Подправил пример, так что на MSVC 10.0 даже в релизе на экран ничего не выводится. Для других компиляторов, уважаемые хабражители, предлагаю в комментах написать свой вариант обхода NRVO (в качестве разминки после выходных).
Подправил пример, так что на MSVC 10.0 даже в релизе на экран ничего не выводится. Для других компиляторов, уважаемые хабражители, предлагаю в комментах написать свой вариант обхода NRVO (в качестве разминки после выходных).
Кстати, твое изменение примера не совсем правильное: здесь ты вручную форсируешь перемещение и получаешь именно то, что ожидаешь, а именно, опустошение строки до ее вывода на экран. Даже, если бы стандарт запретил неявное перемещение при return, у тебя оно все равно бы было.
эти строки полностью эквивалентны, что с вызовом move, что без оного. s есть xvalue, а значит будет перемещена. Стандарт не может запрещать и разрешать одну и туже фичу одновременно :)
Не согласен, строки с move и без не эквивалентны. Например, если компилятор не производит move семантику в return (а стандарт только разрешает это, а не обязывает), то return s вызовет конструктор копий, а return std::move(s) перемещающий конструктор. Такой компилятор не считает s xvalue-выражением.
return s действительно эквивалентно return std::move(s) в Вашем компиляторе только тогда, когда компилятором поддерживается move-оптимизация возвращаемого значения.
Для чистоты эксперимента нужно вернуть именно через return s. Я пытаюсь рассматривать стандарт в общем, а не конкретные компиляторы.
return s действительно эквивалентно return std::move(s) в Вашем компиляторе только тогда, когда компилятором поддерживается move-оптимизация возвращаемого значения.
Для чистоты эксперимента нужно вернуть именно через return s. Я пытаюсь рассматривать стандарт в общем, а не конкретные компиляторы.
Я немного ошибся, не xvalue, а prvalue.
Согласно пунктам 3.10/1, 6.6.3/2. 12.2/1 выражение return s; является prvalue, а выражение return std::move(s); — xvalue. Принимая во внимание, тот факт, что по таксономии типов выражений rvalue включает в себя xvalue и prvalue, а также то, что rvalue имеют жёсткую привязку к rvalue ссылкам мы имеем следующее: результат выражений return std::move(s); и return s; всегда повлечёт за собой вызов move ctor, за исключением случае оптимизации или остутствия оного у возвращаемого типа.
Это поведение стандартно и от компилятора не зависит.
Согласно пунктам 3.10/1, 6.6.3/2. 12.2/1 выражение return s; является prvalue, а выражение return std::move(s); — xvalue. Принимая во внимание, тот факт, что по таксономии типов выражений rvalue включает в себя xvalue и prvalue, а также то, что rvalue имеют жёсткую привязку к rvalue ссылкам мы имеем следующее: результат выражений return std::move(s); и return s; всегда повлечёт за собой вызов move ctor, за исключением случае оптимизации или остутствия оного у возвращаемого типа.
Это поведение стандартно и от компилятора не зависит.
Не хочется долго спорить. Проще факт привести.
Включаем полную оптимизацию в релизе, запускаем код без std::move():
В консоли видим «Hi!», не было move-оптимизации, зато вместо нее NRVO.
Добавляем std::move():
На экране пусто. NRVO не имеет права выполняться, т.к. явно запрошено перемещение, и программист бы сильно удивился, если бы перемещения не произошло.
Изменилось поведение кода в рантайм, это факт. Причем, это не глюк компилятора, все в рамках стандарта. Это экспериментальное доказательство того, что std::move(s); и return s; не одно и то же. (Использовал MSVC 10.0.)
Включаем полную оптимизацию в релизе, запускаем код без std::move():
std::string f()
{
std::string s = "Hi!";
Finalizer fin(s);
return s;
}
В консоли видим «Hi!», не было move-оптимизации, зато вместо нее NRVO.
Добавляем std::move():
std::string f()
{
std::string s = "Hi!";
Finalizer fin(s);
return std::move(s);
}
На экране пусто. NRVO не имеет права выполняться, т.к. явно запрошено перемещение, и программист бы сильно удивился, если бы перемещения не произошло.
Изменилось поведение кода в рантайм, это факт. Причем, это не глюк компилятора, все в рамках стандарта. Это экспериментальное доказательство того, что std::move(s); и return s; не одно и то же. (Использовал MSVC 10.0.)
vScherba, т.е. сначала Вы говорите, что Вам интересно, что написано в стандарте. Потом Вы мне тыкаете кодом из студии, пытаясь опровергнуть мои слова. Хотя Вами написанное ни на йоту не противоречит тому, что написано у меня. NRVO превалирует над семантикой перемещения, т.к. является прямым созданием. Без NRVO эти строки всегда дадут одинаковый результат. Оптимизаторы могут код выполнять в другом порядке про это тоже хотите поговорить? А ведь это тоже, не совсем то, что человек ожидает. Хотя семантика исполнения программы от этого не меняется. Суть семантики перемещения есть ликвидация лишнего копирования, NRVO/RVO делает тоже самое, только еще быстрее. Написание std::move в return операторе есть избыточное действие.
О чём тут можно спорить, когда я Вам привел параграфы стандарта? Идите со стандартизаторами спорьте.
О чём тут можно спорить, когда я Вам привел параграфы стандарта? Идите со стандартизаторами спорьте.
Ну и в качестве пищи для размышлений: MSVC2011: debug версия ничего не выводит в обоих случаях
Кстати, возможно это связано с багом в MSVC2010(точнее в стандарте того времени), когда в выражении участвует lvalue(а выражение s есть lvalue), то студия некорректно пытается использовать привязку к lvalue ссылкам даже в том случае, когда результирующее выражение есть rvalue.
К примеру:
foo(std::string&& str);//#1
foo(const std::string& str);//#2
И вызов:
foo(«Hello world!»);
MSVC2010 — #2
MSVS2011 — #1
Можете почитать мою статью по rvalue ссылкам, там все случаи рассмотрены, как мне показалось :)
К примеру:
foo(std::string&& str);//#1
foo(const std::string& str);//#2
И вызов:
foo(«Hello world!»);
MSVC2010 — #2
MSVS2011 — #1
Можете почитать мою статью по rvalue ссылкам, там все случаи рассмотрены, как мне показалось :)
Касательно жертвенности в обратной совместимости: если кто-то решал использовать локальные переменные после вызова return, то он сам себе «хвостатый буратин». Пишешь костыли — проверяй кода в каждом компиляторе. А для нормального кода нет никаких последствий, по крайней мере в этот части.
Повторюсь, в топике рассматривается чисто стандарт. Стандарт не определяет понятия «Нормальный код». Есть топик «C++11 — removed and deprecated (http://habrahabr.ru/post/131512/), там описаны несовместимые изменения в новом стандарте. Я также добавляю еще одно такое. Я указал в статье, что реально с таким кодом никогда не сталкивался. Это теоретические рассуждения о фишках стандарта в чистом виде.
Так и запишем — RAII это костыль. Продолжайте, Вас очень интересно читать.
почему стандарт позволяет оператору return рассматривать любое выражение как rvalue reference? Ответ очевиден: да потому что после return результат этого выражения никому больше не нужен, даже если выражение является именованным объектом, объект можно смело перемещать.
Что-то в этой формулировке не так: как совместимы «любое выражение» и «никому больше не нужен»? А если так:
T f() { static T o; return o; }
Грубо говоря, NRVO тоже нарушает обратную совместимость в C++.
Ни эта оптимизация первая, ни эта оптимизация последняя.
Ни эта оптимизация первая, ни эта оптимизация последняя.
Убедительная просьба:
При применении аббревиатур в первый раз давайте расшифровку.
Я понимаю что большинство читающих — гики С++, но хотелось бы читать внятный текст и простым смертным вроде меня.
При применении аббревиатур в первый раз давайте расшифровку.
Я понимаю что большинство читающих — гики С++, но хотелось бы читать внятный текст и простым смертным вроде меня.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Замечание по move semantics при операторе return в C++11