[英]Should I use dynamic_cast whenever for downcast?
I noticed that compiler will optimize out some dynamic_cast when the following operation is non-polymorphic, for example the following code:我注意到当以下操作是非多态时,编译器会优化一些 dynamic_cast,例如以下代码:
#include <iostream>
using namespace std;
struct A
{
virtual ~A() = default;
virtual int f()
{
return 1;
};
virtual int g() = 0;
};
struct B : A
{
int g() const
{
return 2;
}
int g() override
{
return 3;
}
int h()
{
return 4;
}
};
int main()
{
A* p = new B();
auto u = p->f();
auto v1 = static_cast<B*>(p)->f();
auto v2 = dynamic_cast<B*>(p)->f();
auto w = p->g();
auto x1 = static_cast<const B*>(p)->g();
auto x2 = dynamic_cast<B*>(p)->g();
auto x3 = dynamic_cast<const B*>(p)->g();
auto y = dynamic_cast<B*>(p)->h();
cout << u << v1 << v2 << w << x1 << x2 << x3 << y << endl;
delete p;
return 0;
}
There are only two dynamic_cast calls compiled with g++ -O2, that means that equals to static_cast, so should I always use dynamic_cast for downcast for no extra overhead to be considered?只有两个用 g++ -O2 编译的 dynamic_cast 调用,这意味着等于 static_cast,所以我是否应该始终使用 dynamic_cast 进行向下转换而不考虑额外的开销?
The main issue with dynamic_cast
is that they are very slow and complicated. dynamic_cast
的主要问题是它们非常缓慢和复杂。 Compilers can indeed optimize it when they are aware of the actual type in compile time but not always.当编译器知道编译时的实际类型时,编译器确实可以优化它,但并非总是如此。 Technically, your code should have 0 dynamic casts if compilers knew how to do it properly.从技术上讲,如果编译器知道如何正确执行,您的代码应该有 0 个动态转换。 So don't trust too much on the exact mechanisms of optimization that compilers use.所以不要太相信编译器使用的优化机制。
Certain parts of your code could've been optimized via abuse of undefined behavior.您的代码的某些部分可以通过滥用未定义的行为进行优化。 For example:例如:
dynamic_cast<B*>(p)->f();
// this is optimized instantly to p->f();
// if dynamic_cast returns nullptr it would be undefined behavior IIRC,
// so optimizer can assume that the cast is successful and it becomes equivalent to
// static_cast<B*>(p)->f() after optimization,
// regardless of whether p is actually of type B or not
In general, it is difficult to provide concrete methodology on dynamic casts, if performance allows it, always dynamic cast on down casting.一般来说,很难提供关于动态转换的具体方法,如果性能允许,总是在向下转换上进行动态转换。 Not doing so is possible undefined behavior and security leak.不这样做可能是未定义的行为和安全漏洞。
If you have no guarantee that the derived class is of this specific type - you have no choice but to use dynamic cast.如果您不能保证派生的 class 是这种特定类型 - 您别无选择,只能使用动态转换。
If you have a guarantee but there might be bugs in the code, consider making a static assert with dynamic cast inside, and using static cast in optimized versions.如果您有保证但代码中可能存在错误,请考虑使用内部动态强制转换的 static 断言,并在优化版本中使用 static 强制转换。
In fact, I don't see any effective difference in using static_cast
and dynamic_cast
here.事实上,我在这里没有看到使用static_cast
和dynamic_cast
的任何有效区别。 First, if you call a virtual function via a casted pointer, all the following calls will have the very same effect — they will trigger dynamic dispatching :首先,如果您通过强制转换的指针调用虚拟 function ,那么以下所有调用都将产生相同的效果——它们将触发动态调度:
auto v0 = p->f();
auto v1 = static_cast<B*>(p)->f();
auto v2 = dynamic_cast<B*>(p)->f();
dynamic_cast
might add some overhead, but the observable effect will be the same. dynamic_cast
可能会增加一些开销,但可观察到的效果是一样的。 Namely.即。 B::f
will be called (if it's overridden). B::f
将被调用(如果它被覆盖)。
As for non-virtual functions from a derived class, you need to cast:至于来自派生 class 的非虚拟函数,您需要强制转换:
auto x1 = static_cast<const B*>(p)->g();
auto x3 = dynamic_cast<const B*>(p)->g();
If those casts are invalid, you will get undefined behavior in both cases.如果这些强制转换无效,您将在这两种情况下得到未定义的行为。 If they are valid, there will be effectively the same.如果它们是有效的,那么实际上将是相同的。 Just again, with some additional overhead of dynamic_cast
.再说一次,还有一些额外的dynamic_cast
开销。
Note that, IMO, there is no dynamic_cast
optimized away in your program.请注意,IMO,您的程序中没有dynamic_cast
优化。 You can observe two dynamic_cast
calls in your assembly, and you have two different forms of dynamic_cast
in your code.您可以在程序集中观察到两个dynamic_cast
调用,并且您的代码中有两个不同的dynamic_cast
forms。 I assume a compiler is doing dynamic_cast<B*>
only once and uses the result 3 times.我假设编译器只执行一次dynamic_cast<B*>
并使用结果 3 次。
Note that you can bypass the dynamic dispatch by manually selecting which f
you want to call by the following syntax:请注意,您可以通过以下语法手动选择要调用的f
来绕过动态调度:
auto y1 = static_cast<B*>(p)->A::f();
auto y2 = static_cast<B*>(p)->B::f();
Or, with dynamic_cast
the very same way.或者,以同样的方式使用dynamic_cast
。
Live demo: https://wandbox.org/permlink/CZRLPWxHjSMk8dFK .现场演示: https://wandbox.org/permlink/CZRLPWxHjSMk8dFK 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.