Тип вывода для перегруженных функций — карри

Данный вызываемый объект (функция) aи аргумент b (или ряд аргументов), я хотел бы вывести тип, возвращаемый из f учитывая, что f перегружен множеством подписей.

одна из моих многочисленных попыток

#include <iostream>
#include <cstdint>
#include <string>
#include <functional>
#include <utility>
#include <typeinfo>

int foo(uint32_t a) { return ((a + 0) * 2); }

bool foo(std::string a) { return (a.empty()); }

/*template <typename A, typename B> auto bar(A a, B b) -> decltype(a(b)) {
return (a(b));
}*/

/*template <typename A, typename B> decltype(std::declval<a(b)>()) bar(A a, B b)
{
return (a(b));
}*/

template <typename A, typename B> void bar(std::function<A(B)> a, B b) {
std::cout << a(b) << "\n";
}

int main() {

// the following 2 lines are trivial and they are working as expected
std::cout << foo(33) << "\n";
std::cout << typeid(decltype(foo(std::string("nothing")))).name() << "\n";

std::cout << bar(foo, 33) << "\n";
//std::cout << bar(foo, std::string("Heinz")) << "\n";

return (0);
}

и 2 варианта шаблона закомментированы и включены в предыдущий код.

я использую declval result_of auto decltype без удачи.

Как работает процесс разрешения перегрузки во время компиляции?

Если кто-то хочет знать, почему я пытаюсь проявить творческий подход, это то, что я пытаюсь реализовать Curry в C ++ 11 работоспособным / аккуратным способом.

1

Решение

Проблема в том, что вы не можете легко создать объект функции из набора перегрузки: когда вы заявляете foo или же &foo (я думаю, что функция в большинстве случаев превращается в указатель на функцию), вы не получаете объект, но получаете набор перегрузки. Вы можете указать компилятору, какую перегрузку вы хотите, либо вызвав его, либо предоставив подпись. Насколько я могу сказать, вы тоже не хотите.

Единственный подход, который мне известен, — это превратить вашу функцию в реальный функциональный объект, который устранит проблему:

struct foo_object
{
template <typename... Args>
auto operator()(Args&&... args) -> decltype(foo(std::forward<Args>(args)...)) {
return foo(std::forward<Args>(args)...);
}
};

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

template <typename Func, typename... Args>
auto bar(Func func, Args&&... args) -> decltype(func(std::forward<Args>(args)...)) {
// do something interesting
return func(std::forward<Args>(args)...);
}

int main() {
bar(foo_object(), 17);
bar(foo_object(), "hello");
}

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

4

Другие решения

Если foo перегружен, вам нужно использовать следующее:

#include <type_traits>

int foo(int);
float foo(float);

int main() {
static_assert(std::is_same<decltype(foo(std::declval<int>())), int>::value, "Nope.");
static_assert(std::is_same<decltype(foo(std::declval<float>())), float>::value, "Nope2.");
}

Если это не так, то этого будет достаточно:

#include <type_traits>
bool bar(int);

int main() {
static_assert(std::is_same<std::result_of<decltype(bar)&(int)>::type, bool>::value, "Nope3.");
}

Да, это многословно, потому что вы пытаетесь явно извлечь то, что неявная перегрузка ad-hoc делает для вас.

3

Это на самом деле уже реализовано для вас std::result_of. Вот возможная реализация

template<class>
struct result_of;

// C++11 implementation, does not satisfy C++14 requirements
template<class F, class... ArgTypes>
struct result_of<F(ArgTypes...)>
{
typedef decltype(
std::declval<F>()(std::declval<ArgTypes>()...)
) type;
};
2