C++ 检查类是否派生自特定类(编译、运行时两个答案都可用)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18099241/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Check if class is derived from a specific class (compile, runtime both answers available)
提问by khajvah
It is easier to explain on an example so,
用一个例子来解释更容易,所以,
class base {
//....
}
class derived1 : public base {
//...
}
In my library, there is a pointer of base class. The user of the library have to make classes derived from either base or derived1 and assign pointer to that class.
在我的库中,有一个基类指针。库的用户必须从基类或派生 1 派生类,并将指针分配给该类。
How can I check what class is user-defined class derived from?
如何检查用户定义的类是从哪个类派生的?
回答by Cassio Neri
I have some remarks on the proposed compile-time x runtime solutions. In addition to when they are evaluated, is_base_of
and dynamic_cast
have different requirements and their answers can be different.
我对建议的编译时 x 运行时解决方案有一些评论。除了当他们评估,is_base_of
并dynamic_cast
有不同的要求和他们的答案可以是不同的。
(1)First of all (as pointed out by others) to use dynamic_cast
, base and derived classes must be polymorphic (must have at least one virtual
method). is_base_of
doesn't require the types to be polymorphic.
(1)首先(正如其他人指出的)要使用dynamic_cast
,基类和派生类必须是多态的(必须至少有一个virtual
方法)。is_base_of
不需要类型是多态的。
(2)The operands of is_base_of
are both types whereas dynamic_cast
needs a type (inside < >
) and an object (inside ( )
).
(2)的操作数is_base_of
都是类型,而dynamic_cast
需要一个类型 (inside < >
) 和一个对象 (inside ( )
)。
(3)dynamic_cast
and is_base_of
can give different answers (or one can compile while the other doesn't) depending on the type of inheritance (public
vs protected
or private
). For instance consider:
(3)dynamic_cast
并且is_base_of
可以根据继承的类型(public
vsprotected
或private
)给出不同的答案(或者一个可以编译而另一个不编译)。例如考虑:
struct B { virtual ~B() {} }; // polymorphic, so it can be used in a dynamic_cast
struct D1 : public B {}; // polymorphic by (public) inheritance
struct D2 : private B {}; // polymorphic by (private) inheritance
D1 d1;
D2 d2;
We have
我们有
static_assert(std::is_base_of<B, D1>::value, "");
static_assert(std::is_base_of<B, D2>::value, "");
assert(dynamic_cast<B*>(&d1));
assert(!dynamic_cast<B*>(&d2)); // Notice the negation.
Actually the last line yields a compiler error in GCC (error: 'B' is an inaccessible base of 'D2'
). VS2010 does compile it (yielding just a warning similar to GCC's error message).
实际上,最后一行在 GCC ( error: 'B' is an inaccessible base of 'D2'
) 中产生了编译器错误。VS2010 确实编译了它(仅产生类似于 GCC 错误消息的警告)。
(4)The requirements on the classes being polymorphic can be relaxed through an exception handling trick. Consider:
(4)可以通过异常处理技巧放宽对多态类的要求。考虑:
struct B { }; // not polymorphic
struct D1 : public B {}; // not polymorphic
struct D2 : private B {}; // not polymorphic
D1 d1;
D2 d2;
template <typename B, typename D>
const B* is_unambiguous_public_base_of(const D* obj) {
try {
throw obj;
}
catch (const B* pb) {
return pb;
}
catch (...) {
}
return nullptr;
}
Then we have
然后我们有
static_assert(std::is_base_of<B, D1>::value, "");
static_assert(std::is_base_of<B, D2>::value, "");
assert((is_unambiguous_public_base_of<B>(&d1)));
assert(!(is_unambiguous_public_base_of<B>(&d2))); // Notice the negation.
It's worth mentionning that is_unambiguous_public_base_of
is far slower than dynamic_cast
and (this became more obvious after the renaming mentioned in the update below) always returns a nullptr
for downcasts:
值得一提的is_unambiguous_public_base_of
是,它比dynamic_cast
and (在下面的更新中提到的重命名后变得更加明显)总是返回 a nullptr
for downcasts :
B* b1 = &d1;
assert(dynamic_cast<D1*>(b1)); // Requires D1 and B to be polymorphic.
assert(!(is_unambiguous_public_base_of<D1>(b1))); // Notice the negation.
A bit outdated reference on this trick is here.
关于这个技巧的一个有点过时的参考是here。
Disclaimer: the implementation of is_unambiguous_public_base_of
above is just a draft to make the point and it doesn't handle const
and volatile
qualifications properly.
免责声明:is_unambiguous_public_base_of
以上的实施只是一个草稿,并没有妥善处理const
和volatile
资格。
Update: In a previous version of this post is_unambiguous_public_base_of
was named my_dynamic_cast
and this was a source of confusion. So I renamed it to a more meaningful name. (Thanks to Jan Herrmann.)
更新:在这篇文章的前一个版本中is_unambiguous_public_base_of
被命名my_dynamic_cast
,这是一个混乱的来源。所以我把它改成了一个更有意义的名字。(感谢 Jan Herrmann。)
回答by sashoalm
You could use dynamic_cast.
您可以使用dynamic_cast。
if (dynamic_cast<DerivedClass*>(ptr)) {
std::cout << "Class is derived from DerivedClass.";
}
回答by juanchopanza
Check if class is derived from a specific class (compile time)
检查类是否派生自特定类(编译时)
You can use std::is_base_of
:
您可以使用std::is_base_of
:
#include <type_traits>
....
const bool isBase = std::is_base_of<base, TheOtherClass>::value;
isBase
is true if TheOtherClass
is derived from base
.
isBase
如果TheOtherClass
从 派生,则为真base
。
回答by Jan Herrmann
I think the answer to this question is verydifficult. Of course there is std::is_base_of
and dynamic_cast
. Both provide you with some very limited information. The third option is function overloading. With all of those techniques you can choose a special path in your code which should be executed.
我认为这个问题的答案是非常困难的。当然有std::is_base_of
和dynamic_cast
。两者都为您提供了一些非常有限的信息。第三个选项是函数重载。使用所有这些技术,您可以在代码中选择一个应该执行的特殊路径。
std::is_base_of
can be interpreted in a boolean context and it is derived from either std::true_type
or std::false_type
. This fact makes it possible to use it as paramter for a function and use compile time polymorphism via function overloading. This first example shows how to use it in a boolean context, but you don't have any further specific type information. So compilation will fail in most circumstances (see here for a further description):
std::is_base_of
可以在布尔上下文中解释,它来自std::true_type
或std::false_type
。这一事实使得将其用作函数的参数并通过函数重载使用编译时多态性成为可能。第一个示例展示了如何在布尔上下文中使用它,但您没有任何进一步的特定类型信息。因此在大多数情况下编译会失败(请参阅此处了解更多说明):
template<class T>
void do_it1(T const& t) {
if (std::is_base_of<T,derived1>::value) {
// we have a derived1
} else {
// we have a base
}
}
The second version is simple function overloading. Here compile time polymorphism is used but all runtime type information is lost (except virtual functions and dynamic_cast
are used):
第二个版本是简单的函数重载。这里使用了编译时多态性,但所有运行时类型信息都丢失了(使用虚函数除外dynamic_cast
):
void do_it2(Base const& b) {
// we have a base your algorithm for base here
}
void do_it2(Derived2 const& d) {
// Derived algorithm here
}
Now the third version combines both:
现在第三个版本结合了两者:
template<class T>
void do_it3_impl(T const& t, std::true_type) {
// here t will be of a type derived from derived1
}
template<class T>
void do_it3_impl(T const& t,std::false_type) {
// here t will be of type not derived from derived1
}
template<class T>
void do_it_3(T const& t) {
do_it3_impl(t, std::is_base_of<T,derived1>()); // here we forward to our impl
}
The third variant is normaly used for header only libraries which don't use runtime poylmorphism (search for std::advance
for an excample).
第三个变体通常用于不使用运行时多态性的仅头文件库(搜索std::advance
示例)。
Now to runtime polymorphism. Here you have the dynaminc_cast
variant:
现在到运行时多态性。在这里你有dynaminc_cast
变体:
void do_it4(Base const* ptr)
if (derived1 const* obj = dynamic_cast<derived*>(ptr)) {
// here we have obj with type derived1*
} else {
// here we have only base
}
If this variant is not fast enough you can implement your onw cast to derived1:
如果此变体不够快,您可以将 onw 强制转换为衍生 1:
class derived1;
class base {
// as above
public:
virtual derived1 const* to_derived1() const {
return 0;
}
};
class derived1
: public base
{
// ...
virtual derived1 const* to_derived1() const {
return this;
}
};
void do_it5(Base const* ptr)
if (derived1 const* obj = ptr->to_derived1() {
// here we have obj with type derived1*
} else {
// here we have only base
}
This is fast but it scales very well for only very few(approximately 1) derived classes.
这很快,但它仅适用于极少数(大约 1 个)派生类。
Last but not least you should think about your class design and deside what methods to make virtual and implement in base
, derived1
or other classes. You should definitly look for strategy pattern.
最后但并非最不重要,你应该想想自己的一流的设计和deside什么让虚拟的方法和执行base
,derived1
或其他类。您绝对应该寻找策略模式。
回答by Frodo
The compiler will only accept pointers to classes derived from your base class if your library functions takes pointers to the base class. My answer is with a classical approach type safety will handle it. To my experience that kind of type checking is enough. Having 25 years experience in the industry I question the need to do this check. Maybe such a fundamental question is not welcome? I would like to see the justification to having the need to do this kind of upcast. I never have to do that. The opposite, i.e. a down cast I need quite frequently.
如果您的库函数采用指向基类的指针,编译器将只接受指向从基类派生的类的指针。我的答案是使用经典方法类型安全来处理它。根据我的经验,那种类型检查就足够了。拥有 25 年的行业经验,我质疑是否需要进行此检查。也许这样一个基本问题不受欢迎?我想看看有必要做这种向上的理由。我从来不必那样做。相反,即我经常需要的向下转换。
回答by Iosif Murariu
You can do this in several ways. The most common ones as the others have pointed out are dynamic_cast<>and std::is_base_of. The latter is used at compile time, while dynamic_cast<>
can be used at runtime.
HOWEVER, dynamic_cast<>
will only work if your source class is polymorphic(i.e. has at least one virtual function - it can be a method or its destructor). If not, the compiler will trigger an error.
您可以通过多种方式执行此操作。正如其他人指出的那样,最常见的是dynamic_cast<>和std::is_base_of。后者在编译时使用,而dynamic_cast<>
可以在运行时使用。
然而,dynamic_cast<>
只有当你的源类是多态的(即至少有一个虚函数——它可以是一个方法或其析构函数)时,才会工作。如果没有,编译器将触发错误。