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

Oli*_*ain 24 c++ templates c++11

考虑以下:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

int* x;
Foo(x);
Run Code Online (Sandbox Code Playgroud)

但事实并非如此.这对我来说似乎很奇怪,因为它const T*可以清楚地绑定到非const Tconst T&can,但是指针变体似乎是"更适合".

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

template <class T> void Foo(T* x) {
  const T* const_x = x;
  Foo(const_x);
}
Run Code Online (Sandbox Code Playgroud)

但那感到错误和不必要.有没有更好的办法?我不理解的是什么(除了标准的xyz部分说这是这样的)?

bol*_*lov 26

你在想:如果我的参数不是常量(或者如果参数是常量的)那么指针将是一个完美的匹配.那是正确的.我所拥有的不同之处在于const.所以只是添加const到上面的场景我也应该得到指针重载.那也是对的.现在怎么可能因为你明显得到了引用过载?嗯,代码与您的想法不符.以下是与您的思路一致的代码,并且确实会选择指针重载:

template <class T> void Foo(T* const x);    
template <class T> void Foo(T const &x);
Run Code Online (Sandbox Code Playgroud)

密切关注的地方const.我已添加const为顶级限定符.虽然T const &x与你所拥有的相同const T& x,T* const x但却不一样const 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*
Run Code Online (Sandbox Code Playgroud)

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

 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*
Run Code Online (Sandbox Code Playgroud)

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

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

简而言之,我能解释的最好,而不是标准的xyz部分

  • 在重载决策期间,函数参数声明中的顶级“const”将被忽略。因此 `template &lt;class T&gt; void Foo(T* const x);` 和 `template &lt;class T&gt; void Foo(T* x);` 是等效的函数模板声明。 (2认同)

小智 5

您的问题是,编译时Foo(x);将执行模板类型推导,并且由于您的 x 是一个,所以int*这将首先被解释为调用Foo<int*>(x),并且您的template <class T> void Foo(const T& x)重载与此完美匹配,因此类型推导在此结束。

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

您可以使用SFINAE技术,例如:

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;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,如果 T 是指针类型,则引用重载将不匹配(在推导类型时),因此编译器也会尝试,Foo<int>(x)并且您的template <class T> void Foo(const T* x)重载将是完美匹配(正是您所期望的)。

样本:

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

Foo(x);
Foo(pX);
Run Code Online (Sandbox Code Playgroud)

输出:

I am the reference overload
I am the pointer overload
Run Code Online (Sandbox Code Playgroud)