¿Hay una opción de GCC para advertir sobre escribir 'this-field `en lugar de`this->field'?


Este siguiente código (que contiene un error vicioso) compila con GCC sin previo aviso. Pero, por supuesto, no funciona como espera el desarrollador (yo).

#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;
}

¿Qué advertencia puedo añadir para el compilador (GCC 4.8) para evitar este tipo de error tipográfico?

Pregunta vinculada: ¿Hay alguna opción para forzar (o advertir) el acceso a variables/funciones miembro con this->?

Author: PJTraill, 2017-09-20

4 answers

Este problema en particular es detectado por 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)

Esto fue sin incluir camino dado. Si añado -I /usr/include/c++/4.8/, el problema aún se detecta:

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

Y luego cppcheck trabaja lentamente a través de las configuraciones #ifdef antes mencionadas.

(Como nota al margen, el error en local_classes.tcc es un falso positivo, pero esto es muy difícil de decir para una herramienta automatizada, ya que tendría que ser consciente de que el bloque catch en este sitio no se debe ingresar cuando la macro __EXCEPTIONS es unset.)

Descargo de responsabilidad: No tengo otra experiencia con cppcheck.

 71
Author: Arne Vogel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-20 11:19:56

No, this - b está realizando aritmética de puntero en el puntero this, a pesar de que b es un tipo bool (b se convierte implícitamente a int).

(Curiosamente, puede siempre establecer this + b a un puntero donde b es un tipo bool, ya que puede establecer un puntero a uno más allá del final de un escalar! Así que incluso tu observador favorito undefined behaviour lo permitiría.)

La comprobación de límites de matriz siempre ha sido el trabajo de un C++ programador.

Tenga en cuenta también que en sus casos el uso de this es superfluo: así que reducir este uso excesivo es una forma de hacer que el problema desaparezca.

 32
Author: Bathsheba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-20 10:32:20

Me gustaría sugerir otra herramienta (aparte de cppcheck propuesto por @arne-vogel), dando una ayuda visual en lugar de la advertencia pidió:

Use clang-format para formatear automáticamente su código. El resultado podría verse así (dependiendo de la configuración), haciendo que el error sea más visible por los espacios agregados alrededor de operator-:

struct A {
  bool b;
  void set(bool b_) { this->b = b_; }
  bool get() const { return this - b; }
};
 13
Author: Simon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-20 19:01:11

No, no hay manera de recibir una advertencia. Las conversiones implícitas, aunque perversas, son permitidas por el lenguaje.

Sin embargo, en este caso de uso específico podemos hacerlo mejor - envolviendo el bool en una clase wrapper que tiene conversiones explícitas y no hay operaciones aritméticas definidas.

Esto resulta en un error del compilador cuando se usa incorrectamente lógicamente, lo que normalmente se ve como preferible a una advertencia si el objetivo es la corrección lógica.

Es interesante observar que c++17 desaprueba bool::operator++ ya que esta aritmética es vista como malvada.

Ejemplo:

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
};
 -1
Author: Richard Hodges,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-20 13:21:06