这个可变模板的例子有什么问题?

What is wrong with this variadic templates example?

本文关键字:什么 问题      更新时间:2023-10-16

基类为:

#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>’

我试着改变它,但我不知道如何修复。

那么,怎么了?如何修正这个例子呢?

用一个更简单的例子来说明这个问题。试着在这里找出问题:

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

这就说得通了:编译器是如何判断给id提供哪些T会导致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;
}

最后,您还有其他需要修复的小问题,Xeo已经概述了这些问题。

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

  • 首先,正如@GMan所说,你关于MakeCallback的论点是不可扣除的。
  • 第二,你的MakeCallback的返回类型是错误的。它应该是CallbackPtr,因为CallbackBasePtr类型定义不存在。这将导致SFINAE启动,并且即使在参数固定时也不考虑将函数作为可能调用的函数。第三,你的FunctionCallback构造函数想要一个funccb* 指针,而funccb已经是一个(函数-)指针,所以你必须传递一个指针到函数指针,例如。new FunctionCallback(&cb)

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

一般来说,使用更少的模板形参也是件好事。

但是,解决这些问题总是很诱人,所以,知道我在做什么,但现在没有直接看,下面是我如何处理它。

简单地Call一个函子的代码不会针对不同类型的函子进行专门化,因此它应该在一般模板的情况下。

如果要对通用模板进行微小的调整,最好使用traits类。

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

修正了一些type- 0和专门的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. deduction .type]第5 - 6段中定义了非演绎上下文。这里有一个项目列表,我不会说我完全理解。我的标记是:

任何时候你看到"::模板参数,那个模板参数在非推导式中Context ,意味着它必须是在调用站点显式指定。