模板和宏的组合可以提供以下功能:
- 如果未在基类上声明类似的函数,则静态断言。
- 在派生类中声明函数。
使用这种方法,宏将定义一个类型特征模板,用于测试基类上特定成员函数的存在。当基类具有指定的成员函数时,特征的value
成员将是true
. 然后value
在静态断言中使用类型特征的 。
#define TRAITNAME_HELPER( PREFIX, FN, LN ) PREFIX ## FN ## LN
#define TRAITNAME( FN, LN ) TRAITNAME_HELPER( has_fn, FN, LN )
#define OVERRIDE_IMPL( CLASS, RETURN_TYPE, FN, ARG_TYPES, LN ) \
/* Type trait used to determine if \
* RETURN_TYPE T::FN( ARG_TYPES ) exists. */ \
template < typename T > \
struct TRAITNAME( FN, LN ) \
{ \
/* Type that expects a value for the specific type. For \
* example, type_check< int, 4 >. */ \
template < typename U, U > struct type_check {}; \
\
/* Use type_check expect a specific \
* pointer-to-member-function on T. */ \
template < typename U > \
static std::true_type \
check( type_check< RETURN_TYPE (T::*)ARG_TYPES, \
&U::FN >* = 0 ); \
\
template < typename U > \
static std::false_type check( ... ); \
\
/* Determine which check function was resolved for T. */ \
typedef decltype( check< T >( 0 ) ) type; \
static constexpr decltype(type::value) value = type::value; \
}; \
static_assert( TRAITNAME( FN, LN )< CLASS >::value, \
"" #RETURN_TYPE " " #FN #ARG_TYPES \
" is not defined in " #CLASS "." ); \
RETURN_TYPE FN ARG_TYPES
#define OVERRIDE( CLASS, RETURN_TYPE, FN, ARG_TYPES ) \
OVERRIDE_IMPL( CLASS, RETURN_TYPE, FN, ARG_TYPES, __LINE__ )
- OVERRIDE_IMPL 宏末尾缺少分号允许在类中声明或定义成员函数。
- 需要额外级别的宏来支持重载方法。它
__LINE__
用于创建独特的类型特征。
使用以下Base
课程:
template < typename TDerived >
struct Base
{
bool CheckSomeFlag();
bool CheckSomeFlag(int, int);
};
如果Dervied
类定义为:
struct Derived : public Base< Derived >
{
OVERRIDE( Base< Derived >, bool, CheckSomeflag, () );
};
然后编译失败并出现以下错误(demo):
error: static assertion failed: "bool CheckSomeflag() is not defined in Base< Derived >."
但是,当类型正确时,它将编译,如下所示:
struct Derived : public Base< Derived >
{
OVERRIDE( Base< Derived >, bool, CheckSomeFlag, () );
OVERRIDE( Base< Derived >, bool, CheckSomeFlag, (int a, int b) )
{
return ( a > b );
}
};
bool Derived::CheckSomeFlag() { return true; }
这种方法很快就会想到两个缺点:
- 返回类型必须完全匹配。如果没有一些额外的工作,这可以防止使用协变返回类型。但是,这可能是 CRTP 模式的理想行为。
- 宏语法掩盖了函数类型。例如,不能
bool(int,int)
用作返回 abool
并具有两个int
参数的函数的类型,而是必须将其作为两个单独的参数传递给宏( ..., bool, ..., (int, int) )
。这可以通过更改宏期望的参数顺序来缓解,但我选择了与函数的正常声明相匹配的顺序:return-type identifier(args)
.