【问题标题】:Difference between member functions for a template class defined inside and outside of the class在类内部和外部定义的模板类的成员函数之间的区别
【发布时间】:2011-01-07 09:33:59
【问题描述】:

在类声明内部和外部为模板类定义成员函数有区别吗?

内部定义:

template <typename T>
class A
{
public:
    void method()
    {
        //...
    }
};

外部定义:

template <typename T>
class B
{
public:
    void method();
};

template <typename T>
void B<T>::method()
{
    //...
}

对于非模板类,这是内联方法和非内联方法的区别。模板类也是这样吗?

我的大多数同事的默认设置是在类内提供定义,但我一直更喜欢在类外定义。我的偏好合理吗?

编辑:请假设上述所有代码都在类的头文件中提供。

【问题讨论】:

  • 我从未在任何地方看到任何引用表明在类声明中定义方法体会使该方法内联。我错过了什么吗?
  • @Dathan:您错过了 C++ 标准的 §9.3/2,其中说:“成员函数可以在其类定义中定义 (8.4),在这种情况下,它是内联的成员函数...”编辑:还要注意这是一个类定义——类声明类似于:class x;
  • 感谢您的澄清。 (c:
  • 我们似乎有两个相互矛盾的答案,但围绕它们的讨论让我相信这两种方法在技术上本质上是等效的。

标签: c++ class templates inline


【解决方案1】:

是的,模板类也是如此。

模板类的方法定义通常首选内联的原因是,对于模板,整个定义必须在模板实例化时可见。

因此,如果将函数定义放在某个单独的 .cpp 文件中,则会出现链接器错误。 唯一通用的解决方案是使函数内联,通过在类内部或外部使用 inline 关键字定义它。但在任何一种情况下,它都必须在调用函数的任何地方都可见,这意味着它通常必须与类定义在同一个标​​头中。

【讨论】:

  • 只是出于好奇,现在链接器错误普遍存在吗?我知道过去一些编译器提供了不同的模板实例化选项。例如,SGI Irix CC 编译器默认链接时实例化,实际上似乎鼓励将模板函数定义放在自己的 .cpp 文件中(即,不在头文件中,非内联,对调用代码不可见,就像任何其他非内联函数定义)。只是想知道内联模板函数定义是否已从编译器偏好变为语言要求。
  • @Darryl,你在描述外部模板。这是一个非标准的扩展,并不是所有的编译器都支持它。新标准 C++1x 正在添加 extern 模板,但我认为它需要关键字。
  • 模板在每个使用该特定实例化的编译单元上编译,但它们是“弱”符号(gcc 术语,我不知道这是多么标准)并且定义了相同的符号在多个编译单元中不会出现链接错误。因此无需将inline 添加到模板函数/方法中。另一个说明是 inline 是对编译器的提示,但编译器可以忽略它(但它会影响符号的创建方式,因此链接器不会抱怨双重定义)。
【解决方案2】:

除了需要输入更多内容之外,没有什么区别。这包括template 位、inline 以及在引用类时必须使用更多“详细”的名称。例如

template <typename T> class A { 
  A method(A a) { 
    // whatever
  } 
}; 

template <typename T> inline A<T> A<T>::method(A a) { 
  // whatever
} 

注意,当方法被定义在里面时,你总是可以在引用A&lt;T&gt;时省略模板参数列表&lt;T&gt;,而直接使用A。在外部定义它时,您必须在返回类型和方法名称中使用“完整”名称(但不能在参数列表中)。

【讨论】:

  • 您在这两种情况下都将“方法”内联,如果函数体很大,这可能会很糟糕。这就是我要避免的。
  • inline 关键字与实际优化代码的关系不大,更多的是与是否允许对同一函数进行多个定义。如果函数在头文件中定义(模板不可避免,或者函数在类声明中定义的情况),并且多个编译单元包含该函数,链接器最终将得到多个(相同的)定义相同的功能/方法。 inline 关键字(应该隐含在模板中)告诉链接器它没问题,否则将是链接器错误。
  • 重点是本,使用模板您别无选择。它必须被标记为内联,否则模板将不起作用。这是一些人不喜欢模板的原因之一。还值得注意的是,编译器实际上不必内联它可以在你背后做任何它喜欢做的事情。
  • @Ben:我将其设为 inline 是为了获得 等效 声明/定义,从而更好地说明您需要输入更多内容才能达到同样的效果。如果您要尝试获取非内联方法,则别无选择,只能使用类外定义。在那种情况下,我不明白你为什么还要考虑(询问)课堂定义。
  • @Goz:为什么必须标记为内联?不,它没有。我将其标记为inline 的唯一原因是使两个声明等效。
【解决方案3】:

我知道..我想这一定对你有什么帮助?

defining a member function outside of its template

这样提供模板类的成员函数的定义是不行的:

 // This might be in a header file:
 template <typename T>
 class xyz {
    void foo();
  };

// ...

 // This might be later on in the header file:
  void xyz<T>::foo() {
// generic definition of foo()
   }

这是错误的,原因有几个。是这样的:

      void xyz<class T>::foo() {
         // generic definition of foo()
      }

正确的定义需要模板关键字和声明类模板定义的相同模板参数。所以这给出了:

       template <typename T>
          void xyz<T>::foo() {
           // generic definition of foo()
                 }

请注意,还有其他类型的模板指令,例如成员模板等,并且每种都采用自己的形式。重要的是要知道你有哪些,这样你就知道如何编写每种口味。尤其如此,因为某些编译器的错误消息可能不清楚什么是错误的。当然,买一本好书和最新的书。

如果模板中有嵌套的成员模板:

    template <typename T>
      struct xyz {
      // ...
      template <typename U>
       struct abc;
        // ...
       };

你如何定义 xyz 之外的 abc?这不起作用:

     template <typename U>
    struct xyz::abc<U> { // Nope
      // ...
  };

也不是这样:

 template <typename T, typename U>
 struct xyz<T>::abc<U> { // Nope
// ...
 };

你必须这样做:

     template <typename T>
       template <typename U>
           struct xyz<T>::abc {
            // ...
           };

请注意,它是 ...abc 而不是 ...abc&lt;U&gt;,因为 abc 是“主要”模板。 IOW,这不好:

// 此处不允许: 模板 模板结构 xyz::abc { };

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-18
    • 1970-01-01
    • 2012-02-18
    • 1970-01-01
    • 2018-02-22
    相关资源
    最近更新 更多