C++ 带有必要副作用的静态初始化被优化掉了

C++ 带有必要副作用的静态初始化被优化掉了,c++,visual-studio,templates,boost,static,C++,Visual Studio,Templates,Boost,Static,我想向类的每个子类发送一个信号。 为了实现这一点,我使用boost的signals2库。由于代码的使用,我需要使用CRTP 包含在头文件中的最小示例: class A { static boost::signals2::signal<void()>& getSignal() { static boost::signals2::signal<void()> signal; return signal; } publi

我想向类的每个子类发送一个信号。 为了实现这一点,我使用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类{
静态升压::信号2::信号&getSignal(){
静态升压::信号2::信号;
返回信号;
}
公众:
静态无效发送信号(){
getSignal();
}
受保护的:
模板
静态升压::信号2::连接添加插槽(插槽\u t插槽){
返回_entityToBeDeleted()。连接(插槽);
}
};
模板
B类:A{
静态boost::signals2::连接_installHandler(){
返回一个::addSlot([])(){
//做点什么
});
}
静态常量boost::信号2::连接;
};
模板
const boost::signals2::连接B::连接=_installHandler();
丙类:公共乙类{
//混凝土亚类
};
希望通知子类的某个客户机类调用
a::sendSignal
,该调用调用连接到该信号的所有插槽。本通知的具体内容与本问题无关。如您所见,需要使用
B::connection
的静态初始值设定项的副作用才能使其工作,但由于从未使用(也不需要)对象本身,因此跳过了完整的初始化

问题是:编译器完全优化了对_installHandler()的调用(我在Visual Studio 2017中使用MSVC)。我通过编写一个额外的测试用例来验证这一点,该测试用例显式地测试连接对象是否有效。当我以这种方式使用该对象时,代码工作正常,所有其他测试用例都通过了。当我删除额外的测试用例时,其他测试用例再次失败

我试着按照答案中的建议声明静态变量volatile,但没有帮助

如何防止要优化的静态连接对象的初始化。或者:如何确保由模板实例化创建的具有副作用的函数始终在可执行文件开始时调用一次,即使返回的对象从未实际使用过;最好是在静态初始化期间。


我考虑在这个类的另一个静态初始值设定项中使用连接(
std::cout类模板的成员可以根据需要实例化。一个可能的修复方法是从
B
的构造函数或析构函数中引用
B::connection

B() { static_cast<void>(&connection); }
B(){static_cast(&connection);}

如果没有其他代码odr使用在可执行文件中定义的任何内容,某些链接器将跳过在该可执行文件中包含对象文件…您的最小示例应至少包含头文件的使用方式。请阅读:@aschepler我知道,问题是如果静态初始化的相关部分不是e返回对象本身(这不是必需的),但它是函数的副作用(即对
signal::connect
的调用。仅从头文件中还不清楚是什么翻译单元导致了这种情况。请注意,由于初始化
B::connection
涉及非constexpr函数调用,因此只能在动态初始化阶段进行。尽管我知道有这样一种技巧,可以故意标记unused函数参数我没有想到在这里使用它。天才。