哪种编译器适用于以下重载/特化行为?
-
03-07-2019 - |
题
请考虑以下代码:
#include <stdio.h>
namespace Foo {
template <typename T>
void foo(T *, int) { puts("T"); }
template <typename T>
struct foo_fun {
static void fun() { foo((T *)0, 0); };
};
}
namespace Foo {
void foo(int *, int) { puts("int"); }
}
using namespace Foo;
int main() {
foo_fun<int> fun;
fun.fun();
}
预期产量是多少? &QUOT; T&QUOT;还是int?
一个编译器(来自Apple的Xcode 3.1.2的gcc 4.0.1)输出“int”,另外两个编译器(gcc 4.1.2和4.1.3)输出“T”。
如果我在foo(T *,int)版本之前移动foo(int *,int)声明/定义,则所有输出都是“int”。在这种情况下,当前标准是否定义了重载/特化的顺序?
解决方案
第二个 void foo(...
是一个重载(而不是一个特殊化),它在 foo_fun :: fun
的定义中不可见,所以它赢了“可以在模板定义的上下文中找到。因为 T *
是一个依赖类型, foo
的解析在 foo((T *)0中, 0)
将被延迟,直到模板实例化时间和实例化的上下文也将被考虑。但是,标准的14.6.4.2表示如果函数名称是 unqualified-id 但不是 template-id 然后对于非ADL查找,只考虑在模板定义点可见的函数。 Foo
命名空间中没有函数参数所以没有参数依赖查找,因此调用 foo
的模板版本而不是非模板重载。
非常感谢对这个答案进行更正。
如果你使它成为如下的特化,那么在模板实例化时选择特化,只要在首次实例化函数模板时相关的特化是可见的,就可以调用特化。 INT 代码>
namespace Foo {
template<>
void foo<int>(int *, int) { puts("int"); }
}
当前标准的第14章,但它不是非常易读:)
编辑:如果我必须选择标准中最相关的部分,它可能是14.6 [temp.res]第9段。(稍微缩写)如果名称不依赖于模板参数,该名称的声明应在名称出现在模板定义中的范围内;该名称绑定到此时发现的声明,并且此绑定不受在实例化时可见的声明的影响。
编辑,编辑:但您还需要考虑14.6.4.2 [temp.dep.candidate]。由于所有的相互依赖性,尝试引用标准是非常困难和危险的,这个答案就是一个很好的例子。
其他提示
作为一般规则,在两个版本的编译器中,后者更可能更标准。