是否有 GCC 警告,用于使用 C 库中的符号而不是通过命名空间 std?



请考虑以下(错误)C++代码:

#include <cmath>
#include <cstdlib>
#include <iostream>
int main() {
if (abs(-0.75) != 0.75) {
std::cout << "Math is broken!n";
return 1;
} else {
return 0;
}
}

这段代码有问题,因为它调用abs(意思是::abs)而不是std::abs。根据实现的不同,::abs可能不存在,或者可能是 Cabs,或者它可能是包含double版本的重载集,就像std::abs一样。

在 Linux 上使用 Clang,至少在我的环境中,事实证明它是第二种选择: Cabs.这会引起两个警告,即使没有明确启用任何警告:

<source>:7:9: warning: using integer absolute value function 'abs' when argument is of floating point type [-Wabsolute-value]
if (abs(-0.75) != 0.75) {
^
<source>:7:9: note: use function 'std::abs' instead
if (abs(-0.75) != 0.75) {
^~~
std::abs
<source>:7:13: warning: implicit conversion from 'double' to 'int' changes value from -0.75 to 0 [-Wliteral-conversion]
if (abs(-0.75) != 0.75) {
~~~ ^~~~~

在GCC上,我在不同的环境中得到不同的结果,我还没有弄清楚环境的哪些细节是相关的。不过,更常见的选择是它也调用 Cabs函数。然而,即使有-Wall -Wextra -pedantic,它也不会给出任何警告。我可以用-Wfloat-conversion强制警告,但这在我的代码库的其余部分上给出了太多误报(也许我应该修复,但这是一个不同的问题):

<source>: In function 'int main()':
<source>:7:18: warning: conversion to 'int' alters 'double' constant value [-Wfloat-conversion]
if (abs(-0.75) != 0.75) {
^

每当我通过全局命名空间使用库函数时,当命名空间std中的版本是重载时,有没有办法得到警告?

这是一个解决方案。我对此不满意,但它可能对您有用:

namespace DontUseGlobalNameSpace {
// put all std functions here you want to catch
int abs(int x);
}
using namespace DontUseGlobalNameSpace;

现在,如果您在没有限定的情况下使用abs(),您将收到"符号不明确"错误。

这将是困难的。GCC<cmath>标头仅包含<math.h>#undefs其宏(以防万一),并将C++函数定义为内联函数,这些函数使用来自<math.h>的标识符。大多数函数实际上都是指编译器内置:例如,std::abs是使用__builtin_abs而不是::abs定义的。

由于<cmath>和你的"buggy程序"都在同一个翻译单元中,所以很难看出可见性是如何分开的:如何允许<cmath>中的内联功能使用<math.h>的东西,而你的代码不会。

好吧,有以下方法:必须重写<cmath>以提供自己的本地范围的声明,用于<math.h>所需的任何内容,并且实际上不包含该标头。

相反,我们可以做的是准备一个头文件,该文件重新声明我们不想要的函数,并带有__attribute__ ((deprecated))

// put the following and lots of others like it in a header:
extern "C" int abs(int) throw () __attribute__ ((deprecated));
#include <cmath>
#include <cstdlib>
#include <iostream>
int main() {
if (abs(-0.75) != 0.75) {
std::cout << "Math is broken!n";
return 1;
} else {
return 0;
}
}

现在:

$ g++ -Wall  buggy.cc
buggy.cc: In function ‘int main()’:
buggy.cc:9:7: warning: ‘int abs(int)’ is deprecated [-Wdeprecated-declarations]
if (abs(-0.75) != 0.75) {
^~~
In file included from /usr/include/c++/6/cstdlib:75:0,
from buggy.cc:4:
/usr/include/stdlib.h:735:12: note: declared here
extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
^~~
buggy.cc:9:16: warning: ‘int abs(int)’ is deprecated [-Wdeprecated-declarations]
if (abs(-0.75) != 0.75) {
^
In file included from /usr/include/c++/6/cstdlib:75:0,
from buggy.cc:4:
/usr/include/stdlib.h:735:12: note: declared here
extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
^~~

链接器警告会更简单。我试过了;问题是这个测试程序实际上并没有生成对abs的外部引用(即使<cmath>中有#undef abs)。调用正在内联,因此会避开链接器警告。

更新:

继DanielH的评论之后,我想出了对技巧的改进,它允许std::abs但阻止abs

#include <cmath>
#include <cstdlib>
#include <iostream>
namespace proj {
// shadowing declaration
int abs(int) __attribute__ ((deprecated));
int fun() {
if (abs(-0.75) != 0.75) {
std::cout << "Math is broken!n";
return 1;
} else {
return std::abs(-1); // must be allowed
}
}
}
int main() {
return proj::fun();
}

可以使用简单的命名空间。此外,我们不需要deprecated属性;我们可以将abs声明为不兼容的函数,或者完全声明为非函数标识符:

#include <cmath>
#include <cstdlib>
#include <iostream>
namespace proj {
// shadowing declaration
class abs;
int fun() {
if (abs(-0.75) != 0.75) {
std::cout << "Math is broken!n";
return 1;
} else {
return std::abs(-1); // must be allowed
}
}
}
int main() {
return proj::fun();
}
$ g++ -std=c++98 -Wall  buggy.cc -o buggy
buggy.cc: In function ‘int proj::fun()’:
buggy.cc:10:18: error: invalid use of incomplete type ‘class proj::abs’
if (abs(-0.75) != 0.75) {
^
buggy.cc:7:9: note: forward declaration of ‘class proj::abs’
class abs;
^~~
buggy.cc:16:3: warning: control reaches end of non-void function [-Wreturn-type]
}
^

使用这种方法,我们只需要一个名称列表并将它们转储到提供此名称的某个标头中:

int abs, fabs, ...; // shadow all of these as non-functions

我在g++命令行中使用-stdc++98来强调这只是老派C++namespace语义在起作用。

此代码将允许您检测陷阱是否存在于特定环境中:

double (*)(double) = &::abs; // fails if you haven't included math.h, possibly via cmath

但它不会帮助你发现你落入陷阱的地方。

相关内容

  • 没有找到相关文章

最新更新