сопоставить перегруженную функцию С++ с гетерогенным кортежем?

Можно ли в C++ сопоставить перегруженную функцию с гетерогенным кортежем? Например:

double f(dobule);
size_t f(std::string);

auto t = std::make_tuple(3.14, "a string");
// should be the same as std::make_tuple(f(std::get<0>(t)), f(std::get<1>(t)));
map(f, make_tuple(3.14, "a string")); // type std::tuple<double, size_t>

Я могу написать функцию карты, которая сопоставляет один и тот же перегруженный экземпляр f каждому элементу кортежа (код ниже), но я не вижу, как отложить разрешение перегрузки f от вызова map до вызова f внутри map. Кто-нибудь понял, как это сделать?

Вот мой код для сопоставления одного экземпляра перегруженной функции с кортежем (где seq и gens взяты из этого ответа https://stackoverflow.com/a/7858971/431282):

// basically C++14's integer sequence
template<int ...>
struct seq { };

template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> { };

template<int ...S>
struct gens<0, S...> {
  typedef seq<S...> type;
};

template<typename R, typename A, typename ...B, int ...S> auto
map_1(R (*f)(A), std::tuple<B...> &&t, seq<S...>)
  -> decltype(std::make_tuple(f(std::get<S>(t))...))
{
  return std::make_tuple(f(std::get<S>(t))...);
}

template<typename R, typename A, typename ...B> auto
map(R (*f)(A), std::tuple<B...> &&t)
  -> decltype(map_1(f, std::forward<std::tuple<B...>>(t), typename gens<sizeof...(B)>::type()))
{
  return map_1(f, std::forward<std::tuple<B...>>(t), typename gens<sizeof...(B)>::type());
}

person Cotton Seed    schedule 01.05.2014    source источник
comment
Я нашел частичное решение: использовать вызываемый объект с перегруженным operator() и изменить подпись map на template<typename F, ...> ... map(F f, ...). Тем не менее, было бы неплохо зафиксировать перегруженную функцию в области видимости как вызываемый объект!   -  person Cotton Seed    schedule 01.05.2014
comment
У boost это уже есть, не так ли в stdlib?   -  person Mooing Duck    schedule 01.05.2014


Ответы (1)


Проблема в том, что это невозможно сделать с помощью одних только указателей на функции, поскольку вы хотите разрешить перегрузку функции, когда она привязана к аргументу. Вы не можете заставить указатель функции работать, не выполняя разрешение перегрузки. Ключевым моментом является предоставление объекта функции, который выполняет перегрузку при вызове, а не пытается получить указатель функции в начале.

Для этого я бы объявил основную функцию как

template<typename Func, typename ...B> auto
map(Func&& f, std::tuple<B...> &&t)
  -> decltype(map_1(std::forward<Func>(f), std::forward<std::tuple<B...>>(t), typename gens<sizeof...(B)>::type()))
{
  return map_1(std::forward<Func>(f), std::forward<std::tuple<B...>>(t), typename gens<sizeof...(B)>::type());
}

А затем аналогичным образом определил map_1.

Затем вы можете создать оболочку объекта функции

struct Wrapper
{
   template<typename T>
   auto operator()(T&& t) const -> decltype( f(std::forward<T>(t)) )
   {
       return f(std::forward<T>(t));
   }
};

и назовите его с помощью map(Wrapper(), make_tuple(3.14, "a string"))

Изменить: если у вас есть С++ 14, вы можете сделать следующее (спасибо @MooingDuck за вдохновение)

#define BINDOVERLOADED(X) [](auto&& t) { return X(std::forward<decltype(t)>(t)); }
auto x = map(BINDOVERLOADED(f), make_tuple(3.14, "a string"));

См. http://coliru.stacked-crooked.com/a/439f958827de8cf2.

person Dave S    schedule 01.05.2014
comment
Я думаю, что на самом деле я бы обернул Wrapper в макрос для f. - person Mooing Duck; 01.05.2014
comment
#define BINDOVERLOADED(X) struct{template<class T>auto operator()(T&&t)const->decltype(X(std::forward<T>(t)){return X(std::forward<T>(t));}}(), а потом map(BINDOVERLOADED(to_string),my_tuple); - person Mooing Duck; 01.05.2014
comment
@MookingDuck: похоже, это не работает по двум причинам. 1) я не могу объявить структуру как часть выражения и 2) я не могу объявить функцию шаблона члена локальной структуры. Не уверен, что это gcc или то, что говорит стандарт на данный момент ideone.com/XBFGMx - person Dave S; 01.05.2014
comment
#define BINDOVERLOADED(X) [](auto&&t){return X(std::forward<decltype(param)>(t));} для С++ 14? (забавно, что они также рассматривают types как перегруженные функции, BINDOVERLOADED(char)(42) ) - person Mooing Duck; 01.05.2014
comment
@MooingDuck что такое Т? - person Marc Glisse; 01.05.2014
comment
Мне нужно научиться C++14 :( Чтение: scottmeyers.blogspot.com/2013/05/ ....... - person Mooing Duck; 01.05.2014
comment
@MooingDuck: отредактировано с помощью примера C++14 - person Dave S; 01.05.2014