¿Cuál es el significado de clang's -Wweak-vtables?

78

Básicamente no entiendo los clang's -Wweak-vtables. Esto es lo que observé hasta ahora:

Caso uno: (activa la advertencia)

class A {
    public:
    virtual ~A(){}        
};

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Caso dos: (no activa la advertencia)

class A {
    public:
    virtual ~A(){}        
};   

int main(){}

Caso tres: (no activa la advertencia)

class A {
    public:
    virtual ~A();

};

A::~A(){}

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Caso cuatro: (Activa la advertencia)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}        
};    

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Caso cinco: (no activa la advertencia)

class A {
    public:
    virtual ~A(){}
    virtual void fun();      
};    

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Caso seis: (no activa la advertencia)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}
};    

class B : public A {};

int main(){}

Caso siete: (no activa la advertencia)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}
};    

class B : public A {
    public:
    virtual void fun(){}
};

int main(){}

La advertencia exacta es

warning: 'A' has no out-of-line virtual method definitions; its vtable 
will be emitted in every translation unit [-Wweak-vtables]

Entonces, aparentemente, si no declaro una función virtual no en línea en una clase, causa algún tipo de problema si y solo si derivo de ella y la clase derivada tiene un destructor virtual.

Preguntas:

  1. ¿Por qué es esto un problema?
  2. ¿Por qué se soluciona esto declarando una función virtual? (Advertencia habla de definiciones)
  3. ¿Por qué no se produce la advertencia cuando no procedo de la clase?
  4. ¿Por qué no aparece la advertencia cuando la clase derivada no tiene un destructor virtual?
Baum mit Augen
fuente

Respuestas:

102

Si todos los de una clase virtual métodos están en línea, el compilador no tiene forma de seleccionar una unidad de traducción en la que colocar una única copia compartida de la vtable; en su lugar, se debe colocar una copia de la vtable en cada archivo de objeto que la necesite. En muchas plataformas, el enlazador puede unificar estas copias múltiples, ya sea descartando definiciones duplicadas o mapeando todas las referencias a una copia, por lo que esto es solo una advertencia.

Implementando un virtual función fuera de línea permite al compilador seleccionar la unidad de traducción que implementa ese método fuera de línea como un "hogar" para los detalles de implementación de la clase, y coloca la única copia compartida de la vtable en la misma unidad de traducción. . Si varios métodos están fuera de línea, el compilador puede hacer una elección arbitraria de método siempre que esa elección esté determinada únicamente por la declaración de la clase; por ejemplo, GCC elige el primer método no en línea en el orden de declaración.

Si no anula ningún método de una clase, la virtualpalabra clave no tiene ningún efecto observable, por lo que no es necesario que el compilador emita un vtable para la clase. Si no deriva de A, o si no declara el destructor de una clase derivada virtual, no hay métodos anulados en Ay, por lo tanto A, se omite la tabla vtable. Si declara un virtualmétodo fuera de línea adicional para suprimir la advertencia y también hace algo que anula un método en A, la implementación del método no en línea virtual(y su copia adjunta de vtable) debe proporcionarse en una unidad de traducción vinculada , de lo contrario, la vinculación fallará porque falta la vtable.

Jeffrey Hantin
fuente
2
¿Qué hace cuando (diferentes) métodos fuera de línea de la misma clase ocurren en varias TU diferentes?
MM
2
@Matt, se elige un método fuera de línea de alguna manera arbitraria pero determinista en función de la declaración de la clase, y la vtable termina en la misma TU que la implementación del método. Todas las unidades de traducción relevantes ven la misma declaración: excluir travesuras de declaración inconsistentes, acordarán el método elegido y, por lo tanto, emitirán colectivamente exactamente una vtable.
Jeffrey Hantin
Esta respuesta no explica los casos 6 y 7.
León
2
@JeffreyHantin ¿Qué quiere decir con "el destructor de B no es virtual" ? El destructor generado por el compilador (declarado implícitamente) Bdebe ser virtual (ya que se ha declarado el destructor de la clase base virtual).
León
2
@Leon, cita la especificación: "Un destructor que está predeterminado y no definido como eliminado se define implícitamente cuando se usa odr (3.2) o cuando está explícitamente predeterminado después de su primera declaración". Nada en los casos 6 o 7 usos o valores predeterminados explícitos ~B(), por lo tanto, el compilador no crea una instancia implícita de la definición predeterminada. ¿Cómo puede ser virtual si no existe? La trivialidad de los ejemplos está provocando algunos efectos contrarios a la intuición.
Jeffrey Hantin