模版分离模型

文章讨论了C++中的模板导出机制(exportingtemplate),一种旨在允许模板定义和使用位置分离的方法,以解决包含模型的一些问题。然而,export关键字的支持有限,且可能导致编译复杂性增加,因此尽管能解决某些问题,但并不常用。文章提到了export可以应用于函数模板、类模板等,但不能与inline关键字一起使用,并指出模板实例化在两个位置处理的缺点,这增加了编译时的负担。

我们在上一节给出的两种方法都可以正常的工作,也完全符合C++标准,然而,标准还给出了另一种机制,导出模版exporting template,这种机制通常也被称为C++模版的分离模型。

6.3. 关键字export

大体上讲,关键字export的功能使用是非常简单的,在一个文件里面定义模版,并在模版的定义和(非定义的)声明的前面加上关键字export, 对于上一节的例子,通过使用export 我们会得到下面的函数模版声明。

#ifndef

#define

//

Export template<typename T>

Void print_typeof(T const &)

#endif

即使在模版定义不可见的条件下,被导出的模版也可以正常使用,换句话说,使用模版的位置和模版定义的位置可以在不同的翻译单元中,在我们的例子中,文件myfirst3_hpp现在只是包含类模版的成员函数的声明,但是相对使用这些成员已经足够了。和刚开始导致编译器报错的那个例子相比,我们只是在代码中添加了关键字export,一切就可以顺利通过了。

在一个预处理文件内部(就是指在一个翻译单元内部),我们只需要在第一个声明前面标记export关键字就可以了,后面的重新声明会隐式的保留这个export特性,这也是我们不需要修改文件myfirst.cpp的原因所在,就是说,myfirst.cpp文件里面的这个定义是隐士exported,因为在它#include的头文件myfirst3.hpp里面,该定义所对应的声明已经被限定为export的了,另一方面,在模版定义中提供了一个冗余的export关键字也是可取的,因为这样可以提高代码的可读性。

实际上关键字export可以应用于函数模版,类模版和成员函数,成员函数模版和类模版的静态数据成员,另外,它还可以用与类模版的声明,这将意味着每个可导出的类成员都被看作导出实体,但是类模版本身实际却没有被导出,因此,类模版的定义仍然需要出现在头文件中,你仍然可以隐式或者显式的定义内联成员函数,然而,内敛函数却是不同可导出的。

Export template<typename T>

Class Myclass

{

public:

void memfunc1();

void memfunc2() {

}

void memfun3();

};

template<typename T>

Inline void Myclass<T>::memfun3()

{

...

}

另外,export关键不能和inline关键字一起使用,如果用于模版的话,export要位于关键字template的前面,譬如下面的程序就是非法的。

template<typename T>

Class Invalid {

Public:

export void wrong(T);

};

Export template<typename T>

Inline void Invalid<T>::wrong(T)

{}

Export template<typename T>

Inline T const & max(T const &a, T const &b)

{

return a < b ?b:a;

}

6.3.2 分离模型的限制

谈到这里,你可能会觉得奇怪,既然导出模版export template可以很好的解决最初的问题,我们为何仍然建议大家使用包含模型呢?事实上,export关键字还有其他一些方面的影响。

首先C++标准推出4年之后,只有一家公司对export关键字支持了。

其次,export看似完美,实际上存在一些缺点。模型分离之后,实例化过程需要处理两个位置,模版被实例化的位置和模版定义出现的位置。类似头文件变化一样,需要都重新编译。

<think>嗯,用户问的是模板为什么不支持分离编译。首先,我得回忆一下C++模板的基本机制。模板在编译时需要进行实例化,也就是说编译器在看到模板定义的时候,必须知道具体的类型参数才能生成对应的代码。如果模板的声明和实现分开写在头文件和源文件里,那么当其他源文件包含这个头文件时,编译器只能看到声明而看不到实现,这时候实例化就会失败。 比如,用户可能写了一个模板类在头文件声明,在cpp文件实现。然后在另一个cpp文件中使用这个模板的不同类型参数,比如int和double。这时候编译器在编译使用这些参数的源文件时,无法找到对应的实现代码,导致链接错误。因为模板的实现没有被实例化到目标文件中。 然后,解决方法有几个。第一个是把模板的定义和声明都放在头文件里,这样包含头文件的时候,编译器就能看到完整的定义,进行实例化。第二个是显式实例化,也就是在源文件中预先实例化需要的类型,但这限制了只能使用那些预先实例化的类型。第三个是用export关键字,不过我记得这个关键字在C++11之后被移除了,所以可能不适用。 用户可能遇到的问题是在实际项目中,当模板代码分离到不同文件时出现的链接错误。这时候需要解释清楚原因,并给出可行的解决方案。例如,建议将模板的实现也放在头文件中,或者使用显式实例化来处理特定的类型。 另外,可能需要提醒用户注意模板代码组织的最佳实践,避免分离编译带来的问题。还有,现代C++中是否还有其他方法,比如使用inline或constexpr等,不过这些可能不直接解决分离编译的问题。</think><< 模板不支持分离编译的原因在于:C++的编译模型要求模板的**完整定义**必须在实例化时可见。当模板的声明和实现分离到不同文件(.h和.cpp)时,编译器在编译调用模板的代码时无法生成具体实例化的代码,导致链接错误。 ### 具体原因: 1. **编译阶段限制** 模板的实例化发生在编译阶段。若模板的实现(成员函数/静态成员)在.cpp文件中,其他.cpp文件调用模板时仅能看到声明,无法生成具体类型的机器码。 2. **符号缺失问题** 若模板未被显式实例化,链接器在最终链接时找不到对应类型的实现代码(如`MyTemplate<int>::foo()`),导致`undefined reference`错误。 --- ### 解决方法: #### 1. 将模板定义放在头文件中(最常见) ```cpp // mytemplate.h #pragma once template <typename T> class MyTemplate { public: void foo() { // 实现直接写在头文件中 } }; ``` #### 2. 显式实例化模板(适用于已知类型) ```cpp // mytemplate.cpp #include "mytemplate.h" // 显式实例化需要的类型 template class MyTemplate<int>; // 仅支持int类型 template class MyTemplate<double>; // 仅支持double类型 ``` #### 3. 使用`export`关键字(C++11已废弃) ```cpp // 注意:主流编译器(如GCC、Clang)均不支持此特性 export template <typename T> class MyTemplate { /* ... */ }; ``` --- ### 示例:分离编译错误场景 ```cpp // mytemplate.h template <typename T> class MyTemplate { public: void foo(); // 只有声明 }; // main.cpp #include "mytemplate.h" int main() { MyTemplate<int> obj; obj.foo(); // 链接错误:找不到MyTemplate<int>::foo() } ``` --- ### 根本原理: C++标准规定模板是**编译期多态**机制,编译器需要根据调用处的具体类型生成代码。若模板实现不可见(如分离到.cpp文件),则无法完成实例化。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值