definition of class in namespace with using
Asked Answered
G

0

11

[class.pre] p3 states:

If a class-head-name contains a nested-name-specifier, the class-specifier shall not inhabit a class scope. If its class-name is an identifier, the class-specifier shall correspond to one or more declarations nominable in the class, class template, or namespace to which the nested-name-specifier refers; they shall all have the same target scope, and the target scope of the class-specifier is that scope.

[ Example:

namespace N {
  template<class>
  struct A {
    struct B;
  };
}
using N::A;
template<class T> struct A<T>::B {};    // OK
template<> struct A<void> {};           // OK

-- end example ]

The first sentence and the example seem to differ.

Is the specialization of A<void> template, which does not contain a nested-name-specifier (but only 'relies' on using) still conformant?

clang and msvc accept it, gcc shows the error

error: explicit specialization of 'template struct N::A' outside its namespace must use a nested-name-specifier [-fpermissive]

Additional Context

The paper, which introduced the original wording and example comment, is P1787R6: Declarations and where to find them, containing the line:

template<> struct A<void> {};         // error: A not nominable in ::

An editorial change Edit: [class.pre] Fix incorrect comment in example changed the example so that the comment now says OK. More discussion relating to it is found in editorial issue 4592

Ghislainegholston answered 3/1, 2022 at 8:50 Comment(35)
GCC doesn't implement CWG727 yet. [temp.expl.spec]/2 was changed to say «An explicit specialization may be declared in any scope in which the corresponding primary template may be defined», and N::A may be defined in the enclosing namespace.Arras
As far as I understand at first sight, CWG727 is not the same issue: The examples in it are either in the correct namespace or with nested namespace specifiers. None of those rely on using for specialization, but perhaps you can correct me.Ghislainegholston
In the resolution of CWG727, the phrase «An explicit specialization whose declarator-id or class-head-name is not qualified shall be declared in the nearest enclosing namespace of the template» was deleted. This is what GCC is complaining about. None of those rely on using for specialization What is your concern? That lookup for A in A<void> finds class template A from namespace N?Arras
You can give GCC both a nested-name-specifier and a using-declaration and see that it is not the using-declaration what GCC doesn't like godbolt.org/z/ac1sTY9YMArras
Yes, GCC does not like the missing nested-name-specifier. The paper P1787R6 really changed a lot. Particularly it removed paragraph 9 of temp.expl.spec "A template explicit specialization is in the scope of the namespace in which the template was defined." without an easily visible replacement for class templates.Ghislainegholston
In class.pre the standard was changed to "Otherwise [in the case of no template specialization], the class-name is an identifier; it is not looked up, and the class-specifier introduces it." I understand the idea, for specializations no new name is introduced, but the primary template is the master. But I am missing the positive rules that for specialization an (optionally unqualified) lookup occurs and there are no requirements, where the specialization is scoped.Ghislainegholston
But I am missing the positive rules that for specialization an (optionally unqualified) lookup occurs An unqualified name is a name that does not immediately follow a nested-name-specifier or the . or -> in a class member access expression… Unless otherwise specified, such a name undergoes unqualified name lookup from the point where it appears. Now one needs to show that it is not otherwise specified, and it is always hard to prove the absence.Arras
It does not make it simpler that basic.lookup.unqual only references using directives (using namespace X) and not using declarations (using Y::X) for lookup. (gcc handles both the same in our use case)Ghislainegholston
The handling of unsign-declarations is described in eel.is/c++draft/basic.lookup.general#3Arras
Now one needs to show that it is not otherwise specified, and it is always hard to prove the absence If you search for "not looked up", of which there are only 7 occurrences, you will find that A in A<void> is not among those things.Arras
The compilers currently (trunk) demand that a member function definition is in an enclosing scope of the class declaration. An the same for class template specializations. Is this reqmt. for member function definitions also lifted with the current standard (draft)? After successful lookup the definition is 'linked' to the declaration without introducing its name, so the namespace of the definition no longer matters? There is still a notion of a definition inhabiting a namespace: class.mfct/2 The definition of the member function f of class X inhabits the global scope [for a specific example]Ghislainegholston
Is this reqmt. for member function definitions also lifted with the current standard (draft)? Ignoring the issue with correspondence, it is covered by eel.is/c++draft/dcl.meaning.general#3.4.sentence-2 and eel.is/c++draft/basic.scope.class#1.sentence-2. Normative wording about target scope of specializations seems missing, there is only Note eel.is/c++draft/temp.pre#7.sentence-3.Arras
Good links! Why is #2 not nominable as N::f in this example eel.is/c++draft/dcl.meaning.general#example-1 Nominability: eel.is/c++draft/basic.scope.scope#6Ghislainegholston
After successful lookup the definition is 'linked' to the declaration without introducing its name, so the namespace of the definition no longer matters? The notion of target scope matters, because it determines how lookup proceeds when it leaves the definition godbolt.org/z/xKnWhabn6Arras
Why is #2 not nominable as N::f Because #2's target scope is N::P, and not N. And N::P is not in inline namespace set of N.Arras
So using directives are not considered for nominability?Ghislainegholston
You mean using-declarations? As it has been already discussed (eel.is/c++draft/basic.lookup.general#3), they are replaced with declarations they refer to.Arras
In the example N has using P::f, so N::f would be replaced with N::P::f? It is found, but discarded, because it is not nominable in N? I think nominability happens before introducing target scope for finding previous declarations, but target scope will be set to same namespace afterwards.Ghislainegholston
Qualified lookup for f in N finds a using-declaration using P::f, which is replaced with the declaration named by it: N::P::f. Since it is not nominable in N, it is ignored.Arras
BTW, Davis confirmed that there is no normative wording supporting eel.is/c++draft/temp.pre#7.sentence-3 (and it is already known issue)Arras
Wouldn't the same missing nominability apply to the code in the original question? Thank you for confirming with Davis, so best is to wait for a clarifying update of the standard?Ghislainegholston
Wouldn't the same missing nominability apply to the code in the original question? Nominability is only relevant for the definition of A<T>::B. so best is to wait for a clarifying update of the standard? What else the current draft is missing, except for the normative wording about specializations' target scope?Arras
Why is nominability not relevant for A<void>? Refer to eel.is/c++draft/dcl.meaning.general#3.2 for unqualified lookup and #3.3 for template specialization. The declarator [of the specialization] is in the global scope. A is not nominable there. Whereas A<T>::B looks for nominability of B in A (the look-up context), which is OK.Ghislainegholston
Why is nominability not relevant for A<void>? Because it specializes a class and not a function template.Arras
Okay, 3.3 states at the end "when identifying any function template specialization being declared." This should be at the beginning of 3 or 3.3, or be valid for class templates, too. Which rule do class template specialisations follow? Or do you say the beginning of 3.3 applies, but not the end? It uses 'when' instead of 'if'.Ghislainegholston
Which rule do class template specialisations follow? I think a lot of them. What do you mean exactly? A is looked up per eel.is/c++draft/basic.lookup.unqual#4.sentence-2 and no lookup results are ignored, because it is a specialization of a class template.Arras
One of the rules of dcl.meaning.general should apply, either 3.3 or 3.4. Nowhere does it say, dcl rules do not apply for class templates.Ghislainegholston
3.3 applies to template<> struct A<void> {}.Arras
BTW, Davis confirmed that there is no normative wording supporting eel.is/c++draft/temp.pre#7.sentence-3 (and it is already known issue) BTW, writing to Davis was complete waste of time, since this topic is discussed in the editorial issue which is mentioned by the commit linked in this question: github.com/cplusplus/draft/issues/4592#issuecomment-830373343Arras
Oh sorry, only found/looked ito the commit page to jensmaurer's branch and not the merge into the master branch, where this discussion is linked github.com/cplusplus/draft/pull/4624Ghislainegholston
The discussion mentions the related question #63165815 where Davis answers that the intent is to make it well-formed and refers to the class.pre/3 (before the p1787 rewrite).Ghislainegholston
Currently only the nested-name-case is described in class.pre/3. In the github discussion xmh0511 suggested adding Otherwise, if the class-head-name is a simple-template-id, the terminal name of the simple-template-id is (unqualified name) looked up; they shall all have the same target scope, and the target scope of the class-specifier is that scope. to class.pre/3.Ghislainegholston
In the github discussion xmh0511 suggested adding … to class.pre/3 eel.is/c++draft/dcl.meaning.general#3.3 looks a better candidate. Also, the fact that the terminal name of a simple-template-id is looked up is already specified and need not be repeated. they shall all have the same target scope also feels redundant, because if the lookup finds more than one declaration of a class template, it is ambiguous. As for function specialization case, the unique function template to specialize is selected by [temp.deduct.decl].Arras
I think the intended semantic difference is that in this wording from xmh0511 the nominability criteria for unqualified names is skipped (IIRC it was/is meant to be put directly after the nested-name-space paragraph eel.is/c++draft/class.pre#3, first word otherwise relates to nested-name-specifier), whereas in dcl.meaning.general#3.3 nominability is a requirement. This has an effect on which programs are well-formed. Of course the nominability could be in the target scope (together with eel.is/c++draft/dcl.meaning.general#3.2).Ghislainegholston
In my personal opinion it would make sense to collect whatever semantics at dcl.meaning.general and refer from class.pre to it instead of having separate rules there.Ghislainegholston

© 2022 - 2024 — McMap. All rights reserved.