【问题标题】:Is object code generated for unused template class methods?是否为未使用的模板类方法生成目标代码?
【发布时间】:2010-09-16 00:50:59
【问题描述】:

我有一个使用 3 个不同类型参数实例化的 C++ 模板类。类只需要为其中一种类型提供一个方法,并且永远不会与其他两种类型一起调用。

该方法的目标代码会生成三次(针对所有实例化模板的类型),还是只生成一次目标代码(针对实际使用的类型)?

【问题讨论】:

    标签: c++ templates footprint


    【解决方案1】:

    通常是的。

    编译器真正知道的是,您的程序可以为每个类创建至少一个实例。但它不知道您将如何处理这些实例。所以几乎肯定会生成代码。

    也就是说,如果所讨论的方法不是虚拟的,并且从未被调用,链接器可以使用其正常的死代码删除功能来删除它们。所以生成(和编译)的代码不会在最终的 EXE 中。

    这在很大程度上取决于所使用的 C++ 编译器,因为它们并不完全相同。

    【讨论】:

      【解决方案2】:

      虚拟成员函数在类模板被实例化时被实例化,但非虚拟成员函数只有在被调用时才会被实例化。

      这在 C++ 标准的 [temp.inst] 中有所涉及(在 C++11 中,这是 §14.7.1/10。在 C++14 中,它是 §14.7.1/11,在 C 中++17 是 §17.7.1/9。摘自下面的 C++17)

      实现不应隐式实例化函数模板、变量模板、成员 模板、非虚拟成员函数、成员类、类模板的静态数据成员,或 constexpr if 语句 (9.4.1) 的子语句,除非需要这样的实例化

      还要注意,即使某些成员函数对于给定的模板参数不可实例化,也可以实例化类模板。例如:

      template <class T>
      class Xyzzy
      {
      public:
          void CallFoo() { t.foo(); }  // Invoke T::foo()
          void CallBar() { t.bar(); }  // Invoke T::bar()
      
      private:
          T t;
      };
      
      class FooBar
      {
      public:
          void foo() { ... }
          void bar() { ... }
      };
      
      class BarOnly
      {
      public:
          void bar() { ... }
      };
      
      int main(int argc, const char** argv)
      {
          Xyzzy<FooBar>  foobar;    // Xyzzy<FooBar> is instantiated
          Xyzzy<BarOnly> baronly;   // Xyzzy<BarOnly> is instantiated
      
          foobar.CallFoo();         // Calls FooBar::foo()
          foobar.CallBar();         // Calls FooBar::bar()
      
          baronly.CallBar();        // Calls BarOnly::bar()
      
          return 0;
      }
      

      这是有效的,即使 Xyzzy::CallFoo() 是不可实例化的,因为没有 BarOnly::foo() 这样的东西。此功能通常用作模板元编程工具。

      但是,请注意,模板的“实例化”与生成多少目标代码没有直接关系。这将取决于您的编译器/链接器实现。

      【讨论】:

      • 我们还应该注意,并非所有函数都可以实例化,只要不调用它们就可以。
      • 完成。刚开始回答的时候也想过这个,但是懒得写完整的解释了。
      • 是否可以将方法标记为强制实例化(即防止它被优化)而不使其成为虚拟方法?谢谢!
      【解决方案3】:

      我认为这取决于编译器和设置。例如,我相信 MSVC6 生成了一切,但 VS2005 没有。规范说编译器不应该,但在现实世界中,它取决于实际的编译器(例如,在 MSVC6 的 boost 中有许多变通方法)。如果启用 /opt:ref ,链接器可以删除未引用的函数(对于 VS,其他编译器存在等效选项)。

      【讨论】:

      • 好旧的 MSVC6 和模板,啊那些日子......恐怖。
      猜你喜欢
      • 2013-01-14
      • 1970-01-01
      • 2015-12-22
      • 2013-10-03
      • 1970-01-01
      • 1970-01-01
      • 2014-07-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多