What is the reason for the odd syntax of [[maybe_unused]] on type aliases?

Since C++17, there is now a standardized way to politely suggest the compiler to stop yelling aboud unused entities. When applying this to variables, functions, and old-skool type-aliases, we have to use the expected syntax which comes in the form:

[[maybe_unused]] int x;
[[maybe_unused]] void f();
[[maybe_unused]] typedef void t;

Nothing strange here. However, when applying this attribute-specifier to structs, enums, and especially modern-day type-aliases, we have to resort to all kinds of gobbledygook to make it work:

struct [[maybe_unused]] s;
union [[maybe_unused]] u;
enum class [[maybe_unused]] e;
using a [[maybe_unused]] = void; // what's up with this?

What is the reason for the odd placement of the [[maybe_unused]] attribute for type-aliases? Could it really not be any other way?

Demo

  • I’m not sure offhand about type aliases, but I can guess the others are because putting the attribute in front is already taken by the potential variable declaration (e.g., [[maybe_unused]] struct s {} obj; would affect obj).

    – 

Because it’s like a spiral: inner attributes are applied to inner names/types, outer attributes are applied to outer names.

   [[maybe_unused]] struct [[maybe_unused]] s {} s_;
//                         ^^^^^^^^^^^^^^^^-^
//                         applied to the type
//
// ^^^^^^^^^^^^^^^^------------------------------^^
// applied to the variable       
[[maybe_unused]] struct s1;     // invalid, there is no variable name
[[maybe_unused]] struct {} s2;  // valid, there is a variable name

As for the alias-declaration:
    using identifier [attribute-specifier-seq] = defining-type-id ;
no clue how to justify that.

The ability to place an attribute-specifier-seq in an alias declaration was added by the resolution to CWG1042. Unfortunately, when I looked through the CWG minutes, I couldn’t find any recorded discussion about the placement of the attribute-specifier-seq. But it’s pretty easy to come up with a guess as to why it goes after the identifier: to mirror the syntax of a typedef declaration:

typedef void a [[maybe_unused]];

A typedef declaration is a type of simple-declaration, that is, it has the same grammatical structure as the declaration of one or more variables or functions. In simple-declarations, attributes follow the name of the entity declared:

int f [[deprecated]] (), x [[maybe_unused]];

Note that f is a deprecated function taking no arguments and returning int, and x is a possibly intentionally unused variable of type int.

You can also put an attribute at the beginning of a simple-declaration, but in that case it appertains to all entities declared in the declaration, whereas putting the attribute after the declarator-id makes it appertain to only a single entity.

Since an alias-declaration only declares one entity at a time, the hypothetical syntax

[[maybe_unused]] using a = void;

would have been reasonable too, and would have the same meaning; perhaps CWG simply did not wish to provide two ways of writing the same thing.

Leave a Comment