#include <iostream> class MyBaseClass { public: static int StaticInt; }; int MyBaseClass::StaticInt = 0; template <int N> class MyClassT : public MyBaseClass { public: MyClassT() { StaticInt = N; }; }; template <int N> static MyClassT<N> AnchorObjT = {}; class UserClass { friend void fn() { std::cout << "in fn()" << std::endl; //this never runs (void)AnchorObjT<123>; }; }; int main() { std::cout << MyBaseClass::StaticInt << std::endl; return 0; }
输出是:
123
…表示调用了MyClassT()构造函数,尽管从未调用过fn().
在gcc和clang上测试-O0,-O3,-Os甚至-Ofast
题
根据C标准,此程序是否具有未定义的行为?
换句话说:如果更高版本的编译器设法检测到永远不会调用fn(),那么它们是否可以在运行构造函数的同时优化模板实例化?
这个代码可以以某种方式确定性,即强制构造函数运行 – 不引用函数名称fn或UserClass之外的模板参数值123吗?
更新:主持人截断了我的问题并建议进一步截断.原始详细版本可以查看here.
模板实例化是代码的函数,而不是任何类型的动态运行时条件的函数.作为一个简单的例子:template <typename T> void bar(); void foo(bool b) { if (b) { bar<int>(); } else { bar<double>(); } }
bar< int>和bar< double>在这里实例化,即使永远不会调用foo,或者即使foo只用true调用.
对于变量模板,具体来说,规则是[temp.inst]/6:
Unless a variable template specialization has been explicitly instantiated or explicitly specialized, the variable template specialization is implicitly instantiated when it is referenced in a context that requires a variable definition to exist or if the existence of the definition affects the semantics of the program.
在你的功能:
06001
AnchorObjT< 123>在需要定义的上下文中引用(无论是否调用fn(),甚至在这种情况下,甚至可以调用),因此它被实例化.
但是AnchorObjT< 123>是一个全局变量,所以它的实例化意味着我们有一个在main()之前构造的对象 – 当我们输入main()时,AnchorObjT< 123>的构造函数将被运行,将StaticInt设置为123.注意我们不需要实际运行fn()来调用这个构造函数 – 这里的fn()角色只是实例化变量模板,其构造函数在别处被调用.
打印123是正确的预期行为.
注意,虽然该语言需要全局对象AnchorObjT< 123>要存在,链接器仍然可以是对象,因为没有对它的引用.假设你的真实程序对这个对象有更多的作用,如果你需要它存在,你可能需要做更多的事情来防止链接器删除它(例如gcc有used
attribute).