Should the name search be deferred for the dependent class / namespace -name in the expression to access the class?

The following code is rejected by both clang and gcc

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); 

My reading of the standard suggests that both expressions should be accepted.

In both cases, the Dependent can only be a type name.

In both cases, the name Dependent must be "looked up in the object expression class" t . Since t is a type-dependent expression, the search should be delayed until the template is created.

Is there something I am missing?

EDIT . If it is assumed that such a name is independent, what is the rationale for this decision? I see that this makes life easier for the developer if they don’t need to postpone the construction evaluation of type t.operator X::Dependent* or tX::Dependent::f , where X can be either a namespace or a type name. I do not understand if this is a suspected or unintended side effect of the current formulation.

Relevant quotes from C ++ Working draft N3337:

3.4.5 Access to the class member [basic.lookup.classref]

If the id expression in the access to the class member is a qualified identifier of the form Namespace-name-class-name or ... :: class name or namespace name following. or → operator , the object expression is first scanned in the class and the name, if found, is used. Otherwise, it is considered in the context of the entire postfix expression. [Note: see 3.4.3 for a name search up to ::, which will only search for a type or namespace name . -end note]

If the id expression is the identifier of the transform function, its transform type identifier is first scanned in the object expression class and the name, if found, is used. Otherwise, it is considered in the context of the entire postfix expression. In each of these searches, only names denoting types or patterns, specialization types are considered .

14.6.2 Dependent Names [temp.dep]

Inside the template, some designs have semantics that may differ from one instance to another. This design depends on the parameters of the template. In particular, types and expressions may depend on the type and / or value of the template parameters (as defined by the template arguments), and this defines the context for finding a name for specific names. Expressions can be dependent on the type (by the type of the template parameter) or depending on the value (by the value of the template parameter of the non-type type).

[...]

Such names are unrelated and are considered at the moment of creating the template instance (14.6.4.1) both in the context of the template definition and the context of the instance creation point.

14.6.2.1 Dependent Types [temp.dep.type]

A name is a member of an unknown specialization if it

[...]

- An identifier denoting a member in an expression of access to a member of a class (5.2.5) in which either

- the type of the expression of the object is the current instance, the current instance has at least one dependent base class and the search for the name of the id expression does not find the member of the current instance or its independent base class; or

- the type of expression of the object depends and is not the current instance .

[...]

Type depends if it

- member of an unknown specialization ,

+4
source share
1 answer

one

Here is how I think your first case, t.Dependent::f works. Firstly, I believe (which means I'm not quite sure) that 14.6.2.1p5 should say "unqualified-id" instead of "id-expression". But regardless of this, your name Dependent::f actually consists of two names (in the standard, each nested nested qualifier name followed by the participant name is called "qualified-id", even if it is grammatically, they are not qualified - Id. Thus, the name foo::bar::baz is a qualified identifier, but also contains another "qualified identifier").

Dependent and Dependent::f . The first is not an “Identifier denoting an element in a class member access expression”, so you cannot just apply the rule applicable to Dependent::f to apply also to Dependent .

Dependent is independent of this, and although it must be looked for in a dependent type, it must be found during the definition. I personally think that we should have a sentence that says: "When you look at a qualified identifier, where the qualifier depends on the type, a search by name gives an empty result." In order to process these “forced names” that need to be executed immediately “gracefully, Anyway, in the end, I think your first case is poorly formed without finding Dependent (paragraph 3.4 cannot simply solve chapter 14 on its own, which the name actually depends on it).

2

In another case, operator Dependent is simpler. You again have two names, Dependent and operator Dependent . Again, I did not find anything that says Dependent is a dependent name here (I'm not sure if this would be wrong or not. It's outside of me).

Comparison of the search by name (say, the equality function in the hash table of the search for names) for the names of the operator’s functional functions: “they are transform identifiers formed with the same type” (3.8). This means that in order to form the name itself (not yet searching by name!), You must not only give a lexical spelling, as for identifiers, but you must provide an identifier of the type that must be provided by Dependent .

To delay the search for the dependent id expression in t.operator Dependent* , simply means that the comparison of the semantic type is delayed. Try this one that should work well

 struct Dependent; // forward decl at global scope t.operator Dependent*(); // in function template 

Your escort

If it is assumed that such a name is independent, what is the rationale for this decision? I see that this makes life easier for the developer if they don’t need to postpone a construction evaluation of type t.operator X :: Dependent * or tX :: Dependent :: f, where X can be either a namespace or a name type.

I do not know the rationale, but I think that you have already given a good moment. This is very similar to a rule that skips dependent base classes when looking for unqualified names. And I think that this justification is applicable for this case. This facilitates the discussion of the function template for the programmer, especially.

 struct Dependent; template<typename T> void f(T t) { t.Dependent::f(); t.operator Dependent*(); } 

The code looks fine, but if T is a member of the Dependent , suddenly Dependent will have a different binding (because first we are told to study a class T , and then in the environment volume). In my understanding of template rules, the above always refers to the surrounding Dependent area, so the code above is “safe” for this trap.

+1
source

Source: https://habr.com/ru/post/1496910/


All Articles