C++11: Как написать функцию-оболочку для создания объектов `std::function`

Я пытаюсь написать оболочку 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;
}

Идея


person tinlyx    schedule 12.02.2014    source источник
comment
Почему вы хотите сделать это в первую очередь? auto f = std::bind(foo, _1, _2); лучше, чем аналогичный std::function!   -  person David Rodríguez - dribeas    schedule 13.02.2014
comment
Причина в том, что мне нужно, чтобы f было введено std::function для параметра функции. И std::function типизированные переменные автоматически не принимают bind результаты или другие вызываемые объекты.   -  person tinlyx    schedule 13.02.2014
comment
Кроме того, это просто что-то хорошее для полноты, ИМО.   -  person tinlyx    schedule 13.02.2014
comment
Что вы подразумеваете под типизированными переменными std::function, которые автоматически не принимают результаты привязки или другие вызываемые объекты? Что-то вроде этого: ">coliru.stacked-crooked.com/a/d66117115e71a7ee ?   -  person Richard Vock    schedule 13.02.2014
comment
Вот что я имею ввиду: stackoverflow.com/questions/20722918/   -  person tinlyx    schedule 13.02.2014
comment
@TingL: Это невозможно в общем смысле, не всегда возможно вывести типы параметров, особенно для функционоидов.   -  person Mooing Duck    schedule 13.02.2014
comment
Итак, вы имели в виду лямбда-выражения, а не результат std::bind... Когда дело доходит до общих лямбда-выражений в С++ 1y, я думаю, это никак не сработает...   -  person Richard Vock    schedule 13.02.2014
comment
Нет, я решил проблему для лямбды, но не для std::bind. Я подозреваю, что это возможно по крайней мере для основанных случаев. Вот почему я спрашиваю здесь.   -  person tinlyx    schedule 13.02.2014
comment
@MooingDuck. Спасибо что подметил это. Я хотел охватить только основные случаи, такие как большинство функций, которые мы видим, лямбда-выражения и std::bind обычных функций. Этого будет достаточно для меня. Кроме того, я не возражаю против конкретного указания типов, если компилятор иногда не может это понять.   -  person tinlyx    schedule 13.02.2014
comment
@TingL: Кроме того, связанный вопрос был связан с тем, что функции не могут вывести тип возвращаемого значения и тип параметра лямбды, что является той же проблемой, с которой вы столкнулись. Если вы сможете решить эту проблему, это полностью устранит необходимость в этом.   -  person Mooing Duck    schedule 13.02.2014
comment
@MooingDuck Думаю, я понял для лямбда вывод типов возврата и параметров. Но тот же метод здесь не работает для std::bind. В каком-то смысле вы правы, вопрос в том, как технически расширить эту технику, чтобы выяснить сигнатуру типа результата std::bind.   -  person tinlyx    schedule 13.02.2014
comment
Мне это нужно как функция удобства, в значительной степени make_pair. Я могу программировать без него.   -  person tinlyx    schedule 13.02.2014
comment
Я не понимаю, что вы имеете в виду. Вы можете попробовать auto v = make_function([](int x, int y, int z) { return x + y + z;})(1,2,3);   -  person tinlyx    schedule 13.02.2014
comment
Вы продолжаете искать что-то, что невозможно решить хорошо (и хакерские решения будут становиться все более плохими с C++1y), и неправильное решение почти для каждого практического случая, который я исследовал, где это пытались решить. . Он не решает лямбда-выражения, потому что make_function([](auto x, auto y, auto z) { return x + y + z;}) не работает (добро пожаловать в C++1y!)   -  person Yakk - Adam Nevraumont    schedule 13.02.2014
comment
Ну, я отошел от своих прошлых привычек С++ и исследую функциональное программирование, если вы это имеете в виду. Я помню, когда STL все еще распространяется в виде кучи текстовых файлов, ее автор утверждал, что, по его мнению, пришло время выйти за рамки ООП и заняться функциональным программированием.   -  person tinlyx    schedule 13.02.2014
comment
Я не знаю, что сказать, если вам так не нравится моя практика. Но я не виню тебя за то, что ты это чувствуешь.   -  person tinlyx    schedule 13.02.2014
comment
что-то приятное для завершения, IMO А как насчет make_int и make_double? make_string? make_vector? Вопрос остается в силе: почему вам нужен выведенный std::function? Есть ли у вас потребность в этом, кроме выяснения, можете ли вы этого достичь? На самом деле вы пытаетесь применить стирание шрифта и оплатить связанные с этим расходы без особой причины. Помимо умственных упражнений, вы в основном стремитесь написать сложный механизм, который снизит производительность вашего приложения.   -  person David Rodríguez - dribeas    schedule 13.02.2014
comment
Это мелочи, поэтому ненужные.   -  person tinlyx    schedule 13.02.2014
comment
В частности, они принимают типы, которые вы ожидаете, что они примут естественным образом, например. строка принимает const char [], так что не беспокойтесь.   -  person tinlyx    schedule 13.02.2014
comment
@TingL: (A) Это редактирование не является частью вопроса и, следовательно, ему не принадлежит. Поместите код на другую страницу и оставьте здесь комментарий со ссылкой на него. (B) В вашем редактировании есть повторяющиеся функции для указателей на элементы, и вы полностью сломали ту, которая обрабатывает bind. Мои версии не принимают возвращаемый тип, потому что его можно вывести и, следовательно, никогда не требуется указывать для функционоидов (включая лямбда-выражения). Вот что означает decltype(t(std::declval<Args>()...)) в моем ответе . Это это ReturnType.   -  person Mooing Duck    schedule 13.02.2014
comment
Я до сих пор не могу понять, как работает ваш make_function([](int x, int y, int z) { return x + y + z;});, или воспроизвести его.   -  person Mooing Duck    schedule 13.02.2014
comment
Странный. У меня никогда не было проблем с make_function([](int x, int y, int z) { return x + y + z;}); в моем исходном коде, как я уже говорил вам ранее. Вы так и не ответили на это до сих пор. См. исходный код на странице stackoverflow.com/a/21665705/683218.   -  person tinlyx    schedule 13.02.2014
comment
Исходный код на ideone: ideone.com/UhAk91 работает нормально.   -  person tinlyx    schedule 13.02.2014
comment
Дайте мне знать, если вы все еще не можете воспроизвести мои результаты. Я переключаю порядок или тип возврата, потому что хочу иметь возможность указать его явно, когда я явно задаю другие параметры типа. Исходный код также работает, определяя возвращаемый тип.   -  person tinlyx    schedule 13.02.2014
comment
@TingL: я понял это! Я не мог воспроизвести его, потому что я захватывал по ссылке rvalue, а вы захватывали по значению. В качестве значения можно было вывести T::operator(), но со ссылкой мне пришлось std::remove_reference<T>::type::operator(). Мне потребовалось некоторое время, но я понял. Код в моем ответе теперь выводит все теоретически выводимые сигнатуры функционоидов, поэтому я считаю его завершенным.   -  person Mooing Duck    schedule 13.02.2014
comment
@MooingDuck В этом случае обновите свой ответ соответствующим образом. Я не думал, что мой код был взломан все это время.   -  person tinlyx    schedule 13.02.2014
comment
Кстати, спасибо за ваш ответ и за то, что действительно работали над ним. Я учился у него. И вы, кажется, единственный респондент, который действительно проделал некоторую работу в этом посте до сих пор.   -  person tinlyx    schedule 13.02.2014
comment
Думали ли вы о последствиях выполнения чего-то вроде void eat_callback(std::function<void(int)>); auto f = make_function([](long) {}); eat_callback(f);?   -  person Luc Danton    schedule 13.02.2014
comment
@LucDanton: f будет std::function<void(long)>&, передача которого функции, ожидающей std::function<void(int)>, будет делать то, что она обычно делает, независимо от кода в вопросе или моем ответе, не так ли?   -  person Mooing Duck    schedule 13.02.2014
comment
Это выглядит довольно похоже на (мой вопрос) functio" title="вывод сигнатуры вызова лямбды или произвольного вызова для make functio"> stackoverflow.com/questions/11893141/   -  person ecatmur    schedule 19.03.2014
comment
@ecatmur Действительно, за исключением того, что мой вопрос здесь касается обработки объектов привязки, которые я не мог решить ранее, как описано в ОП. Есть также другие более ранние вопросы на SO о выводе типов лямбда stackoverflow.com/questions/3867276, что вдохновило меня на вопрос, почему игра с ответами в нем. Кстати, ваш ответ устраняет двусмысленность в объекте привязки?   -  person tinlyx    schedule 19.03.2014


