c++ - std::is_base_of 可能实现的解释

标签 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
当涉及到 void_t 时,我同样不知所措。该行会产生什么样的魔力?
pre_is_base_of2 的两个实现不应该采用 3 种类型吗?第 6 行的继承有什么意义?可能还有更多,但让我们现在停下来。

我需要一些关于这里涉及的魔法的详细解释。基本上,我试图了解该代码的工作原理。

编辑:当default问我有什么错误,我替换了所有出现的 pre_is_base_of,现在没有错误了。

最佳答案

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

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

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

这个重载将匹配任何类型,优先级最低

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

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 {};

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

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

这是执行 enable_if 的更简单方法。 void_t<X>仅当 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>{};

如果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> { };

本质上这是在说:

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;
}

关于c++ - std::is_base_of 可能实现的解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43632614/

相关文章:

c++ 函数队列

c++ - 如何获取游戏 Controller 名称(Windows 10/C++)

C++ - 导致 SIGSEGV 信号的空方法

c++ - 带有引用折叠的函数模板重载

c++ - 指向类函数数组的指针

java - 语义 - 将已实现的接口(interface)传递给方法

c++ - 在函数参数中使用 auto 关键字可以吗?

C++:使用自定义分配器时的高效交换()

c++ - 多个函数需要相同的参数(如何优化),

c++ - std::stringstream 类需要有 dll 接口(interface)