2

我正在读一本关于元编程的书,并且有关于 Trampolines 的分裂:

struct generic_t
{
    void* obj;
    void(*del)(void*);
};
template <typename T> // outer template parameter
generic_t copy_to_generic(const T& value)
{
    struct local_cast // local class
    {
        static void destroy(void* p) // void*-based interface
        {
            delete static_cast<T*>(p); // static type knowledge
        }
    };
    generic_t p;
    p.obj = new T(value); // information loss: copy T* to void*
    p.del = &local_cast::destroy;
    return p;
}

我完全理解它是如何工作的,但我不知道它的应用是什么!你通常在哪里使用这种技术?有人知道吗?谢谢 :)

4

1 回答 1

2

我在程序的很多地方都使用它。我喜欢这种方法的一件事是你可以保存一个不相关类型的列表。例如,我见过很多看起来像这样的代码:

struct Abstract { virtual ~Abstract() = default; };

template<typename P>
struct AbstractHandler : Abstract {
    virtual void handle(P) = 0;
};

template<typename P, typename H>
struct Handler : AbstractHandler<P>, private H {
    void handle(P p) override {
        H::handle(p);
    }
};

struct Test1 {};

struct Test1Handler {
    void handle(Test1) {}
};

struct Test2 {};

struct Test2Handler {
    void handle(Test2) {}
};

int main() {
    std::vector<std::unique_ptr<Abstract>> handlers;

    handlers.emplace_back(std::make_unique<Handler<Test1, Test1Handler>>());
    handlers.emplace_back(std::make_unique<Handler<Test2, Test2Handler>>());

    // some code later....

    dynamic_cast<AbstractHandler<Test1>*>(handlers[0].get())->handle(Test1{});
    dynamic_cast<AbstractHandler<Test2>*>(handlers[1].get())->handle(Test2{});
}

动态转换给程序增加了不必要的开销。相反,您可以使用 type easure 就像您为避免这种开销所做的那样。

另外,甚至没有理由Abstract存在。这是一个没有公开有用功能的接口。这里真正需要的是保存一个不相关接口的列表。


假设我们调整了 easure 类型以允许copy_to_generic将实例转换为父类。

template <typename Parent, typename T>
generic_t to_generic(T&& value) // forward is better.
{
    struct local_cast
    {
        static void destroy(void* p)
        {
            // we cast to the parent first, and then to the real type.
            delete static_cast<std::decay_t<T>*>(static_cast<Parent*>(p));
        }
    };

    generic_t p;

    p.obj = static_cast<Parent*>(new std::decay_t<T>(std::forward<T>(value)));
    p.del = &local_cast::destroy;

    return p;
}

用easure类型查看这段代码:

// No useless interface

template<typename P>
struct AbstractHandler {
   // No virtual destructor needed, generic_t already has a virtual destructor via `del`
    virtual void handle(P) = 0;
};

template<typename P, typename H>
struct Handler : private H {
    void handle(P p) override {
        H::handle(p);
    }
};

struct Test1 {};

struct Test1Handler {
    void handle(Test1) {}
};

struct Test2 {};

struct Test2Handler {
    void handle(Test2) {}
};

int main() {
    std::vector<generic_t> handlers;

    handlers.emplace_back(
        to_generic<AbstractHandler<Test1>>(Handler<Test1, Test1Handler>{})
    );


    handlers.emplace_back(
        to_generic<AbstractHandler<Test2>>(Handler<Test2, Test2Handler>{})
    );

    // some code later....

    static_cast<AbstractHandler<Test1>*>(handlers[0].obj)->handle(Test1{});
    static_cast<AbstractHandler<Test2>*>(handlers[1].obj)->handle(Test2{});
}

没有空界面,也没有动态演员表了!这段代码与另一段代码做同样的事情,但速度更快。

于 2016-12-13T19:13:07.117 回答