Скомпилировать ошибки для простого случая CRTP с зависимыми типами

Я пытаюсь использовать простую форму CRTP (Curiously Recurring Template Pattern), поскольку у меня есть несколько классов, каждый из которых имеет несколько связанных классов, и я хочу, чтобы они связывали их вместе (например, у меня есть классы как Widget, Doobry и Whatsit, со связанными классами WidgetHandle, DoobryHandle и WhatsitHandle).

Каждый класс, который я использую для наследования от Base, добавляет value_type typedef, так что базовый класс может ссылаться на него как typename TWrapper::value_type.

struct WidgetHandle {};
template <typename twrapper="">
class Base
{
public:
 Base(typename TWrapper::value_type value_) 
 : value(value_) {}
 typename TWrapper::value_type value;
};
class Widget : public Base<widget>
{
public:
 typedef WidgetHandle value_type;
 Widget(WidgetHandle value_) : Base<widget>(value_) {}
};
int main(int argc, char* argv[])
{
 Widget i(WidgetHandle());
 return 0;
}
</widget></widget></typename>

Однако я получаю ошибки компиляции:

scratch1.cpp(10): error C2039: 'value_type' : is not a member of 'Widget'
scratch1.cpp(16) : see declaration of 'Widget'
scratch1.cpp : see reference to class template instantiation 'Base<twrapper>' being compiled
1> with
1> [
1> TWrapper=Widget
1> ]
scratch1.cpp(10): error C2039: 'value_type' : is not a member of 'Widget'
</twrapper>

Это с VS2010, хотя я получаю подобные ошибки с clang. Что мне здесь не хватает?

2 ответа

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

struct WidgetHandle {};
template <typename twrapper,="" typename="" handletype="">
class Base
{
public:
 typedef HandleType value_type;
 Base(HandleType value_) 
 : value(value_) {}
 HandleType value;
};
class Widget : public Base<widget, widgethandle="">
{
public:
 Widget(WidgetHandle value_) : Base(value_) {}
};
int main(int argc, char* argv[])
{
 Widget i(WidgetHandle());
 return 0;
}
</widget,></typename>

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

struct WidgetHandle {};
class Widget;
template<class t="">
struct Handle
{
};
template<>
struct Handle<widget>
{
 typedef WidgetHandle type; 
};
template <typename twrapper,="" typename="" handletype="Handle<TWrapper">::type>
class Base
{
public:
 typedef HandleType value_type;
 Base(HandleType value_) 
 : value(value_) {}
 HandleType value;
};
class Widget : public Base<widget>
{
public:
 Widget(WidgetHandle value_) : Base(value_) {}
};
int main(int argc, char* argv[])
{
 Widget i(WidgetHandle());
 return 0;
}
</widget></typename></widget></class>


У вас не может быть круговых зависимостей: Base нужен Widget value_type, который неизвестен при создании базы.

Возможное решение: передать значение value_type в качестве параметра базового шаблона, использовать дополнительный шаблон шаблонов,...

Пример:

template <typename w=""> 
struct WidgetTraits {};
template <typename w="">
class Base
{
 public:
 typedef typename WidgetTraits<w>::value_type value_type;
};
class Widget;
template<>
struct WidgetTraits<widget>
{
 typedef WidgetHandle value_type;
};
class Widget : public Base<widget>
{
};
</widget></widget></w></typename></typename>

Другой (немного другой) Пример:

template <typename c,="" typename="" a="">
class B : public A
{
 public:
 typedef typename A::value_type value_type;
};
class A
{
 public:
 typedef WidgetHandle value_type;
};
class C : public B<c, a="">
{
};
</c,></typename>

licensed under cc by-sa 3.0 with attribution.