Работа с С++ "инициализированное, но не связанное" предупреждение для уничтожения помощников области?

В Visual Studio я часто использую объекты только для целей RAII. Например:

ScopeGuard close_guard = MakeGuard( &close_file, file );

Цель close_guard - убедиться, что файл будет закрыт при выходе из функции, он больше не используется. Однако Visual Studio дает предупреждение, что "локальная переменная инициализируется, но не указана". Я хочу отключить это предупреждение для этого конкретного случая.

Как вы справляетесь с такой ситуацией? Visual Studio считает, что этот объект бесполезен, но это неправильно, поскольку он имеет нетривиальный деструктор.

Я бы не хотел использовать директиву предупреждения #pragma для этого, так как он отключил бы это предупреждение даже по законным причинам.

Ответ 1

Метод 1: Используйте директиву #pragma warning.

#pragma warning позволяет выборочно изменять поведение предупреждающих сообщений компилятора.

#pragma warning( push )
#pragma warning( disable : 4705 ) // replace 4705 with warning number

ScopeGuard close_guard = MakeGuard( &close_file, file );

#pragma warning( pop )

Этот код сохраняет текущее состояние предупреждения, затем отключает предупреждение для определенного кода предупреждения, а затем восстанавливает последнее сохраненное состояние предупреждения.

Способ 2: Используйте обходной путь, как показано ниже. Visual Studio будет счастлива, и вы тоже. Это обходное решение используется во многих образцах Microsoft, а также в других проектах.

ScopeGuard close_guard = MakeGuard( &close_file, file );
close_guard;

Или вы можете создать #define для обхода предупреждения.

#define UNUSED_VAR(VAR) VAR
...
ScopeGuard close_guard = MakeGuard( &close_file, file );
UNUSED_VAR(close_guard);

Некоторые пользователи заявили, что представленный код не будет работать, поскольку ScopeGuard - это typedef. Это предположение неверно.

http://www.ddj.com/cpp/184403758

Согласно стандарту С++, ссылка, инициализированная временным значение делает это временное значение для времени жизни ссылки сам по себе.

Ответ 2

Если ваш объект имеет нетривиальный деструктор, Visual Studio не должен давать вам это предупреждение. Следующий код не генерирует никаких предупреждений в VS2005 с предупреждениями, полностью повернутыми вверх (/W4):


class Test
{
public:
    ~Test(void) { printf("destructor\n"); }
};

Test foo(void) { return Test(); }

int main(void)
{
    Test t = foo();
    printf("moo\n");

    return 0;
}

Комментирование деструктора дает предупреждение; код as-is не работает.

Ответ 3

Мы используем:

static_cast<void>(close_guard);

для переменных, о которых компилятор жалуется.

Ответ 4

В некоторых заголовочных файлах VС++ MS определяет макрос:

#define UNUSED(x) x

используется как:

ScopeGuard close_guard = MakeGuard( &close_file, file );
UNUSED(close_guard);

Что заставляет замолчать предупреждение и документирует его.

Ответ 5

Я бы использовал макрос в этом случае:

#define SCOPE_GUARD(guard, fn, param) \
    ScopeGuard guard = MakeGuard(fn, param); \
    static_cast<void>(guard)

теперь ваш код приятный и короткий:

SCOPE_GUARD(g1, &file_close, file1);
SCOPE_GUARD(g2, &file_close, file2);

Одно из преимуществ этого подхода заключается в том, что позже вы можете добавить __LINE__, __func__ и т.д., чтобы позднее регистрировать действия защиты, если это необходимо.

Ответ 6

Ну, в этом случае ScopeGuard на самом деле является typedef для ссылочного типа. К сожалению, это не сработало.

Не означает ли это, что весь ScopeGuard не работает, потому что в этом случае деструктор не будет называться???

Ответ 7

Вы можете охватить предупреждение #pragma вокруг этой строки кода только с помощью

#pragma warning(push)
#pragma warning(disable:XXXX)
your code here;
#pragma warning(pop)

или

#pragma warning(disable:XXXX)
your code here;
#pragma warning(default:XXXX)

Вы также можете использовать UNREFERENCED_PARAMETER(close_guard); после строки кода выше.

Ответ 8

Я предполагаю, что на практике я бы с грустью пошел с отключением #pragma... или "UNUSED". Однако, как правило, код должен быть очищен от предупреждений даже за счет некоторой дополнительной массы. Он должен компилироваться в нескольких разных компиляторах на разных платформах и в операционных системах без предупреждений. Если это не так, код должен быть исправлен так, чтобы он выполнялся. Поддержание кода, генерирующего предупреждения на уровне gcc -Wall, не является хорошей идеей.

Предупреждения компилятора - ваш друг, и его следует рассматривать как вопрос или принцип. Даже когда это означает, что вещи должны быть реализованы немного более объемными и более подробными способами. Платит за себя в конечном счете, поскольку код портирован, поддерживается и живет навсегда...

Ответ 9

Попробуйте добавить "volatile" в объявление ScopeGuard.

Ответ 10

Я использую smink post выше и должен только добавить, что я придерживаюсь комментария рядом С#define, говорящим//используемым для подавления предупреждения [номер предупреждения] в visual studio

Ответ 11

Вы можете явно создать объект ScopeGuardImpl1 при условии, что в тех случаях, когда вы используете, не так много параметров, что результат нечитабелен. Таким образом, вы избежите ссылки-initialized-with-tempor, которая, по-видимому, не позволяет понять предупреждение VS. Стоимость заключается в том, чтобы записывать вещи из рук в руки, а не получать магии шаблона MakeGuard.

Ответ 12

Основная проблема здесь, похоже, в том, что компилятор не совсем понимает, на что вы идете... что, похоже, использует семантику семантики в С++, чтобы получить код, вызванный, когда переменная освобождается, даже если она не используется. Правильно? Этот механизм сам по себе ставит меня как пограничный... компилятор должен иметь право удалить неиспользуемые переменные, но семантика построения С++ действительно испортила эти вещи. Нет другого способа сделать это, что меньше ловкости рук?