9 Declarations [dcl.dcl]

9.11 Attributes [dcl.attr]

9.11.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.
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 or an alternative token that satisfies the syntactic requirements of an identifier is contained in an attribute-token, it is considered an identifier.
No name 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 ([stmt.stmt], [dcl.dcl], [dcl.decl]).
If an attribute-specifier-seq that appertains to some entity or statement contains an attribute or alignment-specifier 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, that declaration shall be a definition.
For an attribute-token (including an attribute-scoped-token) not specified in this document, 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
]

9.11.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 or class-head, respectively) and to the declaration or definition of an enumeration (in an opaque-enum-declaration or enum-head, respectively).
An alignment-specifier with an ellipsis is a pack expansion.
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 )).
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 diagnostic required
extern S* p;
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
]

9.11.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 each lvalue-to-rvalue conversion 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
]

9.11.4 Contract attributes [dcl.attr.contract]

9.11.4.1 Syntax [dcl.attr.contract.syn]

Contract attributes are used to specify preconditions, postconditions, and assertions for functions.
contract-attribute-specifier:
	[ [ expects contract-level : conditional-expression ] ]
	[ [ ensures contract-level identifier : conditional-expression ] ]
	[ [ assert contract-level : conditional-expression ] ]
contract-level:
	default
	audit
	axiom
An ambiguity between a contract-level and an identifier is resolved in favor of contract-level.
It expresses a function's expectation on its arguments and/or the state of other objects using a predicate that is intended to hold upon entry into the function.
The attribute may be applied to the function type of a function declaration.
It expresses a condition that a function should ensure for the return value and/or the state of objects using a predicate that is intended to hold upon exit from the function.
The attribute may be applied to the function type of a function declaration.
A postcondition may introduce an identifier to represent the glvalue result or the prvalue result object of the function.
[Example
:
int f(char * c)
  [[ensures res: res > 0 && c != nullptr]];

int g(double * p)
  [[ensures audit res: res != 0 && p != nullptr && *p <= 0.0]];
end example
]
It expresses a condition that is intended to be satisfied where it appears in a function body.
The attribute may be applied to a null statement ([stmt.expr]).
An assertion is checked by evaluating its predicate as part of the evaluation of the null statement it applies to.
Preconditions, postconditions, and assertions are collectively called contracts.
The conditional-expression in a contract is contextually converted to bool ([conv]); the converted expression is called the predicate of the contract.
[Note
:
The predicate of a contract is potentially evaluated ([basic.def.odr]).
end note
]
The only side effects of a predicate that are allowed in a contract-attribute-specifier are modifications of non-volatile objects whose lifetime began and ended within the evaluation of the predicate.
An evaluation of a predicate that exits via an exception invokes the function std::terminate ([except.terminate]).
The behavior of any other side effect is undefined.
[Example
:
void push(int x, queue & q)
  [[expects: !q.full()]]
  [[ensures: !q.empty()]]
{
  /* ... */
  [[assert: q.is_valid()]];
  /* ... */
}

int min = -42;
constexpr int max = 42;

constexpr int g(int x)
  [[expects: min <= x]]                         // error
  [[expects: x < max]]                          // OK
{
  /* ... */
  [[assert: 2*x < max]];
  [[assert: ++min > 0]];                        // undefined behavior
  /* ... */
}
end example
]

9.11.4.2 Contract conditions [dcl.attr.contract.cond]

A contract condition is a precondition or a postcondition.
The first declaration of a function shall specify all contract conditions (if any) of the function.
Subsequent declarations shall either specify no contract conditions or the same list of contract conditions; no diagnostic is required if corresponding conditions will always evaluate to the same value.
The list of contract conditions of a function shall be the same if the declarations of that function appear in different translation units; no diagnostic required.
If a friend declaration is the first declaration of the function in a translation unit and has a contract condition, the declaration shall be a definition and shall be the only declaration of the function in the translation unit.
Two lists of contract conditions are the same if they consist of the same contract conditions in the same order.
Two contract conditions are the same if their contract levels are the same and their predicates are the same.
Two predicates contained in contract-attribute-specifiers are the same if they would satisfy the one-definition rule ([basic.def.odr]) were they to appear in function definitions, except for renaming of parameters, return value identifiers (if any), and template parameters.
[Note
:
A function pointer cannot include contract conditions.
[Example
:
typedef int (*fpt)() [[ensures r: r != 0]];     // error: contract condition not on a function declaration

int g(int x)
  [[expects: x >= 0]]
  [[ensures r: r > x]]
{
  return x+1;
}

int (*pf)(int) = g;                             // OK
int x = pf(5);                                  // contract conditions of g are checked
end example
]
end note
]
The predicate of a contract condition has the same semantic restrictions as if it appeared as the first expression-statement in the body of the function it applies to.
Additional access restrictions apply to names appearing in a contract condition of a member function of class C:
  • Friendship is not considered.
  • For a contract condition of a public member function, no member of C or of an enclosing class of C is accessible unless it is a public member of C, or a member of a base class accessible as a public member of C ([class.access.base]).
  • For a contract condition of a protected member function, no member of C or of an enclosing class of C is accessible unless it is a public or protected member of C, or a member of a base class accessible as a public or protected member of C.
