小说网 找小说 无限小说 烟雨红尘 幻想小说 酷文学 深夜书屋

模板函数冲突

    通过前面章节的学习,我们已经知道了什么是函数模板,什么是模板函数?函数模板是模板函数的抽象,模板函数是函数模板的具现。今天,我们主要讲解一下什么是模板函数冲突,模板函数冲突的原因及相应的解决方法。

(一)模板函数冲突

    为了说清什么是模板函数冲突,我们先从一个例子开始。

例1 模板函数冲突

func1.cpp的代码:

#include<iostream>
template<typename T>
void func(T const &v)
{
	std::cout<<"func1.cpp-func:"<<v<<std::endl;
}

void caller1()
{
	func(1);
	func(0.1);
}
func2.cpp的代码:

#include<iostream>
template<typename T>
void func(T const & v)
{
	std::cout<<"func2.cpp-func:"<<v<<std::endl;
}

void caller2()
{
	func(2);
	func(0.2f);
}
main.cpp的代码:

void caller1();
void caller2();
int main()
{
	caller1();
	caller2();
	return 0;
}
运行效果如下:

图1 模板函数冲突的效果图

    在例1中,整个程序由三部分组成,一个是func1.cpp,其中定义了func函数模板和caller1;一个是func2.cpp,其中定义了func函数模板和caller2;最后一个是main.cpp

,其中定义了主函数main,并在主函数中分别调用了caller1和caller2。

    预期目标:输出结果应该为图2所示:

图2 预期输出效果图

    但是实际输出效果却为图1所示,这是因为caller1中生成的模板函数和caller2中生成的模板函数冲突了,才会导致输出结果不能按照预期输出。

    通过例1,我们明白了模板函数冲突就是指由两个函数模板原型一样,但是,函数体不一样的函数模板,生成,函数原型一样,但是函数体不一样的两个模板函数所造成的冲突。

(二)模板函数冲突的原因

    上面小节,我们讲了什么是模板函数冲突,现在,我们来共同分析一下模板函数冲突的原因。我们仍然以例1为例,我们主要分析一下为什么fun2.cpp-func:2会变为func1.cpp-func:2。

    为了讲清楚模板函数冲突的原因,我们分步骤进行:

    (1)观察func1.o的符号表

     1、输入编译命令:g++ -c func1.cpp

     2、输入查看符号表命令:nm -C func1.o | grep func

     最终效果如图3所示:

     

图3 func1.o的模板函数

    (2)观察func2.o的符号表

    1、输入编译命令:g++ -c func2.cpp

    2、输入查看符号表命令:nm -C func2.o | grep func

    最终效果如图4所示:

图4 func2.o的模板函数

    (3)对比func1.o和func2.o的符号表

    1、分析func1.cpp

        由于在func1.cpp中定义了函数caller1,而在函数caller1中又使用了函数模板func,所以编译器为func1.o生成了两个模板函数,分别针对double和int型,因为小数默认为double类型。

    2、分析func2.cpp

        由于在func2.cpp中定义了函数caller2,而在函数caller2中又使用了函数模板func,所以编译器为func2.o生成了两个模板函数,分别针对float和int型,因为这里为小数加了后缀f,所以编译器将其识别为float类型。

    (4) 结论

    1、输入编译链接命令:g++ -o test func1.o func2.o main.cpp

    2、查看test的符号表:nm -C test.exe | grep func

    最终效果如图5所示:

图5 test.exe的模板函数

    通过观察图5可知,编译器为test.exe生成了4个模板函数,1个针对double;1个针对float;2个针对int。问题就出现在int类型上,因为当我们链接程序的时候,是先让链接器扫描func1.o,所以导致链接器先识别了func1.o中的int型函数模板实例,而当链接器扫描到func2.o的时候,虽然也遇见了一个int型的函数模板实例,但是因为这两个模板函数的实例原型一致,导致链接器只使用第一个int模板函数,而不使用第二个int模板函数,所以,整个程序的输出结果不能按照预期输出。

    倘若,我们更改一下链接命令,让其先扫描func2.o,那么效果如图6所示:

图6 模板函数冲突的另一种效果图  

    (三)解决模板函数冲突的方法

    归根揭底,例1中模板函数冲突的原因就是因为两个模板函数的符号名完全相同;所以,若想解决冲突,只要让它们的符号名不同即可,经常使用的方法就是命名空间。

    下面,我们一起来看一下,如果使用命名空间,例1的写法如何。

例 2 使用命名空间解决模板函数冲突

func1.cpp代码:

#include<iostream>
namespace func1
{
	template<typename T>
	void func(T const &v)
	{
		std::cout<<"func1.cpp-func:"<<v<<std::endl;
	}
	void caller1()
	{
		func(1);
		func(0.1);
	}
}
func2.cpp代码:

#include<iostream>
namespace func2
{
	template<typename T>
	void func(T const & v)
	{
		std::cout<<"func2.cpp-func:"<<v<<std::endl;
	}
	void caller2()
	{
		func(2);
		func(0.2f);
	}
}
main.cpp代码:

namespace func1
{
	void caller1();	
}
namespace func2
{
	void caller2();
}
int main()
{
	func1::caller1();
	func2::caller2();
	return 0;
}
编译代码,效果如图7所示:

图7 模板函数冲突解决的效果图

    在例2中,因为使用了命名空间技术,使得func1.cpp中的模板函数符号名和func2.cpp中的模板函数符号名不相同,所以解决了模板函数冲突导致的问题,输出结果符合预期效果。

    为了让大家相信func1.cpp的模板函数的符号名和func2.cpp中的模板函数的符号名已经不同,这里给出了它们的符号表,如下:

图8 func1.o的符号表

图9 func2.o的符号表

    通过仔细观察图8和图9可知,func1.cpp中的int模板函数符号名为func1::func<int>(int const &),而func2.cpp中的int模板函数符号名为func2::func<int>(int const&),

所以当编译链接他们的时候,就不会混淆,图10是test.exe的效果图,如下:

图10 test.exe 的符号表


    (四)小结

    今天,主要讲了什么是模板函数冲突,以及冲突的原因,并给出了详细的符号表,最后讲解了解决模板函数冲突的方法就是在代码中使用命名空间,这样编译器就可以解决模板函数冲突的问题。

   




  

posted on 2014-07-20 16:11  牛栏山1  阅读(153)  评论(1编辑  收藏  举报

导航