示例化为RValues的lock_guards能在C++中工作吗?

eqqqjvef  于 2023-06-25  发布在  其他
关注(0)|答案(1)|浏览(111)

在C++中,堆栈对象通常按出现的顺序创建,当它们超出作用域时按相反的顺序销毁。
但是,这只适用于L值,而不是R值,如下面代码on Godbolt所示:

#include <iostream>
class C{
    int instanceNo;
    public:
    C(){ 
        static int instanceCount = 0;
        instanceNo = ++instanceCount;
        std::cout << "constructed " << instanceNo << "\n";
    }
    ~C(){ 
        std::cout << "destructed " << instanceNo << "\n";
    }
};
int main()
{
    C one;
    C two;
    C{}; // three as RValue
    C four;
    C five;
}

结果:RValue立即被销毁,而不是在main()结束时。到目前为止,这对我来说很好。
我们有一些C++14代码如下:

std::lock_guard<std::mutex> guard(mutex);

在C++17中,我们可以使用模板参数推导(CTAD)并将代码缩短为

std::lock_guard guard(mutex);

然而,一位同事把它写得更短了:

std::lock_guard{mutex};

在我的理解中,这现在是一个R值。它将在构造时锁定互斥体,这很好。然而,根据上面的演示,它的生命周期立即结束,从而解锁互斥锁并有效地删除保护。
这个结论是正确的吗?这个改变破坏了我们类的线程安全性吗?

mo49yndu

mo49yndu1#

简而言之,你的分析是100%正确的。
如果你想专门测试std::lock_guard:它只需要一个实现.lock().unlock()的类,所以你可以创建一个只打印调试输出的伪锁:

#include <iostream>
#include <mutex>

struct dummy_mutex
{  
    void lock()
    {
        std::cout << "Locked!\n" << std::flush;
    }

    void unlock()
    {
        std::cout << "Unlocked!\n" << std::flush;
    }
}; 
   
int main()
{  
    dummy_mutex mtx;

    std::cout << "A\n" << std::flush;
    {
        std::cout << "B\n" << std::flush;
        std::lock_guard{mtx};
        std::cout << "C\n" << std::flush;
    }
    std::cout << "D\n" << std::flush;

    return 0;
}

这将打印:

A
B
Locked!
Unlocked!
C
D

如果将该行更改为std::lock_guard guard{mtx};,则输出将更改为:

A
B
Locked!
C
Unlocked!
D

相关问题