关于 std::add_pointer 实施的问题

A question regarding the implementation of std::add_pointer

来自std::add_pointer

Possible implementation

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

上述(可能的)实现的描述如下:

If T is a reference type, then provides the member typedef type which is a pointer to the referred type.

Otherwise, if T names an object type, a function type that is not cv- or ref-qualified, or a (possibly cv-qualified) void type, provides the member typedef type which is the type T*.

Otherwise (if T is a cv- or ref-qualified function type), provides the member typedef type which is the type T.

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

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

继承只是使用 type_identity 包装器(已经需要形成有效的 return 类型)以最小的努力定义特征的一种方式。重载技巧依赖于 std::remove_reference 是非引用类型的标识,并且(处理了引用)std::add_pointer<T>T* 除非存在 no这样的类型。在这种情况下,SFINAE 会终止 int 过载并选择 ...(仅生成 T)。

关键在于理解 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. "If T is a reference type" - 让我们标记它 T = T2&。那么std::remove_reference<T>::type就是T2。我们可以形成引用的类型也是我们可以形成指针的类型。所以 std::remove_reference<T>::type* 是合式的(它是 T2*),并且第一个重载存在。它在过载解决方案中被拾取。其return类型的嵌套::typeT2*.

  2. "Otherwise, if T names an object type, a function type that is not cv- or ref-qualified, or a (possibly cv-qualified) void type" - 在这种情况下 std::remove_reference<T>::type 就是 T。我们可以形成一个指向前面列表中任何类型的指针,因此 std::remove_reference<T>::type* 再次形成良好(并且它是 T*)。第一个重载再次存在并在重载决议中被拾取。其return类型的嵌套::typeT*.

  3. "Otherwise (if T is a cv- or ref-qualified function type)" - 有趣的一点。这就是像 void (int) const& 这样的类型。这里 std::remove_reference<T>::type 又是 T。但是我们不允许形成指向T的指针,基础语言是禁止的。因此 std::remove_reference<T>::type* 格式错误,对于此重载决议,第一个重载将被忽略。只剩下第二个重载,它是由重载决议拾取的。其 return 类型的嵌套 ::typeT.