`typename` keyword before non-type template parameter defined via nested templated `using` in out-of-line member definition

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

`typename` keyword before non-type template parameter defined via nested templated `using` in out-of-line member definition

Jon Chesterfield via cfe-dev
I hope this is a good audience.

Title seems long an convoluted, but the problem boiled down to gcc and
clang disagreeing on such code:

(This is a grokked version of code which we got during a real world
code-base evolution [if that means anything])
```
#include <type_traits>
#include <iostream>
#include <string>

template <typename T>
struct B {
    template <typename U>
    using nested_templated_using = U;

    template<typename U = T, nested_templated_using<U>* = nullptr >
    void f();
};

template <typename T>
template <typename U, typename B<T>::template nested_templated_using<U>* >
//                   ^ should typename be here or not?
void  B<T>::f(){ }

int main () { return 0;}
```
[godbolt example](https://godbolt.org/g/yjPH1u)

 clang requires `typename` keyword in the indicated spot, while gcc
forbids it. They error out with the same message indicating that the
out-of-line definition does not match declaration.

I've asked this question on
[stackoverflow](https://stackoverflow.com/questions/48448353) but it
wasn't clear yet what is the core issue, I thought gcc misbehaves.

I got some great input by Johannes Schaub, who pointed [CWG issue
#2](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2) and
[CWG issue #560](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#560).
They both treat about referring to the dependent types, though as the
type of return value.

My thoughts are:
1. The type of the parameter is dependent.
2. I am not sure whether the type of the parameter is "a member of the
current instantiation" as understood in
[temp.res#3](http://eel.is/c++draft/temp.res#3)
3. In conjunction with [temp.res#5](http://eel.is/c++draft/temp.res#5)
above renders "wrong guess" about the presence of the keyword a killer
- program is ill-formed.

I might be wrong in my toughts, and I am not sure whether, or not,
`typename` keyword usage changes for non-type template parameters.
Also, inline definition does not require the keyword.

It would be nice if someone could give authoritative answer
(preferably stepping through standard) whether:
1. clang is wrong.
2. gcc is wrong.
3. standard is unclear.

Regards,
Łukasz.
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: `typename` keyword before non-type template parameter defined via nested templated `using` in out-of-line member definition

Jon Chesterfield via cfe-dev
On 4 February 2018 at 18:40, Łukasz Kucharski via cfe-dev <[hidden email]> wrote:
I hope this is a good audience.

Title seems long an convoluted, but the problem boiled down to gcc and
clang disagreeing on such code:

(This is a grokked version of code which we got during a real world
code-base evolution [if that means anything])
```
#include <type_traits>
#include <iostream>
#include <string>

template <typename T>
struct B {
    template <typename U>
    using nested_templated_using = U;

    template<typename U = T, nested_templated_using<U>* = nullptr >
    void f();
};

template <typename T>
template <typename U, typename B<T>::template nested_templated_using<U>* >
//                   ^ should typename be here or not?
void  B<T>::f(){ }

int main () { return 0;}
```
[godbolt example](https://godbolt.org/g/yjPH1u)

 clang requires `typename` keyword in the indicated spot, while gcc
forbids it. They error out with the same message indicating that the
out-of-line definition does not match declaration.

Strictly-speaking, neither is formally correct. For a type involving template parameters, the two must be spelled with the same sequence of tokens (though implementations are allowed to accept additional cases); see [temp.over.link]. So the right thing is:

template <typename T>
struct B {
    template <typename U>
    using nested_templated_using = U;

    template<typename U = T, typename B<T>::template nested_templated_using<U>* = nullptr >
    void f();
};

template <typename T>
template <typename U, typename B<T>::template nested_templated_using<U>* >
void  B<T>::f(){ }

... which Clang and MSVC accept and GCC rejects. (ICC/EDG seems to reject every variation on this.) However, the intent is to permit additional cases where the equivalence of the two declarations can be determined using information from the template itself (see CWG issue 2, wg21.link/cwg2), which also permits your original example.

So we can at least conclude that GCC is wrong to reject your original example. Presumably within the class definition it canonicalizes the type of the template parameter to merely "U *", and is unable to match that up against the "typename B<T>::template nested_templated_using<U> *" within the out-of-class definition of f.

But that leaves open the question of whether the code without the typename/template keywords is valid too. See below!

I've asked this question on
[stackoverflow](https://stackoverflow.com/questions/48448353) but it
wasn't clear yet what is the core issue, I thought gcc misbehaves.

I got some great input by Johannes Schaub, who pointed [CWG issue
#2](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2) and
[CWG issue #560](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#560).
They both treat about referring to the dependent types, though as the
type of return value.

My thoughts are:
1. The type of the parameter is dependent.
2. I am not sure whether the type of the parameter is "a member of the
current instantiation" as understood in
[temp.res#3](http://eel.is/c++draft/temp.res#3)

This is the right question. And, sadly, the standard is unclear on this question; [temp.dep.type] says this depends on whether the name appears "in the definition of [...] a member of a class template", but is the template-parameter-list "in the definition" or not? The answer is by no means obvious.

3. In conjunction with [temp.res#5](http://eel.is/c++draft/temp.res#5)
above renders "wrong guess" about the presence of the keyword a killer
- program is ill-formed.

I might be wrong in my toughts, and I am not sure whether, or not,
`typename` keyword usage changes for non-type template parameters.
Also, inline definition does not require the keyword.

It would be nice if someone could give authoritative answer
(preferably stepping through standard) whether:
1. clang is wrong.
2. gcc is wrong.
3. standard is unclear.

Regards,
Łukasz.
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: `typename` keyword before non-type template parameter defined via nested templated `using` in out-of-line member definition

Jon Chesterfield via cfe-dev
Richard,

Perhaps I should clarify possible foul formatting of the code example.
The indicator for the dubious `typename` keyword was mean to land
before `B<T>::template nested_templated_using<U>*` in the out-of-line
definition. On a fixed font it did, in your reply I see a possibly
quite dramatic misalignment. Hopefully it can be seen on the properly
formatted code (e.g. in the acutal godbolt example.) Sincere
apologies.

So the original question was "whether the code without the
typename/template keywords is valid too."

Luckily you did pick up on it.

As I understand, your answer would be that gcc is wrong to reject the
code with the keyword present, and whether the code with out it should
be acceptable is not obvious.

But assuming that the template-parameter-list is "in the definition",
then shouldn't the superfluous `typename` make the code ill formed
based on [temp.res] $5:

> A qualified name used as the name in a class-or-decltype or an elaborated-type-specifier is implicitly assumed to name a type, without the use of the typename keyword.
> In a nested-name-specifier that immediately contains a nested-name-specifier that depends on a template parameter, the identifier or simple-template-id is implicitly assumed to name a type, without the use of the typename keyword.
> [ Note: The typename keyword is not permitted by the syntax of these constructs.]

I agree that CWG#2 is aimed at permitting such cases, but this note
sounds pretty strict. As I understand it, this puts presence or lack
of the keyword as mutually exclusive options for program to be
well-formed.

Or am I misunderstanding this, and this note does not apply to our case here?

Regards,
Łukasz.


On Tue, Feb 6, 2018 at 1:20 AM, Richard Smith <[hidden email]> wrote:

> On 4 February 2018 at 18:40, Łukasz Kucharski via cfe-dev
> <[hidden email]> wrote:
>>
>> I hope this is a good audience.
>>
>> Title seems long an convoluted, but the problem boiled down to gcc and
>> clang disagreeing on such code:
>>
>> (This is a grokked version of code which we got during a real world
>> code-base evolution [if that means anything])
>> ```
>> #include <type_traits>
>> #include <iostream>
>> #include <string>
>>
>> template <typename T>
>> struct B {
>>     template <typename U>
>>     using nested_templated_using = U;
>>
>>     template<typename U = T, nested_templated_using<U>* = nullptr >
>>     void f();
>> };
>>
>> template <typename T>
>> template <typename U, typename B<T>::template nested_templated_using<U>* >
>> //                   ^ should typename be here or not?
>> void  B<T>::f(){ }
>>
>> int main () { return 0;}
>> ```
>> [godbolt example](https://godbolt.org/g/yjPH1u)
>>
>>  clang requires `typename` keyword in the indicated spot, while gcc
>> forbids it. They error out with the same message indicating that the
>> out-of-line definition does not match declaration.
>
>
> Strictly-speaking, neither is formally correct. For a type involving
> template parameters, the two must be spelled with the same sequence of
> tokens (though implementations are allowed to accept additional cases); see
> [temp.over.link]. So the right thing is:
>
> template <typename T>
> struct B {
>     template <typename U>
>     using nested_templated_using = U;
>
>     template<typename U = T, typename B<T>::template
> nested_templated_using<U>* = nullptr >
>     void f();
> };
>
> template <typename T>
> template <typename U, typename B<T>::template nested_templated_using<U>* >
> void  B<T>::f(){ }
>
> ... which Clang and MSVC accept and GCC rejects. (ICC/EDG seems to reject
> every variation on this.) However, the intent is to permit additional cases
> where the equivalence of the two declarations can be determined using
> information from the template itself (see CWG issue 2, wg21.link/cwg2),
> which also permits your original example.
>
> So we can at least conclude that GCC is wrong to reject your original
> example. Presumably within the class definition it canonicalizes the type of
> the template parameter to merely "U *", and is unable to match that up
> against the "typename B<T>::template nested_templated_using<U> *" within the
> out-of-class definition of f.
>
> But that leaves open the question of whether the code without the
> typename/template keywords is valid too. See below!
>
>> I've asked this question on
>> [stackoverflow](https://stackoverflow.com/questions/48448353) but it
>> wasn't clear yet what is the core issue, I thought gcc misbehaves.
>>
>> I got some great input by Johannes Schaub, who pointed [CWG issue
>> #2](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2) and
>> [CWG issue
>> #560](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#560).
>> They both treat about referring to the dependent types, though as the
>> type of return value.
>>
>> My thoughts are:
>> 1. The type of the parameter is dependent.
>> 2. I am not sure whether the type of the parameter is "a member of the
>> current instantiation" as understood in
>> [temp.res#3](http://eel.is/c++draft/temp.res#3)
>
>
> This is the right question. And, sadly, the standard is unclear on this
> question; [temp.dep.type] says this depends on whether the name appears "in
> the definition of [...] a member of a class template", but is the
> template-parameter-list "in the definition" or not? The answer is by no
> means obvious.
>
>> 3. In conjunction with [temp.res#5](http://eel.is/c++draft/temp.res#5)
>> above renders "wrong guess" about the presence of the keyword a killer
>> - program is ill-formed.
>>
>> I might be wrong in my toughts, and I am not sure whether, or not,
>> `typename` keyword usage changes for non-type template parameters.
>> Also, inline definition does not require the keyword.
>>
>> It would be nice if someone could give authoritative answer
>> (preferably stepping through standard) whether:
>> 1. clang is wrong.
>> 2. gcc is wrong.
>> 3. standard is unclear.
>>
>> Regards,
>> Łukasz.
>> _______________________________________________
>> cfe-dev mailing list
>> [hidden email]
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>
>
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: `typename` keyword before non-type template parameter defined via nested templated `using` in out-of-line member definition

Jon Chesterfield via cfe-dev
On 5 February 2018 at 17:41, Łukasz Kucharski via cfe-dev <[hidden email]> wrote:
Richard,

Perhaps I should clarify possible foul formatting of the code example.
The indicator for the dubious `typename` keyword was mean to land
before `B<T>::template nested_templated_using<U>*` in the out-of-line
definition. On a fixed font it did, in your reply I see a possibly
quite dramatic misalignment. Hopefully it can be seen on the properly
formatted code (e.g. in the acutal godbolt example.) Sincere
apologies.

So the original question was "whether the code without the
typename/template keywords is valid too."

Luckily you did pick up on it.

As I understand, your answer would be that gcc is wrong to reject the
code with the keyword present, and whether the code with out it should
be acceptable is not obvious.

But assuming that the template-parameter-list is "in the definition",
then shouldn't the superfluous `typename` make the code ill formed
based on [temp.res] $5:

> A qualified name used as the name in a class-or-decltype or an elaborated-type-specifier is implicitly assumed to name a type, without the use of the typename keyword.
> In a nested-name-specifier that immediately contains a nested-name-specifier that depends on a template parameter, the identifier or simple-template-id is implicitly assumed to name a type, without the use of the typename keyword.
> [ Note: The typename keyword is not permitted by the syntax of these constructs.]

No. This is talking about the second typename in:

  typename A<X>::typename B::C

... and it says that B would be implicitly assumed to name a type in this context without the use of the typename keyword, but that the above snippet is ill-formed because 'typename' cannot appear after a :: here.

C++ allows you to redundantly use the 'typename' (and 'template') disambiguators in any place in the grammar where they're allowed.

I agree that CWG#2 is aimed at permitting such cases, but this note
sounds pretty strict. As I understand it, this puts presence or lack
of the keyword as mutually exclusive options for program to be
well-formed.

Or am I misunderstanding this, and this note does not apply to our case here?

Regards,
Łukasz.


On Tue, Feb 6, 2018 at 1:20 AM, Richard Smith <[hidden email]> wrote:
> On 4 February 2018 at 18:40, Łukasz Kucharski via cfe-dev
> <[hidden email]> wrote:
>>
>> I hope this is a good audience.
>>
>> Title seems long an convoluted, but the problem boiled down to gcc and
>> clang disagreeing on such code:
>>
>> (This is a grokked version of code which we got during a real world
>> code-base evolution [if that means anything])
>> ```
>> #include <type_traits>
>> #include <iostream>
>> #include <string>
>>
>> template <typename T>
>> struct B {
>>     template <typename U>
>>     using nested_templated_using = U;
>>
>>     template<typename U = T, nested_templated_using<U>* = nullptr >
>>     void f();
>> };
>>
>> template <typename T>
>> template <typename U, typename B<T>::template nested_templated_using<U>* >
>> //                   ^ should typename be here or not?
>> void  B<T>::f(){ }
>>
>> int main () { return 0;}
>> ```
>> [godbolt example](https://godbolt.org/g/yjPH1u)
>>
>>  clang requires `typename` keyword in the indicated spot, while gcc
>> forbids it. They error out with the same message indicating that the
>> out-of-line definition does not match declaration.
>
>
> Strictly-speaking, neither is formally correct. For a type involving
> template parameters, the two must be spelled with the same sequence of
> tokens (though implementations are allowed to accept additional cases); see
> [temp.over.link]. So the right thing is:
>
> template <typename T>
> struct B {
>     template <typename U>
>     using nested_templated_using = U;
>
>     template<typename U = T, typename B<T>::template
> nested_templated_using<U>* = nullptr >
>     void f();
> };
>
> template <typename T>
> template <typename U, typename B<T>::template nested_templated_using<U>* >
> void  B<T>::f(){ }
>
> ... which Clang and MSVC accept and GCC rejects. (ICC/EDG seems to reject
> every variation on this.) However, the intent is to permit additional cases
> where the equivalence of the two declarations can be determined using
> information from the template itself (see CWG issue 2, wg21.link/cwg2),
> which also permits your original example.
>
> So we can at least conclude that GCC is wrong to reject your original
> example. Presumably within the class definition it canonicalizes the type of
> the template parameter to merely "U *", and is unable to match that up
> against the "typename B<T>::template nested_templated_using<U> *" within the
> out-of-class definition of f.
>
> But that leaves open the question of whether the code without the
> typename/template keywords is valid too. See below!
>
>> I've asked this question on
>> [stackoverflow](https://stackoverflow.com/questions/48448353) but it
>> wasn't clear yet what is the core issue, I thought gcc misbehaves.
>>
>> I got some great input by Johannes Schaub, who pointed [CWG issue
>> #2](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2) and
>> [CWG issue
>> #560](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#560).
>> They both treat about referring to the dependent types, though as the
>> type of return value.
>>
>> My thoughts are:
>> 1. The type of the parameter is dependent.
>> 2. I am not sure whether the type of the parameter is "a member of the
>> current instantiation" as understood in
>> [temp.res#3](http://eel.is/c++draft/temp.res#3)
>
>
> This is the right question. And, sadly, the standard is unclear on this
> question; [temp.dep.type] says this depends on whether the name appears "in
> the definition of [...] a member of a class template", but is the
> template-parameter-list "in the definition" or not? The answer is by no
> means obvious.
>
>> 3. In conjunction with [temp.res#5](http://eel.is/c++draft/temp.res#5)
>> above renders "wrong guess" about the presence of the keyword a killer
>> - program is ill-formed.
>>
>> I might be wrong in my toughts, and I am not sure whether, or not,
>> `typename` keyword usage changes for non-type template parameters.
>> Also, inline definition does not require the keyword.
>>
>> It would be nice if someone could give authoritative answer
>> (preferably stepping through standard) whether:
>> 1. clang is wrong.
>> 2. gcc is wrong.
>> 3. standard is unclear.
>>
>> Regards,
>> Łukasz.
>> _______________________________________________
>> cfe-dev mailing list
>> [hidden email]
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>
>
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev