A question regarding the implementation of std::add_pointer

巧了我就是萌 提交于 2020-02-28 07:53:27

问题


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

The description for the above (possible) implementation reads:

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.

In the (possible) implementation code above, apparently struct add_pointer derives from the type returned by detail::try_add_pointer<T>(0).

What is the logic behind deriving from the type returned by the overload of detail::try_add_pointer<T> taking int argument, to resolve the member typedef type to one of the three possibilities described above? Specifically, how does this address the possibility if T is a cv- or ref-qualified function type?


回答1:


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.




回答2:


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).



来源:https://stackoverflow.com/questions/57506069/a-question-regarding-the-implementation-of-stdadd-pointer

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