基于编译时类型的调度
遵循"现代C ++设计"的技术,我正在实现具有各种编译时优化功能的持久性库。如果该变量派生自给定的类,我希望能够将函数分派给模板成员变量:
template<class T, template <class> class Manager = DefaultManager> class Data { private: T *data_; public: void Dispatch() { if(SUPERSUBCLASS(Container, T)) { data_->IKnowThisIsHere(); } else { Manager<T>::SomeGenericFunction(data_); } } }
其中SUPERSUBCLASS是用于确定对象继承的编译时宏。当然,这在所有T确实要从Container继承的情况下都是失败的(或者T是固有类型等),因为编译器正确地抱怨IKnowThisIsHere()不是数据成员,即使永远不会遵循此代码路径,如下所示,经过T = int的预处理:
private: int *data_; public: void Dispatch() { if(false) { data_->IKnowThisIsHere();
编译器显然会抱怨此代码,即使它永远不会执行。使用dynamic_cast的建议也不起作用,因为再次在编译时尝试类型转换是不可能的(例如,使用T = double,std :: string):
void Dispatch() { if(false) { dynamic_cast<Container*>(data_)->IKnowThisIsHere(); error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, DefaultManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class) error: cannot dynamic_cast '((const Data<std::string, DefaultManager>*)this)->Da<sttad::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic)
我真的需要模拟(或者说服!),如果T确实从Container继承,则让编译器发出一组代码,如果不是,则发出另一组代码。
有什么建议?
解决方案
Boost特质适用于:is_base_of
查看boost模板元编程库。另外,根据我们要完成的工作,请查看boost序列化库,因为它可能已经具有所需的功能。
我出于教育的好奇心而对"从第一原理"来做这件事很感兴趣。但是,我将看一下Boost库。
无论如何,我不认为is_base_of与SUPERSUBCLASS宏完全一样。
不幸的是,我也经历过(也是一个运行时调用;))编译器抱怨我们是否以与以前类似的方式传入非多态或者类类型:
error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, RawManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class)
或者
error: cannot dynamic_cast '((const Data<std::string, DefaultRawManager>*)this)->Data<std::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic)
如Alexandrescu在他的书" Modern C ++ Design"中所建议的那样,重载对于实现编译时分派很有用。
我们可以使用这样的类在编译时将布尔值或者整数转换为类型:
template <bool n> struct int2type { enum { value = n}; };
以下源代码显示了可能的应用程序:
#include <iostream> #define MACRO() true // <- macro used to dispatch template <bool n> struct int2type { enum { value = n }; }; void method(int2type<false>) { std::cout << __PRETTY_FUNCTION__ << std::endl; } void method(int2type<true>) { std::cout << __PRETTY_FUNCTION__ << std::endl; } int main(int argc, char *argv[]) { // MACRO() determines which function to call // method( int2type<MACRO()>()); return 0; }
当然真正使这项工作真正起作用的是MACRO()或者作为元函数的更好实现
我们需要一种编译时if
。然后根据情况为true调用函数。这样,编译器就不会偶然发现无法编译的代码(因为它安全地存储在永不实例化的另一个函数模板中)。
有几种实现这种编译时" if"的方法。最常见的是采用SFINAE习惯用法:替换失败不是错误。 Boost的is_base_of
实际上是该惯用语的一个实例。为了正确地使用它,我们不会在if
表达式中编写它,而是将其用作函数的返回类型。
未经测试的代码:
void Dispatch() { myfunc(data_); } private: // EDIT: disabled the default case where the specialisation matched template <typename U> typename enable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) { data_->IKnowThisIsHere(); } template <typename U> typename disable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) { // default case Manager<U>::SomeGenericFunction(data_); }