模板化成员函数的声明和实现

Declaration and implementation of templated member functions

本文关键字:声明 实现 函数 成员      更新时间:2023-10-16

我正在尝试创建一个快速的实用程序类,通过组合扩展std::vector。为此,我想使其尽可能通用。

当在单个文件(即main.cpp)中声明和实现时,该类运行良好:

主.cpp

#include <iostream>
#include <algorithm>
#include <vector>
template<typename T>
class FVector {
public:
FVector(std::vector<T> vector): _vector(vector) {}
template <typename unaryOperation>
void forEach(unaryOperation op) {
std::for_each(_vector.begin(),_vector.end(),op);
};
private:
std::vector<T> _vector;
};
int main(int argc, const char * argv[]) {
auto printInt = [](int i){ std::cout << i << std::endl; };
std::vector<int> numbers{1, 2, 3, 4, 5};
FVector<int> fNumbers{numbers};
fNumbers.forEach(printInt);
return 0;
}

但是当我尝试将类放在它自己的 .h 和 .cpp 文件中时,编译器找不到构造函数或函数。

FVector.h

...
#include <vector>
template<typename T>
class FVector {
public:
FVector( std::vector<T> );
template <typename unaryOperation>
void forEach(unaryOperation);
private:
std::vector<T> _vector;

};
...

.cpp

#include "FVector.hpp"
#include <algorithm>
template <typename T>
FVector<T>::FVector( std::vector<T> vector ): _vector(vector) {}
template <typename T>
template <typename unaryOperation>
void FVector<T>::forEach(unaryOperation op) {
std::for_each(_vector.begin(),_vector.end(),op);
}

主.cpp

#include <iostream>
#include "FVector.hpp"
int main(int argc, const char * argv[]) {
auto printInt = [](int i){ std::cout << i << std::endl; };
std::vector<int> numbers{1, 2, 3, 4, 5};
FVector<int> fNumbers{numbers};
fNumbers.forEach(printInt);
return 0;
}

错误

函数 'FVector::forEach<(lambda at blah/main.cpp:95:21)>' 有 内部链接,但未定义

显然,我的语法中的某些内容非常错误,但是我找不到如何声明/实现这些函数。

谢谢!

C++模板是特殊的动物:模板的每个实例化都是一个不同的类。这意味着,如果您尝试将模板类定义放在其自己的 cpp 中,因为该翻译单元中不需要实例化,编译器将不生成任何实例。当您尝试使用另一个翻译单元中的一个时,链接器会抱怨,因为它找不到具体的定义。

该标准在草案 n4296 中声明(强调我的):

14 模板 [临时]
...
函数模板、类模板的成员函数、变量模板或 类模板应在隐式实例化的每个翻译单元中定义(14.7.1),除非 相应的专业化在某个翻译单元中显式实例化 (14.7.2);无诊断 必填。

这就是为什么常见的用途是在头文件中的模板上编写完整的定义,以确保定义将存在于每个翻译单元中。

现在,只有一个如何为 FVector 创建实例的配方,但没有创建任何实例。您需要实例化一个。源文件无法"知道"需要哪些实例,因为它不包含在使用模板中的类的某个地方,这就是问题所在(好吧,表述不正确,但我希望你明白这一点 - 基本上,当文件包含标头时,它可以使用标头中的所有内容从模板中创建类, 但不是它甚至不知道的某些来源中的内容)

这可以通过编写类似的东西来完成

template class FVector<double>;

在源文件的末尾,使其专用化一倍以上,然后对于应该存在的每个 forEach 专用化都相同。我通常有另一个文件,ClassNameSpecialization.h(在你的例子中是FVectorSpecialization.h),它只包含这样的行,并包含在源文件的末尾。可以轻松添加一些专业。

如果您希望 FVector 是动态的,能够使用任何可能与代码一起使用的模板参数,则需要在标头中实现它,尽管这当然很丑陋。