std :: function中参数的自动dynamic_cast

时间:2017-02-24 12:52:11

标签: c++

我们有多态A和B类:

struct A {
    virtual ~A() {}
};
struct B final : public A {
    void f() { std::cout << "f" << std::endl; }
};

我想从lambda函数中为std::function<void(A*)>分配一个变量,其类型为void(B*),而不是为参数明确应用dynamic_cast

std::function<void(A*)> funcA = [](A* a) {
    [](B* b) { b->f(); }(dynamic_cast<B*>(a));
};
B b;
funcA(&b);

有没有办法自动实现这一点,而不用[](A* a){}包裹内部函数?

1 个答案:

答案 0 :(得分:3)

我的目标是使以下语法有效:

std::function<void(A*)> funcA = dynStdFunc([](B* b) { b->f(); });

为此,dynStdFunc必须:

  1. 检测提供的lambda的参数;
  2. 检测funcA; 的参数 我们实际上并不需要这样做,请参阅最后的更新
  3. 生成一个新的仿函数,通过dynamic_cast将两个参数列表粘合在一起。
  4. 1。检测参数已成为another answer of mine的主题。我们可以使用以下类型特征:

    // C++17's void_t
    template <class...>
    using void_t = void;
    
    // Pack of arbitrary types
    template <class...>
    struct pack { };
    
    namespace detail_parameters {
        template <class F, class = void_t<>>
        struct parameters { };
    
        template <class F>
        struct parameters<F, void_t<decltype(&F::operator ())>>
        : parameters<decltype(&F::operator ())> { };
    
        template <class R, class... Params>
        struct parameters<R(Params...)> { using type = pack<Params...>; };
    
        // More specializations for functions, function pointers,
        // member function pointers...
    }
    
    // Retrieve the parameter list from a functionoid
    template <class F>
    using parameters = typename detail_parameters::parameters<std::remove_reference_t<F>>::type;
    

    这将采用functionoid类型,并返回包含其参数类型的pack<T...>。大。

    2。 std::function内部未知dynStdFunc所需的参数。我们开展此工作的方法是返回一个临时对象,其中包含转化运算符的模板std::function<Ret(Args...)>

    namespace detail_dynStdFunc {
    
        // F = functionoid, Ps = pack of its parameters
        template <class F, class Ps>
        struct wrapper;
    
        template <class F, class... Ps>
        struct wrapper<F, pack<Ps...>> {
    
            template <class Ret, class... Args>
            operator std::function<Ret(Args...)> () {
                // Now we know what parameters the `std::function` needs
            }
    
            F f;
        };
    }
    
    template <class F>
    auto dynStdFunc(F &&f) {
        return detail_dynStdFunc::wrapper<
            std::remove_reference_t<F>,
            parameters<F>
        >{std::forward<F>(f)};
    }
    

    3。我们已经得到了所有我们需要的东西,生成新的仿函数非常简单:

    template <class Ret, class... Args>
    operator std::function<Ret(Args...)> () {
        return [f_ = std::move(f)](Args... args) -> Ret {
            return f_(dynamic_cast<Ps>(args)...);
        };
    }
    

    就是这样!你可以see it working live on Coliru

    更新:结果我完成了我需要的两倍工作,因为std::function实际上可以实例化并直接包装泛型仿函数。谢谢Yakk!

    所以自己进行转换毫无意义 - 让我们放弃wrapper

    template <class F, class... Ps>
    auto dynStdFunc(F &&f, pack<Ps...>) {
        return [f_ = std::forward<F>(f)](auto *... args) -> decltype(auto) {
            return f_(dynamic_cast<Ps>(args)...);
        };
    }
    
    template <class F>
    auto dynStdFunc(F &&f) {
        return dynStdFunc(std::forward<F>(f), parameters<F>{});
    }
    

    See it live on Coliru