Трюк void_t
может помочь. Как работает `void_t` ?
Если у вас нет C ++ 17, вам нужно будет включить определение void_t
:
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
Добавить дополнительный аргумент шаблона в исходный шаблон, по умолчанию void
:
template <typename T, typename = void>
struct function_traits;
Объект черт для простых функций такой же, как у вас уже есть:
template <typename R, typename... A>
struct function_traits<R (*)(A...)>
{
using return_type = R;
using class_type = void;
using args_type = std:: tuple< A... >;
};
Для неконстантных методов:
template <typename R, typename... A>
struct function_traits<R (*)(A...)>
{
using return_type = R;
using class_type = void;
using args_type = std:: tuple< A... >;
};
Не забудьте const
методы:
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...) const> // const
{
using return_type = R;
using class_type = C;
using args_type = std:: tuple< A... >;
};
Наконец, важная черта. Учитывая тип класса, включая лямбда-типы, мы хотим переместиться от T
до decltype(&T::operator())
. Мы хотим убедиться, что эта черта доступна только для типов T
, для которых доступна ::operator()
, и это то, что void_t
делает для нас. Чтобы применить это ограничение, нам нужно поместить &T::operator()
в сигнатуру признака где-то, следовательно, template <typename T> struct function_traits<T, void_t< decltype(&T::operator())
template <typename T>
struct function_traits<T, void_t< decltype(&T::operator()) > >
: public function_traits< decltype(&T::operator()) >
{
};
Метод operator () в (не mutable
, не универсальных) лямбдах - const
, что объясняет, почему нам нужен шаблон const
выше.
Но в конечном итоге это очень ограничительно. Это не будет работать с общими лямбдами или объектами с шаблонами operator()
. Если вы пересмотрите свой дизайн, вы найдете другой, более гибкий подход.