Using a child class as a template parameter of a base class and as a nested name pointer

I use my class as a template parameter for one of its parent class, and this parent class uses it in the template argument (although sizeof ()).

And the compiler gives me:

error: incomplete type 'Invoker :: workerClass {aka MyClass}' used in the naming specifier

However, the class is well defined in the file. I think this is due to the fact that the child class is not created at the time the base class is created, but this happens with CRTP and there are no problems.

The reason I use the child class in the template argument is to make another function call if the child class has a specific function or not.

Here is the minimum code to test

/* Structure similar to boost enable if, to use SFINAE */ template <int X=0, class U = void> struct test { typedef U type; }; enum Commands { Swim, Fly }; /* Structure used for template overloading, as no partial function template specialization available */ template<Commands T> struct Param { }; template <class T> class Invoker { public: typedef T workerClass; workerClass *wc() { return static_cast<workerClass*>(this); } template <Commands command> void invoke() { invoke2(Param<command>()); } /* If the child class has those functions, call them */ /* Needs template paramter Y to apply SFINAE */ template<class Y=int> typename test<sizeof(Y)+sizeof(decltype(&workerClass::fly))>::type invoke2(Param<Fly>) { wc()->fly(); } template<class Y=int> typename test<sizeof(Y)+sizeof(decltype(&workerClass::swim))>::type invoke2(Param<Swim>) { wc()->shoot(); } template<Commands command> void invoke2(Param<command>) { /* Default action */ printf("Default handler for command %d\n", command); } }; template <class T, class Inv = Invoker<T> > class BaseClass : public Inv { public: template<Commands command> void invoke() { Inv::template invoke<command>(); } }; class MyClass : public BaseClass<MyClass> { public: void swim() { printf("Swimming like a fish!\n"); } /* void fly(); */ }; void testing() { MyClass foo; foo.invoke<Fly>(); /* No 'void fly()' in MyClass, calls the default handler */ foo.invoke<Swim>(); /* Should print the swimming message */ } 

Error in line:

 typename test<sizeof(Y)+sizeof(decltype(&workerClass::fly))>::type 

So, is there any compiler that supports this, or is this explicitly specified by the standard as invalid usage of patterns? Should I change the way I do this and find a way? CRTP gives me hope that the code may be valid, but I'm not sure.

If this is really not possible, then why, and why does CRTP work?

+6
source share
1 answer

The solution was, as ildjarn pointed out, to add another level of indirection.

This is done by changing the test function to accept types:

 template <typename X, class U = void> struct test { typedef U type; }; 

And then pass the child class as a template parameter, instead of specifying it from get go:

  template<class Y=workerClass> typename test<decltype(&Y::fly)>::type invoke2(Param<Fly>) { wc()->fly(); } template<class Y=workerClass> typename test<decltype(&Y::swim)>::type invoke2(Param<Swim>) { wc()->swim(); } 

Thus, the nested qualifier is evaluated only when the function is called, and not when the class is evaluated, and by this time the child class is already evaluated. Plus, with the ability to pass a default template argument, we can call the function without any template parameters.

The template is also much more readable. And the sample code only works now:

 class MyClass : public BaseClass<MyClass> { public: void swim() { printf("Swimming like a fish!\n"); } /* void fly(); */ }; void testing() { MyClass foo; foo.invoke<Fly>(); /* No 'void fly()' in MyClass, calls the default handler */ foo.invoke<Swim>(); /* Should print the swimming message */ } 
+2
source

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


All Articles