实施信号(观察者模式):是可变的还是 const_cast 是必要的?

Implementing signals (Observer pattern): is mutable or const_cast necessary?

我正在实现我自己的 signal/slot(观察者模式,Qt 风格)机制,这样我就可以有一个 property 来通知...东西...已经改变.

我认为 C++11 提供了使非常简洁和功能强大的实现成为可能所需的一切。 "issue" 我 运行 是如果我想 "connect" 一个 const 对象的信号,我需要 signal::connect 函数是常量,但修改 callbacks/observers 的列表。有两种直接的方法可以解决这个问题:

  1. const_cast connect.
  2. 中的列表
  3. 列出清单 mutable

在我看来两者都喜欢同一件事(之前有人问过这个问题,例如 this question 中的),逻辑上完全没问题,但风格上有问题。因此问题。有没有办法解决这个问题,或者这是 const_cast/mutable 的真正合理使用?

我现在拥有的一些初步代码:

template<typename... ArgTypes>
class signal
{
public:
  template<typename Callable>
  void connect(Callable&& callback) const
  {
    std::lock_guard<std::mutex> lock(slots_mutex);
    slots.emplace_back(callback);
  }

  void emit(ArgTypes... arguments) const
  {
    std::lock_guard<std::mutex> lock(slots_mutex);
    for(auto&& callback : slots)
    {
      callback(arguments...);
    }
  }

private:
  // mutable here allows to connect to a const object's signals
  mutable std::vector<std::function<void(ArgTypes...)>> slots;
  std::mutex slots_mutex;

};

注意我还没有测试这段代码;这只是反映了我目前的心态。

mutable 通常是这种情况下更好的选择。

尽可能避免 (const) 转换,它很容易出现未定义的行为,而 mutable 保证不会 1).


例如

1 mutable class 会员保证不会进入发出的代码 .text 部分。

There are two straightforward ways to fix this:

  1. const_cast the lists inside connect.
  2. Make the lists mutable.

事实上还有第三种选择(这是一种通用的解决方法,如果 C++ 没有提供 mutable 关键字)——您可以将相关数据移出语法对象:

class X
{
    mutable int           i1_;

    // Data pointed to by i2_ semantically belongs to this object
    // but doesn't constitute a syntactical part of it so it is not
    // subject to const-correctness checks by the compiler.
    std::unique_ptr<int>  i2_;

public:
    void constFunc() const {
        i1_  = 123;
        *i2_ = 456;
    }
};

尽管可以使用此附加选项,但我仍然同意 πάντα ῥεῖ's mutable 关键字是此类情况的正确选择。它以标准化的方式(例如允许被 grepped)明确记录此 class 的概念上的 const 操作在技术上可能不是不可变的。知道这一点很好,例如,在关注 class.

const 函数的线程安全时

signal::connect 确实修改了 signal::slot。所以,我认为你唯一需要做的就是改变你的设计。让 signal::connect 可变,调用者持有可变的 signal 指针。