1
4

2 回答 2

7

我不熟悉 c++ 模板,所以我的第一个问题是我应该把这个模板的实现放在哪里,在处理器的 .cpp 或 .h 文件中?

把它放在头文件中。这是最简单和最强大的解决方案。通常,您希望将函数的定义(即它们的函数体)放在源文件 (.cpp) 中,因为源文件可以独立编译。但这对于模板(*)是不可能的

(*) 略微简化。

类模板只是类的蓝图,函数模板是函数的蓝图。也就是说,函数模板不是函数,换句话说,“模板函数”具有误导性,它不是函数而是模板/蓝图。

从函数模板(或类模板中的类)构建函数的过程称为实例化。结果是一个实例化的函数,或者更一般地说,一个函数模板化。

模板特化不是模板。函数模板特化只是一个名称怪异的普通函数;类模板特化只是一个名称怪异的类。

模板只会针对某些特定的模板参数集进行实例化:

  • 如果您明确要求实例化它
  • 或者如果您只使用专业化(->隐式实例化)。

第二种方式更为常见。一个例子:

template<class T>
struct my_type
{
    T mem;
};

// using the specialization -> implicit instantiation
my_type<int> o;
my_type<double> p;

这将为模板参数实例化一次类模板 ,并为模板参数实例化一次。这将创建两个具有相似名称的独立且不相关的类型:和my_typeintdoublemy_type<int>my_type<double>

对于功能也是如此;除了函数,您通常不会显式提供模板参数。相反,您让编译器从函数参数的类型中推断出模板参数。例子:

template<class T>
void foo(T param)
{
    std::cout << param;
}

foo<int>(42);  // explicitly specifying the template arguments -- DON'T DO THAT
foo(21);       // template argument *deduction*

第二次调用会自动推导出模板参数为int. 同样,我们创建了(隐式实例化)两个具有相似名称foo<int>(int)的函数:(名称是foo<int>并且它有一个类型为 的函数参数int)和foo<double>(double)


为什么将模板的定义放在源文件中不好:请参阅为什么模板只能在头文件中实现?

短版:由于模板是蓝图,为了使用它们,编译器必须实例化它们。但它只能实例化它所知道的。

如果foo在头文件中声明函数模板,在中templ.h定义并在中templ.cpp使用main.cpp,则:

  • main.cpp中,编译器不知道函数模板的定义。它只能实例化 的声明foo而不能实例化定义。

  • templ.cpp中,编译器确实知道定义,并且可以实例化它。但是,它不知道外部的用途templ.cpp——因此它不能为外部使用的所有参数集实例化它。

在 OP 中的示例中,这是可行的,但它似乎是一个疏忽:

[模板.h]

template<class T>
void foo(T);

void ordinary_function();

[模板.cpp]

#include "templ.h"

template<class T>
void foo(T p)
{
    std::cout << p;
}

void ordinary_function()
{
    foo(42);     // implicit instantiation of foo<int>
    foo(2.5);    // implicit instantiation of foo<double>
}

[主.cpp]

#include "templ.h"
int main()
{
    foo(23);    // works fine, uses the foo<int> defined in templ.cpp
    foo('a');   // linker error: foo<char> not defined
    return 0;
}

因为 的定义foo<char>尚未在 中实例化,templ.cpp也无法在 中实例化main.cpp,所以会产生链接器错误。

这就是为什么你不应该依赖这种行为。如果出于某种原因不想在头文件中定义函数模板,则可以使用显式实例化。至少,显式实例化是显式的,并且应该记录在案,这样就不会发生意外。


编译器实际上抱怨的问题与模板无关;)这只是一个名称查找问题。一个简化的例子:

#include <iostream>

struct Foo
{
    typedef int Bar;

    void do_something();
};

Bar Foo::do_something()
{
    std::cout << "something\n";
}

当编译器看到Bar Foo::do_something()它看到的行时Bar,找不到该名称所指的内容。因此错误。另一方面:

#include <iostream>

struct Foo
{
    typedef int Bar;

    void do_something();
};

Foo::Bar Foo::do_something()
{
    std::cout << "something\n";
}

现在您告诉编译器在哪里查找名称Bar,即 inside Foo

于 2013-11-12T12:24:49.753 回答
0

这是我为帮助您而编写的模板的简短示例:
在标题中:

typedef TemplatedClassName< ParameterValue0, ParameterValue1 > TemplateAlias;

在源文件中:
//显式模板实例化

template class TemplatedClassName< ParameterValue0, ParameterValue1 >;
于 2013-11-12T12:10:59.867 回答