著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:余天升
链接:http://www.zhihu.com/question/20630104/answer/15722407
来源:知乎
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:余天升
链接:http://www.zhihu.com/question/20630104/answer/15722407
来源:知乎
谢
@欲三更 邀,这个问题让我想起我在实习的时候犯的一个错误,就是把模版类的定义和实现分开写了,结果编译出错,查了两天才查出问题。
C++中每一个对象所占用的空间大小,是在编译的时候就确定的,在模板类没有真正的被使用之前,编译器是无法知道,模板类中使用模板类型的对象的所占用的空间的大小的。只有模板被真正使用的时候,编译器才知道,模板套用的是什么类型,应该分配多少空间。这也就是模板类为什么只是称之为模板,而不是泛型的缘故。
既然是在编译的时候,根据套用的不同类型进行编译,那么,套用不同类型的模板类实际上就是两个不同的类型,也就是说,stack<int>和stack<char>是两个不同的数据类型,他们共同的成员函数也不是同一个函数,只不过具有相似的功能罢了。&lt;img src="https://i-blog.csdnimg.cn/blog_migrate/5f8342b76089e6323ddf546a65b34d05.png" data-rawwidth="749" data-rawheight="308" class="origin_image zh-lightbox-thumb" width="749" data-original="https://pic2.zhimg.com/1772de8abdd112a50e533e3c018535a1_r.jpg"&gt;如上图所示,很简短的六行代码,用的是STL里面的stack,stack&amp;lt;int&amp;gt;和stack&amp;lt;char&amp;gt;的默认构造函数和push函数的入口地址是不一样的,而不同的stack&amp;lt;int&amp;gt;对象相同的函数入口地址是一样的,这个也反映了模板类在套用不同类型以后,会被编译出不同代码的现象。 如上图所示,很简短的六行代码,用的是STL里面的stack,stack<int>和stack<char>的默认构造函数和push函数的入口地址是不一样的,而不同的stack<int>对象相同的函数入口地址是一样的,这个也反映了模板类在套用不同类型以后,会被编译出不同代码的现象。
C++中每一个对象所占用的空间大小,是在编译的时候就确定的,在模板类没有真正的被使用之前,编译器是无法知道,模板类中使用模板类型的对象的所占用的空间的大小的。只有模板被真正使用的时候,编译器才知道,模板套用的是什么类型,应该分配多少空间。这也就是模板类为什么只是称之为模板,而不是泛型的缘故。
既然是在编译的时候,根据套用的不同类型进行编译,那么,套用不同类型的模板类实际上就是两个不同的类型,也就是说,stack<int>和stack<char>是两个不同的数据类型,他们共同的成员函数也不是同一个函数,只不过具有相似的功能罢了。&lt;img src="https://i-blog.csdnimg.cn/blog_migrate/5f8342b76089e6323ddf546a65b34d05.png" data-rawwidth="749" data-rawheight="308" class="origin_image zh-lightbox-thumb" width="749" data-original="https://pic2.zhimg.com/1772de8abdd112a50e533e3c018535a1_r.jpg"&gt;如上图所示,很简短的六行代码,用的是STL里面的stack,stack&amp;lt;int&amp;gt;和stack&amp;lt;char&amp;gt;的默认构造函数和push函数的入口地址是不一样的,而不同的stack&amp;lt;int&amp;gt;对象相同的函数入口地址是一样的,这个也反映了模板类在套用不同类型以后,会被编译出不同代码的现象。 如上图所示,很简短的六行代码,用的是STL里面的stack,stack<int>和stack<char>的默认构造函数和push函数的入口地址是不一样的,而不同的stack<int>对象相同的函数入口地址是一样的,这个也反映了模板类在套用不同类型以后,会被编译出不同代码的现象。
所以模板类的实现,脱离具体的使用,是无法单独的编译的;把声明和实现分开的做法也是不可取的,必须把实现全部写在头文件里面。为了清晰,实现可以不写在class后面的花括号里面,可以写在class的外面。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:缪天翔
链接:http://www.zhihu.com/question/20630104/answer/50856786
来源:知乎
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:缪天翔
链接:http://www.zhihu.com/question/20630104/answer/50856786
来源:知乎
这个feature叫做Export Template,即外名模板,它的作用在于使得模板代码可依照C/C++语言习惯,将模板声明和实现分开分别放到.h和.cpp文件中,并且可以减少冗长的模板编译时间(否则同一模板实例需要在不同编译单元中分别实例化)。
Export Template曾经是被写入C++98标准中的,然并卵,很少有主流编译器支持这一特性。在最新的C++11标准中,它已经被除名了,代之使用extern关键字阻止编译器在某编译单元内实例化特定模板。
Export Template的实现原理,摘录自《深入实践C++模板编程》。
而现代编译器通常的模板实现方式是在编译单元当场生成实例,随后在链接时从重复实例中随机挑选一个进行链接。然而为了支持Export Template,需要对现有编译器做出巨大改动。从人力时间成本考虑,并且有work around可替代方法,Export Template最终被大部分编译器抛弃了。
于是C++的模板库,一定是开源的^ ^
Export Template曾经是被写入C++98标准中的,然并卵,很少有主流编译器支持这一特性。在最新的C++11标准中,它已经被除名了,代之使用extern关键字阻止编译器在某编译单元内实例化特定模板。
Export Template的实现原理,摘录自《深入实践C++模板编程》。
在编译main.cpp时,como的处理与其他编译器并无太大差异,也是生成一个对square<float>的调用等待链接。而在编译square.cpp时,由于square模板声明是一个外名模板,虽然como不会为其生成任何模板实例代码,但是会额外生成一个square.et文件,其中包含对square函数模板实现的索引信息。之后进入一个预链接(prelink)阶段。在此阶段,编译器将根据之前编译时发现的对模板实例的需求,从所有et文件中查找到所需模板实现所在代码文件(cpp文件),并重新编译出所需模板实例。例如例1.8中,在main.cpp中调用了square<float>。那么como将从square.et文件中找到模板square的实现在文件square.cpp中,然后重新编译square.cpp以生成square<float>供链接使用。随后的链接过程和其他C++编译器类似,最终形成链接完整的可执行文件。有点类似于C++编译器处理全局类对象实例构造的过程,它们需要在main函数之前构造好。
而现代编译器通常的模板实现方式是在编译单元当场生成实例,随后在链接时从重复实例中随机挑选一个进行链接。然而为了支持Export Template,需要对现有编译器做出巨大改动。从人力时间成本考虑,并且有work around可替代方法,Export Template最终被大部分编译器抛弃了。
于是C++的模板库,一定是开源的^ ^