C++模板:这个程序为什么通不过编译(C++11)

C++模板:这个程序为什么通不过编译(C++11),第1张

1):我们将testTemplate.cpp文件从工程中拿掉,即删除testTemplate.cpp的定义。然后直接编译上面的文件,能编译通过。这说明编译器在展开testTemplate.h后编译main.cpp文件的时候并没有去检查模板类的实现。它只是记住了有这样的一个模板声明。由于没有调用模板的成员函数,编译器链接阶段也不会在别的obj文件中去查找类模板的实现代码。因此上面的代码没有问题。

2):把main.cpp文件中,第7行的注释符号去掉。即加入类模板的实例化代码。在编译工程,会发现也能够编译通过。回想一下这个过程,testTemplate.h被展开,也就是说main.cpp在编译是就能找到MyClass<T>的声明。那么,在编译第7行的时候就能正常的实例化一个类模板出来。这里注意:类模板的成员函数只有在调用的时候才会被实例化。因此,由于没有对类模板成员函数的调用,编译器也就不会去查找类模板的实现代码。所以,上面的函数能编译通过。

3):把上面第10行的代码注释符号去掉。即加入对类模板成员函数的调用。这个时候再编译,会提示一个链接错误。找不到printValue的实现。道理和上面只有函数的声明,没有函数的实现是一样的。即,编译器在编译main.cpp第10行的时候发现了对myClass.PrintValue的调用,这时它在当前文件内部找不到具体的实现,因此会做一个标记,等待链接器在其他的obj文件中去查找函数实现。同样,连接器也找不到一个包括MyClass<T>::PrintValue声明的obj文件。因此报告链接错误。

4):既然是由于找不到testTemplate.cpp文件,那么我们就将testTemplate.cpp文件包含在工程中。再次编译,在VS中会提示一个链接错误,说找不到外部类型_thiscall MyClass<int>::PrintValue(int)。也许你会觉得很奇怪,我们已经将testTemplate.cpp文件包含在了工程中了阿。先考虑一个问题,我们说过模板的编译实际上是一个实例化的过程,它并不编译产生二进制代码。另外,模板成员函数也只有在被调用的时候才会初始化。在testTemplate.cpp文件中,由于包含了testTemplate.h头文件,因此这是一个独立的可以编译的类模板。但是,编译器在编译这个testTemplate.cpp文件的时候由于没有任何成员函数被调用,因此并没有实例化PrintValue成员。也许你会说我们在main.cpp中调用了PrintValue函数。但是要知道testTemplate.cpp和main.cpp是两个独立的编译单元,他们相互间并不知道对方的行为。因此,testTemplate.cpp在编译的时候实际上还是只编译了testTemplate.h中的内容,即再次声明了模板,并没有实例化PrintValue成员。所以,当main.cpp发现需要PrintValue成员,并在testTemplate.obj中去查找的时候就会找不到目标函数。从而发出一个链接错误。

5):由此可见,模板代码不能按照常规的C/C++代码来组织。必须得保证使用模板的函数在编译的时候就能找到模板代码,从而实例化模板。在网上有很多关于这方面的文章。主要将模板编译分为包含编译和分离编译。其实,不管是包含编译还是分离编译,都是为了一个目标:使得实例化模板的时候就能找到相应的模板实现代码。大家可以参照这篇文章。

最后,作一个小总结。C++应用程序的编译一般要经历展开头文件->编译cpp文件->链接三个阶段。在编译的时候如果需要外部类型,编译器会做一个标记,留待连接器来处理。连接器如果找不到需要的外部类型就会发生链接错误。对于模板,单独的模板代码是不能被正确编译的,需要一个实例化器产生一个模板实例后才能编译。因此,不能寄希望于连接器来链接模板的成员函数,必须保证在实例化模板的地方模板代码是可见的。

并不是都不支持,有少部分编译器还是支持的,比如ICC和Comeau C/C++。

从标准上来说,C++标准中规定可以通过export关键字来指定模版的分离编译,但大多数编译器都没有实现。

因为这个特性很难通过传统的编译-链接过程来实现,需要另外非常复杂的处理过程,编译器不得不为其单独生成一个中间代码,实现起来非常麻烦,大多数编译器厂商对其持抵制态度。所以导致了这个特性虽然是C++标准中规定的,但却在事实上几乎不成立。

呃, 好多错误...

const ID_ id 中的 const 会被编译器忽略掉, 可以不写(这个不算错误).

这是一个最基本的Error: 类定义 后面 要有   结尾.

ID_ 既然是 int 的 alias, 那 类C 就是一个 非类型实参的类模板. 也就是说, C<>的 <>中应该是个 int的数字, 不能是 int 或者  ID_, 或者 其它类型.

对于 C++98 C++03 而言, vector<C<5>>中最后面的 两个 大于号之间  必须要有至少一个空格. 这是语法规定.  除非你的编译器支持 C++11, 而且你在编译的时候 指定了编译选项

-std=c++14

或者

-std=c++11

最后, 一个修改后的版本:

#include<vector>

typedef int ID_

template <ID_ id>

class C

{

    public:

        void say() {}

}

//typedef std::vector<C> CVec//报错

//typedef std::vector<C<ID_>> CVec//报错

//typedef std::vector<C<int>> CVec//报错

typedef std::vector<C<1> > CVec       // 注意: 这三个是不同的类型

typedef std::vector<C<2> > CVec       // 注意: 这三个是不同的类型

typedef std::vector<C<999> > CVec     // 注意: 这三个是不同的类型

int main()

{

    CVec vec

    return 0

}

最最后, 欢迎交流. :)


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/8107337.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-13
下一篇2023-04-13

发表评论

登录后才能评论

评论列表(0条)

    保存