C++类静态成员必须显示定义,而模板类要写在一个.h文件里,于是出现下面这总情况:
//some_template.h

template <class T>
class some_class


{
public:
static T *ptr;
T* get_ptr(){return ptr;}
};

template <class T>
T* some_class<T>::ptr = NULL;
//1.cpp

#include "some_template.h"




some_class<int> foo;


//2.cpp

#include "some_template.h"




some_class<int> bar;


将以上三个文件编译,就会出现some_class<int>::ptr的重定义。
帖子发了以后忽然想到一个解决办法,能部分的解决这个问题,顺便也解决了模板类在.h文件中定义在.cpp文件中实现的问题。但是这个方法:1. 比较繁复;2. 有时不见得能用;3. 确实比较猥琐...
其实问题的中心在于C++编译器把一个.cpp文件编译成一个.obj文件,当你在一个.cpp文件中使用了某个.h文件中声明的内容时,编译器并没有把相应的实现内容取过来,而是留下一个外部引用的记号,再由连接器把引用和实现连接起来。但我们使用模板时,比如说在上面的1.cpp中,我们使用了some_class<int>这个类,当我们编译1.cpp的时候,如果some_class<T>这个模板的实现不在some_template.h中,编译器就会留下一个记号,等着连接器来连接。然后编译器就去编译别的文件了。而当编译器编译到some_class<T>的实现(比如说在some_template.cpp文件中)时,编译器不知道你再1.cpp文件中使用了some_class<int>,于是它没有生成相应的二进制代码。当然连接器就找不到some_class<int>,进而报错了。
some_class<int>的实现代码没有被生成,那我们就让他生成。唯一的办法是让编译器编译到some_template.cpp时知道some_class<int>被使用了,这很简单,我们使用一次就好了。
具体的办法如下:
在.h文件中只保留some_class的类定义:
//some_template.h

template <class T>
class some_class


{
public:
static T *ptr;
T* get_ptr();
};
在.cpp文件中实现some_class:
//some_template.cpp

template <class T>
T* some_class<T>::ptr = NULL;

template <class T>
T* some_class<T>::get_ptr()


{
return ptr;
}

#include "gen_some_class.h"
在上边的最后一行有一个#include "gen_some_class.h",我们在这个gen_some_class.h中写入生成模板类实例的代码:
//gen_some_class.h

void gen_some_class()


{
some_class<int> sci;
sci.get_ptr();
}
这里的void gen_some_class()是一个谁都不会调用的函数,但是他会让编译器意识到some_class<int>被使用了,some_class<int>::get_ptr()也被使用了,这样编译器就会生成相关代码,连接器就可以将相关代码链接到任何使用它的地方。
事情就这样解决了。
对于比较复杂的情况,比如有100个方法的类,被特化了100个版本,在void gen_some_class()中把100个对象的100个方法逐个调用一边,显然是抽风的做法...另外,对于不能实例化的类(包含纯虚方法),还要费更多的周折。所以基本上这个方法是没有什么应用性的,权当消遣。
//some_template.h
template <class T>
class some_class

{public:
static T *ptr;T* get_ptr(){return ptr;}
};
template <class T>
T* some_class<T>::ptr = NULL;
//1.cpp
#include "some_template.h"



some_class<int> foo;


//2.cpp
#include "some_template.h"



some_class<int> bar;


将以上三个文件编译,就会出现some_class<int>::ptr的重定义。
帖子发了以后忽然想到一个解决办法,能部分的解决这个问题,顺便也解决了模板类在.h文件中定义在.cpp文件中实现的问题。但是这个方法:1. 比较繁复;2. 有时不见得能用;3. 确实比较猥琐...
其实问题的中心在于C++编译器把一个.cpp文件编译成一个.obj文件,当你在一个.cpp文件中使用了某个.h文件中声明的内容时,编译器并没有把相应的实现内容取过来,而是留下一个外部引用的记号,再由连接器把引用和实现连接起来。但我们使用模板时,比如说在上面的1.cpp中,我们使用了some_class<int>这个类,当我们编译1.cpp的时候,如果some_class<T>这个模板的实现不在some_template.h中,编译器就会留下一个记号,等着连接器来连接。然后编译器就去编译别的文件了。而当编译器编译到some_class<T>的实现(比如说在some_template.cpp文件中)时,编译器不知道你再1.cpp文件中使用了some_class<int>,于是它没有生成相应的二进制代码。当然连接器就找不到some_class<int>,进而报错了。
some_class<int>的实现代码没有被生成,那我们就让他生成。唯一的办法是让编译器编译到some_template.cpp时知道some_class<int>被使用了,这很简单,我们使用一次就好了。
具体的办法如下:
在.h文件中只保留some_class的类定义:
//some_template.h
template <class T>
class some_class

{
public:
static T *ptr;
T* get_ptr();
};在.cpp文件中实现some_class:
//some_template.cpp
template <class T>
T* some_class<T>::ptr = NULL;
template <class T>
T* some_class<T>::get_ptr()

{
return ptr;
}
#include "gen_some_class.h"在上边的最后一行有一个#include "gen_some_class.h",我们在这个gen_some_class.h中写入生成模板类实例的代码:
//gen_some_class.h
void gen_some_class()

{
some_class<int> sci;
sci.get_ptr();
}这里的void gen_some_class()是一个谁都不会调用的函数,但是他会让编译器意识到some_class<int>被使用了,some_class<int>::get_ptr()也被使用了,这样编译器就会生成相关代码,连接器就可以将相关代码链接到任何使用它的地方。
事情就这样解决了。
对于比较复杂的情况,比如有100个方法的类,被特化了100个版本,在void gen_some_class()中把100个对象的100个方法逐个调用一边,显然是抽风的做法...另外,对于不能实例化的类(包含纯虚方法),还要费更多的周折。所以基本上这个方法是没有什么应用性的,权当消遣。
本文探讨了C++中模板类静态成员的定义及其实现问题,特别是当多个源文件使用同一模板类实例时产生的重定义错误。提供了一种解决方案,通过在单独的头文件中触发模板实例化,确保编译器生成正确的代码。
4634

被折叠的 条评论
为什么被折叠?



