14

在 Visual Studio 中,我经常将对象仅用于 RAII 目的。例如:

ScopeGuard close_guard = MakeGuard( &close_file, file );

close_guard的全部目的是确保文件将在函数退出时关闭,它不会在其他任何地方使用。但是,Visual Studio 给我一个警告,说“局部变量已初始化但未引用”。我想针对这种特定情况关闭此警告。

你如何处理这种情况?Visual Studio 认为这个对象是无用的,但这是错误的,因为它有一个非平凡的析构函数。

我不想为此使用#pragma 警告指令,因为即使出于正当理由它也会关闭此警告。

4

12 回答 12

9

如果你的对象有一个重要的析构函数,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;
}

注释掉析构函数会给出警告;代码原样没有。

于 2008-10-20T20:34:11.827 回答
6

方法一:使用#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++ 标准,使用临时值初始化的引用使该临时值在引用本身的生命周期内有效。

于 2008-10-20T20:29:22.850 回答
3

我们用:

static_cast<void>(close_guard);

对于编译器抱怨的变量。

于 2008-10-20T20:30:44.173 回答
3

在一些 VC++ 头文件中,MS 定义了一个宏:

#define UNUSED(x) x

像这样使用:

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

这使警告静音并记录下来。

于 2008-10-20T20:31:30.893 回答
3

在这种情况下,我会一直使用宏:

#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__etc 来记录警卫操作。

于 2008-10-20T23:19:11.663 回答
2

好吧,在这种情况下,ScopeGuard 实际上是引用类型的 typedef。不幸的是,这行不通。

那不是意味着整个 ScopeGuard 不起作用,因为在那种情况下不会调用析构函数???

于 2008-10-20T20:37:09.590 回答
1

您只能通过使用

#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);在上面的代码行之后使用。

于 2008-10-20T20:31:24.560 回答
1

我想在实践中,我会不情愿地使用#pragma disable... 或“UNUSED”。然而,作为一个主要规则,即使以一些额外的体积为代价,代码也应该保持干净的警告。它应该在不同平台和操作系统上的多个不同编译器中编译而不会发出警告。如果没有,则必须修复代码以使其正常。维护在 gcc -Wall 级别生成警告的代码不是一个好主意。

编译器警告是你的朋友,应该作为一个问题或原则加以注意。即使这意味着必须以更笨重和更冗长的方式实现事物。从长远来看,随着代码的移植、维护和永久存在,它会为自己买单……

于 2008-10-20T20:40:24.323 回答
0

尝试将 'volatile' 添加到 ScopeGuard 声明中。

于 2008-10-20T20:27:59.747 回答
0

我在上面使用 smink 的帖子,只需要补充一点,我在 #define 旁边添加了一条评论,说 // 用于抑制 Visual Studio 中的警告 [警告编号]

于 2008-10-20T20:35:02.400 回答
0

您可以显式创建 ScopeGuardImpl1 对象,前提是在您使用的情况下没有太多参数导致结果不可读。这样,您就可以避免 VS 警告显然无法理解的临时引用初始化。代价是不得不把事情拼写出来,而不是获得 MakeGuard 模板的魔力。

于 2008-10-20T22:47:55.283 回答
-3

这里的核心问题似乎真的是编译器不太明白你在做什么......这似乎是在 C++ 中使用范围语义来在释放变量时调用一些代码,即使它没有被使用. 对?这种机制本身给我的印象是边缘......编译器应该有权删除未使用的变量,但 C++ 构造语义确实把这些事情搞砸了。没有其他方法可以做到这一点不那么花招吗?

于 2008-10-20T20:35:21.060 回答