Есть ли опция GCC для предупреждения о написании `this-field` вместо` this-> field`?

Этот следующий код (содержащий порочную ошибку) компилируется с 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->?

Ответ 1

Эта особая проблема обнаружена cppcheck:

$ cppcheck --enable=all this-minus-bool.cxx 
Checking this-minus-bool.cxx...
[this-minus-bool.cxx:7]: (warning) Suspicious pointer subtraction. Did you intend to write '->'?
(information) Cppcheck cannot find all the include files (use --check-config for details)

Это было без указания пути. Если я добавлю -I /usr/include/c++/4.8/, проблема все равно будет обнаружена:

Checking this-minus-bool.cxx...
[this-minus-bool.cxx]: (information) Too many #ifdef configurations - cppcheck only checks 12 of 45 configurations. Use --force to check all configurations.
[this-minus-bool.cxx:7]: (warning) Suspicious pointer subtraction. Did you intend to write '->'?
[/usr/include/c++/4.8/bits/ostream.tcc:335]: (style) Struct '__ptr_guard' has a constructor with 1 argument that is not explicit.
[/usr/include/c++/4.8/bits/locale_classes.tcc:248]: (error) Deallocating a deallocated pointer: __c

а затем cppcheck медленно работает через вышеупомянутые конфигурации #ifdef.

(В качестве побочной заметки ошибка в local_classes.tcc является ложной позицией, но это очень сложно сказать для автоматизированного инструмента, так как нужно знать, что блок catch на этом сайте не должен быть если макрос __EXCEPTIONS не установлен.)

Отказ от ответственности: у меня нет другого опыта работы с cppcheck.

Ответ 2

Нет, this - b выполняет арифметику указателя на указателе this, несмотря на то, что b является типом bool (b неявно преобразован в int).

(Интересно, что вы всегда можете установить this + b в указатель, где b - это тип bool, так как вы можете установить указатель на один за концом скаляра! Так что даже ваше любимое поведение undefined споттер позволил бы это сделать.)

Проверка границ массива всегда была задачей программиста на С++.

Обратите внимание, что в ваших случаях использование this является излишним: поэтому ограничение этого чрезмерного использования является одним из способов устранения проблемы.

Ответ 3

Я хотел бы предложить другой инструмент (кроме cppcheck, предложенный @arne-vogel), предоставляя лучшее визуальное сопровождение вместо предупреждения, запрошенного:

Используйте clang-format, чтобы автоматически форматировать ваш код. Результат может выглядеть так (в зависимости от настроек), делая ошибку более заметной с помощью пробелов, добавленных вокруг operator-:

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

Ответ 4

Нет, нет способа получить предупреждение. Неявные преобразования, хотя и порочные, допускаются языком.

Однако в этом конкретном примере использования мы можем сделать лучше - путем обертывания bool в классе-оболочке, который имеет явные преобразования и не определены арифметические операции.

Это приводит к ошибке компилятора при логическом неправильном использовании, что обычно считается предпочтительным для предупреждения, если целью является логическая корректность.

Интересно отметить, что С++ 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
};