For names appearing in a contract condition of a non-member function, friendship is not considered.
[Example
:
class X {
public:
  int v() const;
  void f() [[expects: x > 0]];                  // error: x is private
  void g() [[expects: v() > 0]];                // OK
  friend void r(int z) [[expects: z > 0]];      // OK
  friend void s(int z) [[expects: z > x]];      // error: x is private
protected:
  int w();
  void h() [[expects: x > 0]];                  // error: x is private
  void i() [[ensures: y > 0]];                  // OK
  void j() [[ensures: w() > 0]];                // OK
  int y;
private:
  void k() [[expects: x > 0]];                  // OK
  int x;
};

class Y : public X {
public:
  void a() [[expects: v() > 0]];                // OK
  void b() [[ensures: w() > 0]];                // error: w is protected
protected:
  void c() [[expects: w() > 0]];                // OK
};
end example
]
A precondition is checked by evaluating its predicate immediately before starting evaluation of the function body.
[Note
:
The function body includes the function-try-block and the ctor-initializer.
end note
]
A postcondition is checked by evaluating its predicate immediately before returning control to the caller of the function.
[Note
:
The lifetime of local variables and temporaries has ended.
Exiting via an exception or via longjmp ([csetjmp.syn]) is not considered returning control to the caller of the function.
end note
]
If a function has multiple preconditions, their evaluation (if any) will be performed in the order they appear lexically.
If a function has multiple postconditions, their evaluation (if any) will be performed in the order they appear lexically.
[Example
:
void f(int * p)
  [[expects: p != nullptr]]                     // #1
  [[ensures: *p == 1]]                          // #3
  [[expects: *p == 0]]                          // #2
{
  *p = 1;
}
end example
]
If a postcondition odr-uses ([basic.def.odr]) a parameter in its predicate and the function body makes direct or indirect modifications of the value of that parameter, the behavior is undefined.
[Example
:
int f(int x)
  [[ensures r: r == x]]
{
  return ++x;                   // undefined behavior
}

int g(int * p)
  [[ensures r: p != nullptr]]
{
  *p = 42;                      // OK, p is not modified
}

int h(int x)
  [[ensures r: r == x]]
{
  potentially_modify(x);        // undefined behavior if x is modified
  return x;
}
end example
]

9.11.4.3 Checking contracts [dcl.attr.contract.check]

