7 Declarations [dcl.dcl]

7.6 Attributes [dcl.attr]

7.6.1 Attribute syntax and semantics [dcl.attr.grammar]

If an attribute-specifier contains an attribute-using-prefix, the attribute-list following that attribute-using-prefix shall not contain an attribute-scoped-token and every attribute-token in that attribute-list is treated as if its identifier were prefixed with N::, where N is the attribute-namespace specified in the attribute-using-prefix. [ Note: This rule imposes no constraints on how an attribute-using-prefix affects the tokens in an attribute-argument-clause.  — end note ] [ Example:

[[using CC: opt(1), debug]]        // same as [[CC::opt(1), CC::debug]]
  void f() {}
[[using CC: opt(1)]] [[CC::debug]] // same as [[CC::opt(1)]] [[CC::debug]]
  void g() {}
[[using CC: CC::opt(1)]]           // error: cannot combine using and scoped attribute token
  void h() {}

 — end example ]

Note: For each individual attribute, the form of the balanced-token-seq will be specified.  — end note ]

In an attribute-list, an ellipsis may appear only if that attribute's specification permits it. An attribute followed by an ellipsis is a pack expansion ([temp.variadic]). An attribute-specifier that contains no attributes has no effect. The order in which the attribute-tokens appear in an attribute-list is not significant. If a keyword ([lex.key]) or an alternative token ([lex.digraph]) that satisfies the syntactic requirements of an identifier ([lex.name]) is contained in an attribute-token, it is considered an identifier. No name lookup ([basic.lookup]) is performed on any of the identifiers contained in an attribute-token. The attribute-token determines additional requirements on the attribute-argument-clause (if any).

Each attribute-specifier-seq is said to appertain to some entity or statement, identified by the syntactic context where it appears (Clause [stmt.stmt], Clause [dcl.dcl], Clause [dcl.decl]). If an attribute-specifier-seq that appertains to some entity or statement contains an attribute that is not allowed to apply to that entity or statement, the program is ill-formed. If an attribute-specifier-seq appertains to a friend declaration ([class.friend]), that declaration shall be a definition. No attribute-specifier-seq shall appertain to an explicit instantiation ([temp.explicit]).

For an attribute-token (including an attribute-scoped-token) not specified in this International Standard, the behavior is implementation-defined. Any attribute-token that is not recognized by the implementation is ignored. [ Note: Each implementation should choose a distinctive name for the attribute-namespace in an attribute-scoped-token.  — end note ]

Two consecutive left square bracket tokens shall appear only when introducing an attribute-specifier or within the balanced-token-seq of an attribute-argument-clause. [ Note: If two consecutive left square brackets appear where an attribute-specifier is not allowed, the program is ill-formed even if the brackets match an alternative grammar production.  — end note ] [ Example:

int p[10];
void f() {
  int x = 42, y[5];
  int(p[[x] { return x; }()]);  // error: invalid attribute on a nested
                                // declarator-id and not a function-style cast of
                                // an element of p.
  y[[] { return 2; }()] = 2;    // error even though attributes are not allowed
                                // in this context.
  int i [[vendor::attr([[]])]]; // well-formed implementation-defined attribute.
}

 — end example ]

7.6.2 Alignment specifier [dcl.align]

An alignment-specifier may be applied to a variable or to a class data member, but it shall not be applied to a bit-field, a function parameter, or an exception-declaration ([except.handle]). An alignment-specifier may also be applied to the declaration or definition of a class (in an elaborated-type-specifier ([dcl.type.elab]) or class-head (Clause [class]), respectively) and to the declaration or definition of an enumeration (in an opaque-enum-declaration or enum-head, respectively ([dcl.enum])). An alignment-specifier with an ellipsis is a pack expansion ([temp.variadic]).

When the alignment-specifier is of the form alignas( constant-expression ):

  • the constant-expression shall be an integral constant expression

  • if the constant expression does not evaluate to an alignment value ([basic.align]), or evaluates to an extended alignment and the implementation does not support that alignment in the context of the declaration, the program is ill-formed.

An alignment-specifier of the form alignas( type-id ) has the same effect as alignas(alignof( type-id )) ([expr.alignof]).

The alignment requirement of an entity is the strictest nonzero alignment specified by its alignment-specifiers, if any; otherwise, the alignment-specifiers have no effect.

The combined effect of all alignment-specifiers in a declaration shall not specify an alignment that is less strict than the alignment that would be required for the entity being declared if all alignment-specifiers appertaining to that entity were omitted. [ Example:

struct alignas(8) S {};
struct alignas(1) U {
  S s;
};   // Error: U specifies an alignment that is less strict than
     // if the alignas(1) were omitted.

 — end example ]

