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

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
Sebastian
  • 1,834
  • 2
  • 10
  • 22
  • GCC doesn't implement [CWG727](https://wg21.cmeerw.net/cwg/issue727) 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. – Language Lawyer Jan 03 '22 at 13:50
  • 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. – Sebastian Jan 03 '22 at 14:46
  • 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` finds class template `A` from namespace `N`? – Language Lawyer Jan 03 '22 at 14:54
  • 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 https://godbolt.org/z/ac1sTY9YM – Language Lawyer Jan 03 '22 at 18:59
  • 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. – Sebastian Jan 03 '22 at 19:23
  • 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. – Sebastian Jan 03 '22 at 19:25
  • _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](http://eel.is/c++draft/basic.lookup.unqual#4). Now one needs to show that it is not otherwise specified, and it is always hard to prove the absence. – Language Lawyer Jan 03 '22 at 19:30
  • 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) – Sebastian Jan 03 '22 at 19:53
  • 1
    The handling of unsign-declarations is described in http://eel.is/c++draft/basic.lookup.general#3 – Language Lawyer Jan 03 '22 at 20:18
  • _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` is not among those things. – Language Lawyer Jan 03 '22 at 23:17
  • 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] – Sebastian Jan 04 '22 at 11:46
  • _Is this reqmt. for member function definitions also lifted with the current standard (draft)?_ Ignoring the issue with correspondence, it is covered by http://eel.is/c++draft/dcl.meaning.general#3.4.sentence-2 and http://eel.is/c++draft/basic.scope.class#1.sentence-2. Normative wording about target scope of specializations seems missing, there is only Note http://eel.is/c++draft/temp.pre#7.sentence-3. – Language Lawyer Jan 04 '22 at 15:44
  • Good links! Why is #2 not nominable as N::f in this example http://eel.is/c++draft/dcl.meaning.general#example-1 Nominability: http://eel.is/c++draft/basic.scope.scope#6 – Sebastian Jan 04 '22 at 16:39
  • _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 https://godbolt.org/z/xKnWhabn6 – Language Lawyer Jan 04 '22 at 16:39
  • _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`. – Language Lawyer Jan 04 '22 at 16:43
  • So using directives are not considered for nominability? – Sebastian Jan 04 '22 at 16:46
  • You mean using-declarations? As it has been already discussed (http://eel.is/c++draft/basic.lookup.general#3), they are replaced with declarations they refer to. – Language Lawyer Jan 04 '22 at 16:50
  • 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. – Sebastian Jan 04 '22 at 16:56
  • 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. – Language Lawyer Jan 04 '22 at 18:00
  • BTW, Davis confirmed that there is no normative wording supporting http://eel.is/c++draft/temp.pre#7.sentence-3 (and it is already known issue) – Language Lawyer Jan 04 '22 at 18:03
  • 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? – Sebastian Jan 04 '22 at 18:21
  • _Wouldn't the same missing nominability apply to the code in the original question?_ Nominability is only relevant for the definition of `A::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? – Language Lawyer Jan 04 '22 at 19:18
  • Why is nominability not relevant for A? Refer to http://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::B looks for nominability of B in A (the look-up context), which is OK. – Sebastian Jan 04 '22 at 19:57
  • _Why is nominability not relevant for `A`?_ Because it specializes a class and not a function template. – Language Lawyer Jan 04 '22 at 20:00
  • 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'. – Sebastian Jan 04 '22 at 20:15
  • _Which rule do class template specialisations follow?_ I think a lot of them. What do you mean exactly? `A` is looked up per http://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. – Language Lawyer Jan 04 '22 at 20:22
  • 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. – Sebastian Jan 04 '22 at 20:55
  • 3.3 applies to `template<> struct A {}`. – Language Lawyer Jan 04 '22 at 21:01
  • _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: https://github.com/cplusplus/draft/issues/4592#issuecomment-830373343 – Language Lawyer Jan 05 '22 at 01:16
  • 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 https://github.com/cplusplus/draft/pull/4624 – Sebastian Jan 05 '22 at 06:57
  • The discussion mentions the related question https://stackoverflow.com/questions/63165315/is-it-a-bug-in-clang-that-defines-a-member-of-a-namespace-without-using-nested-n where Davis answers that the intent is to make it well-formed and refers to the class.pre/3 (before the p1787 rewrite). – Sebastian Jan 05 '22 at 07:46
  • 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. – Sebastian Jan 05 '22 at 07:47
  • _In the github discussion xmh0511 suggested adding … to class.pre/3_ http://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]. – Language Lawyer Jan 05 '22 at 15:13
  • 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 http://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 http://eel.is/c++draft/dcl.meaning.general#3.2). – Sebastian Jan 05 '22 at 16:53
  • 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. – Sebastian Jan 05 '22 at 16:55

0 Answers0