All streams
Search
Write a publication
Pull to refresh
39
0
Виктор Щерба @vScherba

Разработчик C++

Send message
Я бы не стал наезжать на этот код. Этот вариант никогда не передаст в m_disconnectAct->setEnabled и m_fwUpdateAct->setEnabled значение false. При m_selectedDevice->IsConnected() == false просто ничего менять не будет. Может, так и было задумано/оптимизировано?
В винде можно не просто подойти к этой проблеме, а заюзать DbgHelp API. Если собирать проект студией, то она создает приличный по объему файл с символами *.pdb, который содержит символы и их привязки к релизному коду. Сам релизный код символов не содержит (кроме экспортных). При краше мы используем функцию MiniDumpWriteDump. Имея *.pdb файл символов, исходники и релизный образ у себя на машине открываем в студии дамп и получаем интерфейс отладчика, с состоянием, как будто бы крэш случился только что на нашей машине под отладчиком. Помимо стек-трейса, можно просмотреть значения локальных переменных, потоки и пр. инфу. Если использовать флаг MiniDumpWithFullMemory, то (судя по справке) вообще всю user-mode память процесса можно получить.
Хотя, рантайм проверка на Вашем юнит-тесте проходит, если исправить строку
member = move_if_rr< T >( rr.Get() );
на
member = move_if_rr< T&& >( rr.Get() );
, что как раз укладывается в моем представлении. Ведь rr имеет тип T&&, а не T.
Сильно не ругайте, это мое видение постановки и решения задачи.
Если суть задачи состоит в том, чтобы у rvalue-объектов можно было «утащить» внутренние члены-данные через аксессоры, а с lvalue-объектами работать как обычно, то словами формулировку задачи можно описать так:
Реализовать функцию для вызова в виде move_if_rr<A>(b), которая вернет rvalue-ссылку на значение b, если тип A является rvalue-ссылкой, иначе вернет b как есть. Тогда так и запишем, слово за словом, используя STL:
#define RET_TYPE \
    typename std::conditional< \
      std::is_rvalue_reference<E>::value \
    , typename std::remove_reference<I>::type&& \
    , I&& \
    >::type

template <typename E, typename I>
RET_TYPE move_if_rr(I&& i)
{
    return static_cast<RET_TYPE>(i);
}

В такой формулировке утилитная функция может быть довольно полезна для перемещения внутреннего свойства из временного объекта, вместо его копирования.
А еще полезнее она была бы в более общем виде:
#define RET_TYPE \
    typename std::conditional< \
      C \
    , typename std::remove_reference<I>::type&& \
    , I&& \
    >::type

template <bool C, typename I>
RET_TYPE move_if(I&& i)
{
    return static_cast<RET_TYPE>(i);
}

move_if(val) — выполнить перемещение, если condition истинно, иначе — оставить тип как есть. В нашем случае использование:
move_if<std::is_rvalue_reference<A>::value>(b);


Но, судя по тестовому коду и static_assert'ам, задачу нужно было решить в другом виде.
Дело в том, что в C++ есть 2 уровня проверки:

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 код компилируется.
Не хочется долго спорить. Проще факт привести.
Включаем полную оптимизацию в релизе, запускаем код без 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.)
Повторюсь, в топике рассматривается чисто стандарт. Стандарт не определяет понятия «Нормальный код». Есть топик «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. Я пытаюсь рассматривать стандарт в общем, а не конкретные компиляторы.
Абсолютно согласен, левая формулировка, не досмотрел. Не хотел нагружать статью терминологией xvalue и пр.
Кстати, твое изменение примера не совсем правильное: здесь ты вручную форсируешь перемещение и получаешь именно то, что ожидаешь, а именно, опустошение строки до ее вывода на экран. Даже, если бы стандарт запретил неявное перемещение при return, у тебя оно все равно бы было.
Здесь, MSVC повел себя по стандарту, ничего запрещенного не сделал. В дебаге не включает NRVO, в релизе включает.
Поведение при включенном/отключенном NRVO может быть разное, т.к. при его включении теряется вызов конструктора копий и деструктора локального объекта, и это известная фича.
Тут проблема в новом стандарте.
Спасибо! Ты меня опередил. Суть топика — рассмотреть особенность нового стандарта в отношении return и move semattics. Пример оказался слишком простым для оптимизатора. Когда в игру вступает NRVO, до rvalue reference и move дело не доходит, и это уже другая история.
Подправил пример, так что на MSVC 10.0 даже в релизе на экран ничего не выводится. Для других компиляторов, уважаемые хабражители, предлагаю в комментах написать свой вариант обхода NRVO (в качестве разминки после выходных).
12 ...
7

Information

Rating
Does not participate
Location
Дубна, Москва и Московская обл., Россия
Date of birth
Registered
Activity