c++ русском Можем ли мы получить тип лямбда-аргумента?



sololearn на пк на русском (2)

Используя std::function , мы можем получить тип аргумента, используя argument_type second_argument_type , second_argument_type и т. Д., Но я не могу найти способ сделать то же самое с лямбдами. Является ли это возможным? (Я использую VS2010)

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

template<typename F> 
static void forward(F f)
{ 
    // Create an object of the type of the first
    // parameter to the function object F
    typedef typename F::argument_type T;
    T t;

    //...do something with 't' here (deserialize in my case)

    // Forward the object to the function
    f(t);
}

Его можно использовать так, и все работает нормально:

std::function<void(int)> f = [](int i) -> void { setValue(i); };
forward(f);

Но это не будет работать напрямую с лямбдами:

forward([](int i) -> void { setValue(i); });
//error C2039: 'argument_type' : is not a 
//member of '`anonymous-namespace'::<lambda1>'

Есть ли способ получить доступ к типам параметров таким образом, который будет работать как для объектов lambdas, так и для std::function ? Может быть, способ получить лямбда-тип std::function сначала, а затем argument_type из этого?

Исходя из ответа ниже, версия, которая работает с lambdas и std::function :

template<typename T, typename F> 
static void forward(F f)
{ 
    T t;

    //...do something with 't' here (deserialize in my case)

    f(t);
}

forward<int>([](int i) -> void { setValue(i); });

Так как int повторяется здесь, я надеялся избавиться от него - не так плохо для int но больше раздражает для длинноименных типов в паре пространств имен. Такова жизнь!


Ответ @ Люка великолепен, но я только что натолкнулся на случай, когда мне также пришлось иметь дело с указателями функций:

template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);

template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);

template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));

Это можно использовать как для функторов, так и для указателей функций:

void function(float);

struct functor {
    void operator() (int);
};

int main() {
    std::cout << std::is_same<first_argument<functor>, int>::value
              << ", "
              << std::is_same<first_argument<decltype(&function)>, int>::value 
              << std::endl;
    return 0;

}

Это не желательно в общем случае. (Обратите внимание, что для std::function<T(A)> довольно просто указать, что, например, argument_type : это просто A ! Он доступен в определении типа.)

Можно было бы потребовать, чтобы каждый тип объекта функции указывал типы аргументов, и, в свою очередь, требовал, чтобы типы замыканий, сгенерированные из лямбда-выражения, делали это. Фактически, функции до C ++ 0x, такие как адаптируемые функторы, работали бы только для таких типов.

Однако мы переходим к этому с C ++ 0x и по веским причинам. Самым простым из которых является просто перегрузка: тип функтора с шаблонным operator() (он же полиморфный функтор) просто принимает все виды аргументов; так какой должен быть argument_type ? Другая причина заключается в том, что универсальный код (обычно) пытается указать наименьшие ограничения на типы и объекты, с которыми он работает, для более легкого (повторного) использования.

Другими словами, универсальный код на самом деле не заинтересован в том, чтобы заданный Functor f , typename Functor::argument бы int . Гораздо интереснее знать, что f(0) является приемлемым выражением. Для этого C ++ 0x предоставляет такие инструменты, как decltype и std::declval (удобно упаковывая их в std::result_of ).

На мой взгляд, у вас есть два варианта: требовать, чтобы все функторы, передаваемые в ваш шаблон, использовали соглашение в стиле C ++ 03 для определения argument_type тип_п argument_type и т. П .; используйте технику ниже; или перепроектировать. Я бы порекомендовал последний вариант, но это ваш звонок, так как я не знаю, как выглядит ваша кодовая база или каковы ваши требования.

Для мономорфного типа функтора (т. Е. Без перегрузки) можно проверить член operator() . Это работает для типов закрытия лямбда-выражений.

Итак, мы объявляем этих помощников

template<typename F, typename Ret, typename A, typename... Rest>
A
helper(Ret (F::*)(A, Rest...));

template<typename F, typename Ret, typename A, typename... Rest>
A
helper(Ret (F::*)(A, Rest...) const);

// volatile or lvalue/rvalue *this not required for lambdas (phew)

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

template<typename F>
struct first_argument {
    typedef decltype( helper(&F::operator()) ) type;
};

[сложный признак может последовательно запрашивать перегрузки lvalue-rvalue / const / volatile и выставлять первый аргумент, если он одинаков для всех перегрузок, или использовать std::common_type .]





c++11