c++ Package 任何可调用对象的模板定义:类成员函数等

yeotifhr  于 2023-05-30  发布在  其他
关注(0)|答案(2)|浏览(138)

我查找了thisthis SO的答案,但没有一个能解决我的问题。
简短描述:模板函数应该能够 Package 任何其他函数,也包括类的函数成员,其中应该发生比这个例子更多的事情。
最小工作示例:

#include <iostream>

template <typename T, typename ...ArgsT>
T wrapper(T(*func)(ArgsT...), ArgsT... args)
{
    return func(args...);
}

class TestClass
{
public:
    int mult_by_2_member(int a)
    {
        return a * 2;
    }

    static int mult_by_2_static(int a)
    {
        return a * 2;
    }

    void wrap_funcs()
    {
        // std::cout << "Wrapped member = " 
        //           << wrapper(&mult_by_2_member, 2)  // Prob: cannot convert `int(TestClass::*)(int) to int(*)(int)`
        //           << std::endl;
        std::cout << "Wrapped static = " << wrapper(mult_by_2_static, 2) << std::endl;
    }
};

int main()
{
    TestClass test_instance;
    test_instance.wrap_funcs();
    // skipping wrapping of a global function... that works correctly
}

上面的代码片段中发布的错误消息是显而易见的。但我还没有弄清楚如何解决它,甚至整个开始的逻辑或意图是否有效。
任何解决方案建议或信息来源?

编辑

一种可能的解决办法是:

/* as above */
static int mult_by_2_static(TestClass* p, int a)
{
   return p->mult_by_2_member(2);
}

void wrap_funcs()
{
    // std::cout << "Wrapped member = " << wrapper(&mult_by_2_member, 2) << " = " << std::endl;
    std::cout << "Wrapped static = " << wrapper(mult_by_2_static, this, 2) << std::endl;
}

但是这样我就必须将所有目标成员函数“预 Package ”成静态成员函数,这是一个很大的样板。

3duebb1j

3duebb1j1#

[...]模板函数应该能够 Package 任何其他函数,也包括类的函数成员,其中应该发生比这个例子更多的事情。
从你的要求,我只能建议有使用std::invoke(自c++17或最新).它将完成区分自由函数/非成员函数指针和成员函数指针的艰巨工作。

#include <functional> //  std::invoke

template <typename Callable, typename ...ArgsT>
constexpr decltype(auto) wrapper(Callable&& func, ArgsT&&... args)
{
    return std::invoke(std::forward<Callable>(func), std::forward<ArgsT>(args)...);
}

现在你可以用成员函数调用它,如下所示:

wrapper(&TestClass::mult_by_2_member, this, 2)

这也将涵盖wrapper(mult_by_2_static, 2)的情况。

j91ykkif

j91ykkif2#

  • 编辑:用C++17的JeJoapproach其实更上级!这里只保留这个变体,以用于只有较旧的标准可用的情况-以及记录成员函数指针是如何工作的,如果在其他场景中需要的话... *

这里的问题是 * 成员函数 * 与 * 静态成员函数 * 有不同的签名(包括调用约定)-从编译器的Angular 来看,后者只是与类作用域绑定的普通函数,可能有访问限制。
你的模板函数能够接受 * 普通 * 或静态成员函数,但不能接受非静态成员函数,正如你已经注意到的那样-成员函数还有一个进一步的问题:你需要一个类型的额外示例来调用成员函数,所以你需要这样的东西:

template <typename R, typename T, typename ... A>
R wrapper(T& objectToCallOn, <WhateverSyntax> memberFunctionPointer, A... a)
{
   return objectToCallOn.memberFunctionPointer(a...);
   //                   ^ actually requires different syntax!
}

这使得您需要两个不同的模板,一个是您已经拥有的,一个是用于成员函数指针的。is的正确语法:

template <typename R, typename T, typename ... A>
R wrapper(T& objectToCallOn, R (T::*memberFunctionPointer)(A ...), A... a)
{
   return (objectToCallOn.*memberFunctionPointer)(a...);
}

请注意,.*运算符(以及它的指针模拟->*运算符)的优先级低于函数调用运算符,因此不能省略括号。
现在需要两个实现相同的模板,除了(成员)函数调用可能不是你想要的。
我的解决方案是将原始模板转换为更通用的形式(从这里依赖于C++11):

template <typename F, typename ...ArgsT>
auto wrapper(F& func, ArgsT... args) -> decltype(func(args...))
{
    return func(args...);
}

通过这种方式,您可以使用任意可调用对象调用模板,这些对象可以是函数或可调用对象(functors)。
现在你可以像以前一样用静态函数调用它。使用lambda执行成员函数调用,如:

wrapper([&theObject](int value) { theObject.mult_by_2_member(value); }, 2);

如果你想避免编写lambda的不便,你仍然可以编写上面的模板重载并将lambda隐藏在里面:

template <typename R, typename T, typename ... A>
R wrapper(T& t, R (T::*f)(A ...), A... a)
{
   return wrapper([&t, &f](A ... a) { (t.*f)(a...); }, a...);
}

所有代码完全未经测试,如果你发现一个错误,请修复自己。
旁注:您可能希望为您的 Package 器函数实现完美的转发...

相关问题