在哪里定义C ++类成员模板函数和实例化它的仿函数?

时间:2010-11-30 16:25:12

标签: c++ templates functor

我有一个Foo类,用于一个小型独立项目。它在Foo.h中有一个类定义,在实现文件Foo.cpp中实现类的成员函数。

第一个问题 - 类Foo的一个成员函数是一个模板方法Foo :: doSomething(),这个方法的实现应该出现在Foo.h中的函数声明吗?

Foo :: doSomething()将被实例化的模板参数是两种Functor类型之一 - 类CalcA和CalcB。

我应该:

  • (A)将两个Functor类的定义和实现全部放在Foo.cpp中(实际上它们被其他Foo成员函数的实现用来调用Foo :: doSomething)。
  • (B)将两个Functor类的定义和实现放在Foo.h中。
  • (C)我应该将Foo.h和Foo.cpp中两个Functors的定义和实现分开,就像普通类一样吗?

4 个答案:

答案 0 :(得分:7)

一般规则:

如果在foo.cpp之外使用foo :: doSomething()(例如,如果它是公共的或受保护的,通常),它必须放在标题中。

如果没有,输入cpp文件是完全可以的,甚至是一个好主意(因为它使杂乱的文件远离头文件)。

所以,如果仿函数只用在cpp文件中,那么一定要把模板函数放在那里。如果这种情况发生变化,人们总是可以在以后重构。

答案 1 :(得分:7)

首先,您必须了解模板机制。模板不会被编译,它们在使用时会被实例化,然后编译它们的实例化。因此编译器需要使用模板函数在每个模块中拥有完整的模板定义,以便根据您传递的参数首先实例化它们。

要解决您的问题,有三种解决方案,但您会发现它们都会导致相同的结果。 要么在类定义中的头文件中实现整个模板(我们使用.hxx后缀而不是.h,以便精确地包含模板定义):

// Foo.hxx

#ifndef __FOO_HXX__
#define __FOO_HXX__

class Foo {
  public:
   template <class T>    
   void bar(const T& t) {
      t.doSomething();
   }
 };
#endif

或者您可以从类中外部化定义,但仍然在头文件中:

// Foo.hxx

#ifndef __FOO_HXX__
#define __FOO_HXX__

class Foo {
    public:
       template <class T>    
       void bar(const T&);
};

template <class T>
void Foo::bar(const T& t) {
   t.doSomething();
}
#endif

最后,您可以在外部文件中实现模板方法主体(出于同样的原因,前缀为.cxx)。它将包含方法的主体,但不包括“Foo.hxx”。相反,它是“Foo.hxx”,它将在类定义后包含“Foo.cxx”。这样,当编译器解析#include指令时,它会在同一模块中找到整个模板定义,允许它实例化它:

// Foo.hxx

#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
    public:
       template <class T>    
       void bar(const T&);
};

#include "Foo.cxx"

#endif

// Foo.cxx
template <class T>
void Foo::bar(const T& t) {
   t.doSomething();
}

实现模板的这三种方式之间的选择取决于可读性(和品味) 第二个和第三个在生成的代码方面是等价的,但我宁愿不使用cxx文件解决方案,因为当你忘记反转include时,它经常会导致愚蠢的错误。

此外,众所周知的C ++库(如STL或Boost)仅在头文件中提出其代码,这是良好设计的标志。通过在标题内使用外部定义,您可以阐明类的定义。您还可以阻止编译器自动内联方法,根据Herb Sutter http://www.gotw.ca/gotw/033.htm

,有时会导致结果不佳

答案 2 :(得分:1)

我的默认设置是将成员函数模板的定义放在.h文件中,如下所示:

class Foo
{
public: 
  template<typename T> void DoSomething(T t);
};

// ... later...

template<typename T>
void Foo::DoSomething(T t)
{
  // ...
}

如果这对于特定案例来说不是最理想的话,那么我会采取更多英雄措施。以{。1}}开头.inc文件与.h文件末尾的定义一起开始,或者甚至可能在.cpp文件中进行显式实例化,我需要使用成员函数模板。

答案 3 :(得分:1)

模板方法定义确实应该在它所属的类的头文件中。

像这样:

class MyClass
{
    template <typename T>
    void foo(const T&)
    {
        // Definition
    }
};

或者像这样(注意模板方法定义可以在类声明后从单独的文件中包含)

class MyClass
{
    template <typename T> void foo(const T&);
};

template <typename T>
void MyClass::foo(const T&)
{
    // Definition
}

其余部分取决于您同意的风格和您的需求。

如果我不仅在Foo中使用它们或者如果Foo将它们作为类成员使用它,我会将函数声明(或者甚至定义,如果它们很简单)放入标题中。