模板通过const指针选择const引用

时间:2017-12-27 07:54:13

标签: c++ c++11 templates

请考虑以下事项:

template <class T> void Foo(const T* x) {
  std::cout << "I am the pointer overload" << std::endl;
}

template <class T> void Foo(const T& x) {
  std::cout << "I am the reference overload" << std::endl;
}

鉴于上述情况,我希望以下内容可以调用指针重载:

int* x;
Foo(x);

但它没有。这对我来说似乎很奇怪,因为const T*可以明显地绑定到非const T,就像const T&一样,但指针变体看起来像是&#34;更合适&# 34。

对于我的应用程序,我希望调用指针变体。我可以通过额外的专业化来完成这项工作:

template <class T> void Foo(T* x) {
  const T* const_x = x;
  Foo(const_x);
}

但这感觉错了,没必要。有没有更好的办法?我不理解的是什么(标准的x.y.z部分除了这样说之外)?

2 个答案:

答案 0 :(得分:25)

你在想:如果我的参数不是常量(或者如果参数是常量的)那么指针将是完美的匹配。这是正确的。 我所拥有的是const。因此,只需在上面的场景中添加const,我就会得到指针重载。那也是对的。现在怎么可能因为你明显得到了引用过载?嗯,代码与您的想法不符。这是与您的思路一致的代码,它确实会选择指针重载:

template <class T> void Foo(T* const x);    
template <class T> void Foo(T const &x);

密切关注const的位置。我添加了const作为顶级限定符。虽然T const &xconst T& x相同,但T* const xconst T* x不同。

让我们看看在这种情况下的重载决议:

 fun                                     | T    | parameter    | argument
-----------------------------------------+------+--------------+-----------
 template <class T> void Foo(T* const x) | int  | int* const   | int*
 template <class T> void Foo(T const &x) | int* | int* const & | int*

让我们看看你的版本的重载:

 fun                                     | T    | parameter    | argument
-----------------------------------------+------+--------------+-----------
 template <class T> void Foo(const T* x) | int  | const int*   | int*
 template <class T> void Foo(T const &x) | int* | int* const & | int*

正如您在第一个版本中看到的那样,首选添加顶级const并选择指针重载。不需要指针转换。

在第二种情况下,指针重载需要从pointer to mutablepointer to const的指针转换。这些是不同类型的指针。但是在第二次重载时,不需要指针转换。只需添加顶级const

简而言之,我能解释的最好的是标准的x.y.z部分

答案 1 :(得分:4)

您的问题是,在编译Foo(x);时,会执行模板类型扣除,而且当您的x为int*时,这将首先解释为Foo<int*>(x)来电和您的{{ 1}}重载是一个完美的匹配,所以类型推导在这里结束。

如果要处理类(而不是模板函数),可以使用部分模板特化来区分指针类型和非指针类型,但对于函数,只允许完全特化。

您可以做的是使用SFINAE等技术:

template <class T> void Foo(const T& x)

在这种情况下,如果T是指针类型,则引用重载将不匹配(当扣除类型时),因此编译器也将尝试template <class T> void Foo(const T* x) { std::cout << "I am the pointer overload" << std::endl; } template <class T> typename std::enable_if<!std::is_pointer<T>::value>::type Foo(const T& x) { std::cout << "I am the reference overload" << std::endl; } 并且您的Foo<int>(x)重载将是完美匹配(正是你所期待的)。

样品:

template <class T> void Foo(const T* x)

输出:

int x = 12;
int* pX = new int(5);

Foo(x);
Foo(pX);