检查成员是否存在并调用它或什么也不做

时间:2013-11-13 16:54:39

标签: c++ templates c++11 metaprogramming sfinae

我有一个带有init()方法的模板类,如果它存在,必须调用子类方法。 Base类的方法init()永远调用。

template <class T>
class Base
{

   template<typename... Args>
   void init(Args... args);

   T subj;

   explicit Base() { subj = new T(); }
}


template<typename... Args>
Base<T>::init(Args... args)
{
    invoke_if_exists<&T::init,subj>(args); // <-- that moment
}

需要实现invoke_if_exists模板。该算法应该像那样的代码

if ( method_exists(T::init) )
{
     subj->init(Args...);
}   

我需要把它包装成模板

非常感谢你。

[更新]:

让我试着解释一下。

class Foo
{
// ... and there is no init()
};

class Right
{
    void init() { ... }
 /// ...
}


auto a = new Base<Foo>();
auto b = new Base<Right>();

a->init();  // there is no call for Foo::init();
b->init();  // there is correct call for Right::init();

我想使用invoke_if_exists<>不仅使用init()方法,它可以是任何。

1 个答案:

答案 0 :(得分:3)

数据和类型接收器,它接收类型和数据,并且不对它们执行任何操作:

struct sink {
  template<typename... Us>
  void operator()(Us&&... us) const {}
};
template<typename... Ts>
struct type_sink:std::true_type {};
然后我们创建一个template invoke_init_if_exists,它接受​​一个目标类型T和一组参数,如果可以使用这些参数调用T*->init,那么这样做。否则,它会将内容抛出到上面的数据接收器中:

template<typename T,typename ArgTypes,typename=void>
struct invoke_init_if_exists:sink {};
template<typename T,template<typename...>class Pack,typename... Args>
struct invoke_init_if_exists<T,
  Pack<Args...>,
  typename std::enable_if< type_sink<
    decltype( std::declval<T*>()->init( std::declval<Args>()... ) )
  >::value >::type
>
{
  void operator()( T* target, Args&&... args ) const {
    target->init( std::forward<Args>(args)... );
  }
};

然后我们使用它来实现您的init方法:

template<typename... Args>
Base<T>::init(Args&&... args)
{
  invoke_init_if_exists<T, type_sink<Args...>>()(&subj, std::forward<Args>(args)...);
}

我们在哪里创建帮助器类,可以是sink,也可以转发到init,具体取决于对init的调用是否有效。

我们最终不得不做这样的事情,因为除非T::init已经有方法T,否则您无法将令牌init命名为有效。因此,我们必须编写一个自定义类,在SFINAE上下文中执行T::init查找,如果失败则回退到什么都不做。