static_assert: определенная функция в Derive «must» скрывает функцию класса Base - PullRequest
0 голосов
/ 15 мая 2019

Я сталкиваюсь со странной редкой проблемой, которую я хочу скрыть функцией Базового класса 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,"");
    }
};

Все отлично работает; Я просто беспокоюсь за ремонтопригодность.
Я хочу убедиться, что функция C::f1(int) всегда скрывает B::f1(int).

Если B::f1(int) изменит подпись в будущем (например, на B::f1(int,int)),
Я хочу, чтобы какая-то ошибка компиляции сообщала программистам, что C::f1(int) также следует изменить на C::f1(int,int).

В реальном мире моя проблемная функция f1 не перегружена.
Но для образовательных целей я хочу знать, как решить эту проблему, если есть и перегрузка. (т.е. необязательно)

Мне нравится симпатичное решение, подобное ASSERT_that_thisFunctionHidParentFunction в моем комментарии к коду.
Я не против MACRO.

Мое плохое решение

Я попытался ввести def для принудительного вызова ошибки компиляции, но в некоторых случаях он не выдает ошибку-сбой ( MCVE-coliru ), поскольку int автоматически приводится к 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;
}

Ответы [ 2 ]

1 голос
/ 15 мая 2019

Вы можете проверить, что указатели функций разные.

С MSVC 2019 и Clang 8 это работало для меня, однако GCC отклонил его как «не константное выражение», поэтому может потребоваться что-то другое или утверждение во время выполнения. Не уверен, что является правильным в отношении стандарта.

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

Будьте очень осторожны, скрывая функцию-член таким образом, поскольку базовый класс является открытым, а метод не виртуальным. Его можно легко привести, и тогда производная функция не вызывается.

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

Если возможно, лучше всего сделать наследство protected или private, чтобы избежать случайного приведения.

0 голосов
/ 15 мая 2019

Как я понимаю ваш вопрос, кажется, что вы хотите убедиться, что несколько классов реализации соответствуют определенной не виртуальной концепции.

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

Вы получите ошибку во время компиляции, если строка(1) не там.

...