A question regarding the implementation of std::add_pointer

浪子不回头ぞ 提交于 2019-12-06 04:09:59

The key is in understanding how overload resolution for detail::try_add_pointer<T>(0) works. Substituting T into detail::try_add_pointer is meant to produce an overload set that will always contain at least one member (the variable argument overload).

Whether or not the overload taking an int is discarded during overload resolution (SFINAE) is determined by the success of substituting T into typename std::remove_reference<T>::type*. When substitution succeeds, the overload exists, and is a better match in overload resolution for 0 (ellipsis are the worst possible match compared an any other conversion sequence). Either way, whichever overload is picked up in overload resolution, decltype(detail::try_add_pointer<T>(0)) will resolve to something that has a nested ::type member.

So let's go on a case by case analysis:

  1. "If T is a reference type" - Let's mark it T = T2&. Then std::remove_reference<T>::type is T2. The types to which we may form a reference are also the types to which we may form a pointer. So std::remove_reference<T>::type* is well-formed (it's T2*), and the first overload exists. It's picked up in overload resolution. The nested ::type of its return type is T2*.

  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" - In this case std::remove_reference<T>::type is simply T. We can form a pointer to any of the types in the previous list, and so std::remove_reference<T>::type* is well formed again (and it's T*). The first overload again exists and is picked up in overload resolution. The nested ::type of its return type is T*.

  3. "Otherwise (if T is a cv- or ref-qualified function type)" - The interesting bit. This speaks of types like void (int) const&. Here again std::remove_reference<T>::type is T. But we are not allowed to form a pointer to T, the base language prohibits it. Therefore std::remove_reference<T>::type* is ill-formed, and for this overload resolution the first overload is disregarded. Left with only the second overload, it's the one picked up by overload resolution. The nested ::type of its return type is T.

The inheritance is just a way of using the type_identity wrapper (already needed to form valid return types) to define the trait with minimal effort. The overload trick relies on the fact that std::remove_reference is the identity for non-reference types and that (having dealt with references) std::add_pointer<T> is T* except when there is no such type at all. In that case, SFINAE kills off the int overload and the ... is selected (where just T is produced).

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!