Ответы (3)


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

//plain function pointers
template<typename... Args, typename ReturnType>
auto make_function(ReturnType(*p)(Args...))
    -> std::function<ReturnType(Args...)> 
{return {p};}

//nonconst member function pointers
template<typename... Args, typename ReturnType, typename ClassType>
auto make_function(ReturnType(ClassType::*p)(Args...)) 
    -> std::function<ReturnType(Args...)>
{return {p};}

//const member function pointers
template<typename... Args, typename ReturnType, typename ClassType>
auto make_function(ReturnType(ClassType::*p)(Args...) const) 
    -> std::function<ReturnType(Args...)>
{return {p};}

//qualified functionoids
template<typename FirstArg, typename... Args, class T>
auto make_function(T&& t) 
    -> std::function<decltype(t(std::declval<FirstArg>(), std::declval<Args>()...))(FirstArg, Args...)> 
{return {std::forward<T>(t)};}

//unqualified functionoids try to deduce the signature of `T::operator()` and use that.
template<class T>
auto make_function(T&& t) 
    -> decltype(make_function(&std::remove_reference<T>::type::operator())) 
{return {std::forward<T>(t)};}

Переменные:

int func(int x, int y, int z) { return x + y + z;}
int overloaded(char x, int y, int z) { return x + y + z;}
int overloaded(int x, int y, int z) { return x + y + z;}
struct functionoid {
    int operator()(int x, int y, int z) { return x + y + z;}
};
struct functionoid_overload {
    int operator()(int x, int y, int z) { return x + y + z;}
    int operator()(char x, int y, int z) { return x + y + z;}
};
int first = 0;
auto lambda = [](int x, int y, int z) { return x + y + z;};
auto lambda_state = [=](int x, int y, int z) { return x + y + z + first;};
auto bound = std::bind(func,_1,_2,_3);

