2010-04-13 66 views
16

一位同事最近向我展示了他在网上找到的一些代码。它似乎允许编译时确定一个类型是否与另一个类型有“是”关系。我认为这是完全可怕的,但我不得不承认,我对这种实际工作方式毫无头绪。任何人都可以向我解释这个吗?C++编译时间类型确定

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

这是相当该死的酷伏都教。 – zneak 2010-04-13 23:06:04

+0

+1令人敬畏的技巧。 – SLaks 2010-04-13 23:11:38

+7

如果您对这些东西感兴趣,请获取Alexandrescus *“Modern C++ Design”*的副本。 – 2010-04-13 23:12:10

回答

11

它声明了两个重载的函数,名为test,一个取值为Base,另一个取值为(...),返回不同的类型。

然后它调用函数Derived并检查其返回类型的大小以查看调用了哪个超载。 (它实际上调用函数的返回值,返回值为Derived,以避免使用内存)

因为enum是编译时常量,所有这些都是在编译时在类型系统内完成的。由于这些函数最终不会在运行时被调用,因此它们没有主体并不重要。

+0

'derived()'不是用于保存内存 - 无论如何sizeof()'表达式在编译时计算 - 这是一种不需要'Derived'是默认可构造的方法避免使用'test(Derived())')。 – 2010-04-13 23:37:25

+0

@gf:我的意思是与声明一个指向“Dervied”的指针相反。 – SLaks 2010-04-13 23:40:51

6

我没有C++高手,但它看起来对我来说,关键是让编译器之间作出选择两个超载test()。如果Derived来自Base那么将使用第一个,其将返回char,否则将使用第二个 - 其返回char[2]。然后sizeof()运营商确定发生了哪些情况并相应地设置了值conversion::exists

+0

true..its仅基于重载分辨率。 – mukeshkumar 2010-04-14 07:28:16

+0

不错的解释:) – 2013-07-02 10:02:36

5

这很酷,但它并不实际工作,因为用户定义的转换优先于省略号匹配,并且const引用可以绑定用户定义转换的临时结果。所以char*p; is_related<bool>(p);将返回true(在VS2010中测试)。

如果您想真正测试继承关系,可以采取类似的方法,但使用指针而不是引用。

2

顺便说一句,您可以使用std::tr1(MSCV 2008编译器对此具有固有支持)中引入的“type_traits”中的__is_base_of

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

例如为:

IsRelated<Base, Derived>::exists 

你必须在编译时获取信息的方式。

1

原始代码将构造派生的对象,它可能会带来意想不到的结果。下面的工作可能是一个替代选择:

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