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