实施信号(观察者模式):是可变的还是 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 的列表。有两种直接的方法可以解决这个问题:
const_cast
connect
. 中的列表
- 列出清单
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:
const_cast
the lists inside connect
.
- 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 指针。
我正在实现我自己的 signal
/slot(观察者模式,Qt 风格)机制,这样我就可以有一个 property
来通知...东西...已经改变.
我认为 C++11 提供了使非常简洁和功能强大的实现成为可能所需的一切。 "issue" 我 运行 是如果我想 "connect" 一个 const
对象的信号,我需要 signal::connect
函数是常量,但修改 callbacks/observers 的列表。有两种直接的方法可以解决这个问题:
const_cast
connect
. 中的列表
- 列出清单
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:
const_cast
the lists insideconnect
.- 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 指针。