有没有GCC选项可以警告写`this-field`而不是`this->field`?

63

这段代码(包含恶性漏洞)在GCC编译时没有任何警告。但是,当然,它不会按照开发者的预期工作(我的预期)。

#include <iostream>

struct A
{
    bool b;
    void set(bool b_) { this->b = b_; }
    bool get() const { return this-b; } // The bug is here: '-' instead of '->'
};

int main()
{
    A a;
    a.set(true);
    std::cout << a.get() << std::endl; // Print 1
    a.set(false);
    std::cout << a.get() << std::endl; // Print 1 too...
    return 0;
}

我应该为编译器(GCC 4.8)添加哪种警告以避免这种错别字?

相关问题:是否有选项可以强制(或警告)使用this ->访问成员变量/函数?


4
这是正确的 C++ 语句,因此没有编译器会发出警告。您应该使用静态代码分析工具来检查。 - Ari0nhh
18
大多数编译器在使用显式警告选项时,会对“正确”的C++语句产生警告。 - eerorika
7
我认为这个完美地展示了单元测试的价值。 - UKMonkey
43
不要使用 this->。这将完全避免问题。 - phuclv
16
大多数编译器会对“正确”的C++语句产生警告:我甚至可以说,所有警告都是来自“正确”的C++语句。如果不是有效的代码,你将会得到一个错误提示而不是警告。 - Sean Burton
显示剩余20条评论
4个回答

71
此问题由cppcheck检测到:
$ cppcheck --enable=all this-minus-bool.cxx 
Checking this-minus-bool.cxx...
[this-minus-bool.cxx:7]: (warning) 可疑指针减法。您是否打算写 '->'?
(information) Cppcheck 无法找到所有的包含文件(使用--check-config获取详情)
没有给出包含路径时也会检测到此问题。如果我添加 -I /usr/include/c++/4.8/,则仍然检测到此问题:
Checking this-minus-bool.cxx...
[this-minus-bool.cxx]: (information) #ifdef 配置过多-cppcheck仅检查了45个配置中的12个。使用 --force 检查所有配置。
[this-minus-bool.cxx:7]: (warning) 可疑指针减法。您是否打算写 '->'?
[/usr/include/c++/4.8/bits/ostream.tcc:335]: (style) 结构 '__ptr_guard' 具有带有1个参数的构造函数,该构造函数不是显式的。
[/usr/include/c++/4.8/bits/locale_classes.tcc:248]: (error) 正在释放已释放指针: __c
然后cppcheck会慢慢地处理前面提到的 #ifdef 配置。
(作为附注,local_classes.tcc中的错误是一个误报,但这对于自动化工具非常难以确定,因为它需要知道当未设置__EXCEPTIONS宏时,此处的catch块不应进入。)
免责声明: 我没有其他关于cppcheck的经验。

11
我宁愿选择误报也不要选择隐蔽的错误。 :-) - Caduchon
当然……这个工具似乎并没有生成很多,而且它还允许编写抑制(我还没有尝试过)。 - Arne Vogel
9
@Caduchon 这是正确的,直到你得到太多的误报警告,以至于你无法看到重要的警告为止。 - JPhi1618
2
@JPhi1618:这也是真的。我在使用英特尔编译器时遇到了问题。我无法禁用boost库中的警告。我得到了超过100,000个boost库的警告,无法看到我的警告。 - Caduchon
3
我是Cppcheck的开发者。我希望Cppcheck成为C/C++开发者默认使用的工具。如果有人尝试过Cppcheck并因某些原因认为它不好用,我很想了解一下。我们真的很努力避免Cppcheck中的噪音...引用@ArneVogel的话说:"在任何情况下,这个工具似乎都不会生成许多警告"。我们有错误抑制功能,但如果您看到错误的警告,建议您报告给我们以便我们修复它们。 - Daniel Marjamäki

32

不,this - b 对指针 this 执行了 指针算术运算,尽管 bbool 类型(b 隐式转换为 int)。

(有趣的是,您始终可以将 this + b 设置为指向一个 bool 类型的指针,因为您可以将指针设置为标量结尾后的位置!所以即使是您最喜欢的 未定义行为 追踪器也会允许这种情况。)

数组边界检查一直是 C++ 程序员的工作。

请注意,在您的情况下使用 this 是多余的:缩短这种过度使用是解决问题的一种方法。


