std :: is_base_of的可能实现的说明

时间:2017-04-26 11:15:20

标签: c++ c++11

1.  template <typename Base> std::true_type is_base_of_test_func( Base* );
2.  template <typename Base> std::false_type is_base_of_test_func( void* );
3.  template <typename Base, typename Derived>
    using pre_is_base_of = decltype( is_base_of_test_func<Base>( std::declval<Derived*>() ) );

4.  template <typename Base, typename Derived, typename = void>
    struct pre_is_base_of2 : public std::true_type {};

5.  template<typename ...> using void_t = void;
6.  template <typename Base, typename Derived>
    struct pre_is_base_of2<Base, Derived, void_t<pre_is_base_of<Base, Derived>>> : public pre_is_base_of<Base, Derived>{};


7.  template <typename Base, typename Derived>
    struct is_base_of : public std::conditional_t<std::is_class<Base>::value && std::is_class<Derived>::value,
                                                  pre_is_base_of2<Base, Derived>,
                                                  std::false_type>
    {
    };

第1行和第2行非常简单。 但是,第3行:using非常模糊,因为我不能简单地用pre_is_base_of替换它的定义。因此,using并不是文档所说的。它也涉及一些宗教信仰。如果我没有错,pre_is_base_of的使用应该返回std::true_typestd::false_type。我&#39;在void_t时,m同样失去了。这条线会做什么样的魔术呢?是pre_is_base_of2的两种实现都不应该采用3种类型吗?第6行的继承点是什么?可能还有更多,但现在停下来。

我需要一些关于魔法的详细解释。基本上我试图理解代码是如何工作的。

修改:当default问我错误是什么时,我替换了pre_is_base_of的每一次出现,现在没有错误。

1 个答案:

答案 0 :(得分:7)

  
      
  1. template <typename Base> std::true_type is_base_of_test_func( Base* );
  2.   

当参数是Base或从Base派生时,此重载具有最高优先级

  
      
  1. template <typename Base> std::false_type is_base_of_test_func( void* );
  2.   

此重载将匹配任何类型,具有最低优先级

  
      
  1. template <typename Base, typename Derived> using pre_is_base_of = decltype( is_base_of_test_func<Base>( std::declval<Derived*>() ) );
  2.   

pre_is_base_of将成为通过使用指向is_base_of_test_func的指针调用Derived返回的类型。如果Derived派生自Base,它将返回std :: true_type,否则将选择void *重载并返回std :: false_type。现在我们已将函数调用结果转换为类型。

  
      
  1. template <typename Base, typename Derived, typename = void> struct pre_is_base_of2 : public std::true_type {};
  2.   

一般情况下,这将是一个true_type。由于第3个模板参数是默认的,因此这将是未创建其他特化时定义的类的版本。

  
      
  1. template<typename ...> using void_t = void;
  2.   

这是一种更简单的enable_if方法。如果X是合法类型,void_t<X>将只是一种类型。

  
      
  1. template <typename Base, typename Derived> struct pre_is_base_of2<Base, Derived, void_t<pre_is_base_of<Base, Derived>>> : public pre_is_base_of<Base, Derived>{};
  2.   

如果void_t是合法类型(即pre_is_base_of<Base>(Derived*)是一个有效的表达式,这将是pre_is_base_of2的特化,它将评估调用测试函数的decltype,如上所述。只有在pre_is_base_of<Base,Derived>是有效类型时才会被选中(即存在对测试函数的调用)

  
      
  1. template <typename Base, typename Derived> struct is_base_of : public std::conditional_t<std::is_class<Base>::value && std::is_class<Derived>::value, pre_is_base_of2<Base, Derived>, std::false_type> { };
  2.   

基本上这是说:

IF Base and Value are classes AND void_t<decltype(is_base_of_test_func<Base>(Derived*))> is a type
THEN
    select the type of pre_is_base_of2<Base, Derived, void_t<...is the expression legal?...>>
ELSE
    select false_type        

更新

希望这个小小的演示程序能够提供一些清晰度:

#include <type_traits>
#include <iostream>

template<class...> using void_t = void;

// this expands in any case where no second type is provided
template<class T, typename = void> struct does_he_take_sugar : std::false_type {};

// the specialisation can only be valid when void_t<expr> evaluates to a type.
// i.e. when T has a member function called take_sugar
template<class T> struct does_he_take_sugar<T, void_t<decltype(std::declval<T>().take_sugar())>> : std::true_type {};


struct X {
    int take_sugar();
};

struct Y {
    int does_not();
};

int main()
{

    // X::take_sugar is a function therefore void_t<decltype(...X)> will evaluate to void
    std::cout << does_he_take_sugar<X>::value << std::endl;

    // Y::take_sugar is not a function therefore void_t<decltype(...Y)> will not evaluate at all
    std::cout << does_he_take_sugar<Y>::value << std::endl;

    // int::take_sugar is not even valid c++ void_t<decltype(...int)> will not evaluate at all
    std::cout << does_he_take_sugar<int>::value << std::endl;
}