2016-10-20 2 views
0

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

Вот пример. Внутри функций match, я хочу использовать знания T.

template<class T> 
void match(void (*)(T*, int)) { /* First */ } 

template<class T> 
void match(void (T::*)(int)) { /* Second */ } 

template<class T> 
void match(std::function<void(T,int)>) { /* Third */ } 

struct A 
{ 
    void f(int) {} 
}; 

void g(A*, int) {} 

match(&A::f);   // Ok, matches first 
match(&g);    // Ok, matches second 
match([](A*, int) {}); // Not Ok 
match([&](A*, int) {}); // Not Ok 
+1

Связано с [is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda] (http://stackoverflow.com/questions/7943525/is -it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda) – Jarod42

+0

@ Jarod42 Brilliant! Это именно то, что я искал! – foxcub

ответ

0

Я знаю только путь: пройти через std::function

match(std::function<void(A*, int)>([](A*, int) {})); 
match(static_cast<std::function<void(A*, int)>>([&](A*, int) {})); 
+0

Уверен, что у меня есть, но это слишком много. И на самом деле он ничего не делает, он требует, чтобы пользователь указывал тип аргумента, предоставляя его 'std :: function'. – foxcub

+0

@foxcub - я знаю; если вы покажете нам, как вы хотите использовать эту функцию в 'match()' (важно: хотите ли вы передать 'match()' параметры для вызова функции?), может быть, кто-то может обнаружить лучший способ – max66

2

Вы не можете.

template<class T> 
void g(T*, int) {} 

не работает

void g(void*, int) {} 
void g(std::string**, int) {} 

не работает.

Та же проблема сохраняется с лямбдами.

Как правило, вы можете спросить «могу ли я вызвать X с типом Y», вы не можете получить подпись.

std::function не лямбда, а лямбда-это не std::function. Они не связаны между собой, кроме того, что вы можете конвертировать лямбда в std::function с любой совместимой подписью, так же, как вы можете конвертировать любой вызываемый объект.

Если вы ограничиваете пространство проблем достаточно, вы можете написать класс признаков для извлечения подписи operator() на входящий объект и рассматривать это как аргументы лямбда.

Это плохая идея в C++ 11, и она обычно ухудшается в C++ 14 и C++ 17. [](auto a, int b){} - это lambda в C++ 14 (и многие компиляторы C++ 11 поддерживают его), и для первого аргумента он не имеет фиксированного типа.

Обычно лучший подход заключается в том, чтобы связывать подпись отдельно, чем вызываемая. Это нарушает DRY (Do not Repeat Yourself) в C++ 11, но в C++ 14 лямбда может просто принимать параметры auto&&.

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

+0

Это немного разочаровывает. Почему существует способ получить возвращаемый тип, но не типы аргументов произвольных вызовов? – foxcub

+0

@foxcub, но есть ли? 'struct foo {int operator() (int); double operator() (double); }; 'какой выведен первый аргумент и возвращаемый тип? Говоря о том, что является первым дополнением общей лямбды? – krzaq

+0

@krzaq Я вижу вашу точку с перегруженным 'operator() (...)'. Я запутался в твоем последнем вопросе.Предполагая, что лямбда имеет более одного аргумента, его первый аргумент однозначен, не так ли? – foxcub