【问题标题】:C++ error: reference to non-static member function must be calledC++ 错误:必须调用对非静态成员函数的引用
【发布时间】:2015-01-09 17:22:09
【问题描述】:

我正在尝试创建一个类来抽象 libuv 网络功能的一些基本行为。

#define TCP_BACKLOG 256
class _tcp {
    uv_tcp_t* tcp = NULL;
    public:
    ~_tcp() { delete tcp; }
    void listen_uv_listen_uv_connection_cb(uv_stream_t* stream, int status) {
        printf("NEW CONNECTION\n");
    }
    void listen(const char* host, int port) {
        tcp = new uv_tcp_t();
        uv_tcp_init(uv_default_loop(), tcp);
        sockaddr_in* addr = new sockaddr_in();
        uv_ip4_addr(host, port, addr);
        uv_tcp_bind(tcp, (const sockaddr*)addr, 0);
        delete addr;

        uv_listen((uv_stream_t*)tcp, TCP_BACKLOG, listen_uv_listen_uv_connection_cb);
    }
};

前面显示的代码的问题是,当我尝试编译它时,我收到以下错误:

error: reference to non-static member function must be called
  on: uv_listen((uv_stream_t*)tcp, TCP_BACKLOG, listen_uv_listen_uv_connection_cb);
                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

它指出listen_uv_listen_uv_connection_cb是罪魁祸首。

有人可以向我解释一下,为什么这是一个错误,我应该如何解决它?

uv_listen()uv_connection_cb 签名声明如下

UV_EXTERN int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb);
typedef void (*uv_connection_cb)(uv_stream_t* server, int status);

【问题讨论】:

  • delete 自己处理空指针,你的检查是多余的
  • @Slava 谢谢,不知道。
  • 你能出示实际的uv_listen()签名吗?
  • 好的,已将其添加到问题中。

标签: c++ libuv


【解决方案1】:

即使具有相同的签名,您也无法将非静态成员函数转换为指向函数的指针,因为从技术上讲,成员函数有一个名为 this 的隐藏参数。解决方案之一是使listen_uv_listen_uv_connection_cb 静态:

class _tcp {
    uv_tcp_t* tcp = NULL;
    public:
    ~_tcp() { delete tcp; }
    static void listen_uv_listen_uv_connection_cb(uv_stream_t* stream, int status) {
        printf("NEW CONNECTION\n");
    }
    void listen(const char* host, int port) {
        tcp = new uv_tcp_t();
        uv_tcp_init(uv_default_loop(), tcp);
        sockaddr_in* addr = new sockaddr_in();
        uv_ip4_addr(host, port, addr);
        uv_tcp_bind(tcp, (const sockaddr*)addr, 0);
        delete addr;

        uv_listen((uv_stream_t*)tcp, TCP_BACKLOG, 
                   &_tcp::listen_uv_listen_uv_connection_cb);
    }
};

PS 为了能够调用非静态方法,您需要一种从“uv_stream_t* stream”参数中获取指向 _tcp 实例的指针的方法。我建议使用此文档中的“void* uv_handle_t.data”指针http://docs.libuv.org/en/latest/handle.html#c.uv_handle_t

static void listen_uv_listen_uv_connection_cb(uv_stream_t* stream, int status) {
    _tcp *tcp = static_cast<_tcp *>( stream->data );
    tcp->regularMethod();
}

当然你应该在初始化uv_tcp_t *时将this指针分配给uv_handle_t.data

void listen(const char* host, int port) {
    tcp = new uv_tcp_t();
    uv_tcp_init(uv_default_loop(), tcp);
    tcp->data = this; // do not forget it
    ...
}

我会将此初始化代码移至构造函数。

对于要与此库一起使用的每个回调,您都需要这样的静态包装器。使用 c++11,您可能可以使用 lambda。

【讨论】:

  • 谢谢@slava。您知道使用非静态函数引用的解决方法吗?因为我打算从listen_uv_listen_uv_connection_cb 函数中访问很多实例变量。
  • 更详细地说,uv_tcp_t* tcp 包含对实际 tcp 连接的引用。如果我想在listen_uv_listen_uv_connection_cb 中对那个连接做点什么(我肯定会这样做),我也必须将uv_tcp_t* tcp 设为静态,如果我将tcp 设为静态,那么我将无法跨越新的连接制作更多 new _tcp() 实例。所以这就是问题所在。
  • @almosnow 不,您不必将uv_tcp_t* tcp 设为静态,您可以将其作为回调中的第一个参数。查看更新的答案
  • 感谢@Slava,并感谢您的解决方法。我不得不接受另一个答案,因为它让我了解了为什么在这种特殊情况下我不能使用常规函数指针,并且感觉很糟糕,因为你的答案也非常好。我将利用 uv_tcp_t 上的 data 属性来传递我的实例引用。再次感谢:D!
  • @almosnow 当您使用 C-API 并提供回调时,您总是(如果库设计得当)得到一个 cookie - 通常是直接或间接指向用户数据的 void 指针(如在这种情况下),这是在 C++ 中使用此类回调的常用方法
【解决方案2】:

uv_listen() 回调连接器需要 static 或免费(类外)函数。

因此你应该像这样声明你的函数

static void listen_uv_listen_uv_connection_cb(uv_stream_t* stream, int status) {
    printf("NEW CONNECTION\n");
    _tcp* thisStream = static_cast<_tcp*>(stream);
}

好吧,static_cast&lt;&gt; 实际上需要您的 _tcp 类继承自 uv_stream_t

class _tcp : public uv_stream_t {
    // ...
};

扩展your comment

“你能解释一下为什么 uv_listen 需要一个静态函数吗?这是所有函数指针参数的行为吗?”

类成员函数指针(需要绑定到类实例才能调用)和普通函数指针(适用于任何函数定义)之间存在差异。

为什么uv_listen() 需要一个普通的函数指针,很难说。可能是因为它是原生 C-API(我其实不知道),或者是为了灵活性。


注意:任何符号都不应使用前导下划线(如class _tcp)!

【讨论】:

  • 谢谢@πάντα ῥεῖ,你能解释一下为什么 uv_listen 需要一个静态函数吗?这是所有函数指针参数的行为吗?
【解决方案3】:
void listen_uv_listen_uv_connection_cb(uv_stream_t* stream, int status) {
        printf("NEW CONNECTION\n");
    };        <<<<<remove ;

函数定义的末尾不应该有分号。

你应该为这个类编写构造函数/复制 ctr/assign 操作符。

【讨论】:

  • 我会说写或禁用
  • 感谢您的观察@ravi,但代码仍然无法正常工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多