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

时间:2017-03-21 09:26:47

标签: c++ c++11 c++14

我试图制作一个快速的实用工具类,通过构图扩展std::vector。为此,我想让它尽可能通用。

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

的main.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;


};
...

FVector.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);
}

的main.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;

}

错误

  

功能&#39; FVector :: forEach&lt;(lambda at blah / main.cpp:95:21)&gt;&#39;具有   内部联系但未定义

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

谢谢!

2 个答案:

答案 0 :(得分:1)

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

这可以通过编写类似

的内容来完成
template class FVector<double>;

在源文件的末尾,将其专门化为double,然后对应存在的每个forEach-specialization都相同。我通常有另一个文件,ClassNameSpecialization.h(在你的情况下是FVectorSpecialization.h),它只包含这些行,并包含在源文件的末尾。可以轻松添加一些特化。

如果你希望FVector是动态的,能够处理任何可能与代码一起使用的模板参数,你需要在标题中实现它,尽管这当然很难看。

答案 1 :(得分:1)

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

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

  

14个模板[temp]
...
  函数模板,类模板的成员函数,变量模板或静态数据成员   类模板应在每个隐式实例化的翻译单元中定义(14.7.1),除非   在一些翻译单元中明确地实例化了相应的专业化(14.7.2);没有诊断   必需的。

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