If the contract-level of a contract-attribute-specifier is absent, it is assumed to be default.
[Note
:
A default contract-level is expected to be used for those contracts where the cost of run-time checking is assumed to be small (or at least not expensive) compared to the cost of executing the function.
An audit contract-level is expected to be used for those contracts where the cost of run-time checking is assumed to be large (or at least significant) compared to the cost of executing the function.
An axiom contract-level is expected to be used for those contracts that are formal comments and are not evaluated at run-time.
end note
]
[Note
:
Multiple contract conditions may be applied to a function type with the same or different contract-levels.
[Example
:
int z;

bool is_prime(int k);

void f(int x)
  [[expects: x > 0]]
  [[expects audit: is_prime(x)]]
  [[ensures: z > 10]]
{
  /* ... */
}
end example
]
end note
]
A translation may be performed with one of the following build levels: off, default, or audit.
A translation with build level set to off performs no checking for any contract.
A translation with build level set to default performs checking for default contracts.
A translation with build level set to audit performs checking for default and audit contracts.
If no build level is explicitly selected, the build level is default.
The mechanism for selecting the build level is implementation-defined.
The translation of a program consisting of translation units where the build level is not the same in all translation units is conditionally-supported.
There should be no programmatic way of setting, modifying, or querying the build level of a translation unit.
During constant expression evaluation, only predicates of checked contracts are evaluated.
In other contexts, it is unspecified whether the predicate for a contract that is not checked under the current build level is evaluated; if the predicate of such a contract would evaluate to false, the behavior is undefined.
The violation handler of a program is a function of type “noexcept function of (lvalue reference to const std::contract_­violation) returning void”, and is specified in an implementation-defined manner.
The violation handler is invoked when the predicate of a checked contract evaluates to false (called a contract violation).
There should be no programmatic way of setting or modifying the violation handler.
It is implementation-defined how the violation handler is established for a program and how the std​::​contract_­violation argument value is set, except as specified below.
If a precondition is violated, the source location of the violation is implementation-defined.
[Note
:
Implementations are encouraged but not required to report the caller site.
end note
]
If a postcondition is violated, the source location of the violation is the source location of the function definition.
If an assertion is violated, the source location of the violation is the source location of the statement to which the assertion is applied.
If a violation handler exits by throwing an exception and a contract is violated on a call to a function with a non-throwing exception specification, then the behavior is as if the exception escaped the function body.
[Note
:
The function std​::​terminate is invoked.
end note
]
[Example
:
void f(int x) noexcept [[expects: x > 0]];

void g() {
  f(0);                                         // std​::​terminate() if violation handler throws
  /* ... */
}
end example
]
A translation may be performed with one of the following violation continuation modes: off or on.
A translation with violation continuation mode set to off terminates execution by invoking the function std::terminate ([except.terminate]) after completing the execution of the violation handler.
A translation with a violation continuation mode set to on continues execution after completing the execution of the violation handler.
If no continuation mode is explicitly selected, the default continuation mode is off.
[Note
:
A continuation mode set to on provides the opportunity to install a logging handler to instrument a pre-existing code base and fix errors before enforcing checks.
end note
]
[Example
:
void f(int x) [[expects: x > 0]];

void g() {
  f(0);         // std​::​terminate() after handler if continuation mode is off;
                // proceeds after handler if continuation mode is on
  /* ... */
}
end example
]

9.11.5 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 redeclared 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
]

9.11.6 Fallthrough attribute [dcl.attr.fallthrough]

The attribute-token fallthrough may be applied to a null statement; 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.
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 should 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
]

9.11.7 Likelihood attributes [dcl.attr.likelihood]

The attribute-tokens likely and unlikely may be applied to labels or statements.
The attribute-tokens likely and unlikely shall appear at most once in each attribute-list and no attribute-argument-clause shall be present.
The attribute-token likely shall not appear in an attribute-specifier-seq that contains the attribute-token unlikely.
[Note
:
The use of the likely attribute is intended to allow implementations to optimize for the case where paths of execution including it are arbitrarily more likely than any alternative path of execution that does not include such an attribute on a statement or label.
The use of the unlikely attribute is intended to allow implementations to optimize for the case where paths of execution including it are arbitrarily more unlikely than any alternative path of execution that does not include such an attribute on a statement or label.
A path of execution includes a label if and only if it contains a jump to that label.
Excessive usage of either of these attributes is liable to result in performance degradation.
end note
]
[Example
:
void g(int);
int f(int n) {
  if (n > 5) [[unlikely]] {     // n > 5 is considered to be arbitrarily unlikely
    g(0);
    return n * 2 + 1;
  }

  switch (n) {
  case 1:
    g(1);
    [[fallthrough]];

  [[likely]] case 2:            // n == 2 is considered to be arbitrarily more
    g(2);                       // likely than any other value of n
    break;
  }
  return 3;
}
end example
]

9.11.8 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 should not 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 should not warn that b is unused, whether or not NDEBUG is defined.
end example
]

9.11.9 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 is discouraged unless explicitly cast to void.
Implementations should 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(); }             // warning not encouraged: not a nodiscard call, because neither
                                // the (reference) return type nor the function is declared nodiscard
end example
]

9.11.10 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 should 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
]

9.11.11 No unique address attribute [dcl.attr.nouniqueaddr]

The attribute-token no_­unique_­address specifies that a non-static data member is a potentially-overlapping subobject ([intro.object]).
It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present.
The attribute may appertain to a non-static data member other than a bit-field.
[Note
:
The non-static data member can share the address of another non-static data member or that of a base class, and any padding that would normally be inserted at the end of the object can be reused as storage for other members.
end note
]
[Example
:
template<typename Key, typename Value,
         typename Hash, typename Pred, typename Allocator>
class hash_map {
  [[no_unique_address]] Hash hasher;
  [[no_unique_address]] Pred pred;
  [[no_unique_address]] Allocator alloc;
  Bucket *buckets;
  // ...
public:
  // ...
};
Here, hasher, pred, and alloc could have the same address as buckets if their respective types are all empty.
end example
]