测试一个类是否是多态的

时间:2009-07-10 06:08:19

标签: c++ templates polymorphism typeid

我们有一个子项目'commonUtils',它包含许多在父项目中使用的通用代码片段。 我看到的一个有趣的东西是: -

/*********************************************************************
If T is polymorphic, the compiler is required to evaluate the typeid 
stuff at runtime, and answer will be true.  If T is non-polymorphic, 
the compiler is required to evaluate the typeid stuff at compile time, 
whence answer will remain false
*********************************************************************/
template <class T> 
bool isPolymorphic() { 
   bool answer=false; 
   typeid(answer=true,T()); 
   return answer; 
}

我相信评论并认为这是一个非常有趣的模板,尽管它不是 在整个项目中使用。为了好奇,我试着像这样使用它......

class PolyBase {
public:
   virtual ~PolyBase(){}
};

class NPolyBase {
public:
   ~NPolyBase(){}
};


if (isPolymorphic<PolyBase>())
  std::cout<<"PolyBase = Polymorphic\n";
if (isPolymorphic<NPolyBase>())
  std::cout<<"NPolyBase = Also Polymorphic\n";

但这些都没有回归真实。 MSVC 2005没有发出警告,但是Comeau警告typeid表达没有任何效果。 C ++标准中的5.2.8节没有说明评论所说的内容,即在编译时为非多态类型评估typeid,在运行时评估多态类型。

1)所以我猜这个评论是误导/明白错误,或者因为这段代码的作者是一位资深的C ++程序员,我错过了什么?

2)OTOH,我想知道我们是否可以使用某种技术测试一个类是否是多态的(至少有一个虚函数)?

3)什么时候想知道某个类是否是多态的?胡乱猜测;使用dynamic_cast<void*>(T)获取类的起始地址(因为dynamic_cast仅适用于多态类)。

等待你的意见。

提前致谢,

5 个答案:

答案 0 :(得分:9)

我无法想象如何使用typeid来检查该类型是多态的。它甚至不能用来断言它,因为typeid可以在任何类型上工作。 Boost有一个实现here。至于为什么可能有必要 - 我知道的一个案例是Boost.Serialization库。如果要保存非多态类型,则只需保存即可。如果保存多态,则必须使用typeid获取其动态类型,然后为该类型调用序列化方法(在某些表中查找)。

更新:看来我确实错了。考虑这个变种:

template <class T> 
bool isPolymorphic() { 
    bool answer=false;
    T *t = new T();
    typeid(answer=true,*t); 
    delete t;
    return answer; 
}

这实际上正如名称所示,确切地说就是原始代码段中的每条评论。如果“不指定多态类类型的左值”(std 3.2 / 2),则不评估typeid内的表达式。因此,在上面的情况中,如果T不是多态的,则不评估typeid表达式。如果T是多态的,则* t确实是多态类型的左值,因此必须对整个表达式进行求值。

现在,你原来的例子还是错的:-)。它使用了T(),而不是*tT()创建 rvalue (标准3.10 / 6)。因此,它仍然会产生一个不是“多态类的左值”的表达式。

这是一个相当有趣的技巧。另一方面,它的实际价值有些限制 - 因为虽然boost :: is_polymorphic为你提供了一个编译时常量,但是这个给你一个运行时值,所以你不能为多态和非多态类型实例化不同的代码

答案 1 :(得分:3)



class PolyBase {
public:   
    virtual ~PolyBase(){}
};

class NPolyBase {
public:
    ~NPolyBase(){}
};

template<class T>
struct IsPolymorphic
{
    struct Derived : T {
        virtual ~Derived();
    };
    enum  { value = sizeof(Derived)==sizeof(T) };
};


void ff()
{
    std::cout << IsPolymorphic<PolyBase >::value << std::endl;
    std::cout << IsPolymorphic<NPolyBase>::value << std::endl;
}

答案 2 :(得分:1)

一个人可以利用以下事实:

    如果参数不是多态类,则
  1. dynamic_cast在编译时失败。以便可以与SFINAE一起使用。
  2. dynamic_cast<void*>是有效的强制类型转换,它返回 complete 多态对象的地址。

因此,在C ++ 11中:

#include <iostream>
#include <type_traits>

template<class T>
auto is_polymorphic2_test(T* p) -> decltype(dynamic_cast<void*>(p), std::true_type{});

template<class T>
auto is_polymorphic2_test(...) -> std::false_type;

template<class T>
using is_polymorphic2 = decltype(is_polymorphic2_test<T>(static_cast<T*>(0)));

struct A {};
struct B { virtual ~B(); };

int main() {
    std::cout << is_polymorphic2<A>::value << '\n'; // Outputs 0.
    std::cout << is_polymorphic2<B>::value << '\n'; // Outputs 1.
}

答案 3 :(得分:0)

自C ++ 11起,它现在在<type_traits>标头中以std::is_polymorphic的形式提供。可以这样使用:

struct PolyBase {
  virtual ~PolyBase() {}
};

struct NPolyBase { 
  ~NPolyBase() {}
};

if (std::is_polymorphic<PolyBase>::value)
  std::cout << "PolyBase = Polymorphic\n";
if (std::is_polymorphic<NPolyBase>::value)
  std::cout << "NPolyBase = Also Polymorphic\n";

这仅显示“ PolyBase =多态”。

答案 4 :(得分:-1)

我在这里有点困惑,我希望能得到一些关于这个答案的评论,解释我所缺少的内容。

当然,如果你想知道一个类是否是多态的,你所要做的就是询问它是否支持dynamic_cast,不是吗?

template<class T, class> struct is_polymorphic_impl   : false_type {};
template<class T> struct is_polymorphic_impl
    <T, decltype(dynamic_cast<void*>(declval<T*>()))> : true_type {};

template<class T> struct is_polymorphic :
    is_polymorphic_impl<remove_cv_t<T>, void*> {};

有人能指出这个实现中的缺陷吗?我想在过去的某个时刻必须有一个,或者必定是一个,因为the Boost documentation继续声称is_polymorphic“无法在C ++语言中移植”。

但“便携”是一个狡猾的词,对吧?也许他们只是暗指MSVC不支持表达式-SFINAE,或者某些方言如嵌入式C ++不支持dynamic_cast。也许当他们说“C ++语言”时,他们的意思是“C ++语言的最低公分母子集”。但我有一种唠叨的怀疑,也许他们的意思是他们所说的,而我是那个错过了什么的人。

OP中的typeid方法(后来使用左值而不是右值的答案修改)看起来也不错,但当然它不是constexpr,它需要实际构建T,这可能是超级昂贵的。所以这种dynamic_cast方法似乎更好......除非它因某些原因不起作用。想法?