struct A{
template<typename U>
void T(){}
};
struct B{
template<typename U>
struct T{
using type = U;
};
};
struct C:A,B{
};
int main(){
C::T<int>::type d;
}
GCC和Clang都不接受这个例子。
根据basic.lookup.qual#1
类或名称空间成员或枚举数的名称可以在::作用域解析操作符([expr.prim.id.qual])之后引用,该操作符应用于表示其类、名称空间或枚举的嵌套名称说明符。如果嵌套名称说明符中的::作用域解析操作符前面没有decltype说明符,则查找::前面的名称时只考虑特化类型为的名称空间、类型和模板。.
这意味着当查找模板名称T
的声明时,T
的专门化应在此上下文中表示类型。另一方面,根据class.member.lookup#4
如果C包含名称f的声明,则声明集包含C中声明的满足查找发生的语言结构要求的所有f声明.
同样,当在C
范围内查找模板T
时,只有那些专门化是类型的模板才应该被此查找考虑。C
的作用域没有针对T
的任何声明,因此将在S(T,C)
的每个基类中对其执行查找。A
中的T
模板不满足要求。同时,在B
的作用域中声明的模板T
也满足要求。因此,查找不是二义性的,B::T
是唯一的结果。这意味着C::T<int>::type d
应该是格式良好的。为什么GCC和Clang都拒绝这个例子?它可以被认为是两者的bug吗?如果我遗漏了什么,那么这个例子是病态的原因是什么呢?
前瞻需要对T
的查找依赖于::
,即使<
的解释依赖于T
的含义被认为是不可取的。因此,查找后跟<
的名称并不局限于类型和名称空间,而不管后面是否找到任何>::
。P1787R6修复了这个问题,限制了对标识符的特殊查找,立即然后是::
(因为其他类型的名称无论如何都不能引用类型或名称空间)。