我们有多态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){}
包裹内部函数?
答案 0 :(得分:3)
我的目标是使以下语法有效:
std::function<void(A*)> funcA = dynStdFunc([](B* b) { b->f(); });
为此,dynStdFunc
必须:
funcA
; dynamic_cast
将两个参数列表粘合在一起。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>{});
}