Специализация класса с использованием CRTP и внутреннего типа - PullRequest
0 голосов
/ 06 июля 2018

Я хотел бы построить следующие классы. Базовый класс определяет функции для реализации, Derived реализует этот интерфейс.

template <class T, class V>
class IBase
{
public:
    virtual void foo(const typename V::t_args&) =0;
};

template<class T>
struct T_args
{
    T z;
};

class Derived : public IBase<double, Derived>
{
public:
    typedef T_args<double> t_args;

    Derived() {}

    void foo(const t_args& x)
    { /* do some stuff */ }
};

Компилятор жалуется на Derived как неполный тип; Я не могу понять причину почему. Есть ли какой-то способ получить правильную структуру класса?

Я вынужден писать код на c ++ 98, но меня интересует любое решение на c ++ 11 и выше.

Ответы [ 3 ]

0 голосов
/ 06 июля 2018

Прочитав объяснение Сэма Варшавчика , я могу решить проблему, добавив класс t_args в сигнатуру шаблона базового класса:

template <class V, class Args>
class IBase
{
public:
    typedef Args t_args;

    virtual void foo(const Args&) =0;
};

template<class T>
struct T_args
{
    T z;
};

template <class T>
class Derived : public IBase<Derived<T>, T_args<T> >
{
public:
    typedef typename Derived::IBase::t_args t_args;

    Derived() {}

    void foo(const t_args&)
    { /* do some stuff */ }
};
0 голосов
/ 06 июля 2018

Другой обходной путь - использовать класс черт:

// The traits class
template <typename T> struct Arg;

template <class T, class V>
class IBase
{
public:
    virtual ~IBase() {}
    virtual void foo(const typename Arg<V>::t_args&) = 0; // V can be incomplete here
                                                          // but Arg<V> should be complete
};

// So go to define Arg<Derived>:
// Result class
template<class T>
struct T_args
{
    T z;
};

// Forward declaration, Arg<V> accept incomplete type
class Derived;

// Specialization for Derived
// should not use internal of Derived as it is incomplete
template <>
struct Arg<Derived>
{
    typedef T_args<double> t_args;
};

// Now definition of Derived
class Derived : public IBase<double, Derived>
{
public:
    typedef Arg<Derived>::t_args t_args; // Should probably go in IBase for ease usage

    Derived() {}

    void foo(const t_args& x) /* override */
    { /* do some stuff */ }
};

Демо

0 голосов
/ 06 июля 2018

В вашем базовом шаблоне класса:

virtual void foo(const typename V::t_args&) =0;

Это ссылка на некоторый внутренний класс или тип с именем t_args его параметра шаблона V. При ссылке на член класса определение класса должно быть полным (чтобы выяснить, что такое t_args). Вы пытаетесь использовать этот класс шаблона следующим образом:

class Derived : public IBase<double, Derived>

Вы передаете Derived для вашего V, однако его определение класса неполное. Если базовый класс шаблона ссылается только на свой параметр V, это обычно нормально. Однако вашему шаблону требуется, чтобы его тип параметра шаблона был завершенным, потому что ему нужно знать, что это за t_args, и ваш производный класс не завершен, пока он не будет полностью определен. Но он не может быть полностью определен, пока его базовый класс не будет полностью определен. Вроде как курица против яйца.

Не существует готового решения для такого рода круговой ссылки, своего рода. Единственное, что можно сделать - это реструктурировать класс, поэтому ваш тип «аргумента» - это независимый класс, а не производный класс.

...