类型擦除和 lambda:(部分)与 lambda 表达式匹配的模板专用化
Type-erasure and lambdas: (Partial) template speciallization matching lambda expressions
首先,一些上下文
作为我目前正在编写的基于策略的粒子引擎的一部分,我已经对策略类进行了一些类型擦除。具体来说,我已经对粒子 evoution 策略进行了类型擦除:
粒子进化策略只是一种策略,它说明了粒子的数据(坐标、速度、颜色等)如何随着模拟而变化。这是它的界面:
struct evolution_policy
{
template<PARTICLE_DATA>
void operator()( PARTICLE_DATA& data );
void step();
};
operator()
重载只是获取粒子数据并根据假定的策略对其进行修改。step()
函数只是一个更新(高级)策略状态(如果策略具有某种状态)的函数。
在其他情况下,我只需要类型擦除以允许用户使用简单的函数实体(函数、lambda 等)用作演进策略,例如:
add_policy( []( some_particle_data_type& data ) { /* do something with */ } );
其中add_policy()
函数采用一些策略并将其存储在向量中。 如您所见,类型擦除的要点是以相同的同构方式处理不同类型的策略类/实体。
问题:
我正在使用动态调度方法进行类型擦除:
template<tyoename PARTICLE_DATA>
struct type_erased_policy
{
public:
void operator()( PARTICLE_DATA& data )
{
(*_policy)( data );
}
void step()
{
_policy->step();
}
private:
struct policy_interface
{
virtual ~policy_interface() {}
virtual void operator()( PARTICLE_DATA& data ) = 0;
virtual void step() = 0;
};
template<typename POLICY>
class policy_impl : public policy_interface
{
public:
void operator()( PARTICLE_DATA& data ) override
{
_policy( particle );
}
void step() override
{
_policy.step();
}
private:
POLICY _policy;
};
std::shared_ptr<policy_interface> _policy;
};
考虑到这一点,很容易编写一个专用化来类型擦除策略的共享指针,例如:
template<typename T>
class policy_impl<std::shared_ptr<T>> : public policy_interface
{
public:
void operator()( PARTICLE_DATA& data ) override
{
(*_policy)( data );
}
void step( cpp::evolution_policy_step step_type ) override
{
_policy->step( step_type );
}
private:
std::shared_ptr<T> _policy;
};
例如,如果我们需要在粒子之间共享策略,这可能会很有用。
我们可以使用该模式为std::function<void(PARTICLE_DATA&)>
策略编写专用化。但是,这仅适用于 std::function<void(PARTICLE_DATA&)>
的显式实例化,即如果我们将 lambda 函数传递给 type_erased_policy
ctor,它将实例化泛型policy_impl
情况,因为 lambda 类型没有专用化。
由于 lambda 函子(闭包)是唯一的(换句话说,lambda 表达式的类型是唯一且未指定的),因此没有简单的方法可以在 lambda 上执行这种类型擦除。
我的问题是:我的目标是获取任何函数实体(lambda,函子,函数指针,std::function
),并以上述方式对其进行类型擦除。是否有任何(其他)方法可以匹配 lambda 和/或其他函数实体以对它们进行类型擦除?
平台:
我正在使用 GCC 4.8.2
首先,一个检测.step()
的策略类:
namespace impl{
// eat a type and do nothing with it:
template<typename T> struct type_sink { typedef void type; };
template<typename T> using TypeSink = typename type_sink<T>::type;
// detect .step on T (T& means rvalue, T means lvalue, and T const& means const lvalue, etc)
template<typename T, typename=void>
struct has_step:std::false_type {};
template<typename T>
struct has_step<T, TypeSink< decltype( std::declval<T>().step() ) > >:
std::true_type
{};
}
template<typename T> struct has_step : impl::has_step<T> {};
所以现在我们有一个 has_step<T>
traits 类,如果T
有一个可调用的.step()
方法,true_type
,否则false_type
。
如果我们将其提供给函数,我们可以选择要使用标签调度运行的实现:
template<typename T>
return_type_whatever type_erase_helper( T&& t, std::true_type /* test passed */ ) {
// branch 1
}
template<typename T>
return_type_whatever type_erase_helper( T&& t, std::false_type /* test failed */ ) {
// branch 2
}
template<typename T>
return_type_whatever type_erase( T&& t ) {
return type_erase_helper( std::forward<T>(t), has_step< typename std::decay<T>::type& >() );
}
如果你真的想根据是否有step
来专门化一个特定的类,你可以使用 SFINAE 技术。 但是类型擦除并不依赖于基于专用化的类型擦除实现:我只是在生成类型擦除实现对象的函数上使用标记调度。
我们可以菊花链式地发送这种标签。 另一个好的可能是is_signature_compatible< T, R(Args...) >
:
namespace impl {
template<typename T, typename Sig,typename=void>
struct is_signature_compatible:std::false_type {};
template<typename T, typename R, typename... Args>
struct is_signature_compatible< T, R(Args...), typename std::enable_if<
std::is_convertible< typename std::result_of< T(Args...) >::type, R >::value
>::type >:std::true_type {};
// dunno if this is needed? Possibly, and shouldn't hurt:
template<typename T, typename... Args>
struct is_signature_compatible< T, void(Args...),
TypeSink< typename std::result_of< T(Args...) >::type >
>:std::true_type {};
}
template<typename T, typename Sig>
struct is_signature_compatible:impl::is_signature_compatible<T,Sig> {};
然后,我们可以使用它以较少的"错误发生 10 递归template
调用深度"的方式将小麦从谷壳中分离出来。
为了方便该技术,将您的 lambda 函数包装在 std::function
中。由于它的构造函数是贪婪的,它将吸收任何可调用的,但包装的好处是声明一个显式类型
auto f1 = [](double a, double b)->int {
return a + b > 5.; // example function body
});
std::function<int(double,double)> f2 ( [](double a, double b)->int {
return a + b > 5.; // example function body
};);
因此,在上述上下文中,f1
面临着您提到的问题,而f2
是一个lambda(保留了声明和使用它的所有便利性),从某种意义上说,它没有"杂散类型"
- .cpp和.h文件中的模板专用化声明
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 调用专用模板时出错"no matching function for call to [...]"
- 可组合的lambda/std::函数与std::可选
- 模板专用化(按容器):value_type
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 如何建立使用模板函数的lambda函数的尾部返回类型
- 如何将lambda作为模板类的成员函数参数
- C++从其他 constexpr 创建 lambda 不能按顺序执行 Constexpr
- 在 lambda 捕获中声明的变量的类型推导
- 我可以将调用类的"this"传递给 lambda 函数吗?
- 为什么lambda在clang上崩溃而不是在gcc上崩溃
- 模板函数指针和lambda
- 两组使用lambda函数的大括号
- 使lambda不可复制/不可移动
- FLTK:按下哪个按钮 - 将数字传递给按钮的回调 (lambda)
- 尝试将lambda函数放在队列中时出现一般分配器错误(可能是与unique_ptr有关的错误)
- 有没有办法根据 lambda 参数返回类型部分专用化我的模板化函数?
- unique_ptr<T> 用于数组专用化的 lambda 自定义删除器
- 类型擦除和 lambda:(部分)与 lambda 表达式匹配的模板专用化