我们有一个子项目'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
仅适用于多态类)。
等待你的意见。
提前致谢,
答案 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()
,而不是*t
。 T()
创建 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)
一个人可以利用以下事实:
dynamic_cast
在编译时失败。以便可以与SFINAE一起使用。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
方法似乎更好......除非它因某些原因不起作用。想法?