std::move 在 std::bind 中使该指针无效

std::move disvalidates this pointer in std::bind

我有一个关注串行设备的 class。 (请记住,我发布了重现我观察到的问题的最小示例。实际上有很多错误处理等,为了简单起见我删除了它们 - 因此不要以此来判断我)

class Device
{
public:
    void open(const std::string& path) { 
        fd = ::open(path.c_str(), O_RDWR | O_NOCTTY));
        readThread = std::thread(std::bind(&Device::readProc, this)); 
    }
    void close() { ::close(fd); }

private:
    void readProc() { auto i = read(fd, buffer, sizeof(buffer)); }
    std::thread readThread;
    int fd;
};

主要是,我将枚举每个设备,如果打开成功,我会将其推入向量中:

for (auto& p : std::filesystem::directory_iterator("/dev/"))
        if (p.path().string().substr(0, 11) == "/dev/ttyACM")
        {
            Device dev;
            if (dev.open(p.path().string()))
            {
                readers.push_back(std::move(dev));
            }
            else
            {
                std::cout << "Failed to open device " << p.path() << std::endl;
            }
        }

问题是,在调用 std::move(dev) 之后,传递给 std::bind 函数的指针似乎失效了。读取 returns -1 并将 errno 设置为 9(无效的描述符)。我调试了它,打开后 fd 的值是 5(随每个程序 运行 变化),而在 readProc()std::move之后变成了-129589(随机值)。

这是正确的行为吗?如果有,如何避免?

编辑: 我已将设备枚举更改为:

readers.emplace_back();
auto& dev = readers.back();
if (!dev.open(p.path().string()))
{
    readers.pop_back();
}

这很有效,因此证实了我对 std::move() 的观察,但这远非正确的方法。

此处:

readThread = std::thread(std::bind(&Device::readProc, this)); 

您将 this 绑定到传递给线程的函数。然后稍后当你打电话时

readers.push_back(std::move(dev));

然后dev被移动,线程成员也被移动,但是线程仍然有指向被移动的from对象的指针。

在您的固定版本中,情况并非如此,您在容器中已有的 Device 上调用 open

Is this the correct behavior?

是的。移动对象 dev 后其 this 仍保持不变。而容器中的对象是不同的this.

If so, how can it be avoided?

小心传递给线程的引用/指针。一般来说,您需要注意引用/指向的对象仍然存在。