Как утвердить функцию как окончательную для CRTP? - PullRequest
0 голосов
/ 10 января 2020

Как мы знаем в CRTP наследуемую базу производного класса, в качестве окончательного наследования.

Что если мы хотим сделать производный класс не финальным, а переопределяющие функции - 'финальными'?

Есть ли способ сделать это с помощью static_assert?

Пример кода:

template <typename D>
struct A
{
    int f()
    {
        return static_cast<D*>(this)->g();
    }

    int g();
};

struct B : A<B> // usually final, but we want it inheritable
{
    int g() // but this should be 'final'
    {
        // TODO: ???
        return 1;
    }
};

struct C : B
{
    int g() // this is bad
    {
        return 2;
    }

    int h(); // this is permissive
};

#include <iostream>

template <typename D>
void f(A<D>& x)
{
    std::cout << x.f() << std::endl;
}

int main()
{
    B b;
    C c;
    f(b); // OK, it's 1
    f(c); // BAD, it's 1
    return 0;
}

Ответы [ 2 ]

3 голосов
/ 10 января 2020

Вы можете использовать final для двух целей.

С https://en.cppreference.com/w/cpp/language/final

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

Вы можете использовать

struct B : A<B>
{
    virtual int g() final
    {
        return 1;
    }
};

, чтобы позволить другим классам наследоваться от B, но не в состоянии переопределить g().

Еще одно потенциальное преимущество использования final заключается в том, что оптимизирующий компилятор может разрешать вызов функции во время компиляции, а не во время выполнения (Спасибо @JesperJuhl).

0 голосов
/ 10 января 2020

Я придумал решение, используя закрытый тег в сигнатуре функции:

template <typename D>
struct A
{
    struct internal_tag
    {};

    int f()
    {
        return static_cast<D*>(this)->g({});
    }

    int g(internal_tag);
};

struct B : A<B>
{
    int g(internal_tag)
    {
        return 1;
    }

private:
    using A<B>::internal_tag;
};

struct C : B
{
    //  int g(internal_tag) // int g(internal_tag) is prohibited
    //  {
    //      return 2;
    //  }

    int h();
};
...