How to detect if a method is virtual?
Asked Answered
V

2

30

I tried to make a traits to find if a method is virtual: (https://ideone.com/9pfaCZ)

// Several 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);
};

Test cases:

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, "");

But I got error: 'void Dvf<T>::f() [with T = NV]' marked final, but is not virtual.
If I don't use sizeof and directly Dvf<T>* in check, I don't have compilation error, but check is not discarded for "bad" type in SFINAE :( .

What is the proper way to detect if a method is virtual ?

Vanhook answered 7/4, 2014 at 11:28 Comment(6)
Sfinae doesn't "look" inside definitions of classes, it's not in "immediate context" (see 14.8.2/8).Fieldwork
I am interested why you would want to know this. Normally, a client should only worry about the interface and not care whether there is virtual dispatch behind the scenes or not.Philip
I think , You will have to look into the IL code of the dll to make out if the call is virtual or not ..Nothing
@MatthieuM.: When answering to explicitly-hide-a-base-function, I would expand my solution with a cleaner traits.Vanhook
@Vanhook This behaviour is expected because sizeof(Dvf<T>) instantiates Dvf<T> whereas Dvf<T>* does not. You cannot instantiate an invalid class during a SFINAE test or you'll get an error. As for a proper way, don't know if possible; trying.Nostology
@MatthieuM., I second that. Especially since in my classes the only public virtual functions I ever have are destructors. (IMAO, virtual functions should be private as a rule for similar reasons as data members.) Any public member function in any of my classes will be nonvirtual, but the bulk of the work may actually be carried out by delegation to a nonpublic virtual function.Lactose
P
18

The code isn't perfect but it basically passes the tests (at least in all clangs available on wandbox and gcc since 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, "");
}

[live demo]


The relevant standard parts that theoretically let the trait fly: [expr.eq]/4.3, [expr.const]/4.23

Procure answered 18/5, 2017 at 12:54 Comment(3)
If either is a pointer to a virtual member function, the result is unspecified. but it doesn't say it can't be true either. Or am I reading the standard wrong? I guess if it's unspecified it could go either true or false, and it may be compiler dependent?Downey
@Downey but: An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:(...) a relational or equality operator where the result is unspecified; (...) Procure
@Downey which means it is unspecified if the two pointers should be equal when they point to virtual method but when the equality is tested inside a constexpr context it is well defined what should compiler do - it is not a core constant expression so it should cause substitution failure.Procure
I
14

There is probably no way to determine if a specific method is virtual. I say this because the Boost project researched traits for years and never produced such a traits test.

However, in C++11, or using the Boost library, you can use the is_polymorphic<> template to test a type to see if the type has virtual functions. See std::is_polymorphic<> or boost::is_polymorphic<> for reference.

Indicative answered 9/4, 2014 at 13:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.