代码之家  ›  专栏  ›  技术社区  ›  Jack White

根据C++14,一个未使用的函数能否实例化一个具有副作用的变量模板?

  •  3
  • Jack White  · 技术社区  · 7 年前

    #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


    问题

    fn() 将永远不会被调用。他们能在运行构造函数的同时优化掉模板实例化吗?

    该代码是否可以以某种方式确定,即强制构造函数运行- 没有 fn 或模板参数值 123 UserClass ?


    更新: 一位主持人删去了我的问题,并建议进一步删去。可以查看原始详细版本 here .

    3 回复  |  直到 7 年前
        1
  •  3
  •   Barry    7 年前

    template <typename T> void bar();
    
    void foo(bool b) {
      if (b) {
        bar<int>();
      } else {
        bar<double>();
      }
    }
    

    二者都 bar<int> 和 bar<double> 在这里被实例化,即使 foo 福 仅使用 true .

    具体来说,对于变量模板,规则是 [temp.inst]/6 :

    除非变量模板专门化已显式实例化或显式专门化,否则在需要变量定义存在的上下文中引用变量模板专门化时,或者如果定义的存在影响程序的语义,则会隐式实例化变量模板专门化。

    在你的职能中:

    friend void fn() 
    { 
        (void)AnchorObjT<123>;
    };  
    

    AnchorObjT<123> 在需要定义的上下文中引用(无论 fn() 曾经调用过,或者在本例中甚至可以调用),因此它被实例化。

    但是 是一个全局变量,所以它的实例化意味着我们有一个之前构造的对象 main() -我们进去的时候 , 安科罗比特<123> 的构造函数将已运行,设置为 StaticInt 到 123 . 请注意,我们不需要实际运行 调用此构造函数- 这里的角色只是实例化变量模板,它的构造函数在别处调用。


    请注意,虽然该语言需要全局对象 若要存在,链接器可能仍然是该对象,因为没有对它的引用。假设您的实际程序对该对象执行更多操作,如果您需要它存在,您可能需要对其执行更多操作,以防止链接器删除它(例如,gcc具有 used attribute

        2
  •  2
  •   Sam Varshavchik    7 年前

    C++编译器可以自由实现任何优化 这没有明显的效果

    编译器可以忽略有关函数调用的所有其他内容,而不是实际编译函数调用本身,但是编译器必须做任何需要做的事情来进行安排,以便初始化静态类成员 犹如 该函数调用已完成。

    如果编译器可以确定程序中没有其他内容实际使用静态类成员,并且完全删除它不会产生明显的效果,那么编译器可以删除静态类成员以及初始化它的函数(因为没有其他内容引用该函数)。

    请注意,即使获取函数(或类成员)的地址也会产生可观察的效果,因此,即使没有任何东西真正调用函数,但某些东西获取了函数的地址,它也不会消失。

    P.S.-以上所有假设在C++代码中没有未定义的行为。当未定义的行为进入画面时,所有的规则都消失了。

        3
  •  1
  •   Yakk - Adam Nevraumont    7 年前

    简单的回答是它有效。

    长话短说的答案是它有效 除非 链接器将丢弃整个翻译单元(.obj)。

    当您创建一个.lib并链接它时,可能会发生这种情况。链接器通常根据“我是否使用obj导出的符号”的依赖关系图从库中选择要链接的.obj。

    我曾在叮当声中体验过这一点。我们正在创建自我注册的工厂,还有一些正在被撤销。为了解决这个问题,我们创建了一些宏,这些宏导致存在一个无关紧要的依赖项,从而防止obj文件被丢弃。

    这与其他答案并不矛盾,因为链接库的过程是决定程序中有什么和没有什么的过程。

    推荐文章