一位同事最近向我展示了他在网上找到的一些代码。它似乎允许编译时间确定类型是否与另一种类型具有“是”关系。我认为这非常棒,但我不得不承认我对这实际上是如何运作一无所知。任何人都可以向我解释这个吗?
template<typename BaseT, typename DerivedT>
inline bool isRelated(const DerivedT&)
{
DerivedT derived();
char test(const BaseT&); // sizeof(test()) == sizeof(char)
char (&test(...))[2]; // sizeof(test()) == sizeof(char[2])
struct conversion
{
enum { exists = (sizeof(test(derived())) == sizeof(char)) };
};
return conversion::exists;
}
定义此功能后,您可以像这样使用它:
#include <iostream>
class base {};
class derived : public base {};
class unrelated {};
int main()
{
base b;
derived d;
unrelated u;
if( isRelated<base>( b ) )
std::cout << "b is related to base" << std::endl;
if( isRelated<base>( d ) )
std::cout << "d is related to base" << std::endl;
if( !isRelated<base>( u ) )
std::cout << "u is not related to base" << std::endl;
}
答案 0 :(得分:11)
它声明了两个名为test
的重载函数,一个采用Base
,一个采用任何(...)
,并返回不同的类型。
然后使用Derived
调用该函数并检查其返回类型的大小以查看调用的重载。 (它实际上使用返回Derived
的函数的返回值调用函数,以避免使用内存)
因为enum
是编译时常量,所以这一切都是在编译时在类型系统中完成的。由于函数最终不会在运行时被调用,因此它们没有任何实体并不重要。
答案 1 :(得分:6)
我不是C ++专家,但在我看来,重点是让编译器在test()
的两个重载之间做出决定。如果Derived
来自Base
,那么将使用第一个,返回char
,否则将使用第二个 - 返回char[2]
。 sizeof()
运算符然后确定发生了哪些操作并相应地设置conversion::exists
的值。
答案 2 :(得分:5)
它非常酷但实际上并不起作用,因为用户定义的转换优先于省略号匹配,而const引用可以绑定用户定义转换的临时结果。因此char*p; is_related<bool>(p);
将返回true
(在VS2010中测试)。
如果要真正测试继承关系,可以采用类似的方法,但使用指针而不是引用。
答案 3 :(得分:5)
你有什么理由不会使用这样的东西:
template<typename BaseT, typename DerivedT>
struct IsRelated
{
static DerivedT derived();
static char test(const BaseT&); // sizeof(test()) == sizeof(char)
static char (&test(...))[2]; // sizeof(test()) == sizeof(char[2])
enum { exists = (sizeof(test(derived())) == sizeof(char)) };
}
e.g:
IsRelated<Base, Derived>::exists
这样你就可以在编译时访问这些信息。
答案 4 :(得分:2)
顺便说一句,您可以使用__is_base_of
中引入的“type_traits”中的std::tr1
(MSCV 2008编译器具有内在支持)。
答案 5 :(得分:1)
原始代码将构造Derived的对象,它可能会带来意想不到的结果。以下工作可能是另一种选择:
template<typename BaseT, typename CheckT>
inline bool isDerived(const CheckT &t){
char test(const BaseT *t);
char (&test(...))[2];
return (sizeof(test(&t)) == sizeof(char) );
}