可能的实施
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)) {};
Run Code Online (Sandbox Code Playgroud)
上述(可能的)实现的描述如下:
如果 T 是引用类型,则提供成员 typedef type,它是指向引用类型的指针。
否则,如果 T 命名对象类型、非 cv 或 ref 限定的函数类型或(可能是 cv 限定的)void 类型,则提供类型 T* 的成员 typedef 类型。
否则(如果 T 是 cv 或 ref 限定的函数类型),则提供成员 typedef type,它是类型 T。
在上面的(可能的)实现代码中,显然struct add_pointer派生自detail::try_add_pointer<T>(0).
detail::try_add_pointer<T>从接受int参数的重载返回的类型派生,将成员解析typedef type为上述三种可能性之一,其背后的逻辑是什么?具体来说,这如何解决 ifT是 acv-或ref-限定函数类型的可能性?
关键是理解重载解析是如何detail::try_add_pointer<T>(0)工作的。代T入detail::try_add_pointer意味着生成一个重载集,该重载集始终包含至少一个成员(变量参数重载)。
int在重载决策 ( SFINAE ) 期间是否丢弃采用 an 的重载取决于是否成功代T入typename std::remove_reference<T>::type*。当替换成功时,重载存在,并且在重载解析中是 0 的更好匹配(与任何其他转换序列相比,省略号是最差的可能匹配)。无论哪种方式,无论在重载解析中选择哪个重载,decltype(detail::try_add_pointer<T>(0))都将解析为具有嵌套::type成员的内容。
那么我们就来具体案例分析一下:
“如果 T 是引用类型” - 让我们标记它T = T2&。然后std::remove_reference<T>::type是T2。我们可以形成引用的类型也是我们可以形成指针的类型。所以std::remove_reference<T>::type*是格式良好的(它是T2*),并且第一个重载存在。它是在过载解析中拾取的。::type其返回类型的嵌套是T2*.
“否则,如果 T 命名一个对象类型、一个不是 cv 或 ref 限定的函数类型,或者一个(可能是 cv 限定的)void 类型” - 在这种情况下std::remove_reference<T>::type就是简单的T。我们可以形成一个指向前面列表中任何类型的指针,因此std::remove_reference<T>::type*又是格式良好的(并且它是T*)。第一个重载再次存在并在重载决策中被拾取。::type其返回类型的嵌套是T*.
“否则(如果 T 是 cv 或 ref 限定的函数类型)” - 有趣的一点。这涉及诸如 之类的类型void (int) const&。这里又std::remove_reference<T>::type是T。但是我们不允许形成指向 的指针T,基本语言禁止这样做。因此std::remove_reference<T>::type*是格式错误的,并且对于此重载决策,第一个重载将被忽略。只剩下第二个重载,这是重载解析所拾取的重载。::type其返回类型的嵌套是T.
| 归档时间: |
|
| 查看次数: |
309 次 |
| 最近记录: |