C++ 类型擦除和lambdas:(部分)模板特殊化匹配lambda表达式 首先,一些背景

C++ 类型擦除和lambdas:(部分)模板特殊化匹配lambda表达式 首先,一些背景,c++,c++11,lambda,type-erasure,C++,C++11,Lambda,Type Erasure,作为我目前正在编写的基于策略的粒子引擎的一部分,我对策略类进行了一些类型擦除。具体来说,我已经对粒子唤醒策略执行了类型擦除: 粒子演化策略只是一种策略,它说明粒子的数据坐标、速度、颜色等在模拟过程中如何变化。这是它的界面: struct evolution_policy { template<PARTICLE_DATA> void operator()( PARTICLE_DATA& data ); void step(); }; 其中add_p

作为我目前正在编写的基于策略的粒子引擎的一部分,我对策略类进行了一些类型擦除。具体来说,我已经对粒子唤醒策略执行了类型擦除:

粒子演化策略只是一种策略,它说明粒子的数据坐标、速度、颜色等在模拟过程中如何变化。这是它的界面:

struct evolution_policy
{
    template<PARTICLE_DATA>
    void operator()( PARTICLE_DATA& data );

    void step(); 
};
其中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;
};
考虑到这一点,编写专门化来删除策略的共享指针很容易,例如:

add_policy( []( some_particle_data_type& data ) { /* do something with */ } );
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策略编写专门化。但是,这仅适用于std::function的显式实例,即如果我们将lambda函数传递给类型_erased_policy ctor,它将实例化泛型policy_impl情况,因为lambda类型没有专门化

由于lambda函子闭包是唯一的,换句话说,lambda表达式的类型是唯一的且未指定的,因此在lambda上进行此类类型擦除并不容易

我的问题是:我的目标是用lambda、functor、函数指针、std::function对任何函数实体进行类型擦除,方法如上所述。是否有其他方法匹配lambda和/或其他功能实体以对其执行类型擦除

站台: 我使用GCC4.8.2来方便地将lambda函数包装到std::函数中。因为它的构造函数是贪婪的,所以它将是任何可调用的,然而包装的好处是声明一个显式类型

因此,在上面的上下文中,f1面临您提到的问题,而f2是一个lambda,它保留了声明和使用它的所有便利性,从某种意义上说,它没有“杂散类型”

,以便将lambda函数包装到std::函数中。因为它的构造函数是贪婪的,所以它将是任何可调用的,然而包装的好处是声明一个显式类型


因此,在上面的上下文中,f1面临您提到的问题,而f2是lambda,它保留了声明和使用它的所有便利性,从某种意义上说,它没有“杂散类型”

首先是一个检测的策略类。步骤:

如果您真的想根据是否具有step来专门化特定类,那么可以使用SFINAE技术。但是类型擦除并不依赖于基于专门化的类型擦除实现:我只在生成类型擦除实现对象的函数上使用标记分派

我们可以菊花链这样的标签发送。另一个好的方法可能是签名兼容


然后,我们可以使用它在10次递归模板调用中以较少的错误将小麦从谷壳中分离出来。

首先,是一个检测的策略类。步骤:

如果您真的想根据是否具有step来专门化特定类,那么可以使用SFINAE技术。但是类型擦除并不依赖于基于专门化的类型擦除实现:我只在生成类型擦除实现对象的函数上使用标记分派

我们可以菊花链这样的标签发送。另一个好的方法可能是签名兼容


然后,我们可以使用它在10次递归模板调用中以较少的错误将小麦从谷壳中分离出来。

@dyp包含的函数实体lambda需要类型擦除,因为它们没有步骤成员函数。如果这类实体与主模板匹配,它就不会编译,这是有意义的。啊,我真傻。我忽略了成员的作用。那么你不能在那里使用SFINAE吗?@dyp我已经尝试过使用类型特征的类型内省,特别是std::is_函数,但是std::reference_包装器的一个专门化与我在问题中编写的std::shared_ptr示例相匹配,因此它对我不起作用。不,我的意思是,你为什么不检查_policy.step是否是一个有效的表达式?您可以从虚拟函数重写器内部通过调用一些私有的、SFINAE重载的成员函数来实现这一点。@dyp包含的函数实体lambda需要类型擦除,因为它们没有步骤成员函数。如果这类实体与主模板匹配,它就不会编译,这是有意义的。啊,我真傻。我忽略了成员的作用。那么,你不能在那里使用SFINAE吗?@dyp我已经尝试了使用类型特征的类型内省,特别是std::is_函数,但是std::reference_包装的专门化类似于我在问题列表中编写的std::shared_ptr示例
切掉它,所以它对我不起作用。不,我的意思是,你为什么不检查_policy.step是否是一个有效的表达式?您可以从虚拟函数重写器内部调用这个函数,调用一些私有的SfimaEn过载成员函数。C++没有错误发生,10个递归模板调用深深地失去了它的大部分吸引力,你不觉得吗?类型:void?@dyp你永远不知道什么时候会有人知道如何让foo过载,void?我喜欢孩子。我只是喜欢把我的水槽弄清楚。另请参见TypeSink的第二个用法,其中没有表达式:介绍,会有罗嗦。几乎猜到。。但对我来说似乎有点复杂:您可以使用TypeSink=void使用模板;或者,如果您想保留一个特殊的标记类型,struct type_sink{};使用TypeSink=type\u sink的模板;并专门用于类型_sink而不是void。@dyp true:我基于C++11之前的类型sinking,然后基于identity。我确实喜欢void,因为它也是enable_if的默认输出。实际上,type_sink可能是最好的:让您将任何类型下沉到任何输出类型。对于标识,我认为间接块类型推断的级别?如果没有任何类似的情况发生在间接上,C++没有错误发生,10个递归模板调用深深地失去了它的大部分吸引力,你不觉得吗?类型:“我喜欢孩子。我只是喜欢把我的水槽弄清楚。另请参见TypeSink的第二个用法,其中没有表达式:介绍,会有罗嗦。几乎猜到。。但对我来说似乎有点复杂:您可以使用TypeSink=void使用模板;或者,如果您想保留一个特殊的标记类型,struct type_sink{};使用TypeSink=type\u sink的模板;并专门用于类型_sink而不是void。@dyp true:我基于C++11之前的类型sinking,然后基于identity。我确实喜欢void,因为它也是enable_if的默认输出。实际上,type_sink可能是最好的:让您将任何类型下沉到任何输出类型。对于标识,我认为间接块类型推断的级别?不知道这里是否发生了类似的间接寻址,可能不会。这正是我目前正在做的,但我想避免的是,写一个lambda这正是我目前正在做的,但我想避免的是,写一个lambda
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
};);
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> {};
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& >() );
}
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> {};