推断回调参数类型并将它们用作 return 类型

Deduce callback arguments type and use them as return type

我有一些函数可以将一些参数和可调用对象(最后一个参数)作为带有结果的回调。我想将其转换为常规通话。我尝试了这种方法并且它有效,但我每次都必须指定参数类型。

有没有解决方案可以帮助我自动推导回调参数类型并将它们从函数转换为 return 类型?

Godbolt link.

#include <functional>
#include <iostream>

void foo1(std::function<void(int)> &&f) {
    f(42);
}

void foo1(std::string arg, std::function<void(std::string)> &&f) {
    f(std::move(arg));
}

void foo2(std::function<void(float)> &&f) {
    f(42.42f);
}

template <typename F>
void foo3(F &&f) {
    f(42);
}

template <typename Arg>
auto call(auto &&f) {
    Arg res;
    f([&res](Arg arg) {
        res = std::move(arg);
    });

    return res;
}

int main() {
    std::cout << call<float>([](auto &&callback) { foo2(callback); }) << std::endl;
    std::cout << call<int>([](auto &&callback) { foo1(callback); }) << std::endl;
    std::cout << call<std::string>([](auto &&callback) { foo1("hello", callback); }) << std::endl;

    // should work not only with std::function
    std::cout << call<int>([](auto &&callback) { foo3(callback); }) << std::endl;

    // is there a way to automatically deduce return type from callback?
    // std::cout << call<auto>([](auto &&callback) { foo2(callback); }) << std::endl;
    // std::cout << call<auto>([](auto &&callback) { foo3(callback); }) << std::endl;

    // // this shouldn't compile, cause of ambiguous call
    // std::cout << call<auto>([](auto &&callback) { foo1(callback); }) << std::endl;
}

如果可能的话,如果回调有多个参数,我还想return元组与结果

void foo4(std::function<void(float, int)> &&f) {
    f(42.42f, 42);
}

auto [a, b] = call<auto>([](auto &&callback) { foo4(callback); });

我将不胜感激。

不确定您到底想要什么,但是...接收 std::functions

的主 call() 怎么样
template <typename Arg>
auto call(std::function<void(std::function<void(Arg)>&&)> const & f)
{
  Arg res;
    
  f([&](Arg arg){ res = std::move(arg); });
    
  return res;
}

加上辅助函数 call()(允许类型推导,如果可能)

template <typename Arg>
auto call (void(f)(std::function<void(Arg)>&&))
 { return call(std::function{f}); }

加上一个辅助 call(),带有概念,用于 lambda 和其他可调用对象(但没有类型推导)

template <typename Arg, typename T>
concept FuncFunctionable = requires (T a) 
 { std::function<void(std::function<void(Arg)>&&)>{a}; };

template <typename Arg, typename L>
auto call (L const & f) requires FuncFunctionable<Arg, L>
 { return call<Arg>(std::function<void(std::function<void(Arg)>&&)>{f}); }

鉴于此,您使用 call() 如下(对 foo1() 函数的两次不同调用 std::string

call(foo2)
call(foo1)
call<std::string>([](std::function<void(std::string)> f) { foo1("hello", std::move(f)); })
call<std::string>([](auto f) { foo1("hello", std::move(f)); })
call<int>(foo3)

std::tuple版本变为

template <typename ... Args>
auto call(std::function<void(std::function<void(Args...)>&&)> const & f)
 {
   std::tuple<Args...> res;
    
   f([&](Args && ... as){ res = std::make_tuple(std::move(as)...); });
                                      
   return res;
 }

template <typename ... Args>
auto call (void(f)(std::function<void(Args...)>&&))
 { return call(std::function{f}); }

template <typename ... Args, typename T>
concept FuncFunctionable = requires (T a) 
 { std::function<void(std::function<void(Args...)>&&)>{a}; };

template <typename ... Args, typename L>
auto call (L const & f) requires FuncFunctionable<Args..., L>
 { return call<Args...>(std::function<void(std::function<void(Args...)>&&)>{f}); }

下面是一个完整的编译示例

#include <functional>
#include <iostream>

void foo1 (std::function<void(int)> && f)
 { f(42); }

void foo1 (std::string arg, std::function<void(std::string)> && f)
 { f(std::move(arg)); }

void foo2 (std::function<void(float)> && f)
 { f(42.42f); }

template <typename F>
void foo3 (F && f)
 { f(42); }

void foo4 (std::function<void(float, int)> && f)
 { f(42.42f, 42); }

template <typename ... Args>
auto call(std::function<void(std::function<void(Args...)>&&)> const & f)
 {
   std::tuple<Args...> res;
    
   f([&](Args && ... as){ res = std::make_tuple(std::move(as)...); });
                                      
   return res;
 }

template <typename ... Args>
auto call (void(f)(std::function<void(Args...)>&&))
 { return call(std::function{f}); }

template <typename ... Args, typename T>
concept FuncFunctionable = requires (T a) 
 { std::function<void(std::function<void(Args...)>&&)>{a}; };

template <typename ... Args, typename L>
auto call (L const & f) requires FuncFunctionable<Args..., L>
 { return call<Args...>(std::function<void(std::function<void(Args...)>&&)>{f}); }


int main ()
 {
    auto [a1]     = call(foo2);
    auto [b1]     = call(foo1);
    auto [c1]     = call<std::string>([](std::function<void(std::string)> f) { foo1("hello", std::move(f)); });
    auto [d1]     = call<std::string>([](auto f) { foo1("hello", std::move(f)); });
    auto [e1]     = call<int>(foo3);
    auto [f1, f2] = call(foo4);
    
    std::cout << a1 << std::endl;
    std::cout << b1 << std::endl;
    std::cout << c1 << std::endl;
    std::cout << d1 << std::endl;
    std::cout << e1 << std::endl;
    std::cout << f1 << ", " << f2 << std::endl;
 }