Should name lookup be deferred for a dependent class/namespace-name in a class-member-access expression?
Clang和GCC均拒绝以下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | template<typename T> void f(T t) { t.Dependent::f(); // clang accepts, gcc rejects t.operator Dependent*(); // both reject } struct Dependent { void f(); }; struct A : Dependent { operator Dependent*(); }; template void f<A>(A); |
我对标准的理解表明两种表述都应该被接受。
在这两种情况下,
在这两种情况下,名称
我有什么东西不见了吗?
编辑:如果打算不依赖这样一个名称,这个决定的理由是什么?我可以看到,如果实现者不必推迟对像
C++的工作草案N33 37的相关引文:
3.4.5 Class member access [basic.lookup.classref]
If the id-expression in a class member access is a qualified-id of the form
class-name-or-namespace-name::...
the class-name-or-namespace-name following the . or -> operator is first looked up in the class of the
object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire
postfix-expression. [ Note: See 3.4.3, which describes the lookup of a name before ::, which will only find a
type or namespace name. —end note ]If the id-expression is a conversion-function-id, its conversion-type-id is first looked up in the class of the
object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire
postfix-expression. In each of these lookups, only names that denote types or templates whose specializations
are types are considered.14.6.2 Dependent names [temp.dep]
Inside a template, some constructs have semantics which may differ from one instantiation to another. Such a
construct depends on the template parameters. In particular, types and expressions may depend on the type
and/or value of template parameters (as determined by the template arguments) and this determines the
context for name lookup for certain names. Expressions may be type-dependent (on the type of a template
parameter) or value-dependent (on the value of a non-type template parameter).[...]
Such names are unbound and are looked up at the point of the template instantiation (14.6.4.1) in both the
context of the template definition and the context of the point of instantiation.14.6.2.1 Dependent types [temp.dep.type]
A name is a member of an unknown specialization if it is
[...]
— An id-expression denoting the member in a class member access expression (5.2.5) in which either
— the type of the object expression is the current instantiation, the current instantiation has at least
one dependent base class, and name lookup of the id-expression does not find a member of the
current instantiation or a non-dependent base class thereof; or— the type of the object expression is dependent and is not the current instantiation.
[...]
A type is dependent if it is
— a member of an unknown specialization,
一
这是我认为你的第一个案例,
因此,
对于你的另一个例子,
运算符函数名的名称查找比较(例如,名称查找哈希表的相等函数)是"它们是用相同类型形成的转换函数ID"(3.8)。这意味着为了形成名称本身(还没有进行名称查找!),您不仅需要像标识符那样给出词汇拼写,还必须提供类型标识,这需要由
1 2 | struct Dependent; // forward decl at global scope t.operator Dependent*(); // in function template |
你的追随者
If it is intended that such a name is not dependent, what is the rationale for this decision? I can see that it makes life easier for the implementor if they do not have to defer evaluation of a construct like t.operator X::Dependent* or t.X::Dependent::f where X could be either a namespace or a type name.
我不知道理由,但我认为你已经给出了一个很好的观点。在查找非限定名时跳过依赖的基类的规则中,这一点非常明显。我认为什么理由适用于那个案例也适用于这个案例。它使程序员更容易理解函数模板,尤其是。
1 2 3 4 5 6 7 8 | struct Dependent; template<typename T> void f(T t) { t.Dependent::f(); t.operator Dependent*(); } |
代码看起来很好,但是如果