这个可变参数模板示例有什么问题?

时间:2011-04-13 22:58:39

标签: c++ c++11 variadic-templates

基类是:

#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

派生类就是这样:

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

创建函数:

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

示例:

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

我一直收到这个错误:

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

我试图改变它,但我无法弄清楚如何解决。

那么,出了什么问题?以及如何修复这个例子?

4 个答案:

答案 0 :(得分:7)

使用更简单的示例可能会有问题。尝试在此处确定问题:

template <typename T>
struct id { typedef T type; };

template <typename T>
void foo(typename id<T>::type x);

foo(5); // error

问题是编译器无法推断T应该是什么;它不是直接在任何地方使用。您必须明确提供它:foo<int>(5),或者以其他方式推断它:

template <typename T>
void foo(typename id<T>::type x, T y);

foo(5, 7); // okay, T is int because 7 is int

这是有道理的:编译器如何确定提供给T的哪些id会导致id<T>::type匹配?可能会有专业化,如果可能的话,整个事情都会很昂贵。


同样,编译器没有任何东西可用于推断RArgs。相反,你应该这样做:

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;
}

最后,您还有其他需要修复的小问题,which Xeo has outlined

答案 1 :(得分:4)

修复了一些类型o和专用的MakeCallback来接受函数指针。正如GMan所说,MakeCallback的模板参数处于不可推断的上下文中。

#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 );
}

<强>更新

C ++标准定义了14.8.2.5 [temp.deduct.type]中的非推导上下文,第5 - 6段。有一个项目符号列表,我不会声称完全理解。我的标记是:

  

任何时候你看到“::”之后   模板参数,该模板   参数在非推导中   上下文,意思是必须的   在呼叫站点明确指定。

答案 2 :(得分:4)

回想一下我在其他答案的评论中提到的内容:

  • 首先,正如@GMan所说,你MakeCallback的论点是不可推论的。
  • 其次,您的MakeCallback返回类型错误。它应该是CallbackPtr,因为CallbackBasePtr typedef不存在。这导致SFINAE开始并且不考虑你的函数作为一个可能的函数来调用即使参数被修复。
  • 第三,您的FunctionCallback构造函数需要funccb* 指针,而funccb已经是(函数)指针,因此您必须传递一个指向函数指针的指针,例如。 new FunctionCallback(&cb)

答案 3 :(得分:4)

最好使用<functional>而不是重新发明它......最好直接从编译器的实现中复制。

通常,使用较少的模板参数也是一件好事。

但是,解决这些问题总是诱人的......所以,知道我做了什么,但现在不直接看,这就是我如何处理它。

简单地Call仿函数的代码不会专门用于不同类型的仿函数,因此它应该在通用模板的情况下。

要对常规模板进行微调,最好使用特征类。

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 ... ); }
};