Имея дело с предупреждением C ++ “инициализировано, но без ссылок” об уничтожении помощников области видимости?

StackOverflow https://stackoverflow.com/questions/219770

Вопрос

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

ScopeGuard close_guard = MakeGuard( &close_file, file );

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

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

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

Это было полезно?

Решение

Метод 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 ++,   ссылка инициализирована с временным   значение делает это временное значение живым   за время существования ссылки   сам по себе.

Другие советы

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

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

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

static_cast<void>(close_guard);

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

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

#define UNUSED(x) x

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

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

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

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

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

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

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

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

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

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

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

Я использую пост Smink выше и должен только добавить, что я добавляю комментарий рядом с поговоркой #define, // используемой для подавления предупреждения [номер предупреждения] в Visual Studio

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top