2

Is there any way to template a function so that it can accept either a T in the generic case, or to a specialization if the template argument resolves to something that is callable, such as a functor, function pointer or std::function?
For example, I would want something like this:

template<typename T>
void use_this(T obj) {
  obj->do_stuff();
}

template<>
void use_this<???>(??? func) {
  use_this(func());
}

use_this(MyObj); // should call first one
use_this([MyObj](){ return MyObj; }); // should call the second one
Baruch
  • 20,590
  • 28
  • 126
  • 201

2 Answers2

1

You can't partially specialize function templates, so you can't get the syntax which you indicate in your question.

You could write two function templates: one for when T is callable with no arguments, and one for when it isn't. You can then disable the function version using expression SFINAE and add an extra function parameter to make overload resolution prefer that version when it's available:

template<typename T>
void use_this(T obj, float) {
  obj->do_stuff();
}

template<typename Func>
auto use_this (Func func, int) -> decltype(func(), void()) {
  use_this(func());
}

Then you can add a wrapper which supplies the disambiguating argument itself:

template <typename T>
void use_this(T&& t) {
    use_this(std::forward<T>(t), 0);   
}

Live demo

Community
  • 1
  • 1
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
0
namespace details {
  template<template<class...>class Z, class, class...Ts>
  struct can_apply : std::false_type{};
  template<template<class...>class Z, class...Ts>
  struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,void, Ts...>

template<class Sig>
using can_invoke = can_apply< std::result_of_t, Sig >;

We now have a traits class can_invoke.

You pass it a signature. It is truthy if the signature is a valid call, and falsy otherwise.

template<class T>
std::enable_if_t< !can_invoke<T()> >
use_this( T obj ) {
  obj->do_stuff();
}
template<class T>
std::enable_if_t< can_invoke<T()> >
use_this( T func ) {
  use_this( func() );
}

I use some C++11, such as enable_if_t, void_t and result_of_t. All are easy to write in C++11, and are easy to search for.

The behavior of result_of when passed an invalid signature was changed in C++14. In C++11, it does not have to be SFINAE friendly. We can replace std::result_of_t with invoke_result_r in C++11 as follows:

template<class Sig, class=void>
struct invoke_result {};
template<class Sig>
using invoke_result = typename invoke_result<Sig>::type;

template<class F, class...Args>
struct invoke_result<F(Args...),
  decltype( void( std::declval<F>()( std::declval<Args>()... ) ) )
> {
  using type = decltype( std::declval<F>()( std::declval<Args>()... ) );
};

This is now SFINAE friendly.

can_apply is similar to C++20's is_detected, but shorter and to the point.

Note that doing this in a non-C++11 compiler, like MSVC, is impractical. You can do something similar with tag dispatching, however.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524