详解C++函数模板与分离编译模式

1.分离编译模式

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件连接起来形成单一的可执行文件的过程称为分离编译模式。

2.使用函数模板在链接时出错

在C++程序设计中,在一个源文件中定义某个函数,然后在另一个源文件中使用该函数,这是一种非常普遍的做法。但是,如果定义和调用一个函数模板时也采用这种方式,会发生编译错误。

下面的程序由三个文件组成:func.h用来对函数模板进行申明,func.cpp用来定义函数模板,main.cpp包含func.h头文件并调用相应的函数模板。

/***func.h***/
template<class T> void func(const T&);
/***end func.h***/

/***func.cpp***/
#include <iostream>
using namespace std;

#include "func.h"
template<class T> void func(const T& t)
{
 cout<<t<<endl;
}
/***end func.cpp***/

/***main.cpp***/
#include <stdio.h>
#include "func.h"

int main()
{
 func(3);
}
/***end main.cpp***/

这是一个结构非常清晰的程序,但是它不能通过编译。在VS2017下的出错信息是:

error LNK2019: 无法解析的外部符号 "void __cdecl func< int>(int const &)" (??$func@H@@YAXABH@Z)

原因出现在分离编译模式上。在分离编译模式下,func.cpp会生成一个目标文件为func.obj,由于在func.cpp文件中,并没有发生函数模板调用,所以不会将函数模板func<T>实例化为模板函数func<int>,也就是说,在func.obj中无法找到关于模板函数func<int>的实现代码。在源文件main.cpp中,虽然函数模板被调用,但由于没有模板代码,也不能将其实例化。也就是说,在main.obj中也找不到模板函数func<int>的实现代码。这样,在链接的时候就会出现func<int>没有定义的错误。

3.解决办法

3.1将函数模板的定义放到头文件

一个简单的解决办法就是将函数模板func<T>的定义写到头文件func.h中。这样的话,只要包含了这个头文件,就会把函数模板的代码包含进来,一旦发生函数调用,就可以依据函数模板代码将其实例化。这个办法虽然简单可行,但是有如下不足。
 (1)函数模板的定义写进了头文件,暴露了函数模板的实现细节。
 (2)不符合分离编译模式的规则,因为分离编译模式要求函数原型申明放在头文件,定义放在源文件。

注意: 这样做,如果在多个目标文件中存在相同的函数模板实例化后的模板函数实体,链接时并不会报函数重定义的错误,这与普通函数不同,因为编译器会对实例化后的重复的模板函数实体进行优化,只保留一份代码实体。如果不同的源文件中都保留一份函数模板实体,会造成代码冗余,实际上,这也是一种代码冗余的解决办法。

3.2仍然采用分离编译模式

有什么办法可以让函数模板实例化时能够找到相应的模板函数的代码呢?一个可能的解决办法就是使用关键字export。也就是说,在func.cpp里定义函数模板的时候,将函数模板头写成:

export template<class T> void func(const T& t);

这样做的目的是告诉编译器,这个函数模板可能再其他源文件中被实例化。这是一个对程序员来说负担最轻的解决办法,但是,目前几乎所有的编译器都不支持关键字export,包括VC++和GNU C++。

3.3显示实例化

显示实例化也称为外部实例化。在不发生函数调用的时候将函数模板实例化,或者在不使用类模板的时候将类模板实例化称之为模板显示实例化。

上面遇到的问题是main.obj和func.obj中找不到模板函数func<int>的实现代码,那么就在func.cpp中将函数模板func<T>显示实例化为模板函数func<int>

template void func<int>(const int&); //函数模板显示实例化

这样,就可以在func.cpp产生模板函数func<int>的实例化代码,编译之后就会产生函数的二进制代码,供其它源文件链接,程序就可以正常运行。当类模板的成员函数的实现定义在源文件中,通过模板类的对象调用成员函数时也会出现找不到函数定义的错误,可以使用同样的方法解决,不再赘述。

