Я бы не стал наезжать на этот код. Этот вариант никогда не передаст в m_disconnectAct->setEnabled и m_fwUpdateAct->setEnabled значение false. При m_selectedDevice->IsConnected() == false просто ничего менять не будет. Может, так и было задумано/оптимизировано?
В винде можно не просто подойти к этой проблеме, а заюзать DbgHelp API. Если собирать проект студией, то она создает приличный по объему файл с символами *.pdb, который содержит символы и их привязки к релизному коду. Сам релизный код символов не содержит (кроме экспортных). При краше мы используем функцию MiniDumpWriteDump. Имея *.pdb файл символов, исходники и релизный образ у себя на машине открываем в студии дамп и получаем интерфейс отладчика, с состоянием, как будто бы крэш случился только что на нашей машине под отладчиком. Помимо стек-трейса, можно просмотреть значения локальных переменных, потоки и пр. инфу. Если использовать флаг MiniDumpWithFullMemory, то (судя по справке) вообще всю user-mode память процесса можно получить.
Если суть задачи состоит в том, чтобы у rvalue-объектов можно было «утащить» внутренние члены-данные через аксессоры, а с lvalue-объектами работать как обычно, то словами формулировку задачи можно описать так:
Реализовать функцию для вызова в виде move_if_rr<A>(b), которая вернет rvalue-ссылку на значение b, если тип A является rvalue-ссылкой, иначе вернет b как есть. Тогда так и запишем, слово за словом, используя STL:
В такой формулировке утилитная функция может быть довольно полезна для перемещения внутреннего свойства из временного объекта, вместо его копирования.
А еще полезнее она была бы в более общем виде:
1. Минимальная проверка синтаксиса шаблона. Если шаблон ее проходит, значит есть ненулевая вероятность, что после инстанцирования его какими-либо типами/константами результат пройдет проверку №2.
2. Полная проверка выражений во время инстанцирования шаблона.
DECLARE_IS_VALID_EXPRESSION(...)
определяет шаблон, и к нему применяется только проверка №1.
BOOST_STATIC_ASSERT( HasF< A >::value )
инстанцирует шаблон HasF параметром A, применяется проверка №2.
Из этого следует, что если вы вставите на проверку код, где забыли закрыть скобку, использовали ключевое слово не так, как полагается и пр. шалости, код будет ill-formed. А код ( ( U * ) NULL )->f() вполне проходит проверку №1, потому что теоретически может существовать тип с методом f().
BOOST_STATIC_ASSERT( HasF< std::string >::value ) не проходит проверку №2, но по правилу SFINAE код компилируется.
На экране пусто. NRVO не имеет права выполняться, т.к. явно запрошено перемещение, и программист бы сильно удивился, если бы перемещения не произошло.
Изменилось поведение кода в рантайм, это факт. Причем, это не глюк компилятора, все в рамках стандарта. Это экспериментальное доказательство того, что std::move(s); и return s; не одно и то же. (Использовал MSVC 10.0.)
Повторюсь, в топике рассматривается чисто стандарт. Стандарт не определяет понятия «Нормальный код». Есть топик «C++11 — removed and deprecated (http://habrahabr.ru/post/131512/), там описаны несовместимые изменения в новом стандарте. Я также добавляю еще одно такое. Я указал в статье, что реально с таким кодом никогда не сталкивался. Это теоретические рассуждения о фишках стандарта в чистом виде.
Не согласен, строки с move и без не эквивалентны. Например, если компилятор не производит move семантику в return (а стандарт только разрешает это, а не обязывает), то return s вызовет конструктор копий, а return std::move(s) перемещающий конструктор. Такой компилятор не считает s xvalue-выражением.
return s действительно эквивалентно return std::move(s) в Вашем компиляторе только тогда, когда компилятором поддерживается move-оптимизация возвращаемого значения.
Для чистоты эксперимента нужно вернуть именно через return s. Я пытаюсь рассматривать стандарт в общем, а не конкретные компиляторы.
Кстати, твое изменение примера не совсем правильное: здесь ты вручную форсируешь перемещение и получаешь именно то, что ожидаешь, а именно, опустошение строки до ее вывода на экран. Даже, если бы стандарт запретил неявное перемещение при return, у тебя оно все равно бы было.
Здесь, MSVC повел себя по стандарту, ничего запрещенного не сделал. В дебаге не включает NRVO, в релизе включает.
Поведение при включенном/отключенном NRVO может быть разное, т.к. при его включении теряется вызов конструктора копий и деструктора локального объекта, и это известная фича.
Тут проблема в новом стандарте.
Спасибо! Ты меня опередил. Суть топика — рассмотреть особенность нового стандарта в отношении return и move semattics. Пример оказался слишком простым для оптимизатора. Когда в игру вступает NRVO, до rvalue reference и move дело не доходит, и это уже другая история.
Подправил пример, так что на MSVC 10.0 даже в релизе на экран ничего не выводится. Для других компиляторов, уважаемые хабражители, предлагаю в комментах написать свой вариант обхода NRVO (в качестве разминки после выходных).
на
, что как раз укладывается в моем представлении. Ведь rr имеет тип T&&, а не T.
Сильно не ругайте, это мое видение постановки и решения задачи.
Реализовать функцию для вызова в виде move_if_rr<A>(b), которая вернет rvalue-ссылку на значение b, если тип A является rvalue-ссылкой, иначе вернет b как есть. Тогда так и запишем, слово за словом, используя STL:
В такой формулировке утилитная функция может быть довольно полезна для перемещения внутреннего свойства из временного объекта, вместо его копирования.
А еще полезнее она была бы в более общем виде:
move_if(val) — выполнить перемещение, если condition истинно, иначе — оставить тип как есть. В нашем случае использование:
Но, судя по тестовому коду и static_assert'ам, задачу нужно было решить в другом виде.
1. Минимальная проверка синтаксиса шаблона. Если шаблон ее проходит, значит есть ненулевая вероятность, что после инстанцирования его какими-либо типами/константами результат пройдет проверку №2.
2. Полная проверка выражений во время инстанцирования шаблона.
определяет шаблон, и к нему применяется только проверка №1.
инстанцирует шаблон HasF параметром A, применяется проверка №2.
Из этого следует, что если вы вставите на проверку код, где забыли закрыть скобку, использовали ключевое слово не так, как полагается и пр. шалости, код будет ill-formed. А код ( ( U * ) NULL )->f() вполне проходит проверку №1, потому что теоретически может существовать тип с методом f().
BOOST_STATIC_ASSERT( HasF< std::string >::value ) не проходит проверку №2, но по правилу SFINAE код компилируется.
Включаем полную оптимизацию в релизе, запускаем код без std::move():
В консоли видим «Hi!», не было move-оптимизации, зато вместо нее NRVO.
Добавляем std::move():
На экране пусто. NRVO не имеет права выполняться, т.к. явно запрошено перемещение, и программист бы сильно удивился, если бы перемещения не произошло.
Изменилось поведение кода в рантайм, это факт. Причем, это не глюк компилятора, все в рамках стандарта. Это экспериментальное доказательство того, что std::move(s); и return s; не одно и то же. (Использовал MSVC 10.0.)
return s действительно эквивалентно return std::move(s) в Вашем компиляторе только тогда, когда компилятором поддерживается move-оптимизация возвращаемого значения.
Для чистоты эксперимента нужно вернуть именно через return s. Я пытаюсь рассматривать стандарт в общем, а не конкретные компиляторы.
Поведение при включенном/отключенном NRVO может быть разное, т.к. при его включении теряется вызов конструктора копий и деструктора локального объекта, и это известная фича.
Тут проблема в новом стандарте.
Подправил пример, так что на MSVC 10.0 даже в релизе на экран ничего не выводится. Для других компиляторов, уважаемые хабражители, предлагаю в комментах написать свой вариант обхода NRVO (в качестве разминки после выходных).