这个可变模板的例子有什么问题?
What is wrong with this variadic templates example?
基类为:
#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
匹配的呢?可能会有专门化,如果可能的话,整个事情无论如何都会很昂贵。
同样,编译器也无法推断出R
和Args
。相反,您应该这样做:
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 ,意味着它必须是在调用站点显式指定。
- 警告处理为错误这里有什么问题
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 当我尝试添加 2 个大字符串时,我无法弄清楚出了什么问题
- 违反const正确性:我应该现实地期待什么问题
- 这个带有模板<类 Vector 的C++代码片段有什么问题>
- 我的逻辑反转字符串中的元音有什么问题?
- 需要以下代码的帮助,下面的代码有什么问题
- 常量公共成员有什么问题?
- 以下代码中的函数模板有什么问题?
- 这个返回元素位置的基于循环的函数有什么问题?
- creat_list2功能有什么问题?
- 格式说明符C++有什么问题
- 任何人都可以告诉我我的 C++ 代码出了什么问题?
- 从 argv[1] 转换为字符 * 字符串后有什么问题?
- 我的堆栈和库存清单程序的结构有什么问题?
- 此工厂功能有什么问题?
- 以下 C++ 代码有什么问题?
- 数组为此合并排序函数提供了正确的输出,但向量给出了不正确的输出.出了什么问题?
- reinterpret_cast,只读访问,简单的可复制类型,会出什么问题?
- 它解决了什么问题,对于非真空初始化,生命周期在初始化之前就开始了