我有一个Foo类,用于一个小型独立项目。它在Foo.h中有一个类定义,在实现文件Foo.cpp中实现类的成员函数。
第一个问题 - 类Foo的一个成员函数是一个模板方法Foo :: doSomething(),这个方法的实现应该出现在Foo.h中的函数声明吗?
Foo :: doSomething()将被实例化的模板参数是两种Functor类型之一 - 类CalcA和CalcB。
我应该:
答案 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将它们作为类成员使用它,我会将函数声明(或者甚至定义,如果它们很简单)放入标题中。