Тесты:

std::function<int(int,int,int)> f0 = make_function(func); assert(f0(1,2,3)==6);
std::function<int(char,int,int)> f1 = make_function<char,int,int>(overloaded); assert(f1(1,2,3)==6);
std::function<int(int,int,int)> f2 = make_function<int,int,int>(overloaded); assert(f2(1,2,3)==6);
std::function<int(int,int,int)> f3 = make_function(lambda); assert(f3(1,2,3)==6);
std::function<int(int,int,int)> f4 = make_function(lambda_state); assert(f4(1,2,3)==6);
std::function<int(int,int,int)> f5 = make_function<int,int,int>(bound); assert(f5(1,2,3)==6);
std::function<int(int,int,int)> f6 = make_function(functionoid{}); assert(f6(1,2,3)==6);
std::function<int(int,int,int)> f7 = make_function<int,int,int>(functionoid_overload{}); assert(f7(1,2,3)==6);
std::function<int(char,int,int)> f8 = make_function<char,int,int>(functionoid_overload{}); assert(f8(1,2,3)==6);

http://coliru.stacked-crooked.com/a/a9e0ad2a2da0bf1f lambda была успешной, потому что она была неявно преобразована в указатель функции, потому что ваш пример не фиксирует никакого состояния. Обратите внимание, что моему коду требуются типы параметров для перегруженных функций, функционоидов с перегруженным оператором() (включая привязку), но теперь он может вывести все неперегруженные функционоиды.