以上就是详解C++函数模板与分离编译模式的详细内容,更多关于C++函数模板与分离编译模式的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++ 类模板、函数模板全特化、偏特化的使用

    一.类模板全特化.偏特化 #pragma once #include <iostream> #include <map> template <typename T, typename U> class TC { public: TC() { std::cout << "泛化版本构造函数" << std::endl; } void funtest() { std::cout << "泛化版本成员函数&quo

  • C++实践排序函数模板项目的参考方法

    [项目-排序函数模板] 已知 void Sort(int a[],int size); void Sort(double a[],int size); 是一个函数模板的两个实例,其功能是将数组a中的前size个元素按从小到大顺序排列.试设计这个函数模板. 参考解答: #include<iostream> using namespace std; template<class T> void Sort(T set[],int n) { int i,j; T temp; for(i=1

  • C++函数模板与类模板实例解析

    本文针对C++函数模板与类模板进行了较为详尽的实例解析,有助于帮助读者加深对C++函数模板与类模板的理解.具体内容如下: 泛型编程(Generic Programming)是一种编程范式,通过将类型参数化来实现在同一份代码上操作多种数据类型,泛型是一般化并可重复使用的意思.泛型编程最初诞生于C++中,目的是为了实现C++的STL(标准模板库). 模板(template)是泛型编程的基础,一个模板就是一个创建类或函数的蓝图或公式.例如,当使用一个vector这样的泛型类型或者find这样的泛型函数

  • C++11/14 线程的创建与分离的实现

    线程的创建 让我们看看示例代码(t1.cpp). #include <iostream> #include <thread> void thread_function() { std::cout << "thread function\n"; } int main() { std::thread t(&thread_function); // 线程 t 开始运行 std::cout << "main thread\n&q

  • C++中函数模板的用法详细解析

    定义 我们知道函数的重载可以实现一个函数名多用,将功能相同或者类似函数用同一个名来定义.这样可以简化函数的调用形式,但是程序中,仍然需要分别定义每一个函数. C++提供的函数模板可以更加简化这个过程. 所谓函数模板实际上是建立一个通用函数,其涵涵素类型额形参类型不具体指定,用一个虚拟的类型来代表,这个通用函数就称为函数模板. 凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需要在模板中定义一次即可.在调用函数时,系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能

  • 深入解析C++中的函数模板和函数的默认参数

    C++函数模板 我们知道,数据或数值可以通过函数参数传递,在函数定义时它们是未知的,只有在发生函数调用时才能确定其值.这就是数据的参数化. 其实,数据类型也可以通过参数来传递,在函数定义是可以不指明具体的数据类型,当发生函数调用时,编译器可以根据传入的参数自动确定数据类型.这就是数据类型参数化. 所谓函数模板,实际上是建立一个通用函数,其返回值类型和形参类型不具体指定,用一个虚拟的类型来代替(实际上是用一个标识符来占位).这个通用函数就称为函数模板(Function Template).凡是函数

  • C++ Template 基础篇(一):函数模板详解

    Template所代表的泛型编程是C++语言中的重要的组成部分,我将通过几篇blog对这半年以来的学习做一个系统的总结,本文是基础篇的第一部分. 为什么要有泛型编程 C++是一门强类型语言,所以无法做到像动态语言(python javascript)那样子,编写一段通用的逻辑,可以把任意类型的变量传进去处理.泛型编程弥补了这个缺点,通过把通用逻辑设计为模板,摆脱了类型的限制,提供了继承机制以外的另一种抽象机制,极大地提升了代码的可重用性. 注意:模板定义本身不参与编译,而是编译器根据模板的用户使

  • C++中读写txt文件并分离字符的方法

    在实际工程中,经常遇到需要读取txt文件,txt文件中存的是一些小数或者整型数据,在C++中,可以利用string类和ifstream库文件对txt进行的读取,不过读回的数据经常是以字符串的形式返回,一般是txt的一行为一个字符串返回.那么如何从字符串中分离出整数或者是小数就涉及到字符串的分割问题,下面就该问题进行总结. 一.C++中txt文件的读取 需要读取的txt文件如下: 代码如下: #include<iostream> #include<string> #include&l

  • 简单掌握C++中的函数模板

    1.函数模板的声明和模板函数的生成 1.1函数模板的声明 函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计.它的最大特点是把函数使用的数据类型作为参数. 函数模板的声明形式为: template<typename 数据类型参数标识符> <返回类型><函数名>(参数表) { 函数体 } 其中,template是定义模板函数的关键字:template后面的尖括号不能省略:typename(或class)是声明数据类型参数标识符的关键字,

  • C++类的分离式写法介绍示例

    介绍 类的分离式写法,使得代码更加规范,增强了阅读性. 分离式写法的规则: 1.类的变量:写在类的里面 2.成员函数:类中写函数的声明,函数的定义写在类体外. 3.写在类外函数定义时,类名前加限定(Object: :),其中,::理解为表示范围的符号. 代码演示 头文件:Object.h #ifndef _OBJECT_H_ #define _OBJECT_H_ class Student { private: char name[32]; int age; public: void SetNa

随机推荐