Cómo detectar si un método es virtual?


Traté de hacer un rasgos para encontrar si un método es virtual: (https://ideone.com/9pfaCZ )

// Seveval structs which should fail depending if T::f is virtual or not.
template <typename T> struct Dvf : T { void f() final; };
template <typename T> struct Dvo : T { void f() override; };
template <typename T> struct Dnv : T { void f() = delete; };

template <typename U>
class has_virtual_f
{
private:
    template <std::size_t N> struct helper {};
    template <typename T>
    static std::uint8_t check(helper<sizeof(Dvf<T>)>*);
    template<typename T> static std::uint16_t check(...);
public:
    static
    constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t);
};

Casos de prueba:

struct V  { virtual void f(); };
struct NV {         void f(); };
struct E  {                   };
struct F  { virtual void f() final; }; // Bonus (unspecified expected output)

static_assert( has_virtual_f< V>::value, "");
static_assert(!has_virtual_f<NV>::value, "");
static_assert(!has_virtual_f< E>::value, "");

Pero tengo error: 'void Dvf<T>::f() [with T = NV]' marked final, but is not virtual.
Si no utilizo sizeof y directamente Dvf<T>* en check, no tengo error de compilación, pero check no se descarta para el tipo "malo" en SFINAE :( .

¿Cuál es la forma correcta de detectar si un método es virtual?

Author: Jarod42, 2014-04-07

2 answers

El código no es perfecto, pero básicamente pasa las pruebas (al menos en todos los sonidos disponibles en wandbox y gcc desde 7.):

#include <type_traits>

template <class T>
using void_t = void;

template <class T, T v1, T v2, class = std::integral_constant<bool, true>>
struct can_be_compaired: std::false_type { };

template <class T, T v1, T v2>
struct can_be_compaired<T, v1, v2, std::integral_constant<bool, v1 == v2>>: std::true_type { };

template <class T, class = void>
struct has_virtual_f: std::false_type { };

template <class T>
struct has_virtual_f<T, void_t<decltype(&T::f)>>{
    constexpr static auto value = !can_be_compaired<decltype(&T::f), &T::f, &T::f>::value;
};

struct V  { virtual void f() { }      };
struct NV {         void f() { }      };
struct E  {                           };
struct F  { virtual void f() final{ } }; // Bonus (unspecified expected output)

int main() {
   static_assert( has_virtual_f< V>::value, "");
   static_assert(!has_virtual_f<NV>::value, "");
   static_assert(!has_virtual_f< E>::value, "");
   static_assert( has_virtual_f< F>::value, "");
}

[demostración en vivo]


Las partes estándar relevantes que teóricamente permiten que el rasgo funcione correctamente: [expr.eq]/3.3, [expr.const]/2.21

 8
Author: W.F.,
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-05-18 19:47:52

Probablemente no hay manera de determinar si un método específico es virtual. Digo esto porque el proyecto Boost investigó rasgos durante años y nunca produjo una prueba de rasgos.

Sin embargo, en C++11, o usando la biblioteca Boost , puede usar la plantilla is_polymorphic<> para probar un tipo y ver si el tipo tiene funciones virtuales. Ver std::is_polymorphic o boost::is_polymorphic para referencia.

 13
Author: llewelly,
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
2016-04-02 13:49:22