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

Данный вызываемый объект (функция) 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 работоспособным / аккуратным способом.

3 ответа

Решение

Проблема в том, что вы не можете легко создать объект функции из набора перегрузки: когда вы заявляете 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");
}

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

Если 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 делает для вас.

На самом деле это уже реализовано для вас 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;
};
Другие вопросы по тегам