Herb Sutter的监视器类是如何工作的?

5

我正在尝试理解Herb Sutter在C++ and Beyond 2012上介绍的monitor类:

template<typename T>
class monitor {
private:
    mutable T t;
    mutable std::mutex m;

public:
    monitor(T t_ = T{}) : t{ t_ } {} 
    template<typename F>
    auto operator()(F f) const -> decltype(f(t))
    {
        std::lock_guard<std::mutex> _{ m }; return f(t);
    }
}; 

我已经成功创建了一个类,用更加老式和简单的方法(至少对我来说)完成了相同的事情:

template<typename T>
class MyMonitor {
public:
    MyMonitor() { t = T(); }

    template<typename F>
    auto callFunc(F f) {
        std::lock_guard<std::mutex> lock(m);
        return f(t);
    }

private:
    T          t;
    std::mutex m;
};  

赫伯·萨特的代码与我的代码相比,有哪些方面更好?

2
尽可能使用构造函数初始化列表,而不是在构造函数体中进行赋值,因为这样可以减少构造的对象数量。指向Herb。能够使用指定为“const”的对象比无法使用它更可取。指向Herb。明确指出operator()的返回值是由f()返回的类型,这意味着程序员对返回类型的潜在混淆较少。指向Herb。能够正确处理返回引用的函数对象。指向Herb。使用名为“_”的变量会降低可读性。指向你。 - Peter
2
请注意,没有尾随返回类型的auto函数是在C++14中添加的(因此它们并不过时),并遵循auto永远不是引用的常规规则。 - molbdnilo
3
是的,完全正确。如果函数对象返回引用,那么您的operator()将返回值。您可以使用decltype(auto)作为返回值,以获得与Herbs代码相同的行为,而无需使用尾随返回类型。 - super
1
有一种比Herb的想法更好的编写监视器类的方法(至少使用起来更好),那就是使用包装器监视器:https://dev59.com/Gmcs5IYBdhLWcg3wrmHC#48408987 - Mike Vine
@Peter:关于你的第一个观点:是的,我现在明白了,当Herb调用复制构造函数时,我调用了构造函数和赋值运算符。谢谢。愚蠢的错误。我会将其替换为:MyMonitor():t(T()){;},这样我就比Herb少使用一个临时对象。另一方面,Herb支持采纳,这可能很有用。 - Andy
显示剩余7条评论
1个回答

3

Herb Sutter的代码比我的好在哪些方面?

  • 你的T应该是可默认构造和可赋值的。
  • 在Herb Sutter的代码中,T应该是可拷贝构造的。

  • Herb Sutter的代码允许初始化成员。

  • 你的operator ()没有处理引用。


2
@idclev463035818:mutableconst与非const的区别... - Jarod42
是的,我的意思是它应该是“const”,但实际上不是,这就阻止了使用“const monitor”,而本来是可以使用的。 - 463035818_is_not_a_number
2
"它应该是const"。我不确定,虽然mutex确实不改变对象的常数语义,但我认为修改T更具争议性。 - Jarod42
@Andy:尝试使用 struct S { S(int) {} }; 然后 monitor<S> m(42); - Jarod42
1
读取一个需要锁定mutex的同步资源不应该是const的,因为有mutex存在,但从语义上来看,它可能被视为const,因为您并没有修改资源本身。所以mutable mutex是可以的,但正如我所说,mutable资源是有争议的。理想情况下,我们希望有两个重载函数,一个是const的,另一个不是。 - Jarod42
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接