любопытно повторяющийся шаблон и бесконечная рекурсия - PullRequest
0 голосов
/ 01 апреля 2020

Взгляните на следующий простой шаблон любопытного повторяющегося шаблона (CRTP):

template<class Derived>
struct base
{
    void foo() {
        static_cast<Derived*>(this)->foo();
    }
};

struct derived
    : public base<derived>
{};

Поскольку derived не имеет функции-члена foo, кроме функции, полученной из base, вызов derived{}.foo() дает бесконечную рекурсию. Можем ли мы изменить определение base::foo таким образом, чтобы static_cast<Derived*>(this)->foo() вызывалось только при наличии Derived::foo?

EDIT : причина, по которой я спрашиваю, заключается в том, что foo в моем реальном приложении есть аргумент шаблона. А поскольку шаблонный метод не может быть виртуальным, CRTP представляется единственным обходным решением.

Ответы [ 2 ]

1 голос
/ 01 апреля 2020

Можем ли мы изменить определение base::foo таким образом, чтобы static_cast<Derived*>(this)->foo() вызывалось только при наличии Derived::foo?

Один способ принудительного определения в Derived::foo перегрузить функции фиктивным аргументом.

template <typename Derived>
struct base
{
    void foo() {
        static_cast<Derived*>(this)->foo(0);   // Requires Derived::foo(int)
    }
};

или

template <typename Derived>
struct base
{
    void foo(int ) {
        static_cast<Derived*>(this)->foo();  // Requires Derived::foo(void)
    }
};
0 голосов
/ 01 апреля 2020

Если вы желаете изменить тип возврата base::foo на фиктивный тег, мы можем проверить, возвращает ли вызов Derived::foo тот же тег или нет, и в зависимости от этого вызова производная версия.

#include <iostream>

template<class Derived>
class base
{
    private:
    struct Tag {};

    public:
    Tag foo() {
        if constexpr (!std::is_same_v<decltype(std::declval<Derived>().foo()), Tag>) {
            static_cast<Derived*>(this)->foo();
        }

        return {};
    }
};

struct derived
    : public base<derived>
{};

struct derived2
    : public base<derived>
{
    void foo() { std::cout << "foo!"; }
};

int main() {
    derived d;
    derived2 d2;

    d.foo();
    d2.foo();
}
...