What is wrong with this variadic templates example?
The base class is :
#include <memory>
namespace cb{
template&l开发者_Python百科t; 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 is this :
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 the 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 couldn't figure out how to fix.
So, what is wrong? And how to fix this example?
The problem might make sense with a simpler example. Try identifying 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 deduce what T
should be; it's not directly used anywhere. You'd have to explicitly provide it: foo<int>(5)
, or let it deduce it in some other 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, supplied to id
, result in id<T>::type
matching? There could be specializations, and the entire thing would be costly anyway, if possible.
Likewise, there's nothing the compiler has available to deduce 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 has outlined.
To recollect what I mentioned in the comments of the other answers:
- First, as @GMan says, your argument of
MakeCallback
was non-deducible. - Second, your return type of
MakeCallback
was wrong. It should beCallbackPtr
, as aCallbackBasePtr
typedef doesn't exist. This lead to SFINAE kicking in and not considering your function as a possible function to call even when the argument was fixed. - Third, your
FunctionCallback
constructor wanted afunccb*
pointer, whilefunccb
already is a (function-)pointer, so you would have to pass a pointer-to-function-pointer, eg.new FunctionCallback(&cb)
It's better to use <functional>
than to reinvent it… It's also better to copy directly from your compiler's implementation.
Generally, using fewer template parameters is a good thing, too.
But, it's always tempting to solve these problems… so, knowing what I do, but not looking directly at that right now, here is how I'd handle it.
The code to simply Call
a functor will not be specialized for the different kinds of functors, so it should be in the general template case.
To make minor adjustments to the general template, a traits class serves best.
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 ... ); }
};
Fixed some type-o's and specialized MakeCallback to accept function pointers. As GMan said, your template arguments to MakeCallback are in a non-deducible 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 non-deduced context in 14.8.2.5 [temp.deduct.type], paragraphs 5 - 6. There is a bulleted list there which I won't claim to fully understand. The marker for me is:
Any time you see "::" after your template parameter, that template parameter is in a non-deduced context, meaning it must be explicitly specified at the call site.
精彩评论