template<class T>
class A { public: friend void test(int x) { cout << 1; } private: T a; };
需求是,test函数想要成为A类的友元函数,来读取A类中的private成员属性,同时A类是一个泛型类,所以理所应当地想到这种写法。在A类还没有实例化对象的时候,这种写法没有任何报错,当A类创建对象的时候,出现了奇怪的bug:
int main() { A<int> x; A<long long> y; test(1); cout << "没有问题" << endl; }
A类创建了两个对象,A类是泛型类,创建了一个int型的类,与一个long long型的类,结果出现了这个的报错:
redefinition,说明void test重定义了,但是为什么?还有更诡异的现象:当x,与y同时存在的时候,就会报这样的错,但是当x,y只存在一个的时候,也就是A类只创建一种类型的对象时,它并不会报错,可以正常运行。当时也没有深究这个问题,但到了现在,随着本人对静态多态的理解加深,这个问题可以得到解答。
首先,先来了解一下c++的泛型:template的实现原理,简单总结起来,就是五个字:自动生成代码。
之所以泛型会被称为静态多态,是因为它并不是运行时来进行,而是在编译期进行的。假如定义了一个泛型函数test:
void test(T t1, T t2) {
//pass
};
在没有对它实体化之前,它只是一个模板,而不是一个函数,编译器就可以认为没有test这个函数。但是当使用到它的时候,编译器会根据test函数的使用情况自动地生成相应的参数的函数,比如下面,main函数中使用到了double参数类型的test与int参数类型的test:
int main() {
double x1 = 0, x2 = 0;
int y1, y2;
test(x1 = 0, x2 = 0);
test(y1 = 0, y2 = 0);
return 0;
}
编译器会检测到你一共使用了int与double两种参数的test,所以自动生成一个如下代码:
void test(int t1, int t2) { //pass }; void test(double t1, double t2) { //pass };
这些事本来是由程序员自己来完成的,用template就可以偷懒,给你一个模板,让编译器自己生成代码。这也是为什么,c++把它叫模板。因为template的函数或是类本身就不是一个函数或者类,它只是一个模板,告诉编译器该怎么生成代码,它本质上就是一种宏的替换,把template<class T>中的T替换成程序中所使用的类型,而这些操作就是在编译期时完成的,而不是在运行时进行的,因为相应的代码已经生成好了,不会有运行时开销,所以称为“静态多态”。
这样做当然会有缺点,就是程序的编译时间会变长,生成的可执行文件会变大,但是用编译时间去换运行时间是很划算的,因为编译只需要编译一次,而一个程序要被运行很多次。
再说回之前的那个问题,知道了template的原理之后,之前的问题也就行容易理解了:在使用了A<int> x;的时候,编译器检测到了使用了int,所以生成了如下代码:
class A { public: friend void test(int x) { cout << 1; } private: int a; //将template<class T>中的T替换成int. };
然后又检测到了使用了A<long long> y;所以又生成了如下代码:
class A { public: friend void test(int x) { cout << 1; } private: long long a; //将template<class T>中的T替换成long long. };
发现问题了吗?void test(int x)被定义了两次,成员函数是可以定义两次的,因为A<int>与A<long long>是两个不同的类,它们有一个名字相同的成员函数当然没有问题,但是友元函数不同,友元函数并不属于这个类,它属于一个独立的函数或是其它类的成员函数。所以出现了两次它的定义,肯定会报redefinition的错。
解决方法当然也很简单,一个函数,可以不能定义两次,但是可以被申明无数次:类内声类,类外实现:
#include<iostream> using namespace std; template<class T> class A { public: friend void test(int x); private: T a; //将template<class T>中的T替换成long long. }; void test(int x) { cout << 1; } int main() { A<int> x; A<long long> y; test(1); cout << endl << "没问题" << endl; return 0; }
运行结果: