Como ve, el tipo de devolución no es un argumento de plantilla o parte de los argumentos, por lo que no puede sobrecargar ni especializarse. Despachar a un ayudante es su mejor opción.
#include <type_traits>
template<typename Func, typename... Args>
void func_impl(Func&& f, Args&&... args, std::true_type)
-> decltype(func_impl(std::forward<Args>(args)...))
{ }
template<typename Func, typename... Args>
void func_impl(Func&& f, Args&&... args, std::false_type)
-> decltype(func_impl(std::forward<Args>(args)...))
{ }
template<typename Func, typename... Args>
auto func(Func&& f, Args&&... args)
-> decltype(func_impl(std::forward<Func>(f), std::forward<Args>(args)...))
{ return func_impl(std::forward<Func>(f), std::forward<Args>(args)...,
std::is_pointer<decltype(f(std::forward<Args>(args)...))>::type); }
parece un poco extraño para mí tomar la función por referencia rvalue sin embargo y también omitir el envío en su original ejemplo.
Otra posible solución podría ser un argumento predeterminado de plantilla y una sobrecarga en eso. Pero eso no funcionaría bien con la lista de argumentos.