使用`void_t`检查类是否具有特定签名的方法

时间:2014-10-14 16:58:43

标签: c++ templates void template-meta-programming c++14

目前,我using this method to check if a class has a method with a specific signature.

在参加Walter E. Brown's metaprogramming CppCon2014 talk之后,我开始想知道在这种特定情况下是否可以使用void_t来使代码更清晰,更易读。

但是我在考虑void_t方面遇到了麻烦 - 到目前为止,我理解void_t可以帮助我在编译时确定表达式是否有效。

示例:

template< class, class = void >
struct has_type_data_member : false_type { };

template< class T >
struct has_type_data_member<T, void_t<decltype(T::data)>> : true_type { };

如果decltype(T::type)是有效表达式,has_type_data_member<T>将是真正的编译时常量。因此,我们确信T有一个名为data的成员字段。

我想使用相同的方法来检查类型T是否具有带特定名称和特定签名的方法。

假设我想检查T是否有一个名为getCount()的方法返回int。这就是我期望的工作((Ideone.com link))

template< class, class = void >
struct hasGetCount : false_type { };

template< class T >
struct hasGetCount<T, VoidT<decltype(T::getCount)>> 
: std::is_same<decltype(std::declval<T>().getCount()), int>::type { };

不幸的是,static_assert测试没有通过。

我做错了什么?在这种情况下是否可以使用void_t

奖金问题:

  • 我如何检查方法签名是否等于用户在原始实现中传递的签名?
  • 我可以使用宏来定义这样的辅助结构:

     DEFINE_METHOD_CHECKER(hasGetCount, getCount);
     // ...
     static_assert(hasGetCount<ClassWithGetCount>::value == true, "");
    

    是否可以先避免定义struct然后检查结构的值?我的意思是,是否可以使用宏来编写这样的东西?例如:

     static_assert(CHECK_METHOD(ClassWithGetCount, getCount)::value == true, "");
    

3 个答案:

答案 0 :(得分:6)

首先,命名非静态成员函数的 id-expression 不能用作未评估的操作数(例如decltype的操作数)。此外,您应该检查整个函数调用表达式是否格式正确,而不仅仅是否有一个名为getCount的成员:

template< class, class = void >
struct hasGetCount : false_type { };

template< class T >
struct hasGetCount<T, VoidT<decltype(std::declval<T>().getCount())>> 
: std::is_same<decltype(std::declval<T>().getCount()), int>::type { };

(如果您想检查左值是否可以调用declval<T&>,请使用getCount()

如果您只是检查是否存在getCount成员,那么如果具有该名称的成员存在但不可调用(例如,数据成员),则会出现硬错误。

虽然此时您还可以考虑使用类似

的内容
template< class T >
struct hasGetCount<T, std::enable_if_t<std::is_same<decltype(std::declval<T>().getCount()), int>::value>> : std::true_type { };

而不是两次写decltype

答案 1 :(得分:6)

您可以使用void_t轻松验证getCount的返回类型是否可转换为int

template< class, class = void >
struct hasGetCount : false_type { };

template< class T >
struct hasGetCount<T,
  VoidT<
    decltype(std::declval<int&>() = std::declval<T>().getCount())
  >> : std::true_type {};

(的 Ideone live code

希望在C ++ 17发布时,我们可以使用Concepts Lite更轻松地完成此任务:

template <typename T>
concept bool hasGetCount = requires (T t) {
  { t.getCount() } -> int;
};

答案 2 :(得分:0)

如果您还想检查函数的参数及其返回类型,类似于Casey的建议

template<typename... > struct Voider { using Type = void; };
template<typename... TArgs> using void_t = typename Voider<TArgs...>::Type;

template<class, typename, class = void>
struct hasGetCount : false_type { };

template< class T,  typename Ret, typename... Args>
struct hasGetCount<T, Ret(Args...), 
    void_t<decltype(std::declval<Ret&>() 
        = std::declval<T>().getCount(std::declval<Args>()...))
          >> : std::is_same<
               decltype(std::declval<T().getCount(std::declval<Args>()...)), 
               Ret>::type {};

用法:

class A {
public:
    int member;
    int getCount() {return 0;}
};
static_assert( hasGetCount<A, int(void)>::value , "A" );