Как обойти GCC ‘*((void*)& b +4)’ может использоваться неинициализированным в этом предупреждении функции при использовании boost::Optional

У меня есть код, похожий на следующий:

#include <boost/optional.hpp>

::boost::optional<int> getitem();

int go(int nr)
{
  boost::optional<int> a = getitem();
  boost::optional<int> b;

  if (nr > 0)
    b = nr;

  if (a != b)
    return 1;

  return 0;
}

При компиляции с помощью GCC 4.7.2 с Boost 1.53 используйте следующую команду:

g++ -c -O2 -Стена -DNDEBUG

Выдается следующее предупреждение:

13:3: предупреждение: ‘((void)& b +4)’ может использоваться неинициализированным в этой функции [-Wmaybe-uninitialized]

Судя по всему, корень проблемы лежит в GCC. См. GCC Bugzilla. Кто-нибудь знает обходной путь?


person Paul Omta    schedule 13.02.2014    source источник
comment
Если конструктор b не инициализирует все, что находится внутри него, то, во что бы то ни стало, b в выражении a != b может быть неинициализирован. Что, если вы на самом деле инициализируете b? Вы все еще получаете предупреждение?   -  person Shahbaz    schedule 13.02.2014
comment
@Shahbaz: конструктор «b» создает необязательное значение, где значение не существует. Это допустимое поведение для необязательного. 'a != b' Должно быть истинным, если обе опции не инициализированы. Так что это должен быть действительный код. Инициализация 'b' значением устраняет предупреждение, но это не вариант, поскольку он меняет поведение кода. То, что возвращает getitem(), может быть неинициализированным необязательным.   -  person Paul Omta    schedule 19.02.2014


Ответы (4)


В gcc есть два уровня неинициализированного анализа:

  • -Wuninitialized: помечает переменные, которые наверняка используются как неинициализированные.
  • -Wmaybe-uninitialized: помечает переменные, которые потенциально используются, как неинициализированные.

В gcc (*) -Wall включает оба уровня, хотя последний имеет ложные предупреждения из-за несовершенства анализа. Ложные предупреждения — чума, поэтому самый простой способ их избежать — пройти -Wno-maybe-uninitialized (после -Wall).

Если вам все еще нужны предупреждения, но они не вызывают сбой сборки (через -Werror), вы можете внести их в белый список, используя -Wno-error=maybe-uninitialized.

(*) Clang не активирует -Wmaybe-uninitialized по умолчанию именно потому, что он очень неточен и имеет большое количество ложных срабатываний; Я бы хотел, чтобы gcc тоже следовал этому правилу.

person Matthieu M.    schedule 13.02.2014

Я обнаружил, что изменение конструкции b на следующий (фактически равный) код:

auto b = boost::make_optional(false,0);

снимает предупреждение. Однако следующий код (который также фактически равен):

boost::optional<int> b(false,0);

не снимает предупреждение. Все равно немного не устраивает...

person Paul Omta    schedule 13.02.2014
comment
как насчет auto b = boost::make_optional<int>(false, 0);? - person rubenvb; 13.02.2014
comment
@rubenvb: используя авто, я считаю, что это лучший стиль. Я изменю ответ. - person Paul Omta; 19.02.2014
comment
У меня было точно такое же предупреждение, но это решение мне не помогло. Мне пришлось полностью избавиться от optional (к счастью, в моей ситуации я смог это сделать). - person Angew is no longer proud of SO; 06.10.2016
comment
Каким-то образом это заставляет gcc устанавливать внутренний флаг TREE_NO_WARNING для этой переменной. Предупреждение, скорее всего, вернется в будущей версии, оно более хрупкое, чем прагма. - person Marc Glisse; 06.06.2018
comment
Вы спасатель жизни! :) Как ни странно, это проблема только с gcc 4.9.2, а на 4.9.3 работает отлично. Более того, на 4.9.2 в отладочных сборках работает нормально, а на релизных сборках не работает. - person Kiril Kirov; 10.03.2021

Была такая же проблема с этим фрагментом кода:

void MyClass::func( bool repaint, bool cond )
{
    boost::optional<int> old = m_sizeLimit; // m_sizeLimit is a boost::optional<int> class attribute

    if ( cond )
        m_sizeLimit = 60;
    else
        m_sizeLimit.reset();

    if ( repaint )
    {
        if ( old != m_sizeLimit ) // warning here
            doSomething();
    }
}

Не удалось избавиться от предупреждения с ответом Пола Омта, попытался написать:

boost::optional<int> old;
if ( m_sizeLimit )
    old = boost::make_optional<int>(true, m_sizeLimit.value());
else
    old = boost::make_optional<int>(false, 0);

...безуспешно.

Не хотелось полностью отключать предупреждение из моего кода, поэтому я нашел альтернативное решение, которое я бы порекомендовал: отключить предупреждение локально:

        #ifdef SDE_MOBILE
        #pragma GCC diagnostic push
        #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
        #endif

        if ( old != m_sizeLimit ) // warning here
            doSomething();

        #ifdef SDE_MOBILE
        #pragma GCC diagnostic pop
        #endif
person jpo38    schedule 07.10.2016
comment
Спасибо за решение. Столкнулся с проблемой с gcc 4.9.2, и ваше решение на основе #pragma сработало и было для меня лучшим вариантом. - person mindriot; 12.05.2017

У меня был тип, который было нелегко сконструировать, поэтому я не хотел идти по пути boost::make_Optional. Назначение автоматической переменной с использованием возврата из функции помогло мне обойти эту проблему. Итак, вы можете сделать:

boost::optional<Foo> Default()
{
    return boost::none;
}

auto var(Default());

Это также будет работать как однострочная лямбда, поэтому вы можете просто сделать:

auto var([]()->boost::optional<Foo> { return boost::none; }());
person Harry    schedule 10.03.2017