Строки decltype сложны, но они используются для вывода типов возвращаемых значений. Обратите внимание, что ни в ОДНОМ из моих тестов мне не нужно указывать тип возвращаемого значения. Давайте разложим make_function<short,int,int> так, как если бы T было char(*)(short, int, int):

-> decltype(t(std::declval<FirstArg>(), std::declval<Args>()...))(FirstArg, Args...)
`std::declval<FirstArg>()` is `short{}` (roughly)
-> decltype(t(short{}, std::declval<Args>()...))(FirstArg, Args...)
`std::declval<Args>()...` are `int{}, int{}` (roughly)
-> decltype(t(short{}, int{}, int{})(FirstArg, Args...)
`t(short{}, int{}, int{})` is an `int{}` (roughly)
-> decltype(short{})(FirstArg, Args...)
`decltype(int{})` is `int`
-> int(FirstArg, Args...)
`FirstArg` is still `short`
-> int(short, Args...)
`Args...` are `int, int`
-> int(short, int, int)
So this complex expression merely figures out the function's signature
well, that should look familiar...
person Mooing Duck    schedule 12.02.2014
comment
Я пробовал: auto v = make_function([](int x, int y, int z) { return x + y + z;})(1,2,3);, вроде нормально. - person tinlyx; 13.02.2014
comment
@TingL: здесь я разобрался с лямбда-выражениями с состоянием, перегруженными функциями и перегруженными функциями-членами. - person Mooing Duck; 13.02.2014
comment
Спасибо за вашу помощь. Я добавил ваш тест lambda_state и попробовал, lambda_state, похоже, отлично работает с кодом в OP. Пожалуйста, смотрите код обновления. Я использую gcc4.8.1 от mingw32. - person tinlyx; 13.02.2014
comment
Не могли бы вы немного объяснить -> std::function<decltype(std::declval<T>()(std::declval<Args>()...))(Args...)> ? Это может быть то, что я пропустил. Захватывает ли это также определенные operator()? - person tinlyx; 13.02.2014
comment
@TingL: он захватит любой функционоид с любым количеством параметров. К сожалению, чтобы использовать эту версию, вы также должны указать типы параметров, чтобы он мог определить, какую перегрузку использовать. - person Mooing Duck; 13.02.2014
comment
@MooingDuck Вы должны называть оболочки для указателей на функции-члены как-то иначе, поскольку вы используете их только для вывода типа. В то же время вы можете сделать правильные для функций-членов, которые добавляют в качестве первого аргумента ClassType&. Это позволит вам также работать с функциями-членами, такими как &SomeClass::foo. - person Svalorzen; 05.07.2016
comment
@Svalorzen: не так просто, как я ожидал, поскольку кажется, что лямбда без сохранения состояния использует эту перегрузку. - person Mooing Duck; 06.07.2016
comment
@MooingDuck Как насчет чего-то подобного? coliru.stacked-crooked.com/a/d9a12a37d61056a8 я также немного упростил удалив вещь FirstArg, так как я думаю, что это на самом деле не нужно (по крайней мере, утверждения проходят в любом случае). Не стесняйтесь обновлять свой ответ, используя это, если вам это нравится. - person Svalorzen; 07.07.2016
comment
@Svalorzen: я действительно подумал об этом после вашего первого прохода, но я чувствовал, что они сбивают с толку, поскольку приводят к другой подписи, и не нашел времени, чтобы выяснить, нормально это или нет. Я думаю, что я был бы счастлив с make_function(&random_class::foo, r);, который приводит к ожидаемой подписи, но обнаружил, что это нетривиально, и не закончил его. - person Mooing Duck; 07.07.2016
comment
@MooingDuck Имеет смысл, я тоже попробую изучить это. Тем не менее, я не нахожу разные сигнатуры сбивающими с толку - напротив, они ясно указывают, что эти функции на самом деле никогда не вызываются. Мне пришлось немного повозиться с кодом, прежде чем я понял это (поскольку они, похоже, возвращают значение, как и все другие функции). - person Svalorzen; 07.07.2016
comment
@MooingDuck Как насчет этого? coliru.stacked-crooked.com/a/57562ee242b6cce9 Я пробовал с std::bind, но это было очевидно, что это не сработает, так почему бы не использовать лямбда-выражения в этот момент. - person Svalorzen; 07.07.2016
comment
@MooingDuck Не удалось отредактировать мой предыдущий комментарий, так как я избегаю бесполезного <Args...> coliru.stacked -crooked.com/a/d3479dd8a17c657c - person Svalorzen; 07.07.2016

В общем, вы не можете решить эту проблему без строгого ограничения: все, что вы передаете make_function, может вызываться только с одной сигнатурой.

Что вы собираетесь делать с чем-то вроде:

struct Generic
{
    void operator()() { /* ... */ }
    void operator()() const { /* ... */ }

    template<typename T, typename... Ts>
    T operator()(T&& t, Ts&&...) { /* ... */ }

    template<typename T, typename... Ts>
    T operator()(T&& t, Ts&&...) const { /* ... */ }
};

Общие лямбда-выражения С++ 14 будут иметь ту же проблему.

Подпись в std::function основана на том, как вы планируете ее вызывать, а не на том, как вы ее создаете/назначаете.

Вы также не можете решить это для std::bind, так как оно имеет неопределенную арность:

void foo() { std::cout << "foo()" << std::endl; }
//...

auto f = std::bind(foo);
f();                 // writes "foo()"
f(1);                // writes "foo()"
f(1, 2, 3, 4, 5, 6); // writes "foo()"
person Nevin    schedule 12.02.2014
comment
Спасибо за ваши комментарии. В случае неоднозначности можно ли конкретно указать тип результата и тип аргументов в make_function? В настоящее время я хотел, чтобы базовые случаи работали, а другие оставались для будущей работы. - person tinlyx; 13.02.2014
comment
@TingL: чем auto f = std::make_function<void,int>(Generic()) лучше, чем std::function<void,int> f = Generic? - person Mooing Duck; 13.02.2014
comment
Да, в худшем случае, наверное, ненамного лучше. Но во многих случаях обычного использования, когда параметры типа могут быть выведены (например, текущая необобщенная лямбда), это намного лучше. - person tinlyx; 13.02.2014
comment
@Nevin, твой пример auto f = std::bind(foo); не компилируется в моем случае. - person tinlyx; 13.02.2014
comment
См. мой другой пост stackoverflow.com/questions/20722918/. Арити не было проблемой для лямбды. По общему признанию, я полностью игнорирую проблему параметров по умолчанию и, возможно, подобные проблемы. - person tinlyx; 13.02.2014
comment
В С++ 14 для каждой лямбды с определенной комбинацией типов шаблонов будет несколько операторов вызова функций? - person tinlyx; 13.02.2014

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

Хороший способ решить эту проблему включает std::result_of.

Предположим, вы создаете структуру управления циклом, которая принимает лямбда-выражение или другой функционал. Если этот функционал возвращает void, вы хотите, чтобы цикл не контролировался. Если он возвращает bool или подобное, вы хотите зациклиться, пока он возвращает true. Если он возвращает enum ControlFlow, обратите внимание на возвращаемое значение ControlFlow (скажем, continue или break). Рассматриваемая функция принимает либо повторяющийся элемент, и, возможно, некоторые дополнительные данные (индекс в итерации, возможно, некоторую метаинформацию о «местоположении» об элементе и т. д.).

std::result_of позволит вам симулировать вызов переданного типа с другим количеством аргументов. Затем класс свойств может выяснить, какая из вышеперечисленных сигнатур является «наиболее подходящей», а затем направить к реализации, которая обрабатывает эту сигнатуру (возможно, обернув «более простые» случаи в лямбду и вызвав более сложные случаи).

Наивно, ваш make_function мог бы столкнуться с этой проблемой, потому что тогда вы могли бы просто перегрузить различные std::function< blah(etc) > случаи. Но с auto параметрами, поступающими по трубе, и std::bind уже выполняющими идеальную пересылку, это подходит только для самых простых случаев.

std::result_of классы свойств (и, возможно, связанные с ними сопоставление понятий и requires предложений) и диспетчеризация тегов (или SFINAE в крайнем случае).

Большим недостатком является то, что вам в конечном итоге придется управлять порядком переопределения самостоятельно в полуручном режиме. Я мог бы увидеть некоторую полезность во вспомогательных классах, где вы предоставляете список сигнатур для сопоставления, и он либо создает boost::variant, либо вы также создаете канонический вывод и метод преобразования в этот канонический вывод.

Короткий ответ? Реализация std::bind зависит от конкретной реализации, но она может включать в себя эквивалент идеальной пересылки пакетов переменных параметров и, как таковая, не подходит для вашего метода «получить адрес единственного конкретного operator()», который вы используете.

В качестве другого примера:

template <typename A,typename B> 
vector<B> map(std::function<B (A)> f, vector<A> arr) {
   vector<B> res;
   for (int i=0;i<arr.size();i++) res.push_back(f(arr[i]));
   return res;
}

следует писать так:

template<typename expression>
using result = typename std::result_of<expression>::type;
template<typename expression>
using decayed_result = typename std::decay<result<expression>>::type;

template <typename function,typename B> 
vector<decayed_result<function(B)>> map(function&& f, vector<A> const& arr) {
   vector<decayed_result<function(B)>> res;
   res.reserve( arr.size() );
   for (A const& a : arr) {
     res.push_back( f(a) );
   }
   return res;
}

опять же, result_of - правильное решение, а не преобразование без необходимости в std::function.

Для fold_right получаем:

template<bool b, typename T=void>
using EnableIf = typename std::enable_if<b,T>::type;

template<typename function, typename src, typename dest>
EnableIf<
  std::is_convertible< result<function(src, dest)>, dest >::value,
  std::vector<dest>
>
fold_right( function&& f, std::vector<src> const& v, dest initial )

который снова пропускает любое стирание типа на f. И если вы действительно хотите стереть тип на f, вы можете сделать:

template<typename T> struct identity { typedef T type; };
template<typename T> using do_not_deduce = typename identity<T>::type;

template<typename src, typename dest>
std::vector<dest> fold_right( do_not_deduce< std::function<dest(src,dest)> > f, std::vector<src> const& v, dest init );

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

Результат make_function зависит от полного типа источника, что делает вывод стирания типа почти полностью бесполезным.

person Yakk - Adam Nevraumont    schedule 12.02.2014
comment
Я думаю, вы неправильно поняли мою причину преобразования лямбда в std::function. По крайней мере, ваша интерпретация сильно отличается от того, что я думал или писал. - person tinlyx; 13.02.2014
comment
Но в стороне, спасибо за указание на эту проблему реализации. Я надеюсь, что std::bind. Надеюсь, кто-то сможет дать развернутый ответ по технической проблеме. - person tinlyx; 13.02.2014
comment
@TingL Предоставьте мне практическую задачу, которая напечатает вывод того, что std::function вы хотите преобразовать, чтобы решить. Я уверен, что может быть один там, но я, честно говоря, не встречал его. Я добавил решение проблемы, которая вдохновила вас на ваш предыдущий пост, в свой, снова заменив необходимость выводить тип std::function<A(B)>. - person Yakk - Adam Nevraumont; 13.02.2014
comment
дайте конкретные ссылки. И снова у меня нет времени на очередной раунд обсуждения вариантов использования. Как я уже сказал в вопросе, см. мой другой пост об использовании. Если вам это не нравится, покритикуйте меня там. - person tinlyx; 13.02.2014
comment
Это может быть непрактично для вас, я уважаю это. Я использую его в своем собственном программировании. Этого достаточно для меня. Я надеюсь, вы можете терпеть это и тот факт, что у меня нет времени объяснять, что делает мой код. - person tinlyx; 13.02.2014
comment
Просто чтобы дать вам представление, я написал sed-скрипты для извлечения сигнатур типа std::function из функций до того, как смог получить грубый make_function. Версия, которая у меня есть до сих пор, уже очень помогает мне. - person tinlyx; 13.02.2014
comment
Бессмысленно утверждать, что один правильный код должен быть написан по-другому только потому, что он вам нравится. Сколько бы вы ни показывали, что result_of работает, вы не показали, что не следует использовать std::function‹›, где сигнатура типа легко читается и пишется по буквам. - person tinlyx; 13.02.2014
comment
Кроме того, вы так же, как и многие люди в этом посте, догадываетесь о чьих-то намерениях по минимальному примеру (пример map), специально созданному для демонстрации проблемы на уровне языка, а затем углубляетесь в то, как минимальный пример может лучше писать по учебнику. - person tinlyx; 13.02.2014
comment
@TingL, поэтому я попросил привести реальный пример, в котором вывод типа лямбда или другого функтора в тип, стертый std::function, является практической проблемой. Если вы приводите только игрушечные примеры, и каждый отдельный игрушечный пример лучше решается (как эффективно, так и идиоматически) с помощью других методов... не стоит ли рассматривать ваш метод как неправильный подход в C++? ? Теперь верно то, что в C++11 результирующий код нелегко читать: но с облегченными концепциями в C++1y методы, которые я описываю, обобщаются в концепции. Ваша техника становится менее полезной в C++1y. - person Yakk - Adam Nevraumont; 13.02.2014
comment
вполне возможно, что мой подход неверен для всех. Но в моем случае ясность имеет большее значение, потому что я хотел бы использовать функции более высокого порядка. Если result_of и тому подобное, код становится (только мой личный опыт) доступным только для записи в определенный момент времени. - person tinlyx; 13.02.2014
comment
c++1y, с концепциями — это, конечно, то, чего все с нетерпением ждут. Но пока я не увижу, как концепции преобразуются в удобочитаемость типов, сравнимую с использованием std::function (или вы можете назвать ее новым именем, например функтором), я думаю, что я сам по себе, верно? - person tinlyx; 13.02.2014
comment
Немногие люди понимают проблему читабельности, поскольку они гордятся тем, что могут писать сложный код, который они могут понять только потому, что они его написали или помнят, как они его написали. Я полностью уважаю это. Я просто хотел, чтобы мой код был читабелен для меня спустя годы, и я не понимаю, буду ли я придерживаться простых вариантов использования (фиксированная арность, явно написанные типы, если мне нужно...), почему такой код станет менее актуальным в будущее. - person tinlyx; 13.02.2014
comment
Другая мысль заключается в том, что стандарты С++ также развиваются. Многие структуры в предыдущих стандартах устарели. {Унарная, двоичная} функция, xxx_ptr. С другой стороны, сохраняются некоторые наивные подходы (копирование и возврат std::vector по значению). Люди изобретают совершенно новые функции (семантику перемещения) только для того, чтобы приспособиться к этому. Вот почему я не считаю свой простой подход обязательно неправильным в долгосрочной перспективе. - person tinlyx; 13.02.2014