首页 > 解决方案 > clang 不会警告标题中的“已定义但未使用”,gcc 会

问题描述

我在 clang 和 gcc 如何警告未使用的变量方面遇到了一些差异。

gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04)
clang version 6.0.0-1ubuntu2

在 foo.h

const int f = 3;

在 foo.cpp

#include "foo.h"

const int a = 2;

int main() {
    int i;
    return 0;
}

我有

$ clang -o foo foo.cpp -Wall -Wunused-variable -Wunused-const-variable
foo.cpp:7:9: warning: unused variable 'i' [-Wunused-variable]
    int i;
        ^
foo.cpp:4:11: warning: unused variable 'a' [-Wunused-const-variable]
const int a = 2;
          ^
2 warnings generated.

$ gcc -o foo foo.cpp -Wall -Wunused-variable -Wunused-const-variable
foo.cpp: In function ‘int main()’:
foo.cpp:7:9: warning: unused variable ‘i’ [-Wunused-variable]
     int i;
         ^
foo.cpp: At global scope:
foo.cpp:4:11: warning: ‘a’ defined but not used [-Wunused-const-variable=]
 const int a = 2;
           ^
In file included from foo.cpp:1:0:
foo.h:1:11: warning: ‘f’ defined but not used [-Wunused-const-variable=]
 const int f = 3;

我有几个问题:

为什么 gcc 抱怨标题中的常量?将客户的常量放在那里不是很常见吗?我怎样才能让clang表现得像gcc?

标签: c++gccclang++

解决方案


我怎样才能让clang表现得像gcc?

我认为只有报告这个令人惊讶的clang错误并等待修复。(它仍然存在于 clang 7 中)。

您定义的翻译单元foo.cpp必须与预处理生成的文件相同:

$ clang -E -P foo.cpp >foo.ii
$ cat foo.ii
const int f = 3;

const int a = 2;

int main() {
    int i;
    return 0;
}

和:

$ clang --version
clang version 6.0.1-svn330209-1~exp1~20180427232138.77 (branches/release_60)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

但是clang 6搞砸了:

$ clang -o foo foo.cpp -Wall -Wunused-variable -Wunused-const-variable
foo.cpp:6:9: warning: unused variable 'i' [-Wunused-variable]
    int i;
        ^
foo.cpp:3:11: warning: unused variable 'a' [-Wunused-const-variable]
const int a = 2;
          ^
2 warnings generated.

然而:

$ clang -o foo foo.ii -Wall -Wunused-variable -Wunused-const-variable
foo.ii:6:9: warning: unused variable 'i' [-Wunused-variable]
    int i;
        ^
foo.ii:1:11: warning: unused variable 'f' [-Wunused-const-variable]
const int f = 3;
          ^
foo.ii:3:11: warning: unused variable 'a' [-Wunused-const-variable]
const int a = 2;
          ^
3 warnings generated.

现在同意:

$ gcc -o foo foo.ii -Wall -Wunused-variable -Wunused-const-variable
foo.ii: In function ‘int main()’:
foo.ii:6:9: warning: unused variable ‘i’ [-Wunused-variable]
     int i;
         ^
foo.ii: At global scope:
foo.ii:3:11: warning: ‘a’ defined but not used [-Wunused-const-variable=]
 const int a = 2;
           ^
foo.ii:1:11: warning: ‘f’ defined but not used [-Wunused-const-variable=]
 const int f = 3;
           ^

之后

当它可以是库的一部分时,为什么警告适用于标头中的常量?

头文件(和)不是编译器识别的东西。预处理器通过以下方式识别头文件:

#include <headername>
...
#include "headername"

使用其指定的或默认的搜索路径 ( -I dir),预处理器解析并粘贴内容来headername代替编译器使用的翻译单元中的指令 。该翻译单元没有所有预处理器指令。编译器不消耗:/some/actual/headername/some/actual/headername#include

foo.cpp

#include "foo.h"

const int a = 2;

int main() {
    int i;
    return 0;

}

它消耗:

foo.ii

const int f = 3;

const int a = 2;

int main() {
    int i;
    return 0;
}

您观察到的 clang 行为意味着,在内部,该工具虚拟化了预处理编译之间的分界- 这实际上是 C/C++ 实现中的常规历史实践 - 但在虚拟分界中引入了这个错误。不管它对源代码的真正作用是什么,它与先预处理它,然后编译预处理的输出并不完全相同;它应该是

因此,在头文件中定义常量并不是 C++ 实现可以扩展任何特殊慈善的实践。如果您正在编写一个在其 API 标头中公开常量的库bar.h,并且您不希望该库的用户因未能引用在 它bar.h的每个编译中定义的每个常量而面临未使用变量警告的风险#include,那么您不会将这些常量定义 . 您将执行其他三件事之一:constbar.h

将常量定义为enumor的成员enum class

enum class E : int {
    F = 3
    //...
};

或者,声明常量但在库源文件1extern定义它们:bar.h

酒吧.h

#ifndef BAR_H
#define BAR_H

extern const int f;

#endif

酒吧.cpp

#include "bar.h"

const int f = 3;

或者,将常量定义为预处理器宏:

#define F 3

以老式的 C 方式。你不会,因为在 C++ 中,如果可以的话,我们会避开预处理器。


[1] 如何extern避免警告?因为const文件作用域变量隐含static在 C++ 中(尽管不在 C 中),并且编译器从不认为extern变量有资格进行未使用的诊断,因为您告诉它该变量可能在提供给链接器的代码中被引用,而编译器无法看到。


推荐阅读