【问题标题】:Checking a member exists, possibly in a base class, C++11 version检查成员是否存在,可能在基类中,C++11 版本
【发布时间】:2012-03-20 19:35:41
【问题描述】:

https://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++11 final 类,因为它继承自被测类,而 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 仍然可以工作?

【问题讨论】:

  • 在 C++11 上,你可以只使用“sfinae for expressions”而不是在 C++03 中使用这种迂回的方式。
  • @JohannesSchaub-litb 寻求解决方案!我很想看看它对这种示例的影响。

标签: c++ c++11 final template-meta-programming typetraits


【解决方案1】:

实际上,由于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 中链接多个表达式,并且只有最后一个实际决定类型。方便检查多个操作。

【讨论】:

  • 难道T 不需要是文字类型才能成为constexpr 函数的参数吗?好的,在模板上,constexpr 在实例化时被删除,而它不能是 constexpr,但你在模板参数列表中使用它,它必须在哪里。
  • @MarcMutz-mmutz:为避免创建空指针,您可以使用std::declval&lt;T&gt;(),它将返回一个T(如果您愿意,可以将它与T const&amp; 一起使用)。
  • 将一个不够简单的类类型传递给(...) 不是一个好主意(例如,有条件地支持它)。我仍然更喜欢使用未计算表达式的传统方式。
  • @Luc 我想你可以用不同的方式看待它:他只是函数调用替换。这样的事情需要一个函数调用表达式并将其转换为另一个不再必须是函数调用的表达式。最后他有表达式false
  • 未来编辑请注意:我使用了来自to cull剔除(而不是“调用”)。
【解决方案2】:

一个版本也依赖于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&lt;int&gt;&amp; 确实支持reserve:这里检查的是表达式,而不是类型。这个特征回答的问题是“给定一个左值lval 用于这种类型T,是表达式lval.reserve(0); 格式正确”。与“此类型或其任何基类型是否声明了 reserve 成员”问题不同。

另一方面,可以说这是一个功能!请记住,新的 C++11 特征的样式为 is_default_constructiblenot has_default_constructor。区别是微妙的,但有优点。 (以is_*ible 的风格找到一个更合适的名称作为练习。)

无论如何,您仍然可以使用诸如std::is_class 之类的特征来实现您想要的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-17
    • 2017-06-16
    • 1970-01-01
    • 2017-07-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多