一个模板函数的重载问题~

Eastsun 2007-11-29
RT,下面这段代码为什么编译通不过?
#include<iostream>
using namespace std;

/**
 *  为什么这段代码编译出错??
 */

template<typename T>
void print(T value);


int main(){
    print(1);
}

template<typename T>
void print(T value){
    cout<<"template<typename T> :"<<value<<endl;
}

template<> void print<int>(int i){
    cout<<"template<> :"<<i<<endl;
}

void print(int i){
    cout<<"simple :"<<i<<endl;
}




改成这样就没问题:
#include<iostream>
using namespace std;


template<typename T>
void print(T value){
    cout<<"template<typename T> :"<<value<<endl;
}
template<> void print<int>(int i){
    cout<<"template<> :"<<i<<endl;
}

void print(int i){
    cout<<"simple :"<<i<<endl;
}

int main(){
    print(1);
}


jimmy_c 2007-11-30
跟template没关系吧。你的void print(int i)没有声明,当然编不过。
Eastsun 2007-11-30
如果是ls所说的原因,那为什么这样又可以编译通过呢?
#include<iostream>
using namespace std;

template<typename T>
void print(T value);

int main(){
    print(1);
}


template<typename T>
void print(T value){
    cout<<"template<typename T> :"<<value<<endl;
}


void print(int i){
    cout<<"simple :"<<i<<endl;
}
jimmy_c 2007-11-30
前面说的不够准确。

这应该是个c/c++的普遍性问题,是因为代码顺序造成的。和你的
void print(int i) {}
声明与否没有关系。这段代码完全可以删掉。
和template也没有太大关系。

第一种写法,因为
template<> void print<int>(int i) {}
的实现写在main()之后,编译器在编译main的时候是不知道有这个函数特化实现的,所以使用的是
template<typename T> 
void print(T value)
版本的实现,因为前面有它的声明。而编译器这时候就会做一个模板实例化的工作,全局查找到
template<typename T> 
void print(T value)
的实现,把实现中的T用int替换,自动生成一个int版本的模板函数,也就是
template<> void print<int>(int value)。
但是接下来往下编译,它会发现另一个版本的
template<> void print<int>(int value),所以就会有同名函数的冲突问题。
jimmy_c 2007-11-30
这个问题引申一下,模板函数和“实”函数编译器处理上的区别。如果上面的程序写成:

template<typename T> 
void print(T value); 

template<typename T>
void func()
{
    print(1); 
}
 
template<typename T> 
void print(T value)

    cout<<"template<typename T> :"<<value<<endl; 


template<>
void print<int>(int i)
{
    cout<<"simple :"<<i<<endl; 
}


int main()
{
func<int>();
return 0;
}

结果就是对的,因为func<T>是个模板函数,编译器编译时会查找print<T>的对应版本。能否找到我想应该和调用func()的位置有关。而把func<T>的模板参数去掉,编译结果就不正确,虽然这个模板参数并没有什么意义。因为对于“实”函数,编译器会认为它所需要的函数应该在它前面声明。

我想产生这个结果的原因应该是编译器模板实例化的时刻吧!编译器会在需要实例化的时候去产生模板的实例函数,所以“调用点”的位置应该很有关系。
jimmy_c 2007-11-30
另外,对于
template<>
void print<int>(int i)

void print(int i)

编译器会认为它们是两个不同的函数,C++标准对于模板函数匹配的情况是有一个优先级顺序的,试验的结果似乎是先匹配实函数,再匹配模板函数。当然前提是声明要在调用的前面。
Eastsun 2007-11-30
嗯,按楼上所说,那为什么下一种写法又能编译通过呢?
#include<iostream>
using namespace std;

template<typename T>
void print(T value);

template <> void print<int>(int);

int main(){
    print(1);
}

template<typename T>
void print(T value){
    cout<<"template<typename T> :"<<value<<endl;
}

template <> void print<int>(int v){
    cout<<"template<> :"<<v<<endl;
}

void print(int i){
    cout<<"simple :"<<i<<endl;
}


而且这段代码运行的结果是:
template<> :1

按照LS的推理,我声明了
template <> void print<int>(int);
但没有声明
void print(int i)
所以main中的函数使用的是template <> void print<int>(int);
但接着往下编译,它会发现另一个版本的
void print(int i);
而且在编译器中,这个版本的优先级更高.
所以应该有同名函数冲突才对.

但结果不是这样的,为什么呢?

ps:不管怎么样,非常感谢LS的讨论.
另外,这个问题是我阅读<C++ prime>第四版的中文版的231页时产生的.
Eastsun 2007-11-30
哦,没看到LS刚刚发的
Eastsun 2007-11-30
我想知道的是
对于这个问题有没有一个官方的说法,比如C++标准里面.
我觉得C++最恶心的地方就在于: 没有一个完全符合标准的编译器.
如果写代码出问题了,有可能是自己写错了,也可能是编译器对C++当前标准支持不够,甚至可能是像VC一样的非标准C++造成的.
Eastsun 2007-11-30
还有,我想知道:
现在对于调用 print(1)来说,它有三种实现方式.
<C++ Prime>里面说的是普通函数优先级>显式具体化>模板实现.
那么,在三个实现都可见的情况下,使用print(1)会使用普通的实现.
而使用print<int>(1)会调用显式具体化那个实现.
那如果我想调用模板那个实现该怎么办呢?

<C++ prime>里面提到声明时可以指定的,也就是这样:
template void print<int>(int);
声明,就会使用模板实现.
但事实上这样编译不过去.why?
Global site tag (gtag.js) - Google Analytics