有这个:
#include <iostream>
void test(const int& ref){
std::cout << "By reference\n";
}
void test(const int *ptr){
std::cout << "By pointer\n";
}
template<typename T>
void test_template(const T &ref){
std::cout << "By reference\n";
}
template<typename T>
void test_template(const T *ptr){
std::cout << "By pointer\n";
}
int main() {
int *p = 0;
test(p);
test_template(p);
return 0;
}
我得到输出(使用g ++ 4.8.4):
By pointer
By reference
为什么模板版本的行为不同(是c ++缺陷还是编译器错误)?
答案 0 :(得分:1)
没有。
test_template(p);
呼叫
void test_template<int*>(int* const& p);
由于void test_template<int>(const int* ptr)
在编译器候选列表中排名较低,因为p
是int*
而不是const int*
,所以没有歧义。
答案 1 :(得分:1)
令您感到困惑的是,您的模板并不完全复制重载的行为。当编译器执行(非模板)重载解析时,它发现const int*
比const int&
更好匹配,因为指针重载中int*
不需要转换(但是,两者都有限定符转换,这并不重要。)
然而,对于你的模板,事情有点不同
此:
template<typename T>
void test_template(const T &ref){
std::cout << "By reference\n";
}
可以实例化为const int& ref
,是的,但它也可以实例化为int* const& ref
(因为T
是const,T
是int*
,要创建一个const指针,请在const
之后放置int*
。将其与:
template<typename T>
void test_template(const T *ptr){
std::cout << "By pointer\n";
}
其中,T
可以实例化为int
以获取const int*
所以现在编译器必须问自己,这是一个更好的匹配,
int* const& ref
或const int* ptr
?答案是第一个。鉴于在订购模板之前删除了引用和cv限定符,我们将进行比较
int*
反对const int*
所以第二个需要从int*
到const int*
的类型转换,而第一个则不需要。 (指针语法很奇怪,你记得在我们上面的例子中,遗留的const
是类型的一部分,而不是限定符)。请注意,如果您将p
定义为const int *p = 0;
,则编译器会选择第二个。{/ p>
要完全重新创建非模板重载的行为,您必须显式测试T
是否为指针类型:
template<typename T, typename std::enable_if<!std::is_pointer<T>::value, int>::type = 0>
void test_template(const T& ref){
std::cout << "By reference\n";
}
template<typename T>
void test_template(const T* ptr){
std::cout << "By pointer\n";
}
但是,我认为只要将const
移动到T*
的另一端以获得指针重载,那么“足够好”是真的:
template<typename T>
void test_template(const T &ref){
std::cout << "By reference\n";
}
template<class T>
void test_template(T* const ptr){
std::cout << "By pointer\n";
}
这是有效的,因为在订购模板时会从类型中删除第一个cv限定符和引用。这让我们
T ref
与 T* ptr
和T*
被认为比T
更专业,因此将根据需要选择第二个。