0

I face a strange rare problem that I want to hide a Base class's function B::f1(int).

class B{
    public: void f1(int){}
    public: void f1(float){}
};
class C : public B{
    public: void f1(int){
        //static_assert(ASSERT_that_thisFunctionHidParentFunction,"");
        //some rare ugly hacky stuff
    }
    public: void f1(char){
        //static_assert(!ASSERT_that_thisFunctionHidParentFunction,"");
    }
};

Everything works fine ; I just worry about maintainability.
I wish to make sure that a function C::f1(int) always hides B::f1(int).

If B::f1(int) happen to change signature in the future (e.g. to B::f1(int,int)),
I want some compile error to notify programmers that C::f1(int) should be changed to C::f1(int,int) as well.

In real world, my problematic function f1 doesn't have overload.
But for educational purpose, I wish to know how to solve it if there are overload too. (i.e. optional)

I love a cute solution like ASSERT_that_thisFunctionHidParentFunction in my code comment.
I don't mind MACRO.

My poor solution

I tried to typedef to force compile error, but it doesn't assert-fail in some case (MCVE-coliru), because int is automatically casted to B::f1(float).

class B{
    public: void f1(int,int){}
    public: void f1(float){}
};
class C : public B{
    public: void f1(int){
        using XXX=decltype(std::declval<B>().f1(std::declval<int>()));
        //static_assert(ASSERT_that_thisFunctionHidParentFunction,"");
    }
    public: void f1(char){
        //static_assert(!ASSERT_that_thisFunctionHidParentFunction,"");
    }
};
int main() {
   return 0;
}
javaLover
  • 6,347
  • 2
  • 22
  • 67
  • Such check would be easy if your functions were `virtual`, see `override` specifier. What real problem do you try to solve? Do you really need to inherit to an implementation (non-interface) class and override a non-virtual method? – Igor G May 15 '19 at 08:49
  • As for your attempted solution, you may find `` useful (yet complicated). – Igor G May 15 '19 at 08:56
  • @Igor G : I has a base class `Ship` that has function `PhysicBody* Ship::createPhysicBody()`. I have `BattleShip` that inherit from `Ship`. I want `BattleShip::createPhysicBody()` to do something a little different from the parent class. ... Thank for the override keyword (my bad, I totally forgot about it), but I don't want to make it virtual yet. – javaLover May 15 '19 at 08:57
  • @Igor G : Thank for ``. It interests me. :) – javaLover May 15 '19 at 09:08
  • if `f1` is not template, detecting presence and signature of `&B::f1` should do the job. These is also [`std::experimental::is_detected`](https://en.cppreference.com/w/cpp/experimental/is_detected). – Jarod42 May 15 '19 at 10:49

2 Answers2

1

You could check that the function pointers are different.

With MSVC 2019 and Clang 8 this worked for me, however GCC rejected it as "not a constant expression", so might need something different or a a runtime assert there. Not sure which is right in regards to the standard.

class B {
public:
    void f1(int) {}
    void f2(int) {}
    void f3(int) {}
    void f1(float) {}
};
class C : public B {
public:
    void f1(int) {}
    void f1(char) {}
    void f3(int) {}
};
static_assert(&B::f1 != &C::f1); // Won't work because of the overloading, can static_cast to get the overload you want
static_assert(static_cast<void(B:: *)(int)>(&B::f1) != static_cast<void(C:: *)(int)>(&C::f1));
static_assert(static_cast<void(B:: *)(int)>(&B::f2) != static_cast<void(C:: *)(int)>(&C::f2)); // static assertion failed
static_assert(&B::f3 != &C::f3); // passes, no static_cast as not overloaded

Be very careful with hiding a member function in this way, as the base class is public and the method is not virtual. It can easily be cast and then the derived function is not called.

C *c = ...;
B *b = c; // Implicit
b->f1(5); // Calls B::f1, not C::f1

If possible it may be best to make the inheritance protected or private to avoid accidental casting.

Fire Lancer
  • 29,364
  • 31
  • 116
  • 182
  • `B::f1` is not static function (sad face - like you already mentioned): [MCVE](http://coliru.stacked-crooked.com/a/859141aec6347d76). – javaLover May 15 '19 at 09:07
  • @javaLover Yes, as I said GCC specifically didn't like it, but not sure which compiler is right/wrong. An `assert` would work, but would only trigger at runtime (for example in a static initialiser or something else very early in the program). – Fire Lancer May 15 '19 at 09:09
  • Thank. I may try to verify which one is wrong later ... For now, I almost like your answer. XD – javaLover May 15 '19 at 09:11
0

The way I understand your question, it seems that you want to make sure that several implementation classes comply with a certain non-virtual concept.

template <typename Candidate>
struct ShipConcept
{
    constexpr ShipConcept()
    {
        using ProtoFoo = void (Candidate::*)(int);
        (void)static_cast<ProtoFoo>(&Candidate::foo);

        // Other tests ...
    }
};


struct Ship_A
 : private ShipConcept<Ship_A>
{
    Ship_A()
    {
    }

    void foo(int, int);
    void foo(float);
    void foo(int);        // (1)
};

You'll get compile-time error if line (1) isn't there.

Igor G
  • 1,838
  • 6
  • 18