Some boilerplate:
namespace details {
template<template<class...>class, class, class...>
struct can_apply:std::false_type{};
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<template<class...>class Z, class... Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
templateclass Z, class... Ts>
using can_apply = details::can_apply;
A trait to determine if you should *
:
template<class T>
using unary_star_r = decltype( *std::declval<T>() );
template<class T>
using can_unary_star = can_apply<unary_star_r, T>;
dispatch
takes two arguments and picks between them at compile time:
template<bool /*false*/>
struct dispatch_t {
template<class T, class F>
F operator()(T, F f)const{ return std::move(f); }
};
template<>
struct dispatch_t<true> {
template<class T, class F>
T operator()(T t, F)const{ return std::move(t); }
};
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__))\
->decltype(__VA_ARGS__)\
{ return __VA_ARGS__; }
template<bool b, class T, class F>
auto
dispatch( T t, F f )
RETURNS( dispatch_t<b>{}( std::move(t), std::move(f) ) )
We are almost done...
Now our work. We write function objects that represent dereferencing a type, doing nothing, and maybe doing either:
struct maybe_deref_t;
struct do_deref_t;
struct identity_t {
template<class T>
T operator()(T&& t)const { return std::forward<T>(t); }
};
struct do_deref_t {
template<class T>
auto operator()(T&& t)const
RETURNS( maybe_deref_t{}( *std::forward<T>(t) ) )
};
Here is the work:
struct maybe_deref_t {
template<class T>
auto operator()(T&& t)const
RETURNS(
dispatch< can_unary_star<T>::value >(
do_deref_t{},
identity_t{}
)(
std::forward<T>(t)
)
)
};
and a helper for better syntax:
template<class T>
auto maybe_deref( T&& t )
RETURNS( maybe_deref_t{}( std::forward<T>(t) ) )
Test code:
int main() {
auto bob = new int*( new int(7) ); // or 0 or whatever
std::cout << maybe_deref(bob) << "\n";
}
live example.
I originally wrote this in a C++14 style, then back-translated it to C++11. In C++14 it is much cleaner.