我想向一个类的每个子类发送一个信号。 为了实现这一点,我使用了来自boost的signals2库。由于代码的使用,我需要使用CRTP。
最小示例,包含在头文件中:
class A {
static boost::signals2::signal<void()>& getSignal() {
static boost::signals2::signal<void()> signal;
return signal;
}
public:
static void sendSignal() {
getSignal()();
}
protected:
template <typename slot_t>
static boost::signals2::connection addSlot(slot_t slot) {
return _entityToBeDeleted().connect(slot);
}
};
template <typename concrete_B>
class B : A {
static boost::signals2::connection _installHandler() {
return A::addSlot([]() {
//Do something
});
}
static const boost::signals2::connection connection;
};
template<typename concrete_B>
const boost::signals2::connection B<concrete_B>::connection = _installHandler();
class C : public B<C> {
//Concrete subclass
};
某个想要通知子类的客户端类调用A::sendSignal
,调用连接到该信号的所有插槽。该通知的内容究竟与此问题的目的无关。正如您所看到的,需要B<concrete_B>::connection
的静态初始化器的副作用才能使用,但由于对象本身从未使用过(也不需要),因此将跳过完整的初始化。
问题是:对_installHandler()的调用完全由编译器优化(我在Visual Studio 2017中使用MSVC)。我通过编写一个额外的测试用例来验证这一点,该测试用例显式测试连接对象是否有效。当我以这种方式使用对象时,代码工作,所有其他测试用例都通过了。当我删除其他测试用例时,其他人再次失败。
我尝试按照this question的答案中的建议声明静态变量volatile,但它没有帮助。
如何防止静态连接对象的初始化被优化掉。或者:如何确保具有由模板实例创建的副作用的函数始终在可执行文件的开头调用一次,即使返回的对象从未实际使用过;理想情况下,在静态初始化期间。
我想过在这个类的另一个静态初始化器(std::cout << connection.connected() << '\n';
或类似的东西)中以某种方式使用连接已经可以安全地被调用(因为这个初始化的对象肯定是使用的),但是这看起来像讨厌的黑客。
编辑:我实施了“hack”并且它完美无瑕地运行,但是如果有一种方法以“更清洁”的方式实现所需的行为,我感兴趣。
答案 0 :(得分:1)
根据需要实例化类模板的成员。一种可能的解决方法是从B<concrete_B>::connection
的构造函数或析构函数引用B
:
B() { static_cast<void>(&connection); }