std:: add_pointer, what the try_add_pointer for in the possible implementation?

Viewed 281

In cppreference add_pointer, it is said we may implement it as:

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

My question is what is the try_add_pointer for? I know it is SFINAE. But why the implementation need it here?

2 Answers

If you read over the page on cppreference, you'll notice this sentence

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

Function have their own types. Normally, what you'll see in sane code is types like int(int), i.e. the type of a function taking a single integer and returning an integer. This is the type of argument std::function expects, e.g std::function<int(int)>.

However the set of function types also contains oddities that relate to member functions. For instance

struct foo {
  int bar(int) const;
};

int(int) const is the function type of bar. While this type exists in the type system, it is limited in its uses.

[dcl.fct]

6 A cv-qualifier-seq or a ref-qualifier shall only be part of:

  • the function type for a non-static member function,
  • the function type to which a pointer to member refers,
  • the top-level function type of a function typedef declaration or alias-declaration,
  • the type-id in the default argument of a type-parameter ([temp.param]), or
  • the type-id of a template-argument for a type-parameter ([temp.names]).

The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. In the latter case, the cv-qualifiers are ignored. [ Note: a function type that has a cv-qualifier-seq is not a cv-qualified type; there are no cv-qualified function types. — end note ] [ Example:

typedef void F();
struct S {
  const F f;        // OK: equivalent to: void f();
};

— end example ] The return type, the parameter-type-list, the ref-qualifier, and the cv-qualifier-seq, but not the default arguments ([dcl.fct.default]) or the exception specification ([except.spec]), are part of the function type.

So the trait allows you to feed it a function type like int() const, and it's expected to return it unchanged.

That's where try_add_pointer comes in. Since as you can see from the list above, there are no regular pointers to these sort of functions, we will get a substitution failure in typename std::remove_reference<T>::type*. But thanks to SFINAE, a fallback exists.

It is to handle reference "abominable function types"

std::add_pointer<int&>::type is int*. int&* is invalid.

std::add_pointer_t<int() const> is int () const

Related