Why can templated aliases of anonymous struct/class-es not be defined directly?
Asked Answered
N

1

6

I can create the following:

using Foo = struct { /*Implementation*/ };

template<class>
using Bar = Foo;

However the following is not allowed:

template<class>
using Bar = struct { /*Implementation*/ };

The error from Clang is more helpful than GCC, and states:

error: '(anonymous struct at file:line:column)' cannot be defined in a type alias template


Any reasons why the second code example is not allowed?

Note:

  • Please state any examples for which the second code example (if allowed) may cause problems with the language.

  • Any citation from the standard is also helpful.

Nonstop answered 27/4, 2019 at 7:14 Comment(23)
I had no idea you could do the first snippet.Gilt
and why would you want to do something like that? Why not struct Foo? Where and why do you need it?Gilt
@Gilt It's meant to contain a static_assert(false) with a message for the base template definition. Specializations of the same would provide the required functionality.Nonstop
And I ask again: why not struct Bar? Why do you want anonymous classes? Also please note that a static assert on the base template definition that is false for every imaginary instantiation is UB.Gilt
@Gilt I could use a struct instead of a using, just curious why this is not allowed. :)Nonstop
@AnirbanSarkar Creating an alias to an anonymous struct, instead of just giving the struct a name in the first place? I fail to see a point. The best answer to why it's not allowed is probably that no one cares to implement it, since it's not useful.Kerplunk
@Kerplunk The way I see this is as an inconsistency in the language, since non-templated usings get away just fine. Also this saves a few characters.Nonstop
@Gilt I could leave the base definition empty, the static_assert is just trying to be helpful stating that the specializations should be used instead of the base template definition. If that leads to UB, then I can remove it, and add a comment stating the same.Nonstop
@AnirbanSarkar using Bar = struct vs struct Bar does not save any characters. It is a bit inconsistent, but I would not spend my time fixing something that's not broken.Kerplunk
@Kerplunk It does save characters, you don't need to create an additional using. Also a better argument than it's not helpful would be helpful. Also I don't want to give it a name since it's an implementation detail. :)Nonstop
@AnirbanSarkar You're missing the point. If you just give it a name in the first place you don't need a using.Kerplunk
I guess the root reason is that you can write using Bar1 = struct Foo1 { /*Implementation*/ }; but not template<class> using Bar2 = struct Foo2 { /*Implementation*/ }; as you get error: types may not be defined in alias template declarations, here is the inconsistency IMHOFebrific
on the other hand, using on a template type is not defining a type by itself, but another templated thing, thus even if using seems to have the same syntax, it is eventually not creating the same kind of things.Febrific
using Bar = struct { /*Implementation*/ }; vs struct Bar { /*Implementation*/ } you don't save any characters. And even if you would, you write code once, you read it multiple times, a few extra characters to save are nowhere nearly as important as having clean code. Also, on the inconsistency front: nobody cares, because nobody uses using Foo = struct { /* ... */ }.Gilt
@AnirbanSarkar When you declare a using you are giving it a name. It's not any different from giving it a name in the first place. Hence my confusion about your motivation. If something is to be included in the c++ standard, someone needs to write a proposal for it, implement it etc. I think the fact that something is not useful leads to the fact that no-one will go through the trouble of doing that work is a fairly reasonable explanation.Kerplunk
eel.is/c++draft/dcl.typedef#2.sentence-5 but I don't know why this restriction existsGonophore
I am not asking for a proposal to fix this since it's not a major issue. I'm asking why the restriction as linked by @Gonophore exists.Nonstop
I added a language-lawyer tag. Everything but references to the standard must be considered speculation.Alkahest
@Alkahest I agree and thanks for the edit.Nonstop
wg21.link/cwg686 wg21.link/cwg1159Diabolo
Afaics, the closest we get to a rationale for the prohibiting clause in [dcl.typedef]/2 is in the introducing N3092 comment US 74, in the lines of "what could ever be the use of this?"; specifically "... it's not clear that this is desirable when the alias-declaration is part of a template alias" and "Either prohibit the definition of classes and enumerations in template aliases, or prohibit the use of template parameters in such definitions, or add an example illustrating this usage."Quad
... It seems as if no one protested to prohibiting the use of template parameters in such definitions, implying that it's likely that no one was able to give an example illustrating where this usage is useful.Quad
@dfri Please post that as an answer. I'll accept it.Nonstop
Q
7

Defining a class or an enumeration in an alias-declaration that is part of a template alias is forbidden by [dcl.typedef]/2:

A typedef-name can also be introduced by an alias-declaration.

...

The defining-type-specifier-seq of the defining-type-id shall not define a class or enumeration if the alias-declaration is the declaration of a template-declaration.

The latter was introduced as CWG issue 1159 was accepted, as part of FCD N3092.

The comments and proposed resolution of the associated N3092 comment US 74 does provide some rationale as to why this restriction was introduced [emphasis mine]:

Comment (ID) US 74

Comment

An alias-declaration allows a class or enumeration type to be defined in its type-id (7.1.6p3). However, it's not clear that this is desirable when the alias-declaration is part of a template alias:

template<typename T> using A =
struct { void f(T) { } };

Proposed resolution

Either prohibit the definition of classes and enumerations in template aliases, or prohibit the use of template parameters in such definitions, or add an example illustrating this usage.

Owner & issue

CWG 1159

Disposition

ACCEPTED

Definition of a class or enumeration is now prohibited in a template alias.

It would seem as if no one protested (convincingly enough) to prohibiting the definition of classes and enumerations in template aliases, implying that it's likely that no one was able to give a convincing example illustrating where this would be useful.

Quad answered 27/4, 2019 at 10:36 Comment(1)
A template alias cannot be specialized, so a class defined in an alias would be un-specializable. This would let you define a template functor that cannot be overloaded (which may make templates much more secure, since it prevents malicious overload injection by dependencies).Trap

© 2022 - 2024 — McMap. All rights reserved.