10
@Caduchon: 那个决定完全令人不快。你如何划定底线?使用this调用私有成员函数?花点钱解决吧。请注意,我使用m_来表示成员变量,使用s_表示静态变量。回到我的领域;-) - Bathsheba
9
@Bathsheba:使用this->可以在编译时保证它是一个成员。使用m_s_并不能防止本地/全局变量(不幸的是)命名相同。这就是原因。但当然,重新考虑这个决定可能会很有趣。StackOverflow不是讨论这类问题的最佳平台(我认为)。哪个平台比较好? - Caduchon
2
我怀疑一个足够聪明的编译器可以发出警告,依赖于 bool(this-n) 总是为真的事实。例如,clang已经对隐式 bool(this) 发出了警告。 - Oktalist
10
我不认为这个回答的任何部分与有关警告的问题相关。当然,它涉及指针算术。但这并不意味着没有办法检测出它的可疑之处或生成警告。 - user2357112
2
@snb 请参考 https://dev59.com/GXM_5IYBdhLWcg3wymY0。我*认为*我在高盛时就开始使用`m_`和`s_`前缀了。我也见过将`my`作为成员变量的前缀,以及将`our`作为静态变量的前缀。但是个人而言,我不喜欢依赖语法着色来表示成员变量:这样做会让vi难以配置! - Bathsheba
显示剩余8条评论

13

除了@arne-vogel提出的cppcheck之外,我想建议另一种工具,它可以提供更好的可视化辅助,而不是所要求的警告:

使用clang-format自动格式化您的代码。根据设置结果可能看起来像这样,通过添加围绕operator-的空格使错误更加明显:

struct A {
  bool b;
  void set(bool b_) { this->b = b_; }
  bool get() const { return this - b; }
};

如果您的回答中能够添加一些代码着色或编译器输出着色工具,那么它将会变得更有价值。 - displayName
@displayName,我不确定我理解你的建议,因为我不知道有哪个软件开发编辑器或 IDE 没有内置的语法高亮功能。 - Simon
这让我感觉很老,因为我知道有一些编辑器至少没有语法高亮。算了吧。我只是想说,如果你要利用代码格式化来捕捉问题(在我看来是个好方法),那么也要使用编译器输出等格式化来捕捉这个以及其他潜在的错误。 - displayName
好主意。但实际上,我讨厌编辑器格式化我的代码。出于各种(愚蠢的)原因,我决定在某个时刻按照某个规则进行格式化,出于可读性、美观性、历史性等原因...我更喜欢掌控这一切。顺便说一下,我已经有一个编辑器可以自动完成、预编译、格式化、高亮显示...这只是一个简单的代码,写得非常快,这只是我的错误。 - Caduchon

-1

不,无法得到警告。虽然隐式转换十分反常,但语言是允许这种情况的。

然而,在这个特定的使用案例中,我们可以做得更好——通过包装布尔值的包装类,该类具有显式转换和未定义算术运算。

这会导致编译器在逻辑上误用时报错,如果逻辑正确性是目标,则通常被视为优于警告。

有趣的是,c++17已经弃用了bool::operator ++,因为这种算术操作被认为是邪恶的。

示例:

struct Bool
{
    explicit Bool(bool b) : value_(b) {}
    explicit operator bool() const { return value_; }
private:
    bool value_;

    // define only the operators you actually want
    friend std::ostream& operator<<(std::ostream& os, const Bool& b) {
        return os << b;
    }
};

struct X
{
    bool foo() {
        // compilation failure - no arithemetic operators defined.
        // return bool(this-b);

        // explicit conversion is fine
        return bool(b);
    }

    Bool b { true }; // explicit initialisation fine
};

2
那么,我还需要重新创建一个std::vector<Bool>来减少内存吗?这显然不是一个解决方案。我不想在我的所有代码中更改基本类型... - Caduchon
3
我不确定用一个问题来替换多个其他问题(例如,用自定义类替换旧的bool用法或者出现代码库不一致,确保新的开发人员也知道并使用它等等,并且到处都是样板代码)是否是一个好的解决方案。嗯,这做到了原始问题的目的,但我真心希望没有人会真的这样做。 - Noctiphobia
3
@RichardHodges: 我明确要求编译器发出警告。 - Caduchon
1
有几种隐式转换会生成警告,例如clang中的-Wundefined-bool-conversion - Oktalist
5
不,没有办法获得警告。尽管隐式转换很奇怪,但是它们被编程语言允许。这是一个不合逻辑的说法。许多(?全部)警告都是因为虽然编程语言允许,但可能是错误的事情。例如,对于许多编译器,“if (a = f())”会产生警告(通过“if ((a = f()))”可以消除警告)。 - Martin Bonner supports Monica
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接