5

我需要运行时多态性,所以我使用了dynamic_cast.
但是现在我遇到了两个问题——太慢dynamic_cast了!(向下滚动查看基准。)

长话短说,我最终以这种方式解决了这个问题,使用static_cast

struct Base
{
    virtual ~Base() { }
    virtual int type_id() const = 0;

    template<class T>
    T *as()
    { return this->type_id() == T::ID ? static_cast<T *>(this) : 0; }

    template<class T>
    T const *as() const
    { return this->type_id() == T::ID ? static_cast<T const *>(this) : 0; }
};

struct Derived : public Base
{
    enum { ID = __COUNTER__ };  // warning: can cause ABI incompatibility
    int type_id() const { return ID; }
};

int main()
{
    Base const &value = Derived();
    Derived const *p = value.as<Derived>();  // "static" dynamic_cast
}

但我肯定不是第一个遇到这个问题的人,所以我认为这可能值得一问:

与其想出像这样的自制解决方案,是否有一个众所周知的模式/库我可以在未来用来解决这个问题?


样本基准

要了解我在说什么,请尝试下面的代码 -dynamic_cast大约 比在我的机器上调用慢15 倍virtual(110 毫秒。对 1620 毫秒。下面的代码):

#include <cstdio>
#include <ctime>

struct Base { virtual unsigned vcalc(unsigned i) const { return i * i + 1; } };
struct Derived1 : public Base 
{ unsigned vcalc(unsigned i) const { return i * i + 2; } };
struct Derived2 : public Derived1
{ unsigned vcalc(unsigned i) const { return i * i + 3; } };

int main()
{
    Base const &foo = Derived2();
    size_t const COUNT = 50000000;
    {
        clock_t start = clock();
        unsigned n = 0;
        for (size_t i = 0; i < COUNT; i++)
            n = foo.vcalc(n);
        printf("virtual call: %d ms (result: %u)\n",
            (int)((clock() - start) * 1000 / CLOCKS_PER_SEC), n);
        fflush(stdout);
    }
    {
        clock_t start = clock();
        unsigned n = 0;
        for (size_t i = 0; i < COUNT; i++)
            n = dynamic_cast<Derived1 const &>(foo).vcalc(n);
        printf("virtual call after dynamic_cast: %d ms (result: %u)\n",
            (int)((clock() - start) * 1000 / CLOCKS_PER_SEC), n);
        fflush(stdout);
    }
    return 0;
}

当我简单地删除单词virtual并更改dynamic_cast为 时static_cast,我得到了 79 毫秒的运行时间——因此虚拟调用仅比静态调用慢约 25%!

4

2 回答 2

3

的大多数用法dynamic_cast可以替换为双重调度(又名访问者模式)。这相当于两个虚拟调用,根据您的基准测试,它仍然比dynamic_cast.

于 2012-09-10T01:48:36.173 回答
2

您可能对这种恒定时间实现感兴趣:http: //www.stroustrup.com/isorc2008.pdf

还要注意,许多向上转换可能会在特定约束下被简化——例如,如果你不使用多重继承,只使用浅继承,或者以其他方式保证该类型没有共同的祖先,一些评估可能会被短路并且穷举dynamic_cast不需要执行评估(由 提供)。

像往常一样,针对您的给定用例和实际的类层次结构,针对您的供应商的实现来分析您的实现。

于 2012-09-10T01:39:05.370 回答