Compile errors for a simple CRTP case with dependent types

I am trying to use a simple CRTP (Curiously Recurring Template Pattern) form, as I have several classes, each of which has several related classes, and I want them to bind them together (for example, I have classes like Widget, Doobry and Whatsit , with the related classes WidgetHandle, DoobryHandle, and WhatsitHandle).

Each class that I use to inherit from Base adds a value_type typedef so that the base class can refer to it as 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; } 

However, I get compilation errors:

 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' 

This is with VS2010, although I get similar errors with clang. What am I missing here?

+6
source share
2 answers

Modify the Base definition to take the descriptor type as the second parameter to avoid circular dependency.

 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; } 

You can also use the feature class to get the WidgeHandle type for the widget.

 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; } 
+1
source

You cannot have circular dependencies: Base needs a Widget value_type, which is unknown when creating the base.

Possible solution: pass the value_type value as a parameter of the base template, use an additional template template, ...

An example :

 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> { }; 

Another (slightly different) Example :

 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> { }; 
+1
source

Source: https://habr.com/ru/post/952286/


All Articles