【问题标题】:Using `void_t` to check if a class has a method with a specific signature使用 `void_t` 检查类是否具有具有特定签名的方法
【发布时间】:2014-12-09 13:34:13
【问题描述】:

目前,我是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&lt;T&gt; 将是一个真正的编译时常量。因此,我们确定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, "");
    

【问题讨论】:

  • 命名非静态成员函数的 id-expression 不能用于 decltype
  • @T.C.:我明白了 - 是否有另一种方法可以根据类型是否有/没有方法来生成有效/无效表达式?

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


【解决方案1】:

首先,命名非静态成员函数的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 { };

(如果您想检查 getCount() 是否可以在左值上调用,请使用 declval&lt;T&amp;&gt;。)

如果您只是检查 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

【讨论】:

  • declval&lt;T&gt; 产生一个 xvalue,因此如果您想检查是否可以在左值上调用 getCount(),请使用 declval&lt;T&amp;&gt;(与引用限定符相关)。
  • SFINAE 是涉及函数模板的类模板特化和重载决议规则的结果。 enable_if 既不是必要的也不是充分的成分。如果你想重写函数模板形式的测试,template&lt;typename T&gt; typename std::is_same&lt;int, decltype( std::declval&lt;T&amp;&gt;().getCount() )&gt;::type test(int); 是一个好的开始,不使用enable_if
【解决方案2】:

您可以使用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;
};

【讨论】:

  • 虽然此检查仅对= 的 lhs 上的非类类型是正确的,否则,您正在检查可分配性。同样,据我所知,它不适用于函数类型和数组(直接)。
【解决方案3】:

如果您还想检查函数的参数及其返回类型,类似于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" );

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-26
    • 1970-01-01
    • 2021-12-29
    • 2015-06-07
    • 2022-12-12
    • 2023-03-12
    • 1970-01-01
    相关资源
    最近更新 更多