What is wrong with this example of variation patterns?

Base class:

#include <memory> namespace cb{ template< typename R, typename ... Args > class CallbackBase { public: typedef std::shared_ptr< CallbackBase< R, Args... > > CallbackPtr; virtual ~CallbackBase() { } virtual R Call( Args ... args) = 0; }; } // namespace cb 

Derived class:

 namespace cb{ template< typename R, typename ... Args > class FunctionCallback : public CallbackBase< R, Args... > { public: typedef R (*funccb)(Args...); FunctionCallback( funccb cb_ ) : CallbackBase< R, Args... >(), cb( cb_ ) { } virtual ~FunctionCallback() { } virtual R Call(Args... args) { return cb( args... ); } private: funccb cb; }; } // namespace cb 

Function to create:

 namespace cb{ template < typename R, typename ...Args > typename CallbackBase< R, Args... >::CallbackBasePtr MakeCallback( typename FunctionCallback< R, Args... >::funccb cb ) { typename CallbackBase< R, Args... >::CallbackBasePtr p( new FunctionCallback< R, Args... >( cb ) ); return p; } } // namespace cb 

And an example:

 bool Foo_1args( const int & t) { return true; } int main() { auto cbObj = cb::MakeCallback( & Foo_1args ); } 

I keep getting this error:

 error: no matching function for call to 'MakeCallback(bool (*)(const int&))' error: unable to deduce 'auto' from '<expression error>' 

I tried to change it, but I could not figure out how to fix it.

So what's wrong? And how to fix this example?

+4
source share
4 answers

The problem may make sense with a simpler example. Try to identify the problem here:

 template <typename T> struct id { typedef T type; }; template <typename T> void foo(typename id<T>::type x); foo(5); // error 

The problem is that the compiler cannot determine what should be T ; It is not used directly anywhere. You must explicitly specify it: foo<int>(5) or allow it to output it in another way:

 template <typename T> void foo(typename id<T>::type x, T y); foo(5, 7); // okay, T is int because 7 is int 

This makes sense: how could the compiler figure out which T 's provided in id result in an id<T>::type match? There may be specializations, and all of this would be costly if possible.


Similarly, there is nothing that the compiler had available to output R and Args . Instead, you should do this:

 template < typename R, typename ...Args > typename CallbackBase< R, Args... >::CallbackBasePtr MakeCallback( R cb(Args...) ) { typename CallbackBase< R, Args... >::CallbackBasePtr p( new FunctionCallback< R, Args... >( cb )); return p; } 

Finally, you have other minor issues that need fixing, which Xeo outlined .

+7
source

Fixed some types-o and specialized MakeCallback for accepting function pointers. As GMan said, your MakeCallback template arguments are in an undetectable context.

 #include <memory> template< typename R, typename ... Args > class CallbackBase { public: typedef std::shared_ptr< CallbackBase< R, Args... > > CallbackPtr; virtual ~CallbackBase() { } virtual R Call( Args ... args) = 0; }; template< typename R, typename ... Args > class FunctionCallback : public CallbackBase< R, Args... > { public: typedef R (*funccb)(Args...); FunctionCallback( funccb cb_ ) : CallbackBase< R, Args... >(), cb( cb_ ) { } virtual ~FunctionCallback() { } virtual R Call(Args... args) { return cb( args... ); } private: funccb cb; }; template < typename R, typename ...Args > typename CallbackBase< R, Args... >::CallbackPtr MakeCallback( R (*cb)(Args...) ) { typename CallbackBase< R, Args... >::CallbackPtr p( new FunctionCallback< R, Args... >( cb ) ); return p; } bool Foo_1args( const int & t) { return true; } int main() { auto cbObj = MakeCallback( & Foo_1args ); } 

Update:

The C ++ standard defines a non-deducible context in 14.8.2.5 [temp.deduct.type], clauses 5-6. There is a bulleted list that I won’t require to fully understand. Marker for me:

Each time you see "::" after its template template, this template parameter is in an undetectable context, that is, it must be explicitly specified on the call site.

+4
source

Recall that I mentioned in the comments on other answers:

  • First, as @GMan says, your MakeCallback argument MakeCallback not derivable.
  • Secondly, your MakeCallback return MakeCallback was incorrect. It must be CallbackPtr , because CallbackBasePtr typedef does not exist. This will result in SFINAE , but does not consider your function as a possible function to call, even when the argument has been fixed.
  • Thirdly, your FunctionCallback constructor needs a funccb* pointer, and funccb already a (function-) pointer, so you will need to pass a pointer to a pointer function, for example. new FunctionCallback(&cb)
+4
source

It is better to use <functional> than to invent it ... It is also better to copy directly from your compiler implementation.

Generally, using fewer template options is also good.

But it is always tempting to solve these problems ... therefore, knowing what I am doing, but not looking right at it right now, this is how I would deal with it.

The code is just a Call functor will not be specialized for different types of functors, so it should be in the general case of the template.

To make minor adjustments to the overall template, it is best to use a feature class.

 template< typename F, typename = void > struct callback_traits { typedef F const &local_type; // fallback case: only keep a reference }; template< typename F > struct callback_traits< F, typename std::enable_if< // GCC 4.4 missing is_copy_constructible: std::is_constructible< F, F const& >::value >::type > { typedef F local_type; // better to keep a copy as this is a callback }; template< typename F > struct Callback { typedef typename callback_traits< F >::local_type local_type; local_type fn; Callback( local_type const &fn_in ) : fn( fn_in ) {} template< typename ... Args > typename std::result_of< local_type( Args ... ) >::type Call( Args ... a ) { return fn( a ... ); } }; 
+4
source

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


All Articles