If the defining declaration of an entity has an alignment-specifier, any non-defining declaration of that entity shall either specify equivalent alignment or have no alignment-specifier. Conversely, if any declaration of an entity has an alignment-specifier, every defining declaration of that entity shall specify an equivalent alignment. No diagnostic is required if declarations of an entity have different alignment-specifiers in different translation units.

Example:

// Translation unit #1:
struct S { int x; } s, *p = &s;

// Translation unit #2:
struct alignas(16) S;           // error: definition of S lacks alignment; no
extern S* p;                    // diagnostic required

 — end example ]

Example: An aligned buffer with an alignment requirement of A and holding N elements of type T can be declared as:

alignas(T) alignas(A) T buffer[N];

Specifying alignas(T) ensures that the final requested alignment will not be weaker than alignof(T), and therefore the program will not be ill-formed.  — end example ]

Example:

alignas(double) void f();                         // error: alignment applied to function
alignas(double) unsigned char c[sizeof(double)];  // array of characters, suitably aligned for a double
extern unsigned char c[sizeof(double)];           // no alignas necessary
alignas(float)
  extern unsigned char c[sizeof(double)];         // error: different alignment in declaration

 — end example ]

7.6.3 Carries dependency attribute [dcl.attr.depend]

The attribute-token carries_dependency specifies dependency propagation into and out of functions. It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present. The attribute may be applied to the declarator-id of a parameter-declaration in a function declaration or lambda, in which case it specifies that the initialization of the parameter carries a dependency to ([intro.multithread]) each lvalue-to-rvalue conversion ([conv.lval]) of that object. The attribute may also be applied to the declarator-id of a function declaration, in which case it specifies that the return value, if any, carries a dependency to the evaluation of the function call expression.

The first declaration of a function shall specify the carries_dependency attribute for its declarator-id if any declaration of the function specifies the carries_dependency attribute. Furthermore, the first declaration of a function shall specify the carries_dependency attribute for a parameter if any declaration of that function specifies the carries_dependency attribute for that parameter. If a function or one of its parameters is declared with the carries_dependency attribute in its first declaration in one translation unit and the same function or one of its parameters is declared without the carries_dependency attribute in its first declaration in another translation unit, the program is ill-formed; no diagnostic required.

Note: The carries_dependency attribute does not change the meaning of the program, but may result in generation of more efficient code.  — end note ]

Example:

/* Translation unit A. */

struct foo { int* a; int* b; };
std::atomic<struct foo *> foo_head[10];
int foo_array[10][10];

[[carries_dependency]] struct foo* f(int i) {
  return foo_head[i].load(memory_order_consume);
}

int g(int* x, int* y [[carries_dependency]]) {
  return kill_dependency(foo_array[*x][*y]);
}

/* Translation unit B. */

[[carries_dependency]] struct foo* f(int i);
int g(int* x, int* y [[carries_dependency]]);

int c = 3;

void h(int i) {
  struct foo* p;

  p = f(i);
  do_something_with(g(&c, p->a));
  do_something_with(g(p->a, &c));
}

The carries_dependency attribute on function f means that the return value carries a dependency out of f, so that the implementation need not constrain ordering upon return from f. Implementations of f and its caller may choose to preserve dependencies instead of emitting hardware memory ordering instructions (a.k.a. fences).

Function g's second parameter has a carries_dependency attribute, but its first parameter does not. Therefore, function h's first call to g carries a dependency into g, but its second call does not. The implementation might need to insert a fence prior to the second call to g.

 — end example ]

7.6.4 Deprecated attribute [dcl.attr.deprecated]

The attribute-token deprecated can be used to mark names and entities whose use is still allowed, but is discouraged for some reason. [ Note: in particular, deprecated is appropriate for names and entities that are deemed obsolescent or unsafe.  — end note ] It shall appear at most once in each attribute-list. An attribute-argument-clause may be present and, if present, it shall have the form:

( string-literal )

Note: the string-literal in the attribute-argument-clause could be used to explain the rationale for deprecation and/or to suggest a replacing entity.  — end note ]

The attribute may be applied to the declaration of a class, a typedef-name, a variable, a non-static data member, a function, a namespace, an enumeration, an enumerator, or a template specialization.

