gpt4 book ai didi

c++ - 在 C++20 中仍然无法转发所有可调用对象吗?

转载 作者:行者123 更新时间:2023-12-03 10:05:34 24 4
gpt4 key购买 nike

我想做一个通用的包装函数,它可以接受任何

  • 独立或静态功能,
  • 成员函数(也许作为第一个参数用作 *this 的特化)

  • 包括重载或模板化 case 和可变参数。然后,这样的包装器将在主体中准确地使用转发的参数调用该函数。
    示例 :
    template<typename Fnc,typename...Args>
    void wrapper(Fnc fnc, Args&&...args){
    // Do some stuff

    // Please disregard the case when return type is void,
    // that be SFINAED with std::result_of.
    auto res = fnc(std::forward<Args>(args)...);

    // Do some stuff
    return res;
    }
    #include <vector>

    auto foo(int i){ return i; }
    auto foo(double& d){ return d; }
    auto foo(double&& d){ return d; }
    auto foo(const char* str){ return str; }

    template<typename...T>
    auto generic_foo(T&&...ts){/* ...*/ return /* ...*/; }

    template<typename T>
    void constrained_foo(std::vector<T>& lref,std::vector<T>&& rref, std::vector<T> value){ /**/}

    int main(){
    // Basics
    wrapper(foo, 1);// foo(int)
    wrapper(foo, 1.1); // foo(double&&)
    wrapper(foo, "ahoj"); // foo(const char*)

    // Conversion must work too
    wrapper(foo, 1.1f); // foo(double&&)
    wrapper(foo, (short)1); // foo(int)

    // Detecting lvalues, rvalues is also a must
    double y;
    foo(y);
    foo(std::move(y));

    // Similarly for templates
    int x;
    std::vector<int> v1, v2, v3;
    wrapper(generic_foo, 1, 1.1, "ahoj", x, &x);
    wrapper(constrained_foo, v1, std::move(v2), v3);
    }
    我确实对此感到非常沮丧的是,我正在提供所有必要的信息来使这些电话彼此相邻,关于调用什么没有额外的歧义,我可以自己调用它,我可以(将)一个可以做到这一点的宏,但是AFAIK没有合适的C++语法。
    我在尝试在某些“上下文”中自动调用我的所有方法时发现了它的需要。但是仔细想想,我相信这对于 <algorithm> 来说真的可以得到广泛的使用。 , <thread>图书馆也是。您不必使用单语句 lambda 来调用具有 lambda 参数或捕获的内容的函数/运算符。
    问题出现在任何接受另一个函数的函数中,该函数最终将与传递的已知参数一起被调用。
    我的尝试 generic_foo如果返回类型是固定的,则可以解决:
    template<typename...Args>
    void wrapper(void(*f)(Args&&... args) , Args&&...args){
    // ...
    f(std::forward<Args>(args)...);
    }

    int main(){
    int x;
    wrapper(generic_foo, 1, 1.1, "ahoj", x, &x, std::move(x));
    }
    这很好用,返回类型也可以通过 std::invoke_result_t 的一些晦涩而巧妙的使用来解决。 ,但目前是一种带参数类型的先有鸡下蛋的情况。因为唯一的事情是如何解析名称 generic_foo是强制它衰减到函数指针,然后没有名称可以放入 std::invoke_result_t因为参数还在推导中。
    只要存在完全匹配,这也适用于重载,因此它无法进行转换。
    当事先不知道函数名称时,这种方法是我可以在没有宏的情况下获得的。
    如果可调用对象的名称是固定的,那么 lambda 技巧的常用变体是:
    template<typename Fnc, typename...Args>
    void wrapper(Fnc f , Args&&...args){
    // ...
    f(std::forward<Args>(args)...);
    }

    int main(){
    int x;
    wrapper([](auto&&...args)
    { return generic_foo(std::forward<decltype(args)>(args)...);},
    1, 1.1, "ahoj", x, &x, std::move(x));
    }
    如果我添加一个宏这样做:
    #define WRAP_CALL(wrapper_fnc, called_fnc, ...) \
    wrapper_fnc([&](auto&&...args) \
    { return called_fnc(std::forward<decltype(args)>(args)...);}, \
    __VA_ARGS__ );

    int main(){
    int x;

    WRAP_CALL(wrapper, generic_foo, 1, 1.1, "ahoj", x, &x, std::move(x))
    }
    我得到了我能想到的最少宏感染的工作解决方案,它适用于任何可调用和任何可以保持正确 C++ 函数的包装器。但我想要这个的无宏版本,尤其是对于函数。
    所以我大概还是会用这个版本,好像也不是太不合理。有没有我应该知道的角落案例?
    我还没有写过一个 C++20 的概念,所以我仍然希望可能有一些东西可以在那个领域工作?但可能不是因为 std::thread(foo,1);也深受其害。
    因此,这可能需要更改语言,因为当前无法将重载集或模板的名称传递到任何地方,即使作为某种聚合类型也是如此。所以也许类似于 std::initializer_list类加上它有时神奇的语法?
    如果情况确实如此,我很乐意接受列出任何可能有助于解决此问题的当前有效提案的任何答案。如果有的话。
    我确实找到了 N3579 - A Type trait for signatures如果解决了鸡-蛋问题,它也许可以与函数指针解决方案一起使用。但是这个提议看起来很死。

    最佳答案

    “重载或模板化案例”不是 实体 可以是函数/模板参数——在某些情况下,尽管重载解析可以使用上下文信息。您的意思 foo在您的 wrapper(foo,…)只不过是一个 token (特别是,它是一个重载集),并且该语言根本无法寻址这样的对象,因为它没有(“聚合”?)类型。 (相反,宏确实对 token 进行操作,这就是它们适用于此处的原因。)您似乎了解其中的大部分内容,但了解为什么它显然不可能,以及为什么考虑重载集是个坏主意可能会有所帮助作为“可调用的”。 (毕竟,从语法上讲,类型名称也是“可调用的”,但将其作为函数参数传递没有任何意义。)
    即使一个电话f(g,…)被定义为尝试 g 的每个重载反过来(这是为了推断 f 的模板参数的狭隘目的),这对(包含重载集)模板无济于事:甚至无法评估 f的 SFINAE 条件给出了 g尚未选择专业的模板。
    标准 lambda 你也说明了的技巧是一种利用参数列表执行重载解析的方法,这就是为什么它几乎是唯一有效的方法。有certainly proposals自动化该过程,包括变幻莫测的 SFINAE 友好性和异常规范。

    关于c++ - 在 C++20 中仍然无法转发所有可调用对象吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65044318/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com