Как вывести тип в CRTP?

Я хочу реализовать CRTP в шаблоне С++. Код выглядит следующим образом:

template <typename T>
class A{
public:
    typedef typename T::Scalar Scalar;
};
template <typename T>
struct B:public A<B<T> > {
public:
    typedef T Scalar;
};

Но когда я компилирую код, компилятор показывает:

error: no type named ‘Scalar’ in ‘struct B<int>’

Кто-нибудь может объяснить это?


person maple    schedule 07.07.2016    source источник
comment
Дублировать, но этот вопрос намного понятнее, поэтому я думаю, что мы должны закрыть другой один раз на это есть ответ.   -  person Quentin    schedule 07.07.2016
comment
@Quentin Есть идеи по этому поводу?   -  person maple    schedule 07.07.2016
comment
Мой ответ на этот связанный вопрос подходит и для этого (B неполный, когда вы создаете экземпляр A<B>), но я чувствую, что копирую/ исправление/вставка моего ответа здесь было бы дешево.   -  person Quentin    schedule 07.07.2016
comment
@Quentin Итак, я должен добавить черты?   -  person maple    schedule 07.07.2016


Ответы (3)


Ошибка связана с неполным типом. Посмотрите на строку ниже:

template<typename T>
struct B : public A<B<T>> 

Тело B<T> еще не запущено, и вы используете его в качестве параметра в здании A<T>. На самом деле это разрешено, но, как упоминалось в ответе Квентина, это предостережение CRTP. Помимо этого ответа, вот еще один способ:

template <template<typename> class Base, typename T>
class A{ // use `<typename...>` for C++11
public:  
  typedef T Scalar;
  // use `Base<T>` wherever required
};

template <typename T>
struct B : public A<B, T> {
public: //        ^^^^^^^
    typedef T Scalar;
};
person iammilind    schedule 07.07.2016

Проблема в том, что API для A<T> говорит, что T::Scalar должен быть определен, чего пока нет в template<typename U> struct B:public A<B<U> > {.

Простое изменение заключается в исправлении API A:

template <typename SCALAR>
class A{
public:
    typedef SCALAR Scalar;
};
template <typename T>
struct B:public A<T> {
};

В абстрактных терминах использование A<T>, T::Scalar является формой передачи по имени, тогда как A<SCALAR> является обычной передачей по аргументу. И передача по имени проблематична, когда имена еще не определены в том месте, где они вам нужны.

[править] И поскольку это кажется неочевидным, вы все равно можете сохранить CRTP:

template <typename SCALAR, typename CRTP>
class A{
public:
    typedef SCALAR Scalar;
};
template <typename T>
struct B:public A<T, B<T>> {
};
person MSalters    schedule 07.07.2016
comment
Но здесь A вообще не имеет никакой информации о B. Как теперь A<T> может использовать какой-либо метод B<T>? CRTP больше нет! - person iammilind; 07.07.2016
comment
@iammilind: Ну, вы можете сохранить CRTP и все еще передать SCALAR отдельно. Они не исключают друг друга. CRTP просто был не нужен для B::Scalar. - person MSalters; 07.07.2016
comment
Вероятно, вы предполагаете, что в этом случае CRTP используется только для получения Scalar; Правильно? Но что, если CRTP и Scalar нужны для разных целей? Из вопроса кажется, что OP хочет CRTP, а с другой стороны он / она наткнулся на внутренний typedef из Scalar. - person iammilind; 07.07.2016
comment
@iammilind: T::Scalar не является внутренним определением типа в вопросе, это аргумент, передаваемый по имени для A<T>. - person MSalters; 07.07.2016

Другой способ взглянуть на это,

B<int> b is called which will invoke
B<int> : A<B<int> > which will further invoke // B is not yet done waiting on A<B<int> >

A<B<int>> { typedef B<int>::Scalar Scalar } will try to fetch B<int> //which is not yet constructed as many pointed out.

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

template <typename T>
class A{
public:
    typedef typename T::Scalar Scalar;
};

template <typename T>
class B:public A<B<T> > {
public:
    typedef T Scalar;
};

template <>
class A<B<int> > {
public :
        typedef int Scalar;
};

int main()
{
        B<int> b;
}

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

person Raghavendar Reddy    schedule 07.07.2016