GCC 接受以下代码:
template <typename T>
struct meta
{
typedef typename T::type type;
};
struct S {};
template <typename T>
typename meta<T>::type foo(T, S);
int foo(int, int);
int main()
{
foo(0, 0);
}
但是 clang 拒绝它并出现以下错误:
test.cpp:4:22: error: type 'int' cannot be used prior to '::' because it has no members
typedef typename T::type type;
^
test.cpp:10:10: note: in instantiation of template class 'meta<int>' requested here
typename meta<T>::type foo(T, S);
^
test.cpp:10:24: note: while substituting deduced template arguments into function template 'foo' [with T = int]
typename meta<T>::type foo(T, S);
^
这似乎表明 GCC 和 clang 在重载解析期间执行某些操作的顺序有所不同。S
由于第二个参数 ( vs. int
)中的类型不匹配,GCC 似乎在尝试实例化候选模板的返回类型之前丢弃了候选模板,而 clang 似乎以相反的方式进行。
谁是对的?
我相信这个问题对模板库的作者有重要意义。具体来说,如果 clang 是正确的,那么模板的作者foo
将不得不做额外的工作来将错误转化为替换失败。
编辑:请注意,GCC和clang都拒绝了以下稍微简单的示例,并出现类似的错误:
template <typename T>
struct meta
{
typedef typename T::type type;
};
template <typename T>
typename meta<T>::type foo(T);
int foo(int);
int main()
{
foo(0);
}
暗示 GCC 知道“只有在函数类型及其模板参数类型的直接上下文中的无效类型和表达式才会导致推导失败”。此示例与原始示例之间的区别在于原始示例中存在第二个函数参数,基于此,GCC 在尝试对返回类型执行替换之前就抛出了候选模板。我认为问题是,GCC 按该顺序执行操作是否正确,或者它是否应该在考虑参数类型匹配之前尝试对返回类型执行替换。
更新: Luc Danton 的回答让我相信 clang 拒绝代码是正确的。我相应地提交了一个 GCC 错误。