【问题标题】:C++ Passing struct function as parameter for a function in an inherited structC ++将结构函数作为继承结构中函数的参数传递
【发布时间】:2016-03-20 05:02:38
【问题描述】:

我有两个结构体,结构体 B 继承自结构体 A。我想将一个函数作为参数从结构体 B 传递给结构体 A 以供结构体 A 使用。

这是我想要实现的目标和遇到的问题的示例,完整的代码将是 TL;DR。

struct A
{
    int32 mynum;

    void Tick(float delta, bool doThis, void (funcparam)(float, bool, int32))
    {
        funcparam(delta, doThis, mynum);
    }
};

struct B : public A
{
    void RunThis(float deltaTime, bool doIt, int32 which)
    {
        // do something when called by Struct A
    };

    void DoSomething()
    {
        Tick(0.1f, true, &B::RunThis);
    };
};

问题在于这一行:Tick(0.1f, true, &B::RunThis); 来自函数 void DoSomething(),除非我从一开始就做错了,但我想我传递错了,因为它仍然在我的结构的标题中m 当前定义?

错误(我已经修改了错误以适合我的示例,我不认为我搞砸了..):

error C2664: 'void A::Tick(float,bool,void (__cdecl *)(float,bool))': cannot convert argument 3 from 'void (__cdecl B::* )(float,bool)' to 'void (__cdecl *)(float,bool)'

&B::RunThis 中省略B:: 当然不能解决任何问题。

【问题讨论】:

  • 查看std::function(),您的函数指针void (funcparam)(float, bool, int32) 无法获取成员函数指针。
  • void (funcparam)(float, bool, int32) 不是你想要的。首先,(非成员)函数指针的签名是void (* funcparam) (float, bool, int32)(你错过了*)。其次,您可以使 Tick() 成为基类的函数,然后调用一个虚函数,在派生类中覆盖该虚函数。与使用 std:function 相比,这仍然是一种更好的方法,只要您具有“派生类”:“您要执行的功能”的 1:1 关系。
  • @BitTickler - 这对我来说是一个更好的解决方案,谢谢!至于具体问题的答案,我还是等贴出来,让google类似问题的人有准确答案
  • @πάνταῥεῖ - 看起来不错,如果你能用一个简短的例子发布答案,我会打勾

标签: c++ struct parameter-passing


【解决方案1】:

第一种选择:改用虚函数。

如果每个派生类有 1 个且只有 1 个函数,效果最好。

struct A {
    virtual void RunThis(float deltaTime, bool doIt, int32 which) = 0;
    void Tick(float delta, bool doThis )
    {
        //...
        RunThis(delta, doThis, which);
        // ...
    }
    virtual ~A() // virtual destructor...
    {}
};

struct B : public A
{
    virtual void RunThis(float deltaTime, bool doIt, int32 which )
    {
    }
    void DoSomething(/*...*/) 
    {
        // ...
        Tick(/*...*/);
    }

};

第二个选项:std::function + lambda 或 struct B 中的成员函数

#include <functional>
struct A 
{
     void Tick(float delta, bool doit, std::function<void(float,bool,int32)> action )
     {
     }
};

struct B : public struct A
{
    void DoSomething( /*...*/ )
    {
         // you can use a lambda, if convenient...
         Tick(/*...*/,
             [this](float delta, bool doit, int32_t which) -> void {
                // ...
             });
   }

   // or you can also use a pointer to member:
   void RunThis(/*...*/) { /* ... */ }
   void DoSomething(/*...*/)
   {
        std::function<void(float,bool,int32)> f = std::bind( &B::RunThis, this, _1,_2,_3);
        Tick(/*...*/, f);
   }
 };

【讨论】:

  • 或者一个 lambda 和一个方法,而不是 std::bind[this](float delta, bool doit, int32_t which){ return this-&gt;RunThis(delta, doit, which); }
  • @6502 是的,唯一不起作用的是尝试在没有 std::function 的情况下将 lambda 传递给 A::tick(),那样的话,类型将是未知的......我仍然认为这不是一件好事:)
【解决方案2】:

不幸的是,C++ 函数指针没有上下文......换句话说,一个指向接受整数的函数的指针

void (*f)(int x)

不允许将任何数据“绑定”到指针,并且只能访问参数和全局变量。 为某些编译器实现的解决方法是 "trampoline" libraries,它允许将自定义数据绑定到函数指针,但它们都依赖于运行时动态代码创建,这在标准 C++ 的边界内是不可能的。

标准 C++ 中的解决方案是使用

std::function<void(int)>

而不是

void (*)(int)

std::function 对象可以保持与函数关联的数据并允许创建闭包。它还向后兼容函数指针(即函数指针可以隐式转换为std::function 对象)。

使用你的代码可以成为例子

struct A {
    int32 mynum;
    void Tick(float delta, bool doThis,
              std::function<void(float, bool, int32)> funcparam) {
       funcparam(delta, doThis, mynum);
    }
};

struct B : public A {
    void RunThis(float deltaTime, bool doIt, int32 which) {
        // do something when called by Struct A
    };
    void DoSomething() {
        Tick(0.1f, true, [this](float dt, bool di, int32 w) {
            this->RunThis(dt, di, w);
        });
    }
};

使用 lambda 调用方法。

如果您无法更改接受函数指针的代码,则一种可移植的解决方法是为给定签名预先分配一组函数指针...例如

void *closure_data[10];
void (*closure_code[10])(void *, int);

void closure0(int x){ closure_code[0](closure_data[0], x); }
void closure1(int x){ closure_code[1](closure_data[1], x); }
void closure2(int x){ closure_code[2](closure_data[2], x); }
...
void closure9(int x){ closure_code[9](closure_data[9], x); }

void (*closures[10])(int) = {closure0,
                             closure1,
                             ...
                             closure9};

然后你需要“分配”一个闭包i,将你的上下文数据放入对应的closure_data[i],代码在closure_code[i]中,并作为函数指针closures[i]传递。

通常体面的 C++03 代码或接受回调函数指针的 C 代码总是提供上下文参数...即接口具有形式

void Tick(void *context,
          void (*callback)(float, bool, int32, void *))

并将调用回调,同时传递由提供函数指针的调用者指定的不透明void *context

【讨论】:

  • C++ 中有 /is/ 指向成员 (void( Foo::* funcptr)()) 的指针。但是为了在这种情况下工作,结构 A 必须知道结构 B,这显然是不可取的。
  • @BitTickler:指向 C++ 中成员的指针不携带任何上下文,需要应用对象。如果一个 API 调用不幸地接受了一个函数指针,你不能传递一个指向成员的指针......它们是不同的抽象(当然,static 成员函数除外......这些确实只是具有时髦名称的常规函数​​)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-18
  • 2022-11-29
  • 2018-09-17
  • 1970-01-01
相关资源
最近更新 更多