开发者

What is wrong with this variadic templates example?

开发者 https://www.devze.com 2023-02-24 06:21 出处:网络
The base class is : #include <memory> namespace cb{ template&l开发者_Python百科t; typename R, typename ... Args >

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 be CallbackPtr, as a CallbackBasePtr 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 a funccb* pointer, while funccb 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消