11

I came up with the following code detecting at compile time if inherited functions are overridden in the derived class. It works with all major compilers - gcc/clang/msvc. But is this approach actually backed by the standard?

#include <type_traits>

struct B {
    virtual void f1() {}
    virtual void f2() {}
    void f3() {}
    void f4() {}
};

struct D: B {
    void f1() override {}
    void f3() {}
};

int main()
{
    static_assert(!std::is_same_v<decltype(&B::f1), decltype(&D::f1)>, "overriden");
    static_assert(std::is_same_v<decltype(&B::f2), decltype(&D::f2)>, "base");
    static_assert(!std::is_same_v<decltype(&B::f3), decltype(&D::f3)>, "overriden");
    static_assert(std::is_same_v<decltype(&B::f4), decltype(&D::f4)>, "base");
    return 0;
}
NathanOliver
  • 150,499
  • 26
  • 240
  • 331
amosk
  • 375
  • 1
  • 9
  • This seems weird to me, intuitively the types `void(B::*)()` and `void(D::*)()` should be different whether or not a function is overloaded. But I'm no language lawyer... – Peter Jan 04 '21 at 17:00
  • 4
    How does this work with private inheritance? And `D::f3` is more accurately said to _hide_ `B::f3()`, rather than override. `B::f3` in this example should never be considered to be overridden in the `virtual` sense. Do you wish to distinguish overridden `virtual` functions from non-`virtual` functions? – alter igel Jan 04 '21 at 17:00
  • Default inheritance for `struct` is public. – The Philomath Jan 04 '21 at 17:05

1 Answers1

6

Found it, this is covered by by section 20.15.9 Member relationships, point 5 of the standard:

Note: The type of a pointer-to-member expression &C::b is not always a pointer to member of C, leading to potentially surprising results when using these functions in conjunction with inheritance.

Given example:

struct A { int a; };
struct B { int b; };
struct C: public A, public B { };

// The following will succeed because, despite its appearance,
// &C::b has type "pointer to member of B of type int"
static_assert(is_pointer_interconvertible_with_class( &C::b ));

And so on and so forth. That explain why in your example, &B::f2 and &D::f2 as well as &B::f4 and &D::f4 have the same type void(B::*)().

Peter
  • 2,784
  • 1
  • 9
  • 25
  • Could you cite and emphasize the relevant passages? The trailing comment is a bit hard to read. And what does this have to do with `virtual` functions or member functions at all? – alter igel Jan 04 '21 at 17:15
  • Nice answer, well found @Peter. Hope you don't mind the edit. – Bathsheba Jan 04 '21 at 17:17
  • @alterigel: The fact that the pointer to members we're dealing with here are pointers to virtual functions is not relevant to why this works, it only matters that a pointer to member for an inherited (non-overriden and non-hidden) member has type pointer to member of base class of type ... – Peter Jan 04 '21 at 17:24
  • thanks! marking this as the answer – amosk Jan 04 '21 at 18:16