正在检查成员是否存在,可能在基类C++11版本中

Checking a member exists, possibly in a base class, C++11 version

本文关键字:基类 C++11 版本 检查 成员 是否 存在      更新时间:2023-10-16

Inhttps://stackoverflow.com/a/1967183/134841,提供了一种静态检查成员是否存在的解决方案,可能存在于类型的子类中:

template <typename Type> 
class has_resize_method
{ 
   class yes { char m;}; 
   class no { yes m[2];}; 
   struct BaseMixin 
   { 
     void resize(int){} 
   }; 
   struct Base : public Type, public BaseMixin {}; 
   template <typename T, T t>  class Helper{}; 
   template <typename U> 
   static no deduce(U*, Helper<void (BaseMixin::*)(), &U::foo>* = 0); 
   static yes deduce(...); 
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
};

然而,它在C++11final类上不起作用,因为它继承了final所阻止的被测类。

OTOH,这个:

template <typename C>
struct has_reserve_method {
private:
    struct No {};
    struct Yes { No no[2]; };
    template <typename T, typename I, void(T::*)(I) > struct sfinae {};
    template <typename T> static No  check( ... );
    template <typename T> static Yes check( sfinae<T,int,   &T::reserve> * );
    template <typename T> static Yes check( sfinae<T,size_t,&T::reserve> * );
public:
    static const bool value = sizeof( check<C>(0) ) == sizeof( Yes ) ;
};

在基类中找不到reserve(int/size_t)方法。

这个元函数的实现是否既在T的基类中找到reserved(),又在Tfinal的情况下仍然有效?

事实上,由于decltype和后期返回绑定机制,C++11中的工作变得容易多了。

现在,使用测试方法更简单:

// Culled by SFINAE if reserve does not exist or is not accessible
template <typename T>
constexpr auto has_reserve_method(T& t) -> decltype(t.reserve(0), bool()) {
  return true;
}
// Used as fallback when SFINAE culls the template method
constexpr bool has_reserve_method(...) { return false; }

然后您可以在类中使用它,例如:

template <typename T, bool b>
struct Reserver {
  static void apply(T& t, size_t n) { t.reserve(n); }
};
template <typename T>
struct Reserver <T, false> {
  static void apply(T& t, size_t n) {}
};

你这样使用它:

template <typename T>
bool reserve(T& t, size_t n) {
  Reserver<T, has_reserve_method(t)>::apply(t, n);
  return has_reserve_method(t);
}

或者您可以选择enable_if方法:

template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<has_reserve_method(t), bool>::type {
  t.reserve(n);
  return true;
}
template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<not has_reserve_method(t), bool>::type {
  return false;
}

请注意,这种切换实际上并不那么容易。一般来说,当只存在SFINAE时会容易得多——并且您只想enable_if一种方法,而不提供任何回退:

template <typename T>
auto reserve(T& t, size_t n) -> decltype(t.reserve(n), void()) {
  t.reserve(n);
}

如果替换失败,此方法将从可能的重载列表中删除。

注意:由于,(逗号运算符)的语义,您可以在decltype中链接多个表达式,并且只有最后一个表达式真正决定类型。方便检查多个操作

一个版本,它也依赖于decltype,但不依赖于向(...)传递任意类型[无论如何,这实际上都不是问题,请参阅Johannes的评论]:

template<typename> struct Void { typedef void type; };
template<typename T, typename Sfinae = void>
struct has_reserve: std::false_type {};
template<typename T>
struct has_reserve<
    T
    , typename Void<
        decltype( std::declval<T&>().reserve(0) )
    >::type
>: std::true_type {};

我想指出的是,根据这个特性,像std::vector<int>&这样的类型确实支持reserve:这里检查表达式,而不是类型。这个特征回答的问题是"给定这样一个类型T的左值lval,表达式lval.reserve(0);是否形成良好"。与问题"此类型或其任何基类型是否声明了reserve成员"不同。

另一方面,可以说这是一个特点!请记住,新的C++11特性的样式是is_default_constructible而不是has_default_constructor。这种区别很微妙,但也有优点。(找到一个更适合is_*ible风格的名字作为练习。)

在任何情况下,你仍然可以使用std::is_class这样的特质来实现你想要的。