模板重载的行为与非模板重载的行为不同

时间:2015-12-18 15:30:26

标签: c++ templates

有这个:

#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 ++缺陷还是编译器错误)?

2 个答案:

答案 0 :(得分:1)

没有。

test_template(p);

呼叫

void test_template<int*>(int* const& p);

由于void test_template<int>(const int* ptr)在编译器候选列表中排名较低,因为pint*而不是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,Tint*,要创建一个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更专业,因此将根据需要选择第二个。