10

我们有一个子项目“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";

但是这些都没有返回true。MSVC 2005 未给出警告,但 Comeau 警告 typeid 表达式无效。C++ 标准中的第 5.2.8 节没有像注释所说的那样说任何东西,即 typeid 在编译时对非多态类型进行评估,在运行时对多态类型进行评估。

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

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

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

等待您的意见。

提前致谢,

4

5 回答 5

9

我无法想象任何可能的方式如何使用该 typeid 来检查该类型是多态的。它甚至不能用来断言它是,因为 typeid 将适用于任何类型。Boost 在这里有一个实现。至于为什么它可能是必要的——我知道的一种情况是 Boost.Serialization 库。如果您要保存非多态类型,那么您可以保存它。如果保存多态类型,则必须使用 typeid 获取其动态类型,然后为该类型调用序列化方法(在某个表中查找)。

更新:看来我实际上是错的。考虑这个变体:

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

顾名思义,这实际上确实有效,完全符合您原始代码片段中的评论。如果 typeid 内的表达式“没有指定多态类类型的左值”(std 3.2/2),则不会评估它。因此,在上述情况下,如果 T 不是多态的,则不会计算 typeid 表达式。如果 T 是多态的,那么 *t 确实是多态类型的左值,因此必须评估整个表达式。

现在,你原来的例子仍然是错误的:-)。它用过T(),没有*t。并T()创建右值(std 3.10/6)。因此,它仍然产生一个不是“多态类的左值”的表达式。

这是相当有趣的把戏。另一方面,它的实际价值有些有限——因为 boost::is_polymorphic 给你一个编译时常量,这个给你一个运行时值,所以你不能为多态和非多态类型实例化不同的代码.

于 2009-07-10T07:03:55.187 回答
4

从 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 = Polymorphic”。

于 2018-12-11T03:50:42.333 回答
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;
}

于 2009-07-10T06:57:49.833 回答
1

可以使用以下事实:

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

因此,在 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.
}
于 2019-07-30T10:03:21.373 回答
0

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

当然如果你想知道一个类是否是多态的,你所要做的就是问它是否支持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*> {};

谁能指出这个实现中的一个缺陷?我想一定有一个,或者在过去的某个时候一定有一个,因为Boost 文档继续声称is_polymorphic“不能在 C++ 语言中可移植地实现”。

但是“便携式”是一个狡猾的词,对吧?也许他们只是在暗示 MSVC 不支持表达式-SFINAE,或者嵌入式 C++ 等某些方言不支持dynamic_cast. 也许当他们说“C++ 语言”时,他们的意思是“C++ 语言的最低公分母子集”。但我有一种挥之不去的怀疑,也许他们说的是真的,而我就是那个遗漏了什么的人。

OP 中的typeid方法(由稍后的答案修改为使用左值而不是右值)似乎也很好,但当然它不是 constexpr 并且它需要实际构造 a T,这可能非常昂贵。所以这种dynamic_cast方法似乎更好......除非由于某种原因它不起作用。想法?

于 2017-07-12T21:06:21.543 回答