设为首页 加入收藏

TOP

C++ primer例子分析 C++11语法使用,模板编译问题解决
2015-07-20 17:39:55 来源: 作者: 【 】 浏览:3
Tags:primer 例子 分析 语法 使用 模板 编译 问题 解决

TestQueue_16th_2.obj : error LNK2019: 无法解析的外部符号 "class std::basic_ostream > & __cdecl operator<<

该符号在函数 _wmain 中被引用

首先一般按照平常的头文件和源文件分开处理使用包含形式编译,但是实际上我们包含的是头文件而不是源文件。注定是个错误。

先解析一下包含编译和分离编译:

包含模式:#include A 就是试用包含模式,当包含的是A.h文件,则需要在本文件的obj对象中为A.Obj增加地址映射;若是A.cpp或A.hpp,则是直接分别映射A.obj的每个函数。

分离模式:首先得支持export关键字的操作定义,一般直接在头文件中定义改模板为export就行了,简单,具体操作交给编译器;现实是,我没用过这类似的编译模式,就是在YY。


在VS2012中编译下面代码:

template export class QueueItem


结果

1>d:\cpp_lab\testqueue_16th\testqueue_16th\queue.h(8): warning C4237: 目前还不支持“export”关键字,但已保留该关键字供将来使用

简单点:VS不支持分离式编译模式,至于Linux,我好久没见过啦。MAC的Xcode还没有时间去验证。暂时搁置。


下面是一段转载翻译形式分享:

来自《C++ Template: The Complete Guide》

http://wenku.baidu.com/view/68e2623a43323968011c9225.html

主要是有关编译器和模板编译的内容。

首先,C++标准中提到,一个编译单元[translation unit]是指一个.cpp文件以及它所include的所有.h文件,.h文件里的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp文件为一个.obj文件,后者拥有PE[Portable Executable,即windows可执行文件]文件格式,并且本身包含的就已经是二进制码,但是,不一定能够执行,因为并不保证其中一定有main函数。当编译器将一个工程里的所有.cpp文件以分离的方式编译完毕后,再由连接器(linker)进行连接成为一个.exe文件。

包含编译模式会把所有的的数据成员添加在本文件.obj中,

而".h"没有生成obj对象,而是hpp和cpp生成obj对象,然后编译器,通过头文件的映射关系将函数链接起来,例如:


//---------------test.h-------------------// 
void f();//这里声明一个函数f 
//---------------test.cpp--------------// 
#include”test.h” 
void f() 
{ 
…//do something 
} //这里实现出test.h中声明的f函数 
//---------------main.cpp--------------// 
#include”test.h” 
int main() 
{ 
f(); //调用f,f具有外部连接类型 
} 

当编译器在main.obj中 call f 时,链接器从头文件表中寻找f的实现代码,然后将test.obj的实际地址和对象链接在代码段中成为exe。


但是我开始还是忘记了模板未特化之前只是一段数据声明,实例化之后才是代码(具体模板代码存放在代码段,在特化之前没有具体含义)。所以按照编译结果链接起来,发现test.obj的该代码段是执行的是不匹配的。这也是模板编译目前在VS只支持包含模式的原因,必须将定义和声明放在相同的源文件中,这样子在编译期main.obj call f(如文库的文档所言,应该jmp 0X??? )指向的地址才是特化后的代码地址。最后才能转化为机器码。


包含模式意味编译器多次实例化了同一个模板的各种版本(含重复部分),实际使用的话,我们可以再头文件中显示实例化某个常有版本,然后使用extern 关键字拓展某个版本的声明,当然这样子可能会导致代码段冗余。


代码如下:

template 
  
   
class QueueItem
{
private:
	friend class Queue
   
    ; template 
    
      friend std::ostream & operator << (std::ostream & os,const Queue
     
       &q); QueueItem(const Type &element):item(element),next(nullptr) {}; Type item; QueueItem * next; };
     
    
   
  
友元模板的声明在此例子中其实没有什么好说的,本身type的类型就不会实例化出其他版本的operator<<,所以

template 
  
   
	friend std::ostream & operator << (std::ostream & os,const Queue
   
     &q);
   
  

改成上面的形式也不会有什么负面影响,当还是要坚持版本1,尽量在代码在自己的思维控制之下,能自己解决的问题不交给其他代码库或者

template 
  
   
void Queue
   
    ::copy_elems(const Queue
    
      &Q) { std::for_each( Q.m_pHead,Q.m_pTail, [=](QueueItem
     
       i) { this->push_back(i.item); } ); } template 
      
        template 
       
         void Queue
        
         ::copy_elems(Iter begin,Iter end) { std::for_each( begin, end, [this](decltype(*begin) i) { this->push_back(i); } ); /*for (; begin != end; ++begin) { this->push_back(*begin); }*/ } template 
         
           std::ostream & operator<< (std::ostream & os,const Queue
          
            &q) { os<<"<"; QueueItem
           
             *p; for (p = q.m_pHead; p!=nullptr ; p = p->next) { os<
            
             item<<" "; } os<<">"; return os; }
            
           
          
         
        
       
      
     
    
   
  

使用了decltype来获取参数中的实际类型来声明i,之前说过auto/decltype这把双刃剑的使用范围是在接口交互,今天把它贴出来做个印证。

其实,还有一组代码是这段博客的要讲的:


std::for_each(
		begin,
		end,
		[this](typename Iter::value_type   i)
		{
			this->push_back(i);
		}
	);

照样编译通过并执行通过,模板推导竟然可以将类型上升命名空间对象。而我用Iter::value_type iterator等方式声明参数时,确实改参数无法实例化。参数就是一个vector ::iterator(begin和end)。

具体代码见链接:

例子源码下载

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇深入理解 hash 函数、HashMap、Li.. 下一篇STL algorithm算法minmax,minmax_..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·Redis 分布式锁全解 (2025-12-25 17:19:51)
·SpringBoot 整合 Red (2025-12-25 17:19:48)
·MongoDB 索引 - 菜鸟 (2025-12-25 17:19:45)
·What Is Linux (2025-12-25 16:57:17)
·Linux小白必备:超全 (2025-12-25 16:57:14)