Using decltype at end of specified return in CRTP base class

I am trying to use decltype in the late specified return of a member function in the CRTP base class, and with the error: invalid use of incomplete type const struct AnyOp<main()::<lambda(int)> > .

 template<class Op> struct Operation { template<class Foo> auto operator()(const Foo &foo) const -> typename std::enable_if<is_foo<Foo>::value, decltype(static_cast<const Op*>(nullptr)->call_with_foo(foo))>::type { return static_cast<const Op*>(this)->call_with_foo(foo); } }; template<class Functor> struct AnyOp : Operation<AnyOp<Functor> > { explicit AnyOp(Functor func) : func_(func) {} template<class Foo> bool call_with_foo(const Foo &foo) const { //do whatever } private: Functor func_; }; 

I am basically trying to move the entire sfinae boiler plate to the base class, so I don’t need to repeat it for every operation I created (currently each operation has 6 different calls, and there are ~ 50 operations, so there are many repetitions with enable_if).

I tried an overload-based solution, but one of the types that can be passed is all that can be called (it could be a regular functor from C ++ 03 or C ++ 0x lambda), which I linked to std: : function, unfortunately, the overhead from std :: function, although very minimal, actually matters in this application.

Is there a way to fix what I have or is there a better solution to solve this problem?

Thanks.

+6
c ++ c ++ 11 decltype crtp
source share
2 answers

You, as another answer already describes, are trying to access a member of a class in one of the base class of the class. This will happen because the item has not yet been declared at this point.

When he creates an instance of the base class, he creates all of his member declarations, so he needs to know the type of return value. You can make the return type dependent on Foo , which causes it to delay evaluating the return type until Foo is known. This will change the base class as follows:

 // ignore<T, U> == identity<T> template<typename T, typename Ignore> struct ignore { typedef T type; }; template<class Op> struct Operation { template<class Foo> auto operator()(const Foo &foo) const -> typename std::enable_if<is_foo<Foo>::value, decltype(static_cast<typename ignore<const Op*, Foo>::type>(nullptr)->call_with_foo(foo))>::type { return static_cast<const Op*>(this)->call_with_foo(foo); } }; 

This artificially casts static_cast to a type dependent on Foo , so it does not require an immediate full Op type. Rather, the type should be completed when operator() is created with the corresponding template argument.

+6
source share

You are trying to access a member of a class from one of its own base classes that will fail because the body of the class does not exist in its base class. Can you pass call_with_foo return type calculation logic as a metafile to the base class? Will this logic be complicated?

Another option, depending on how flexible you are in changing the class hierarchy (and remember that you have typedefs templates), is that the shell inherits from the implementation class, and not vice versa. For example, you can write AddParensWrapper<T> , which inherits from T and has operator() , which will go to T::call_with_foo . This will fix the dependency problem.

+1
source share

All Articles