我发现自己对模板的理解存在挑战,我理解模板的源代码必须放在头文件中才能访问模板实例化的所有相关类型。
所以,在这个例子中:
// This code cannot be placed in the cpp file
template <typename T> T foo(T v)
{
return -v;
}
T foo(T v)
的主体必须放在头文件中,然后,当foo
函数在某处实例化时,创建“真实”的body函数替换T
符号。真实的类型。使用此代码:
int bar = 5;
float baz = 6.66f;
bar = foo<int>(bar);
baz = foo<float>(baz);
模板实例化机制基于先前定义的模板创建以下函数:
int foo(int v)
{
return -v;
}
float foo(float v)
{
return -v;
}
但是,当我们有一个模板类时,该类可以具有根本不使用依赖类型的函数:
template <typename T> class Foo
{
Foo() : mistery(0), value(0) {}; // We're using the dependant type.
AddMistery() { ++mistery; }; // We are not using the dependant type.
int mistery;
T value;
};
我首先想到AddMistery
方法可以放在cpp文件中,因为这个方法没有使用依赖类型,但是当我尝试它时,链接失败了。就在这时,我打了个盹,记得模板类的不同实例不是同一个类。因此,当链接器正在执行它的工作时,它会查找AddMistery
方法的主体并且找不到它,因为ir被放入cpp文件中:
// Foo.h
template <typename T> class Foo
{
Foo() : mistery(0), value(0) {}; // We're using the dependant type.
AddMistery(); // We are not using the dependant type.
int mistery;
T value;
};
// Foo.cpp
#include "Foo.h"
template <typename T> Foo<T>::AddMistery()
{
++mistery;
}
// Main.cpp
#include "Foo.h"
int main(int argc, char **argv)
{
Foo<int> i;
Foo<float> f;
i.AddMistery(); // Link Error, where's the Foo<int>::AddMistery body?
f.AddMistery(); // Link Error, where's the Foo<float>::AddMistery body?
return 0;
};
所以,最后,这里有一个问题:有一种方法可以在头文件和cpp文件之间拆分模板类,将所有非类型相关方法的主体移动到cpp,而不是将所有方法体保持在头部文件?
答案 0 :(得分:1)
简短的回答是否定的,因为除非您使用某个模板参数对其进行实例化,否则类模板不是类。所以没有Foo::AddMystery()
来实现。
以这种方式思考:成员函数具有依赖类型的知识,因为它们具有隐式的第一个参数,该参数是指向其类的对象的指针。所以
Foo<int> f;
f.AddMistery();
相当于
Foo<int> f;
Foo<int>::AddMistery(&f);
如果你有某些类型的模板实例化,那么可以在Foo<int>::AddMistery
文件中实现.cpp
,但这与无关函数不需要模板参数。
答案 1 :(得分:1)
除非您打算将模板仅用于有限的众所周知的一组类型,例如,这是不可能的。 int
,short
和double
。在这种情况下,您可以使用显式实例化并在.cpp
文件中放置模板函数定义。