指向函数和指针到对象的语义

时间:2016-08-18 17:22:17

标签: c++ pointers c++14 clang++ c++17

我遇到了一个问题,即在另一个线程中使用变量参数稍后调用部分限定的函数对象。

在GCC中,我一直在使用我制作的宏和typedef,但我正在完成我的项目,试图清除警告。

#define Function_Cast(func_ref) (SubscriptionFunction*) func_ref
typedef void(SubscriptionFunction(void*, std::shared_ptr<void>));

使用如下所示的Function_Cast宏会导致“警告:在指向函数的指针和指向对象的指针之间进行转换是有条件支持的”

Subscriber* init_subscriber = new Subscriber(this, Function_Cast(&BaseLoaderStaticInit::init), false);

我真正需要的是一个指针,我可以创建一个std :: bind&lt; function_type&gt;的对象。这通常是怎么做的?

此外,这种有条件支持的东西真的很烦人。我知道在x86上我的代码工作正常,我知道依赖于sizeof(void *)== sizeof(this *)的所有这些*的限制。

另外,有没有办法让clang处理像数据指针这样的函数指针,以便我的代码可以编译?我很想知道它有多糟糕(如果有的话)。

相关守则:

#define Function_Cast(func_ref) (SubscriptionFunction*) func_ref
typedef void(SubscriptionFunction(void*, std::shared_ptr<void>));
typedef void(CallTypeFunction(std::shared_ptr<void>));

Subscriber(void* owner, SubscriptionFunction* func, bool serialized = true) {
    this->_owner = owner;
    this->_serialized = serialized;
    this->method = func;

    call = std::bind(&Subscriber::_std_call, this, std::placeholders::_1);
}

void _std_call(std::shared_ptr<void> arg) { method(_owner, arg); }

2 个答案:

答案 0 :(得分:0)

它通常是这样做的:

#include <functional>
#include <memory>

struct subscription
{
  // RAII unsubscribe stuff in destructor here....
};

struct subscribable
{
  subscription subscribe(std::function<void()> closure, std::weak_ptr<void> sentinel)
  {
    // perform the subscription

    return subscription {
      // some id so you can unsubscribe;
    };
  }


  //
  //

  void notify_subscriber(std::function<void()> const& closure, 
                         std::weak_ptr<void> const & sentinel)
  {
    if (auto locked = sentinel.lock())
    {
      closure();
    }
  }
};

答案 1 :(得分:0)

这里的问题是你试图使用成员函数指针来代替函数指针,因为你知道,在引擎盖下,它通常被实现为{{ 1}}。

function(this, ...)

http://ideone.com/fork/LJiohQ

但是,这并不能保证这实际上会起作用,而且很明显的情况(虚拟功能)可能会赢得它。

成员函数旨在像这样传递:

struct S {
    void f() {}
};

using fn_ptr = void(*)(S*);

void call(S* s, fn_ptr fn)
{
    fn(s);
    delete s;
}

int main() {
    call(new S, (fn_ptr)&S::f);
}

并像这样调用:

void call(S* s, void (S::*fn)())

http://ideone.com/bJU5lx

当人们想要支持不同类型时,人们如何解决这个问题的方法是使用蹦床,这是一种非会员功能。您可以使用静态[member]函数或lambda:

执行此操作
(s->*fn)();

或者如果您希望在您的呼叫站点输入安全性,那么这是一个模板化的构造函数:

auto sub = new Subscriber(this, [](auto* s){ s->init(); });

http://ideone.com/lECOp6

如果你的template<typename T> Subscriber(T* t, void(T::*fn)(), bool x); 构造函数采用Subscriber而不是函数指针,则可以传递捕获lambda并消除采用std::function<void(void))>的需要:

void*