From what I can tell, it is based off dynamic cast logic. The dynamic cast that can fail takes a pointer and returns a pointer.
Similarly, get that can fail takes a pointer and returns one.
But really it looks like a tiny bikeshed decision of not much importance.
If we had these 3 overloads:
T const* std::get_if<T>( variant const& );
T* std::get_if<T>( variant & );
T* std::get_if<T>( variant&& ) = delete;
it would work fine. The decision to take an argument by pointer is not based off of the problem with taking references.
With dynamic_cast
and any_cast
etc, the convention is the signature is:
T& XXX_cast<T>(X&);
T* XXX_cast<T>(X*) noexcept;
ie, a throwing "cannot fail" reference cast, and a non-throwing "can fail" pointer cast. get_if
following that pattern is probably the primary reason why it takes a pointer.
Now, with the pointer version, you can do this:
using A = std::variant< int, double >;
using B = std::variant< std::string, char >;
std::variant< A, B > bob;
int* pint = std::get_if<int>( std::get_if<A>( &bob ) );
which is much more complex without get_if
taking a pointer.
A better question is probably why it is called std::get_if
and not std::get
; or, rather why it isn't called std::dynamic_cast_if
.