Я пытаюсь написать оболочку make_function
, которая, как и std::make_pair
, может создать объект std::function
из подходящих вызываемых объектов.
Как и make_pair
, для указателя функции foo
auto f0 = make_function(foo);
создает std::function
объект функции f0
сигнатуры правильного типа. Просто чтобы уточнить, я не возражаю время от времени давать параметры типа для make_function
в случае, если трудно (или невозможно) вывести тип полностью из параметров.
То, что я придумал до сих пор (код ниже), отлично работает для лямбда-выражений, некоторых указателей функций и функторов (я не рассматривал volatile). Но я не мог заставить его работать для результатов std::bind
или std::bind<R>
. В коде ниже
auto f2 = make_function(std::bind(foo,_1,_2,_3)); //not OK
не будет компилироваться/работать с gcc 4.8.1. Я предполагаю, что я неправильно зафиксировал operator()
для результата bind
, но я не знаю, как это исправить.
Приветствуется любая помощь в том, как исправить этот случай или улучшить другие крайние случаи.
Мой вопрос, конечно же, как исправить ошибку в примере ниже.
Для фона один из случаев, когда я использую эту оболочку, можно найти в этом вопросе: Как заставить функции C++11, принимающие параметры‹› функций, автоматически принимать лямбда-выражения. Если вы не одобряете использование std::function
или мой конкретный способ его использования, оставьте свои комментарии в этом сообщении и обсудите технические вопросы здесь.
--- ИЗМЕНИТЬ ---
Из некоторых комментариев я узнал, что это связано с проблемой неоднозначности (неоднозначность оператора вызова функции () результатов std::bind
). Как указано в ответе @Mooing Duck, решение состоит в том, чтобы явно указать типы параметров. Я обновил код, чтобы объединить три функции в ответе @Mooing Duck (с небольшим изменением параметров типа), так что оболочка make_function
теперь может обрабатывать/выводить однозначные случаи, как и раньше, и разрешать указание полной подписи типа, когда есть является двусмысленностью.
(Мой исходный код для однозначных случаев находится по адресу: https://stackoverflow.com/a/21665705/683218 и может можно протестировать по адресу: https://ideone.com/UhAk91):
#include <functional>
#include <utility>
#include <iostream>
#include <functional>
using namespace std;
// For generic types that are functors, delegate to its 'operator()'
template <typename T>
struct function_traits
: public function_traits<decltype(&T::operator())>
{};
// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
enum { arity = sizeof...(Args) };
typedef function<ReturnType (Args...)> f_type;
};
// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) > {
enum { arity = sizeof...(Args) };
typedef function<ReturnType (Args...)> f_type;
};
// for function pointers
template <typename ReturnType, typename... Args>
struct function_traits<ReturnType (*)(Args...)> {
enum { arity = sizeof...(Args) };
typedef function<ReturnType (Args...)> f_type;
};
template <typename L>
static typename function_traits<L>::f_type make_function(L l){
return (typename function_traits<L>::f_type)(l);
}
//handles bind & multiple function call operator()'s
template<typename ReturnType, typename... Args, class T>
auto make_function(T&& t)
-> std::function<decltype(ReturnType(t(std::declval<Args>()...)))(Args...)>
{return {std::forward<T>(t)};}
//handles explicit overloads
template<typename ReturnType, typename... Args>
auto make_function(ReturnType(*p)(Args...))
-> std::function<ReturnType(Args...)> {
return {p};
}
//handles explicit overloads
template<typename ReturnType, typename... Args, typename ClassType>
auto make_function(ReturnType(ClassType::*p)(Args...))
-> std::function<ReturnType(Args...)> {
return {p};
}
// testing
using namespace std::placeholders;
int foo(int x, int y, int z) { return x + y + z;}
int foo1(int x, int y, int z) { return x + y + z;}
float foo1(int x, int y, float z) { return x + y + z;}
int main () {
//unambuiguous
auto f0 = make_function(foo);
auto f1 = make_function([](int x, int y, int z) { return x + y + z;});
cout << make_function([](int x, int y, int z) { return x + y + z;})(1,2,3) << endl;
int first = 4;
auto lambda_state = [=](int y, int z) { return first + y + z;}; //lambda with states
cout << make_function(lambda_state)(1,2) << endl;
//ambuiguous cases
auto f2 = make_function<int,int,int,int>(std::bind(foo,_1,_2,_3)); //bind results has multiple operator() overloads
cout << f2(1,2,3) << endl;
auto f3 = make_function<int,int,int,int>(foo1); //overload1
auto f4 = make_function<float,int,int,float>(foo1); //overload2
return 0;
}
auto f = std::bind(foo, _1, _2);
лучше, чем аналогичныйstd::function
! - person David Rodríguez - dribeas   schedule 13.02.2014f
было введеноstd::function
для параметра функции. Иstd::function
типизированные переменные автоматически не принимаютbind
результаты или другие вызываемые объекты. - person tinlyx   schedule 13.02.2014std::bind
. - person tinlyx   schedule 13.02.2014make_pair
. Я могу программировать без него. - person tinlyx   schedule 13.02.2014auto v = make_function([](int x, int y, int z) { return x + y + z;})(1,2,3);
- person tinlyx   schedule 13.02.2014make_function([](auto x, auto y, auto z) { return x + y + z;})
не работает (добро пожаловать в C++1y!) - person Yakk - Adam Nevraumont   schedule 13.02.2014make_int
иmake_double
?make_string
?make_vector
? Вопрос остается в силе: почему вам нужен выведенныйstd::function
? Есть ли у вас потребность в этом, кроме выяснения, можете ли вы этого достичь? На самом деле вы пытаетесь применить стирание шрифта и оплатить связанные с этим расходы без особой причины. Помимо умственных упражнений, вы в основном стремитесь написать сложный механизм, который снизит производительность вашего приложения. - person David Rodríguez - dribeas   schedule 13.02.2014bind
. Мои версии не принимают возвращаемый тип, потому что его можно вывести и, следовательно, никогда не требуется указывать для функционоидов (включая лямбда-выражения). Вот что означаетdecltype(t(std::declval<Args>()...))
в моем ответе . Это этоReturnType
. - person Mooing Duck   schedule 13.02.2014make_function([](int x, int y, int z) { return x + y + z;});
, или воспроизвести его. - person Mooing Duck   schedule 13.02.2014make_function([](int x, int y, int z) { return x + y + z;});
в моем исходном коде, как я уже говорил вам ранее. Вы так и не ответили на это до сих пор. См. исходный код на странице stackoverflow.com/a/21665705/683218. - person tinlyx   schedule 13.02.2014T::operator()
, но со ссылкой мне пришлосьstd::remove_reference<T>::type::operator()
. Мне потребовалось некоторое время, но я понял. Код в моем ответе теперь выводит все теоретически выводимые сигнатуры функционоидов, поэтому я считаю его завершенным. - person Mooing Duck   schedule 13.02.2014void eat_callback(std::function<void(int)>); auto f = make_function([](long) {}); eat_callback(f);
? - person Luc Danton   schedule 13.02.2014f
будетstd::function<void(long)>&
, передача которого функции, ожидающейstd::function<void(int)>
, будет делать то, что она обычно делает, независимо от кода в вопросе или моем ответе, не так ли? - person Mooing Duck   schedule 13.02.2014