You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Herb Sutter的monitor示例采用T而非T&或T&&?

分析Herb Sutter的Monitor类构造函数局限与改进方案

Hey there, let's take a close look at that monitor class Herb Sutter walked through in his 2012 C++ and Beyond talk—specifically around the 40:30 mark in the C++ Concurrency session. Here's the exact code he showed:

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);
    }
};

Now, if you've tried using this in real code, you've probably hit a snag: the constructor only accepts a T by value, with no overloads for lvalue references (T&) or rvalue references (T&&). That creates a few headaches:

  • Non-copyable types (like std::unique_ptr) won't work at all here—since passing by value requires a copy constructor, which these types intentionally don't have.
  • For large objects (like a big std::vector), passing by value forces an unnecessary copy, which eats up performance for no good reason.
  • You can't directly move an existing object into the monitor; you'd have to do awkward workarounds to transfer ownership without copying.

Luckily, fixing this is straightforward by adding the missing constructor overloads and leveraging move semantics properly. Here's the updated, more flexible version:

template<typename T>
class monitor {
private:
    mutable T t;
    mutable std::mutex m;
public:
    // Default constructor for empty initialization
    monitor() : t{} {}

    // Original pass-by-value constructor, optimized with move
    explicit monitor(T t_) : t{std::move(t_)} {}

    // Overload for lvalue references (copy existing objects efficiently)
    explicit monitor(const T& t_) : t{t_} {}

    // Overload for rvalue references (enable move semantics)
    explicit monitor(T&& t_) : t{std::move(t_)} {}

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

Let's break down the key improvements:

  • We added a default constructor for cases where you don't have an initial value to pass in.
  • The original pass-by-value constructor now uses std::move to transfer the incoming T into our member variable—this cuts out an extra copy that would have happened in the original code.
  • The lvalue reference overload (const T&) lets us copy an existing object directly into the monitor without an unnecessary intermediate copy.
  • The rvalue reference overload (T&&) enables perfect forwarding, so we can move objects into the monitor efficiently—this is what makes non-copyable types like std::unique_ptr work with the monitor.
  • We marked all constructors explicit to prevent accidental implicit conversions, which is a safe practice for wrapper classes like this (it stops code from silently converting a T to a monitor<T> when you don't want it to).

Here's how you can use these new constructors in practice:

// Create an empty monitor with a default-constructed vector
monitor<std::vector<int>> empty_monitor;

// Copy an existing vector into the monitor
std::vector<int> my_vec{1, 2, 3, 4};
monitor<std::vector<int>> copied_monitor(my_vec);

// Move a unique_ptr directly into the monitor (no copy possible!)
monitor<std::unique_ptr<int>> moved_monitor(std::make_unique<int>(42));

内容的提问来源于stack exchange,提问作者Michael Conlen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:11:47