Принять любой тип вызываемого, а также узнать тип аргумента

Я не уверен, возможно ли это, так что я хочу узнать.

Я хотел бы создать функцию, которая принимает любой объект-функтор/вызываемый объект, но я хочу знать, что такое тип аргумента. (но не применять его)

Итак, это фиксирует все, но не дает мне тип аргумента:

template < typename T >
void optionA( T );

Этот захват больше всего и имеет тип аргумента

template < typename T >
void optionB( std::function< void(T) > );

Но это не позволяет лямбда, поэтому

optionB( [](int){} );

не будет компилироваться. Что несколько странно, поскольку это скомпилируется:

std::function< void(int) > func = [](int){};
optionB( func );

Итак, есть способ принять все параметры, а также узнать, какой тип аргумента ожидается?

заблаговременно!

- изменить -

Причина, по которой я хочу это сделать, связана с тем, что я хочу, чтобы пользователь моей библиотеки зарегистрировал обратный вызов с определенным типом. Для меня наиболее естественным способом является

auto callback = []( int val ) { cout << "my callback " << val << endl; };
object.register( callback );

(с использованием или без использования обратного вызова в качестве промежуточной переменной)

Поскольку мне нужно изменить поведение, основанное на типе значения, которое ожидает пользователь, мне нужно знать, какой тип он/она ожидает.

Ответ 1

Вот пример, который будет работать для большинства callables, включая функторы и lambdas (хотя не для общих функторов как @Yakk, продемонстрированных в комментарии к вопросу).

Код также может быть полезен при определении типа возвращаемого значения и нескольких аргументов.

template <typename T>
struct func_traits : public func_traits<decltype(&T::operator())> {};

template <typename C, typename Ret, typename... Args>
struct func_traits<Ret(C::*)(Args...) const> {
    using result_type =  Ret;

    template <std::size_t i>
    struct arg {
        using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
    };
};

template <typename T>
void option(T&& t) {
    using traits = func_traits<typename std::decay<T>::type>;

    using return_t = typename traits::result_type;         // Return type.
    using arg0_t = typename traits::template arg<0>::type; // First arg type.

    // Output types.
    std::cout << "Return type: " << typeid(return_t).name() << std::endl;
    std::cout << "Argument type: " << typeid(arg0_t).name() << std::endl;
}

Чтобы добавить поддержку для обычных функций, добавьте специализацию, например.

template <typename Ret, typename... Args>
struct func_traits<Ret(*)(Args...)> { /* ... */ }

Дополнительная информация: Возможно ли определить тип параметра и тип возвращаемого значения лямбда?

Ответ 2

template < typename T >
void option( function< void(T) > )
{
    cout << typeid( T ).name() << endl;
}

template < typename T >
void option( void (*func)(T) )
{
    option( function< void(T) >( func ) );
}

template< typename F, typename A >
void wrapper( F &f, void ( F::*func )( A ) const )
{
    option( function< void(A) >( bind( func, f, placeholders::_1 ) ) );
}

template< typename F, typename A >
void wrapper( F &f, void ( F::*func )( A ) )
{
    option( function< void(A) >( bind( func, f, placeholders::_1 ) ) );
}

template < typename T >
void option( T t )
{
    wrapper( t, &T::operator() );
}

void test( int )
{
}

struct Object
{
    void operator ()( float )
    {
    }
};

int main( int, char *[] )
{
    Object obj;

    option( test );
    option( [](double){} );
    option( obj );

    return 0;
}

Основываясь на информации, найденной здесь С++ 0x: перегрузка по лямбда-арности, которую я нашел через ссылку @dyps

Это не лучшее решение, так как оно требует перегрузок для const/non-const/volatile и т.д. Он выполняет свою работу с точки зрения исходной проблемы, которую я пытался решить...