A name or entity declared without the deprecated attribute can later be re-declared with the attribute and vice-versa. [ Note: Thus, an entity initially declared without the attribute can be marked as deprecated by a subsequent redeclaration. However, after an entity is marked as deprecated, later redeclarations do not un-deprecate the entity.  — end note ] Redeclarations using different forms of the attribute (with or without the attribute-argument-clause or with different attribute-argument-clauses) are allowed.

Note: Implementations may use the deprecated attribute to produce a diagnostic message in case the program refers to a name or entity other than to declare it, after a declaration that specifies the attribute. The diagnostic message may include the text provided within the attribute-argument-clause of any deprecated attribute applied to the name or entity.  — end note ]

7.6.5 Fallthrough attribute [dcl.attr.fallthrough]

The attribute-token fallthrough may be applied to a null statement ([stmt.expr]); such a statement is a fallthrough statement. The attribute-token fallthrough shall appear at most once in each attribute-list and no attribute-argument-clause shall be present. A fallthrough statement may only appear within an enclosing switch statement ([stmt.switch]). The next statement that would be executed after a fallthrough statement shall be a labeled statement whose label is a case label or default label for the same switch statement. The program is ill-formed if there is no such statement.

Note: The use of a fallthrough statement is intended to suppress a warning that an implementation might otherwise issue for a case or default label that is reachable from another case or default label along some path of execution. Implementations are encouraged to issue a warning if a fallthrough statement is not dynamically reachable.  — end note ]

Example:

void f(int n) {
  void g(), h(), i();
  switch (n) {
  case 1:
  case 2:
    g();
    [[fallthrough]];
  case 3: // warning on fallthrough discouraged
    h();
  case 4: // implementation may warn on fallthrough
    i();
    [[fallthrough]]; // ill-formed
  }
}

 — end example ]

7.6.6 Maybe unused attribute [dcl.attr.unused]

The attribute-token maybe_unused indicates that a name or entity is possibly intentionally unused. It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present.

The attribute may be applied to the declaration of a class, a typedef-name, a variable, a non-static data member, a function, an enumeration, or an enumerator.

Note: For an entity marked maybe_unused, implementations are encouraged not to emit a warning that the entity is unused, or that the entity is used despite the presence of the attribute.  — end note ]

A name or entity declared without the maybe_unused attribute can later be redeclared with the attribute and vice versa. An entity is considered marked after the first declaration that marks it.

Example:

[[maybe_unused]] void f([[maybe_unused]] bool thing1,
                        [[maybe_unused]] bool thing2) {
  [[maybe_unused]] bool b = thing1 && thing2;
  assert(b);
}

Implementations are encouraged not to warn that b is unused, whether or not NDEBUG is defined.  — end example ]

7.6.7 Nodiscard attribute [dcl.attr.nodiscard]

The attribute-token nodiscard may be applied to the declarator-id in a function declaration or to the declaration of a class or enumeration. It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present.

Note: A nodiscard call is a function call expression that calls a function previously declared nodiscard, or whose return type is a possibly cv-qualified class or enumeration type marked nodiscard. Appearance of a nodiscard call as a potentially-evaluated discarded-value expression (Clause [expr]) is discouraged unless explicitly cast to void. Implementations are encouraged to issue a warning in such cases. This is typically because discarding the return value of a nodiscard call has surprising consequences.  — end note ]

Example:

struct [[nodiscard]] error_info { /*...*/ };
error_info enable_missile_safety_mode();
void launch_missiles();
void test_missiles() {
  enable_missile_safety_mode(); // warning encouraged
  launch_missiles();
}
error_info &foo();
void f() { foo(); } // reference type, warning not encouraged

 — end example ]

7.6.8 Noreturn attribute [dcl.attr.noreturn]

The attribute-token noreturn specifies that a function does not return. It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present. The attribute may be applied to the declarator-id in a function declaration. The first declaration of a function shall specify the noreturn attribute if any declaration of that function specifies the noreturn attribute. If a function is declared with the noreturn attribute in one translation unit and the same function is declared without the noreturn attribute in another translation unit, the program is ill-formed; no diagnostic required.

If a function f is called where f was previously declared with the noreturn attribute and f eventually returns, the behavior is undefined. [ Note: The function may terminate by throwing an exception.  — end note ] [ Note: Implementations are encouraged to issue a warning if a function marked [[noreturn]] might return.  — end note ]

Example:

[[ noreturn ]] void f() {
  throw "error";        // OK
}

[[ noreturn ]] void q(int i) { // behavior is undefined if called with an argument <= 0
  if (i > 0)
    throw "positive";
}

 — end example ]