c++ - 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?
解决方案
我怎样才能让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
,那么您不会将这些常量定义为 . 您将执行其他三件事之一:const
bar.h
将常量定义为enum
or的成员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
变量有资格进行未使用的诊断,因为您告诉它该变量可能在提供给链接器的代码中被引用,而编译器无法看到。
推荐阅读
- powershell - Powershell Get-ChildItem 不为文件夹返回任何内容
- amazon-web-services - 没有看到使用 asyncio 和 aioboto3 进行 DynamoDB 写入的性能改进
- bash - 如何运行需要两种不同类型输入文件的命令(1000 次)
- android - 如何在我的应用中为将来的自动填充登录显示 Google 的用户和密码保护消息?
- python - 使用 Django 会话存储名称值以用于另一个视图
- ios - 在运行时观察对象 - Objective C
- java - 令牌过期时如何使用 AuthExceptionEntryPoint 更改响应?
- html - 如何更新工具提示的字体大小和背景颜色并使其图标更小?
- angular - 角度测试 - 类型错误:无法读取未定义的属性“7”
- redirect - Wiremock 如何在 JSON 中映射 302 响应以进行独立运行