C++ 在带指针参数的模板实例化之间进行选择

C++ 在带指针参数的模板实例化之间进行选择,c++,templates,compiler-construction,C++,Templates,Compiler Construction,查看以下测试代码: template<class T> struct Wrap {}; template<typename T> inline void fun (T *&Int) // **choice 1** {} template<typename T> inline void fun (Wrap<T> *&Int) // **choice 2** {} int main() { int i

查看以下测试代码:

template<class T> struct Wrap {};

template<typename T> inline
void fun (T *&Int)          // **choice 1**
{}

template<typename T> inline
void fun (Wrap<T> *&Int)    // **choice 2**
{}

int main()
{
  int i = 6;
  fun((char*&)(i));         // **call 1**
  fun((Wrap<char>*&)(i));   // **call 2**
}
template struct Wrap{};
模板内联
无效乐趣(T*&Int)//**选项1**
{}
模板内联
无效乐趣(换行符*&Int)//**选项2**
{}
int main()
{
int i=6;
乐趣((字符*&)(i));//**呼叫1**
乐趣((包装*&)(i));//**呼叫2**
}
当我在LinuxG++中运行这段代码时,它按照预期工作。当使用char*&调用fun()时,它会直接调用选择1的函数。 然而,当我们用Wrap*&调用fun()时,我很感兴趣,它调用了choice 2。即使选项1和2对第二次调用都有效,编译器还是设法选择稍好的竞争者->选项2(因为它存在)


<强>问题< /强>:它是否保证,对于C++的任何其他编译器,都将保留相同的行为?如果没有,那么是否有其他选择使其具有确定性?

对规范有更深入了解的人可以证实这一点,但我相信由于
Wrap
是一种比简单的T更为具体的类型,调用2将始终解析为“选择2”,在所有平台的编译器上。

选择第二个选项是因为它比第一个选项更专业-也就是说,
T*&
可以绑定到任何非临时的
T*
,但是
Wrap*&
只能绑定到非临时的
Wrap*
。据我所知,这是标准的,应该是可移植的行为,但在实践中,当涉及到这类事情时,什么是可移植的,什么不是可移植的,通常不是什么是标准的定义。

一件可能使这一点更成问题的事情是,专门化模板类和模板函数的规则差别很大。这是为了防止重载模板函数的可能性,而不可能重载类。因为我在这个话题上不是很坚定,所以我将链接到能够更深入地解释它的人:


虽然代码看起来像是模板专门化,但事实并非如此。该语言不允许部分模板函数专门化。这两个模板是不相关的,碰巧是重载

编译器将使用常用的查找机制查找对
fun((Wrap*&)i)
的调用,将找到两个模板,并确定有两个潜在的重载:

template <typename T> void fun( T*& );      // with T == Wrap<char>
template <typename T> void fun( Wrap<T>*& ) // with T == char
template void fun(T*&);//带T==Wrap
模板void fun(Wrap*&)//带T==char

然后,重载解析将确定第二个是更好的匹配项并实例化它。标准保证了这一点,但请注意:它们不是同一个模板,而是不同的模板,您可能会遇到意想不到的结果。查看@LiKao链接的文章以了解更多信息。

除非有编译器的错误;)编译器错误!?从未听说过……:)它实际上是可移植的,但它们不是一个模板的专门化,而是两个单独的模板,碰巧是重载。这里不是专门化,而是重载。专门化将在函数标识符和函数参数之间显示一个模板参数列表(以及在模板之后显示一个空模板参数列表,因为函数专门化必须是完全专门化)。内联的
在模板函数上基本上是无用的。这是没有必要的,因为它们是(非专门化的)模板,因此不会导致链接器错误,编译器比我们更好地确定何时内联和何时不内联。。。可能会完全无视你的要求。