Работа с C ++ «инициализировано, но не указано» предупреждение для уничтожения помощников области? - PullRequest
13 голосов
/ 21 октября 2008

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

ScopeGuard close_guard = MakeGuard( &close_file, file );

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

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

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

Ответы [ 12 ]

8 голосов
/ 21 октября 2008

Если у вашего объекта нетривиальный деструктор, 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;
}

Комментируя деструктор, выдает предупреждение; код как есть - нет.

5 голосов
/ 21 октября 2008

Метод 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

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

3 голосов
/ 21 октября 2008

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

#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__ и т. Д., Чтобы регистрировать действия охраны позже, если это необходимо.

3 голосов
/ 21 октября 2008

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

#define UNUSED(x) x

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

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

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

3 голосов
/ 21 октября 2008

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

static_cast<void>(close_guard);

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

2 голосов
/ 21 октября 2008

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

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

1 голос
/ 21 октября 2008

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

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

1 голос
/ 21 октября 2008

Вы можете поместить предупреждение #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); после строки кода выше.

0 голосов
/ 21 октября 2008

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

0 голосов
/ 21 октября 2008

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

...