上个星期,不小心用到模板,为了让接口与实现分离,很自然的写了两个.h和.cpp文件。于是一场恶梦引发了 。。。我几乎花了所有课余时间去解决一个看似不可理喻的链接错误。。。。程序大致如下:
/**/
/****************************************
FileName: zyklist.h
ModifyTime: 2007.03.17 15:20PM
Author: ChinaInvent
*****************************************/
#ifndef ZYK_LIST_H
#define ZYK_LIST_H
template < calss T >
class List
... {
//.....
public:
bool remove(T& val);
//...
} ;
#endif
/**/ /*******************************************
FileName: zyklist.cpp
ModifyTime: 2007.03.17 15:20PM
Author: ChinaInvent
********************************************/
#include " zyklist.h "
#include < iostream >
using namespace std;
template < class T >
bool List < T > ::remove(T & val)
... {
//....
}
/**/ /*******************************************
FileName: main.cpp
ModifyTime: 2007.03.17 15:20PM
Author: ChinaInvent
********************************************/
#include < iostream >
#include " zyklist.h "
using namespace std;
int main()
... {
List<int> lst;
int a;
lst.remove(a); //链接出错
return 0;
}
FileName: zyklist.h
ModifyTime: 2007.03.17 15:20PM
Author: ChinaInvent
*****************************************/
#ifndef ZYK_LIST_H
#define ZYK_LIST_H
template < calss T >
class List
... {
//.....
public:
bool remove(T& val);
//...
} ;
#endif
/**/ /*******************************************
FileName: zyklist.cpp
ModifyTime: 2007.03.17 15:20PM
Author: ChinaInvent
********************************************/
#include " zyklist.h "
#include < iostream >
using namespace std;
template < class T >
bool List < T > ::remove(T & val)
... {
//....
}
/**/ /*******************************************
FileName: main.cpp
ModifyTime: 2007.03.17 15:20PM
Author: ChinaInvent
********************************************/
#include < iostream >
#include " zyklist.h "
using namespace std;
int main()
... {
List<int> lst;
int a;
lst.remove(a); //链接出错
return 0;
}
由于宿舍不能上网,我就一直死磕,总以为是自己的格式有问题。但是,事实证明我太爱死磕了!早上重看《C++对象模型》最后那一章,并未找到答案,但我开始怀疑编译器的问题了。刚才到网上一查,哑然失笑。。。别人早就遇到这个问题啦。。。
经验:以后有事,还是到网上查查吧。
有一位网友提供了解决方案:
引自:http://guan98413.itpub.net/post/21943/221569
解决方案(
1
):
采用常用做法,改变程序结构,将模版函数的定义包含在函数声明的头文件中,也就是说将函数声明和定义放在一起。
代码实现如下:
删除Templatetest.cpp文件,将函数的定义和函数声明一起实现。
Templatetest.h
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
#ifndef _TEMPLATETEST_H__
#define _TEMPLATETEST_H__
#include
using namespace std;
template
inline void print( const T & t);
#endif
评论:此方法可行,也是目前常用的方式,缺点是打破了一般规则,很难实现代码隐藏,让人看起来整个程序架构不太清晰。
解决方案( 2 ):
依据C ++ 标准说明,你可以在函数定义前加上 " export " 关键字来做到这一点,但只有少数几个编译器支持这一特性,而且主流的编译器开发商已经明确声明他们无意去实现这一特性。Microsoft声明说,他们不会支持export, 原因如下:要正确地实现这一特性,将带来很多复杂性,而这并未因此带来性能上的提高,AFAIK, gcc 也不支持export。但也有支持这一特性的编译器,采用EDG(Edison Design Group)思想(他们的理念是We do our job, you do yours, and together we can create some great products)的一些c ++ 编译器就支持export,一些编译器开发商,比如Comeau 和 Intel C ++ 都采用了EDG,但目前好象只有Comeau真正实现了这一点。
代码实现如下:
Templatetest.cpp
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
#include " templatetest.h "
template
export void print( const T & t)
... {
cout << t << endl;
}
评论:此方法正确,但不可行。因为主流编译器基本都不支持export。
解决方案( 3 ):
将Templatetest.cpp函数名改成Templatetest.inl(后缀可以换成别的,但绝对不能是cpp,因为编译器对cpp文件做特殊处理)
将Templatetest.h文件内容改成
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
#ifndef _TEMPLATETEST_H__
#define _TEMPLATETEST_H__
#include
using namespace std;
template
void print( const T & t);
#include “Templatetest.inl”
#endif
评论:这一方法既能解决unresolved externals问题,又不打破程序结构。唯一的缺陷是函数定义所在文件的后缀有点改动。
综合以上三种解决方案,虽然都能解决unresolved externals问题,但也都存在一些缺陷。具体哪中最合适,笔者认为还要根据开发人员的喜好,根据习惯来决定采用哪种。
参考资料:
C ++ Template Metaprogramming
http: // www.codecomments.com/
http: // www.codeguru.com/
http: // www.velocityreviews.com/
采用常用做法,改变程序结构,将模版函数的定义包含在函数声明的头文件中,也就是说将函数声明和定义放在一起。
代码实现如下:
删除Templatetest.cpp文件,将函数的定义和函数声明一起实现。
Templatetest.h
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
#ifndef _TEMPLATETEST_H__
#define _TEMPLATETEST_H__
#include
using namespace std;
template
inline void print( const T & t);
#endif
评论:此方法可行,也是目前常用的方式,缺点是打破了一般规则,很难实现代码隐藏,让人看起来整个程序架构不太清晰。
解决方案( 2 ):
依据C ++ 标准说明,你可以在函数定义前加上 " export " 关键字来做到这一点,但只有少数几个编译器支持这一特性,而且主流的编译器开发商已经明确声明他们无意去实现这一特性。Microsoft声明说,他们不会支持export, 原因如下:要正确地实现这一特性,将带来很多复杂性,而这并未因此带来性能上的提高,AFAIK, gcc 也不支持export。但也有支持这一特性的编译器,采用EDG(Edison Design Group)思想(他们的理念是We do our job, you do yours, and together we can create some great products)的一些c ++ 编译器就支持export,一些编译器开发商,比如Comeau 和 Intel C ++ 都采用了EDG,但目前好象只有Comeau真正实现了这一点。
代码实现如下:
Templatetest.cpp
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
#include " templatetest.h "
template
export void print( const T & t)
... {
cout << t << endl;
}
评论:此方法正确,但不可行。因为主流编译器基本都不支持export。
解决方案( 3 ):
将Templatetest.cpp函数名改成Templatetest.inl(后缀可以换成别的,但绝对不能是cpp,因为编译器对cpp文件做特殊处理)
将Templatetest.h文件内容改成
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
#ifndef _TEMPLATETEST_H__
#define _TEMPLATETEST_H__
#include
using namespace std;
template
void print( const T & t);
#include “Templatetest.inl”
#endif
评论:这一方法既能解决unresolved externals问题,又不打破程序结构。唯一的缺陷是函数定义所在文件的后缀有点改动。
综合以上三种解决方案,虽然都能解决unresolved externals问题,但也都存在一些缺陷。具体哪中最合适,笔者认为还要根据开发人员的喜好,根据习惯来决定采用哪种。
参考资料:
C ++ Template Metaprogramming
http: // www.codecomments.com/
http: // www.codeguru.com/
http: // www.velocityreviews.com/