【问题标题】:Callable being required to be static in C++ class templateC++ 类模板中的可调用对象必须是静态的
【发布时间】:2020-07-13 08:11:31
【问题描述】:

我想在程序中设置以下类:

  • 实现缓冲区的类。此缓冲区在满时会生成一个线程,该线程会进行回调以处理如何处理已满的缓冲区。
  • 包含缓冲区对象的基类模板。实现回调函数,该函数调用派生类中定义的虚函数。
  • 从基类继承并实现数据处理方式的派生类。

首先,最小的可重现示例:

#include <vector>
#include <iostream>
#include <thread>

template <typename T>
class CallbackBuffer
{
    public:

        std::vector<T> buffer;
        void (*callback)(std::vector<T>);
        std::thread writerThread;

        CallbackBuffer(int bufferSize = 10)
        {
            buffer.resize(bufferSize);
        }

        void setCallback(void (*cb)(std::vector<T>))
        {
            callback = cb;
        }

        void writeCall()
        {
            writerThread = std::thread(callback, buffer);
        }
};

template <typename T>
class Base
{
    public:

        CallbackBuffer<T> buffer;

        Base()
        {
            buffer.setCallback(bufferHandler);
        }

        void bufferHandler(std::vector<T> v)
        {
            for(auto &i : v)
            {
                write(i);
            }
        }

        virtual void write(T i) = 0;
};

class Derived : public Base<int>
{
    public:

        Derived()
        {
        }

        void write(int i)
        {
            std::cout << i << std::endl;
        }
};

int main()
{
    Derived d;
    return 0;
}

我收到以下编译器错误:

error: invalid use of non-static member function ‘void Base<T>::bufferHandler(std::vector<T>) [with T = int]’

所以编译器需要 bufferHandler 是静态的,但如果我这样做了,那么我将无法访问对象的成员。有没有办法解决这个问题,或者只是一个可怕的想法?

【问题讨论】:

    标签: c++ templates static callable


    【解决方案1】:

    您正在传递类成员函数,因此您需要在您的 CallbackBuffer 类中包含类似:

    void (Base<T>::*callback)(std::vector<T>);
    
    // ...
    
    void setCallback(void (Base<T>::*cb)(std::vector<T>)) {
        callback = cb;
    }
    

    Base 类中:

    Base() {
        buffer.setCallback(&Base<T>::bufferHandler);
    }
    

    Demo

    【讨论】:

    • 不错。重复的 Base 声明怎么没有问题?如果每个类都定义在不同的文件中,这会导致循环头依赖吗?
    • @derkomai 前向声明是一种常见的事情,不应导致任何循环依赖
    【解决方案2】:

    成员函数指针的类型与常规函数不同,这就是您的代码不起作用的原因。您可能想要的是使用 std::function 而不是原始指针:

    #include <functional>
    
    //...
    std::function<void(std::vector<T>)> callback;
    
    // ...
    
    void setCallback(const std::function<void(std::vector<T>)>& cb) {
        callback = cb;
    }
    
    

    然后这样传递:

    Base() {
        buffer.setCallback([this](auto& vec){ this->bufferHandler(vec); });
    }
    

    恕我直言,这比传递成员指针更具可读性和灵活性

    【讨论】:

    • 这可能更具可读性,但std::function 比成员指针更昂贵。
    • @NutCracker,你能详细说明一下吗?此外,似乎 lambda 中的 auto 需要 c++14(这里不是问题)。
    • @derkomai 检查这个帖子here
    • @NutCracker 谢谢,这两种解决方案都是 100% 有效的,但我现在肯定更喜欢最便宜的一种。
    猜你喜欢
    • 2017-04-20
    • 2011-09-23
    • 2019-01-31
    • 2011-06-30
    • 2012-04-22
    • 1970-01-01
    • 2015-06-01
    • 2018-03-26
    相关资源
    最近更新 更多