Подтвердить что ты не робот

Вывод сигнатуры вызова лямбда или произвольного вызываемого для "make_function"

В некоторых ситуациях желательно иметь возможность стирать вызываемый (например, функцию, указатель функции, экземпляр объекта с operator(), лямбдой, mem_fn), например, в Использование адаптеров Boost с С++ 11 lambdas, где требуется назначаемый для копирования и стандартный по умолчанию тип.

std::function был бы идеальным, но, похоже, нет возможности автоматически определять, какая подпись создает экземпляр класса std::function с. Есть ли простой способ получить подпись функции произвольного вызываемого и/или обернуть ее в соответствующий экземпляр экземпляра std::function (т.е. Шаблон функции make_function)?

В частности, я ищу того или иного из

template<typename F> using get_signature = ...;
template<typename F> std::function<get_signature<F>> make_function(F &&f) { ... }

так что make_function([](int i) { return 0; }) возвращает a std::function<int(int)>. Очевидно, что это не должно было бы работать, если экземпляр может быть вызван более чем одной сигнатурой (например, объекты с более чем одним, шаблоном или параметром по умолчанию operator() s).

Boost отлично, хотя предпочтительны не-Boost решения, которые не являются чрезмерно сложными.


Изменить: отвечая на мой собственный вопрос.

4b9b3361

Ответ 1

Я придумал довольно неприятное небиблиотечное решение, используя тот факт, что lambdas имеют operator():

template<typename T> struct remove_class { };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...)> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); };

template<typename T>
struct get_signature_impl { using type = typename remove_class<
    decltype(&std::remove_reference<T>::type::operator())>::type; };
template<typename R, typename... A>
struct get_signature_impl<R(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(&)(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(*)(A...)> { using type = R(A...); };
template<typename T> using get_signature = typename get_signature_impl<T>::type;

template<typename F> using make_function_type = std::function<get_signature<F>>;
template<typename F> make_function_type<F> make_function(F &&f) {
    return make_function_type<F>(std::forward<F>(f)); }

Любые идеи, где это можно упростить или улучшить? Любые очевидные ошибки?

Ответ 2

невыполнима. Вы можете взять адрес operator() для некоторых типов, но не для произвольного вызываемого, потому что он может иметь перегрузки или параметры шаблона. Независимо от того, будет ли это работать на лямбда, наверняка не определено, AFAIK.

Ответ 3

Для невариантных неосновных безграмотных лямбда-функций, а также простых свободных функций можно использовать следующий подход:

#include <iostream>

#include <cstdlib>

template< typename L, typename R, typename ...A >
constexpr
auto // std::function< R (A...) >
to_function_pointer(L l, R (L::*)(A...) const)
{
    return static_cast< R (*)(A...) >(l);
}

template< typename L, typename R, typename ...A >
constexpr
auto // std::function< R (A...) >
to_function_pointer(L l, R (L::*)(A...)) // for mutable lambda
{
    return static_cast< R (*)(A...) >(l);
}

template< typename L >
constexpr
auto
to_function_pointer(L l)
{
    return to_function_pointer(l, &L::operator ());
}

template< typename R, typename ...A >
constexpr
auto // std::function< R (A...) >
to_function_pointer(R (* fp)(A...))
{
    return fp;
}

namespace
{

void f() { std::cout << __PRETTY_FUNCTION__ << std::endl; }

}

int
main()
{
    to_function_pointer([] () { std::cout << __PRETTY_FUNCTION__ << std::endl; })();
    //to_function_pointer([&] () { std::cout << __PRETTY_FUNCTION__ << std::endl; })(); // can't cast from non-captureless lambda to function pointer
    to_function_pointer([] () mutable { std::cout << __PRETTY_FUNCTION__ << std::endl; })();
    to_function_pointer(f)();
    to_function_pointer(&f)();
    return EXIT_SUCCESS;
}