有关std :: add_pointer

时间:2019-08-15 07:14:37

标签: c++ std

来自std::add_pointer

  

可能的实现

namespace detail {

template <class T>
struct type_identity { using type = T; }; // or use std::type_identity (since C++20)

template <class T>
auto try_add_pointer(int) -> type_identity<typename std::remove_reference<T>::type*>;
template <class T>
auto try_add_pointer(...) -> type_identity<T>;

} // namespace detail

template <class T>
struct add_pointer : decltype(detail::try_add_pointer<T>(0)) {};

上述(可能)实现的说明为:

  

如果T是引用类型,则提供成员typedef类型,其中   是指向所引用类型的指针。

     

否则,如果T命名为对象类型,则该函数类型不是cv-   或ref限定,或(可能是cv限定)void类型,可提供   成员typedef类型,即类型T *。

     

否则(如果T是cv或ref限定的函数类型),则提供   成员typedef类型,即类型T。

在上面的(可能的)实现代码中,显然struct add_pointer源自detail::try_add_pointer<T>(0)返回的类型。

detail::try_add_pointer<T>的重载返回int到将成员typedef type解析为上述三种可能性之一的背后得出的逻辑是什么?具体来说,如果Tcv-ref-限定的函数类型,这如何解决可能性?

2 个答案:

答案 0 :(得分:2)

关键在于了解detail::try_add_pointer<T>(0)的重载解析的工作方式。将T替换为detail::try_add_pointer是为了产生一个重载集,该重载集将始终包含至少一个成员(变量参数重载)。

在过载解析(SFINAE)期间是否丢弃承受int的过载取决于是否将T替换为typename std::remove_reference<T>::type*。替换成功后,将存在过载,并且过载分辨率与0匹配更好(与其他任何转换序列相比,省略号是最糟糕的匹配)。不管采用哪种方式,无论以重载分辨率获得重载,decltype(detail::try_add_pointer<T>(0))都将解析为具有嵌套::type成员的对象。

所以让我们逐个案例进行分析:

  1. “如果T是引用类型”-我们将其标记为T = T2&。那么std::remove_reference<T>::typeT2。我们可以形成引用的类型也是我们可以形成指针的类型。因此std::remove_reference<T>::type*的格式正确(它是T2*),并且存在第一个重载。它是在过载解析中获得的。其返回类型的嵌套::typeT2*

  2. “否则,如果T命名为对象类型,非cv或ref限定的函数类型或(可能是cv限定的)无效类型”,在这种情况下,std::remove_reference<T>::type为只需T。我们可以形成一个指向上一个列表中任何类型的指针,因此std::remove_reference<T>::type*的格式再次正确(它是T*)。第一个重载再次存在,并以重载分辨率进行拾取。其返回类型的嵌套::typeT*

  3. “否则(如果T是cv或ref限定的函数类型)” –有趣的位。这涉及到void (int) const&之类的类型。这里std::remove_reference<T>::type再次是T。但是我们不允许形成指向T的指针,基本语言禁止使用它。因此std::remove_reference<T>::type*格式错误,对于此重载解决方案,将忽略第一个重载。只剩下第二个过载,它是由过载分辨率拾取的。其返回类型的嵌套::typeT

答案 1 :(得分:1)

继承只是使用type_identity包装器(已经需要形成有效的返回类型)来定义特征的一种方法。重载技巧依赖于以下事实:std::remove_reference是非引用类型的标识,std::add_pointer<T>T*(已处理引用),除非没有此类类型。在这种情况下,SFINAE消除了int的重载,并选择了...(仅生成T)。