Date:  2023-12-19
Project:  Programming Language C++
Reference:  ISO/IEC IS 14882:2020
Reply to:  Jens Maurer
 jens.maurer@gmx.net


C++ Standard Core Language Defect Reports and Accepted Issues, Revision 113


This document contains the C++ core language issues that have been categorized as Defect Reports by the Committee (PL22.16 + WG21) and other accepted issues, that is, issues with status "DR," "accepted," "DRWP," "WP," "CD1," "CD2," "CD3," "CD4," "CD5," "CD6," "TC1," "C++11," "C++14," "C++17," "C++20," and "C++20," along with their proposed resolutions. Issues with DR, accepted, DRWP, and WP status are NOT part of the International Standard for C++. They are provided for informational purposes only, as an indication of the intent of the Committee. They should not be considered definitive until or unless they appear in an approved Technical Corrigendum or revised International Standard for C++.

This document is part of a group of related documents that together describe the issues that have been raised regarding the C++ Standard. The other documents in the group are:

For more information, including a description of the meaning of the issue status codes and instructions on reporting new issues, please see the Active Issues List.

Section references in this document reflect the section numbering of document WG21 N4971.


Issues with "DR" Status


1698. Files ending in \

Section: 5.2  [lex.phases]     Status: DR     Submitter: David Krauss     Date: 2013-06-10

[Accepted as a DR at the November, 2023 meeting.]

The description of how to handle file not ending in a newline in 5.2 [lex.phases] paragraph 1, phase 2, is:

  1. Each instance of a backslash character (\) immediately followed by a new-line character is deleted, splicing physical source lines to form logical source lines. Only the last backslash on any physical source line shall be eligible for being part of such a splice. If, as a result, a character sequence that matches the syntax of a universal-character-name is produced, the behavior is undefined. A source file that is not empty and that does not end in a new-line character, or that ends in a new-line character immediately preceded by a backslash character before any such splicing takes place, shall be processed as if an additional new-line character were appended to the file.

This is not clear regarding what happens if the last character in the file is a backslash. In such a case, presumably the result of adding the newline should not be a line splice but rather a backslash preprocessing-token (that will be diagnosed as an invalid token in phase 7), but that should be spelled out.

CWG 2023-07-14

Addressed by the resolution for issue 2747.




2747. Cannot depend on an already-deleted splice

Section: 5.2  [lex.phases]     Status: DR     Submitter: Jim X     Date: 2021-09-14

[Accepted as a DR at the November, 2023 meeting.]

(From editorial issue 4903.)

Subclause 5.2 [lex.phases] paragraph 2 specifies:

... Each sequence of a backslash character (\) immediately followed by zero or more whitespace characters other than new-line followed by a new-line character is deleted, splicing physical source lines to form logical source lines. ... A source file that is not empty and that does not end in a new-line character, or that ends in a splice, shall be processed as if an additional new-line character were appended to the file.

This is confusing, because the first sentence deletes all splices, and then the last sentence checks for a splice that has already been deleted.

Proposed resolution (approved by CWG 2023-07-14):

Change in 5.2 [lex.phases] paragraph 2 as follows:

... Each sequence of a backslash character (\) immediately followed by zero or more whitespace characters other than new-line followed by a new-line character is deleted, splicing physical source lines to form logical source lines. ... A source file that is not empty and that (after splicing) does not end in a new-line character, or that ends in a splice, shall be processed as if an additional new-line character were appended to the file.

CWG 2023-07-14

CWG noted that a lone backslash at the end of a file remains (in the status quo and with the proposed change) and turns into an ill-formed preprocessing-token. The wording as amended seems sufficiently clear to consider issue 1698 resolved.




2764. Use of placeholders affecting name mangling

Section: 6.4.1  [basic.scope.scope]     Status: DR     Submitter: Hubert Tong     Date: 2023-07-05

[Accepted as a DR at the November, 2023 meeting.]

Paper P2169R4 (A nice placeholder with no name), as approved by WG21 in Varna, added a placeholder facility. The intent was that the use of placeholders is sufficiently limited such that they never need to be mangled. Quote from 6.4.1 [basic.scope.scope] paragraph 5 as modified by the paper:

A declaration is name-independent if its name is _ and it declares a variable with automatic storage duration, a structured binding not inhabiting a namespace scope, the variable introduced by an init-capture, or a non-static data member.

The following example does not seem to follow that intent:

  struct A { A(); };
  inline void f() {
    static union { A _{}; };
    static union { A _{}; };
  }
  void g() { return f(); }

The preceding example needs handling similar to the following example, which is unrelated to the placeholder feature:

  struct A { A(); };
  inline void f() {
    { static union { A a{}; }; }
    { static union { A a{}; }; }
  }
  void g() { return f(); }

A similar problem may arise for static or thread_local structured bindings at block scope.

Finally, another example involving placeholders in anonymous unions:

  static union { int _ = 42; };
  int &ref = _;
  int foo() { return 13; }
  static union { int _ = foo(); };
  int main() { return ref; }

Possible resolution (reviewed by CWG 2023-08-25) [SUPERSEDED]:

Change in 6.4.1 [basic.scope.scope] paragraph 5 and add bullets as follows:

A class is name-dependent if it is an anonymous union declared at namespace scope or with a storage-class-specifier (11.5.2 [class.union.anon]). A declaration is name-independent if its name is _ and it declares

Proposed resolution (approved by CWG 2023-09-15):

Change in 6.4.1 [basic.scope.scope] paragraph 5 and add bullets as follows:

A declaration is name-independent if its name is _ and it declares



2793. Block-scope declaration conflicting with parameter name

Section: 6.4.3  [basic.scope.block]     Status: DR     Submitter: Jason Merrill     Date: 2023-08-31

[Accepted as a DR at the November, 2023 meeting.]

Consider:

  void f(int i) { extern int i; } 

According to 6.4.3 [basic.scope.block] paragraph 2, the target scope of the declaration is relevant (which would be the global scope), but not the scope in which the name is bound. That seems wrong. For comparison, template parameter names use the latter rule (13.8.2 [temp.local] paragraph 6).

Proposed resolution (approved by CWG 2023-09-15):

If a declaration that is not a name-independent declaration and whose target scope is that binds a name in the block scope S of a potentially conflicts with a declaration whose target scope is the parent scope of S, the program is ill-formed.



2753. Storage reuse for string literal objects and backing arrays

Section: 6.7.2  [intro.object]     Status: DR     Submitter: Brian Bi     Date: 2023-06-29

[Accepted as a DR at the November, 2023 meeting.]

Subclause 6.7.2 [intro.object] paragraph 9 specifies the general principle that two objects with overlapping lifetimes have non-overlapping storage, which can be observed by comparing addresses:

Unless an object is a bit-field or a subobject of zero size, the address of that object is the address of the first byte it occupies. Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are of different types; otherwise, they have distinct addresses and occupy disjoint bytes of storage.

After P2752, there are two exceptions: string literal objects and backing arrays for initializer lists.

Subclause 5.13.5 [lex.string] paragraph 9 specifies:

Evaluating a string-literal results in a string literal object with static storage duration (6.7.5 [basic.stc]). Whether all string-literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified.

Subclause 9.4.4 [dcl.init.ref] paragraph 5, after application of P2752R3 (approved in June, 2023), specifies:

Whether all backing arrays are distinct (that is, are stored in non-overlapping objects) is unspecified.

It is unclear whether a backing array can overlap with a string literal object.

Furthermore, it is unclear whether any such object can overlap with named objects or temporaries, for example:

  const char (&r) [] = "foo";
  const char a[] = {'f', 'o', 'o', '\0'};

  int main() {  
    assert(&r == &a);   // allowed not to fail?
  }

Proposed resolution (approved by CWG 2023-11-09):

  1. Add a new paragraph before 6.7.2 [intro.object] paragraph 9 and change the latter as follows:

    An object is a potentially non-unique object if it is a string literal object (5.13.5 [lex.string]), the backing array of an initializer list (9.4.4 [dcl.init.ref]), or a subobject thereof.

    Unless an object is a bit-field or a subobject of zero size, the address of that object is the address of the first byte it occupies. Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are of different types, or if they are both potentially non-unique objects; otherwise, they have distinct addresses and occupy disjoint bytes of storage.

    [Example 2:

      static const char test1 = 'x';
      static const char test2 = 'x';
      const bool b = &test1 != &test2;  // always true
    
      static const char (&r) [] = "x";
      static const char *s = "x";  
      static std::initializer_list<char> il = { 'x' };
      const bool b2 = r != il.begin();        // unspecified result
      const bool b3 = r != s;                 // unspecified result
      const bool b4 = il.begin() != &test1;   // always true
      const bool b5 = r != &test1;            // always true
    

    -- end example]

  2. Change in subclause 5.13.5 [lex.string] paragraph 9 as follows:

    Evaluating a string-literal results in a string literal object with static storage duration (6.7.5 [basic.stc]). [ Note: String literal objects are potentially non-unique (6.7.2 [intro.object]). Whether all string-literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified. -- end note ]
  3. Change in subclause 9.4.4 [dcl.init.ref] paragraph 5, after application of P2752R3 (approved in June, 2023), as follows:

    Whether all backing arrays are distinct (that is, are stored in non-overlapping objects) is unspecified. [ Note: Backing arrays are potentially non-unique objects (6.7.2 [intro.object]). -- end note ]

CWG 2023-07-14

CWG resolved that a named or temporary object is always disjoint from any other object, and thus cannot overlap with a string literal object or a backing array. The lines b4 and b5 in the example highlight that outcome.

Backing arrays and string literals can arbitrarily overlap among themselves; CWG believes the proposed wording achieves that outcome.

The ancillary question how address comparisons between potentially non-unique objects are treated during constant evaluation is handled in issue 2765.




2795. Overlapping empty subobjects with different cv-qualification

Section: 6.7.2  [intro.object]     Status: DR     Submitter: Jonathan Caves     Date: 2023-09-04

[Accepted as a DR at the November, 2023 meeting.]

Subclause 6.7.2 [intro.object] paragraph 9 specifies:

... Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are of different types; otherwise, they have distinct addresses and occupy disjoint bytes of storage. [ Footnote: ... ]

Types T and const T are different types, but it is unlikely the rule is intending to differentiate along that line.

Suggested resolution [SUPERSEDED]:

Change in 6.7.2 [intro.object] paragraph 9 as follows:

... Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are of different types (ignoring top-level cv-qualifiers); otherwise, they have distinct addresses and occupy disjoint bytes of storage. [ Footnote: ... ]

Proposed resolution (approved by CWG 2023-09-15):

(Hypothetically, pointer-to-member types can be empty, but might differ in non-top-level cv-qualification.)

Change in 6.7.2 [intro.object] paragraph 9 as follows:

... Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are not of different similar types (7.3.6 [conv.qual]); otherwise, they have distinct addresses and occupy disjoint bytes of storage. [ Footnote: ... ]



2725. Overload resolution for non-call of class member access

Section: 7.6.1.5  [expr.ref]     Status: DR     Submitter: Richard Smith     Date: 2023-04-26

[Accepted as a DR at the November, 2023 meeting.]

Consider:

  struct A {
    static void f();
    static void f(int);
  } x;
  void (*p)() = x.f;   // error

This is ill-formed as confirmed by issue 61. Various other changes (see issue 2241) have put the following example into the same category:

  struct B {
    static void f();
  } y;
  void (*q)() = y.f;   // error

If this is the intended outcome (although major implementations disagree), then the rules in 7.6.1.5 [expr.ref] should be clarified accordingly.

Proposed resolution (approved by CWG 2023-06-13):

Change in 7.6.1.5 [expr.ref] bullet 6.3 as follows:

This also addresses issue 1038.




2780. reinterpret_cast to reference to function types

Section: 7.6.1.10  [expr.reinterpret.cast]     Status: DR     Submitter: Lauri Vasama     Date: 2023-08-07

[Accepted as a DR at the November, 2023 meeting.]

Subclause 7.6.1.10 [expr.reinterpret.cast] paragraph 11 specifies:

A glvalue of type T1, designating an object x, can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result is that of *reinterpret_cast<T2 *>(p) where p is a pointer to x of type “pointer to T1”. No temporary is created, no copy is made, and no constructors (11.4.5 [class.ctor]) or conversion functions (11.4.8 [class.conv]) are called. [ Footnote: ... ]

The wording does not cover references to function type, only references to object types. All major implementations accept the following example:

  void f() {}

  void(&g())(int) {
    return reinterpret_cast<void(&)(int)>(f);
  }

Proposed resolution (approved by CWG 2023-09-15):

Change in 7.6.1.10 [expr.reinterpret.cast] paragraph 11 as follows:

A glvalue of type T1, designating an object or function x, can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result is that of *reinterpret_cast<T2 *>(p) where p is a pointer to x of type “pointer to T1”. No temporary is created, no copy is made, and no constructors (11.4.5 [class.ctor]) or conversion functions (11.4.8 [class.conv]) are called. [ Footnote: ... ]



2823. Implicit undefined behavior when dereferencing pointers

Section: 7.6.2.2  [expr.unary.op]     Status: DR     Submitter: CWG     Date: 2023-11-06

[Accepted as a DR at the November, 2023 meeting.]

Subclause 7.6.2.2 [expr.unary.op] paragraph 1 specifies:

The unary * operator performs indirection. Its operand shall be a prvalue of type “pointer to T”, where T is an object or function type. The operator yields an lvalue of type T denoting the object or function to which the operand points.

It is unclear what happens if the operand does not point to an object or function.

Proposed resolution (approved by CWG 2023-11-08):

Change in 7.6.2.2 [expr.unary.op] paragraph 1 as follows:

The unary * operator performs indirection. Its operand shall be a prvalue of type “pointer to T”, where T is an object or function type. The operator yields an lvalue of type T denoting the object or function to which the operand points. If the operand points to an object or function, the result denotes that object or function; otherwise, the behavior is undefined except as specified in 7.6.1.8 [expr.typeid].



2792. Clean up specification of noexcept operator

Section: 7.6.2.7  [expr.unary.noexcept]     Status: DR     Submitter: Jan Schultke     Date: 2023-08-30

[Accepted as a DR at the November, 2023 meeting.]

The introductory sentence "can throw an exception" is misleading, because it might be interpreted to cover exceptions thrown as the result of encountering undefined behavior.

Proposed resolution (approved by CWG 2023-10-06):

Change all of 7.6.2.7 [expr.unary.noexcept] as follows:

The noexcept operator determines whether the evaluation of its operand, which is an unevaluated operand (7.2.3 [expr.context]), can throw an exception (14.2 [except.throw]).

noexcept-expression:
        noexcept ( expression )

The operand of the noexcept operator is an unevaluated operand (7.2.3 [expr.context]). If the operand is a prvalue, the temporary materialization conversion (7.3.5 [conv.rval]) is applied.

The result of the noexcept operator is a prvalue of type bool. The result is false if the full-expression of the operand is potentially-throwing (14.5 [except.spec]), and true otherwise.

[Note 1: A noexcept-expression is an integral constant expression (7.7 [expr.const]). —end note]

If the operand is a prvalue, the temporary materialization conversion (7.3.5 [conv.rval]) is applied. The result of the noexcept operator is true unless the full-expression of the operand is potentially-throwing (14.5 [except.spec]).




2102. Constructor checking in new-expression

Section: 7.6.2.8  [expr.new]     Status: DR     Submitter: Richard Smith     Date: 2015-03-16

[Accepted as a DR at the November, 2023 meeting.]

According to 7.6.2.8 [expr.new] paragraph 25,

If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function (11.4.11 [class.free]), and the constructor (11.4.5 [class.ctor]).

The mention of “the constructor” here is strange. For the “object of class type” case, access and ambiguity control are done when we perform initialization in paragraph 17, and we might not be calling a constructor anyway (for aggregate initialization). This seems wrong.

For the “array of objects of class type” case, it makes slightly more sense (we need to check the trailing array elements can be default-initialized) but again (a) we aren't necessarily using a constructor, (b) we should say which constructor — and we may need overload resolution to find it, and (c) shouldn't this be part of initialization, so we can distinguish between the cases where we should copy-initialize from {} and the cases where we should default-initialize?

Additional notes (May, 2023):

It is unclear whether default-initialization is required to be well-formed even for an array with no elements.

Proposed resolution (approved by CWG 2023-06-16):

  1. Insert a new paragraph before 7.6.2.8 [expr.new] paragraph 9:

    If the allocated type is an array, the new-initializer is a braced-init-list, and the expression is potentially-evaluated and not a core constant expression, the semantic constraints of copy-initializing a hypothetical element of the array from an empty initializer list are checked (9.4.5 [dcl.init.list]). [ Note: The array can contain more elements than there are elements in the braced-init-list, requiring initialization of the remainder of the array elements from an empty initializer list. -- end note ]

    Objects created by a new-expression have dynamic storage duration (6.7.5.5 [basic.stc.dynamic]). ...

  2. Change in 7.6.2.8 [expr.new] paragraph 25 as follows:

    If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation]), and the constructor (11.4.5 [class.ctor]) selected for the initialization (if any). If the new-expression creates an array of objects of class type, the destructor is potentially invoked (11.4.7 [class.dtor]).
  3. Change in 7.6.2.8 [expr.new] paragraph 28 as follows:

    A declaration of a placement deallocation function matches the declaration of a placement allocation function if it has the same number of parameters and, after parameter transformations (9.3.4.6 [dcl.fct]), all parameter types except the first are identical. If the lookup finds a single matching deallocation function, that function will be called; otherwise, no deallocation function will be called. If the lookup finds a usual deallocation function and that function, considered as a placement deallocation function, would have been selected as a match for the allocation function, the program is ill-formed. For a non-placement allocation function, the normal deallocation function lookup is used to find the matching deallocation function (7.6.2.9 [expr.delete]). In any case, the matching deallocation function (if any) shall be non-deleted and accessible from the point where the new-expression appears.
  4. Change in 9.4.1 [dcl.init.general] paragraph 7 as follows:

    To default-initialize an object of type T means:
    • ...
    • If T is an array type, the semantic constraints of default-initializing a hypothetical element shall be met and each element is default-initialized.
    • ...
  5. Change in 9.4.1 [dcl.init.general] paragraph 9 as follows:

    To value-initialize an object of type T means:
    • if If T is a (possibly cv-qualified) class type (Clause 11 [class]), then
      • if T has either no default constructor (11.4.5.2 [class.default.ctor]) or a default constructor that is user-provided or deleted, then the object is default-initialized;
      • otherwise, the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized;.
    • if If T is an array type, the semantic constraints of value-initializing a hypothetical element shall be met and each element is value-initialized;.
    • otherwiseOtherwise, the object is zero-initialized.



2758. What is "access and ambiguity control"?

Section: 7.6.2.9  [expr.delete]     Status: DR     Submitter: CWG     Date: 2023-06-12

[Accepted as a DR at the November, 2023 meeting.]

Subclause 7.6.2.9 [expr.delete] paragraph 12 specifies:

Access and ambiguity control are done for both the deallocation function and the destructor (11.4.7 [class.dtor], 11.4.11 [class.free]).

It is unclear what that means. In particular, ambiguity checking is part of overload resolution, and access checking requires a point of reference.

Proposed resolution (approved by CWG 2023-08-25):

  1. Change in 7.6.2.9 [expr.delete] paragraph 6 as follows:

    If the value of the operand of the delete-expression is not a null pointer value and the selected deallocation function (see below) is not a destroying operator delete, evaluating the delete-expression will invoke invokes the destructor (if any) for the object or the elements of the array being deleted. The destructor shall be accessible from the point where the delete-expression appears. In the case of an array, the elements will be are destroyed in order of decreasing address (that is, in reverse order of the completion of their constructor; see 11.9.3 [class.base.init]).
  2. Change in 7.6.2.9 [expr.delete] paragraph 10

    If more than one deallocation function is found, the The deallocation function to be called is selected as follows:
    • ...

    Unless the deallocation function is selected at the point of definition of the dynamic type's virtual destructor, the selected deallocation function shall be accessible from the point where the delete-expression appears.

  3. Remove 7.6.2.9 [expr.delete] paragraph 12:

    Access and ambiguity control are done for both the deallocation function and the destructor (11.4.7 [class.dtor], 11.4.11 [class.free]).



2749. Treatment of "pointer to void" for relational comparisons

Section: 7.6.9  [expr.rel]     Status: DR     Submitter: lprv     Date: 2023-03-12     Liaison: SG22

[Accepted as a DR at the November, 2023 meeting.]

(From editorial issue 6173.)

Subclause 7.6.9 [expr.rel] paragraph 4 and paragraph 5 specify:

The result of comparing unequal pointers to objects [ Footnote: ] is defined in terms of a partial order consistent with the following rules: ...

[Note 1: A relational operator applied to unequal function pointers or to unequal pointers to void yields an unspecified result. -- end note]

Comparing pointers to objects that are stored in a variable of type "pointer to void" should be fine.

Proposed resolution (approved by CWG 2023-06-16):

Change in 7.6.9 [expr.rel] paragraph 4 and paragraph 5 as follows:

The result of comparing unequal pointers to objects [ Footnote: ... ] is defined in terms of a partial order consistent with the following rules: ...

[Note 1: A relational operator applied to unequal function pointers or to unequal pointers to void yields an unspecified result. A pointer value of type "pointer to cv void" can point to an object (6.8.4 [basic.compound]). -- end note]




2796. Function pointer conversions for relational operators

Section: 7.6.9  [expr.rel]     Status: DR     Submitter: Alisdair Meredith     Date: 2023-09-14

[Accepted as a DR at the November, 2023 meeting.]

Consider:

  void f() {}
  void g() noexcept {}

  void q() {
    bool b1 = f == g;     // OK
    bool b2 = f > g;      // error: different types
  }

For the equality operators, 7.6.10 [expr.eq] paragraph 3 specifies:

If at least one of the operands is a pointer, pointer conversions (7.3.12 [conv.ptr]), function pointer conversions (7.3.14 [conv.fctptr]), and qualification conversions (7.3.6 [conv.qual]) are performed on both operands to bring them to their composite pointer type (7.2.2 [expr.type]). Comparing pointers is defined as follows: ...

In contrast, the corresponding rule for relational operators in 7.6.9 [expr.rel] paragraph 3 specifies:

The usual arithmetic conversions (7.4 [expr.arith.conv]) are performed on operands of arithmetic or enumeration type. If both operands are pointers, pointer conversions (7.3.12 [conv.ptr]) and qualification conversions (7.3.6 [conv.qual]) are performed to bring them to their composite pointer type (7.2.2 [expr.type]). After conversions, the operands shall have the same type.

However, all major implementations accept the example.

Proposed resolution (approved by CWG 2023-10-06):

Change in 7.6.9 [expr.rel] paragraph 3 as follows:

The usual arithmetic conversions (7.4 [expr.arith.conv]) are performed on operands of arithmetic or enumeration type. If both operands are pointers, pointer conversions (7.3.12 [conv.ptr]), function pointer conversions (7.3.14 [conv.fctptr]), and qualification conversions (7.3.6 [conv.qual]) are performed to bring them to their composite pointer type (7.2.2 [expr.type]). After conversions, the operands shall have the same type.



2768. Assignment to enumeration variable with a braced-init-list

Section: 7.6.19  [expr.ass]     Status: DR     Submitter: Shafik Yaghmour     Date: 2023-07-06

[Accepted as a DR at the November, 2023 meeting.]

Consider:

   enum class E {E1};

   void f() {
     E e;
     e = E{0}; // #1
     e = {0};  // #2
   }

#1 first initializes a temporary of type E and then assigns that to e. For #2, 7.6.19 [expr.ass] bullet 8.1 specifies that #2 is equivalent to #1:

A braced-init-list may appear on the right-hand side of

However, there is no syntactic hint that #2 would invoke direct-initialization, and in fact gcc, icc, and MSVC reject #2, but clang accepts.

Proposed resolution (approved by CWG 2023-11-06):

Change in 7.6.19 [expr.ass] paragraph 8 as follows:

A braced-init-list B may appear on the right-hand side of



2755. Incorrect wording applied by P2738R1

Section: 7.7  [expr.const]     Status: DR     Submitter: Jens Maurer     Date: 2023-06-28

[Accepted as a DR at the November, 2023 meeting.]

P2738R1 (constexpr cast from void*: towards constexpr type-erasure) applied incorrect wording to 7.7 [expr.const] bullet 5.14:

The issue is that T is defined to be a pointer type, but the "similar to" phrasing uses it as the pointee type.

Proposed resolution (approved by CWG 2023-07-14):

Change in 7.7 [expr.const] bullet 5.14 as follows:




2760. Defaulted constructor that is an immediate function

Section: 7.7  [expr.const]     Status: DR     Submitter: Corentin Jabot     Date: 2023-07-08

[Accepted as a DR at the November, 2023 meeting.]

Consider:

  consteval int f(int);
  struct S {
   int x = f(0);
   S() = default;
  };

  int main() {
    S s;     // OK?
  }

Is S an immediate function?

The relevant specification is in 7.7 [expr.const] paragraph 18:

An immediate function is a function or constructor that is

Suggested resolution [SUPERSEDED]:

Change in 7.7 [expr.const] paragraph 18 as follows:

An immediate function is a function or constructor that is

Proposed resolution (approved by CWG 2023-08-25):

  1. Change in 7.7 [expr.const] paragraph 18 as follows:

    An immediate function is a function or constructor that is
    • declared with the consteval specifier, or
    • an immediate-escalating function F whose function body contains an immediate-escalating expression E such that E's innermost enclosing non-block scope is F's function parameter scope. [ Note: Default member initializers used to initialize a base or member subobject (11.9.3 [class.base.init]) are considered to be part of the function body (9.5.1 [dcl.fct.def.general]). -- end note ]
  2. Change in 9.5.1 [dcl.fct.def.general] paragraph 1 as follows:

    Any informal reference to the body of a function should be interpreted as a reference to the non-terminal function-body, including, for a constructor, default member initializers or default initialization used to initialize a base or member subobject in the absence of a mem-initializer-id (11.9.3 [class.base.init]).



2763. Ignorability of [[noreturn]] during constant evaluation

Section: 7.7  [expr.const]     Status: DR     Submitter: Jiang An     Date: 2023-07-10

[Accepted as a DR at the November, 2023 meeting.]

Subclause 9.12.10 [dcl.attr.noreturn] paragraph 2 specifies:

If a function f is called where f was previously declared with the noreturn attribute and f eventually returns, the behavior is undefined.

Undefineed behavior is, in general, detected during constant evaluation, thus requiring an implementation to actually support the noreturn attribute, such as in the following example:

  [[noreturn]] constexpr void f() {}
  constexpr int x = (f(), 0);

It might be desirable to treat the assume and noreturn attributes alike in that regard.

Proposed resolution (approved by CWG 2023-07-14) [SUPERSEDED]:

Split into a separate paragraph and change 7.7 [expr.const] paragraph 5 as follows:

It is unspecified whether E is a core constant expression if E satisfies the constraints of a core constant expression, but evaluation of E would evaluate

CWG 2023-07-14

As an alternative, all of 9.12 [dcl.attr] could be added to the "library undefined behavior" bullet. However, CWG felt that a case-by-case consideration is warranted, given that assumptions set precedent in requiring special treatment.

Possible resolution (reviewed by CWG 2023-08-25) [SUPERSEDED]:

Split into a separate paragraph and change 7.7 [expr.const] paragraph 5 as follows:

It is unspecified whether E is a core constant expression if E satisfies the constraints of a core constant expression, but evaluation of E would evaluate

Proposed resolution (approved by CWG 2023-11-09):

Split into a separate paragraph and change 7.7 [expr.const] paragraph 5 as follows:

It is unspecified whether E is a core constant expression if E satisfies the constraints of a core constant expression, but evaluation of E would evaluate



2798. Manifestly constant evaluation of the static_assert message

Section: 7.7  [expr.const]     Status: DR     Submitter: Jason Merrill     Date: 2023-09-12

[Accepted as a DR at the November, 2023 meeting.]

The message of a static_assert declaration is a conditional-expression and thus is not manifestly constant evaluated. Consider this example:

  struct X {
    std::string s;
    const char *p;
  };
  consteval X f() { return {.s = "some long string that requires a heap allocation", .p = "hello"}; }

  static_assert(cond, f().p);

The example is ill-formed, because the immediate invocation f() lets a pointer to the heap escape.

Proposed resolution (approved by CWG 2023-10-06):

  1. Change in 7.7 [expr.const] paragraph 19 as follows:

    [Note 11: Except for a static_assert-message, a A manifestly constant-evaluated expression is evaluated even in an unevaluated operand (7.2.3 [expr.context]). —end note]
  2. Change the grammar in 9.1 [dcl.pre] as follows:

    static_assert-message:
      unevaluated-string
      conditional-expression constant-expression
    
  3. Change in 9.1 [dcl.pre] bullet 11.2 as follows:

    • ...
    • if the static_assert-message is a conditional-expression constant-expression M, ...



2791. Unclear phrasing about "returning to the caller"

Section: 8.7.4  [stmt.return]     Status: DR     Submitter: Jan Schultke     Date: 2023-08-23

[Accepted as a DR at the November, 2023 meeting.]

In 8.7.4 [stmt.return] and 8.7.5 [stmt.return.coroutine], the standard uses the phrasing "returns to its caller" when specifying return or co_return. It would be better to talk about transfer of control, which is a term used elsewhere in the standard.

Proposed resolution (approved by CWG 2023-10-06):

  1. Change in 7.6.2.4 [expr.await] paragraph 1 as follows:

    The co_await expression is used to suspend evaluation of a coroutine (9.5.4 [dcl.fct.def.coroutine]) while awaiting completion of the computation represented by the operand expression. Suspending the evaluation of a coroutine transfers control to its caller or resumer.
  2. Change 8.7.4 [stmt.return] paragraph 1 as follows:

    A function returns control to its caller by the return statement.
  3. Change 8.7.5 [stmt.return.coroutine] paragraph 1 as follows:

    A coroutine returns to its caller or resumer (9.5.4 [dcl.fct.def.coroutine]) by the co_return statement or when suspended (7.6.2.4 [expr.await]). A co_return statement transfers control to the caller or resumer of a coroutine (9.5.4 [dcl.fct.def.coroutine]). A coroutine shall not enclose a return statement (8.7.4 [stmt.return]).
  4. Change in 9.5.4 [dcl.fct.def.coroutine] paragraph 10 as follows:

    If the allocation function returns nullptr, the coroutine returns transfers control to the caller of the coroutine and the return value is obtained by a call to T::get_return_object_on_allocation_failure(), where T is the promise type.



2556. Unusable promise::return_void

Section: 8.7.5  [stmt.return.coroutine]     Status: DR     Submitter: Davis Herring     Date: 2022-03-24

[Accepted as a DR at the November, 2023 meeting.]

Subclause 8.7.5 [stmt.return.coroutine] paragraph 3 specifies:

If p.return_void() is a valid expression, flowing off the end of a coroutine's function-body is equivalent to a co_return with no operand; otherwise flowing off the end of a coroutine's function-body results in undefined behavior.

However, 9.5.4 [dcl.fct.def.coroutine] paragraph 6 suggests:

If searches for the names return_void and return_value in the scope of the promise type each find any declarations, the program is ill-formed. [Note: If return_void is found, flowing off the end of a coroutine is equivalent to a co_return with no operand. Otherwise, flowing off the end of a coroutine results in undefined behavior (8.7.5 [stmt.return.coroutine]). —end note]

The difference is between the conditions "valid expression" and "found by name lookup". Effectively, it means that undefined behavior might result where the implementation could instead diagnose an ill-formed use of return_void (for example, because it is inaccessible, deleted, or the function call requires arguments).

Proposed resolution (approved by CWG 2023-06-17):

Change in 8.7.5 [stmt.return.coroutine] paragraph 3 as follows:

If p.return_void() is a valid expression a search for the name return_void in the scope of the promise type finds any declarations, flowing off the end of a coroutine's function-body is equivalent to a co_return with no operand; otherwise flowing off the end of a coroutine's function-body results in undefined behavior.



2531. Static data members redeclared as constexpr

Section: 9.2.6  [dcl.constexpr]     Status: DR     Submitter: Davis Herring     Date: 2022-02-16

[Accepted as a DR at the November, 2023 meeting.]

C++17 made constexpr static data members implicitly inline (9.2.6 [dcl.constexpr] paragraph 1):

A function or static data member declared with the constexpr or consteval specifier is implicitly an inline function or variable (9.2.8 [dcl.inline]).

However, that makes the following well-formed C++14 program ill-formed, no diagnostic required, per 9.2.8 [dcl.inline] paragraph 5:

If a function or variable with external or module linkage is declared inline in one definition domain, an inline declaration of it shall be reachable from the end of every definition domain in which it is declared; no diagnostic is required.
  // x.hh
  struct X {
    static const int x;
  };

  // TU 1
  #include "x.hh"
  constexpr int X::x{};

  // TU 2
  #include "x.hh"
  int main() { return !&X::x; }

Proposed resolution (reviewed by CWG 2023-02-07, approved by CWG 2023-11-07):

Change 9.2.6 [dcl.constexpr] paragraph 1 as follows:

A function or static data member declared with the constexpr or consteval specifier on its first declaration is implicitly an inline function or variable (9.2.8 [dcl.inline]).

Drafting note: Functions must be declared constexpr on every declaration if on any, so this isn't a change for them.




2801. Reference binding with reference-related types

Section: 9.4.4  [dcl.init.ref]     Status: DR     Submitter: Brian Bi     Date: 2023-09-18

[Accepted as a DR at the November, 2023 meeting.]

Consider:

  int* p;
  const int*&& r = static_cast<int*&&>(p);

The intent of core issues 2018 and 2352 was to make this example ill-formed, because it surprisingly introduces a temporary.

Proposed resolution (approved by CWG 2023-10-20):

Change in 9.4.4 [dcl.init.ref] bullet 5.4 as follows:




2252. Enumeration list-initialization from the same type

Section: 9.4.5  [dcl.init.list]     Status: DR     Submitter: Richard Smith     Date: 2016-03-22

[Accepted as a DR at the November, 2023 meeting.]

According to 9.4.5 [dcl.init.list] bullet 3.8,

Otherwise, if T is an enumeration with a fixed underlying type (9.7.1 [dcl.enum]), the initializer-list has a single element v, and the initialization is direct-list-initialization, the object is initialized with the value T(v) (7.6.1.4 [expr.type.conv]); if a narrowing conversion is required to convert v to the underlying type of T , the program is ill-formed.

This could be read as requiring that there be a conversion from v to the underlying type of T, leaving the status of an example like the following unclear:

  enum class E {};
  struct X { operator E(); };
  E{X()}; // ok? 

Notes from the March, 2018 meeting:

CWG disagreed that the existing wording requires such a conversion, only that if such a conversion is possble, it must not narrow. A formulation along the lines of “if that initialization involves a narrowing conversion to the underlying type of T...” was suggested to clarify the intent. This will be handled editorially, and the issue will be left in "review" status until the change has been verified.

Additional notes (August, 2023)

Issue 2374 has meanwhile clarified that v is required to implicitly convert to the underlying type of the enumeration for 9.4.5 [dcl.init.list] bullet 3.8 to apply. Now, the logic falls through to 9.4.5 [dcl.init.list] bullet 3.9 for the above example, making it well-formed.

CWG 2023-09-15

Class types with conversions to scalar types were not in view when the wording in this bullet was conceived.

Proposed resolution (approved by CWG 2023-10-06):

Change in 9.4.5 [dcl.init.list] bullet 3.8 as follows:

Otherwise, if T is an enumeration with a fixed underlying type (9.7.1 [dcl.enum]) U, the initializer-list has a single element v of scalar type, v can be implicitly converted to U, and the initialization is direct-list-initialization, the object is initialized with the value T(v) (7.6.1.4 [expr.type.conv]); if a narrowing conversion is required to convert v to U, the program is ill-formed.



2570. Clarify constexpr for defaulted functions

Section: 9.5.2  [dcl.fct.def.default]     Status: DR     Submitter: Gabriel dos Reis     Date: 2022-04-18

[Accepted as a DR at the November, 2023 meeting.]

After the application of P2448R2, 9.5.2 [dcl.fct.def.default] paragraph 3 reads:

A function explicitly defaulted on its first declaration is implicitly inline (9.2.8 [dcl.inline]), and is implicitly constexpr (9.2.6 [dcl.constexpr]) if it satisfies the requirements for a constexpr function.

It is unclear that no other such defaulted function is implicitly constexpr.

Proposed resolution (approved by CWG 2023-06-17):

A function explicitly defaulted on its first declaration is implicitly inline (9.2.8 [dcl.inline]), and is implicitly constexpr (9.2.6 [dcl.constexpr]) if it satisfies the requirements for a constexpr function. [Note: Other defaulted functions are not implicitly constexpr. -- end note ]



2754. Using *this in explicit object member functions that are coroutines

Section: 9.5.4  [dcl.fct.def.coroutine]     Status: DR     Submitter: Christof Meerwald     Date: 2023-06-23

[Accepted as a DR at the November, 2023 meeting.]

Subclause 9.5.4 [dcl.fct.def.coroutine] paragraph 4 specifies:

In the following, pi is an lvalue of type Pi , where p1 denotes the object parameter and pi+1 denotes the ith non-object function parameter for a non-static member function, and pi denotes the ith function parameter otherwise. For a non-static member function, q1 is an lvalue that denotes *this; any other qi is an lvalue that denotes the parameter copy corresponding to pi , as described below.

An explicit object member function is a non-static member function, but there is no this.

Proposed resolution (approved by CWG 2023-07-14):

Change in 9.5.4 [dcl.fct.def.coroutine] paragraph 4 as follows:

In the following, pi is an lvalue of type Pi , where p1 denotes the object parameter and pi+1 denotes the ith non-object function parameter for a non-static an implicit object member function, and pi denotes the ith function parameter otherwise. For a non-static an implicit object member function, q1 is an lvalue that denotes *this; any other qi is an lvalue that denotes the parameter copy corresponding to pi, as described below.



2733. Applying [[maybe_unused]] to a label

Section: 9.12.8  [dcl.attr.unused]     Status: DR     Submitter: Barry Revzin     Date: 2023-05-25     Liaison: EWG

[Accepted as a DR at the November, 2023 meeting.]

Subclause 9.12.8 [dcl.attr.unused] paragraph 2 specifies:

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

Absent from that list are labels, but both gcc and clang accept [[maybe_unused]] on a label, and behave accordingly.

Proposed resolution (approved by CWG 2023-07-14)

Change in 9.12.8 [dcl.attr.unused] as follows:

The attribute-token maybe_unused indicates that a name, label, or entity is possibly intentionally unused. No attribute-argument-clause shall be present.

The attribute may be applied to the declaration of a class, a typedef-name, a variable (including a structured binding declaration), a non-static data member, a function, an enumeration, or an enumerator, or to an identifier label (8.2 [stmt.label]).

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.

Recommended practice: For an entity marked maybe_unused, implementations should not emit a warning that the entity or its structured bindings (if any) are used or unused. For a structured binding declaration not marked maybe_unused, implementations should not emit such a warning unless all of its structured bindings are unused. For a label to which maybe_unused is applied, implementations should not emit a warning that the label is used or unused.

[Example 1:
  [[maybe_unused]] void f([[maybe_unused]] bool thing1,
                          [[maybe_unused]] bool thing2) {
    [[maybe_unused]] bool b = thing1 && thing2;
    assert(b);
#ifdef NDEBUG
    goto x;
#endif
    [[maybe_unused]] x:
  }
Implementations should not warn that b or x is unused, whether or not NDEBUG is defined. — end example]

CWG 2023-07-14

CWG has reviewed and approved the proposed resolution. However, this is a new (albeit small) feature, thus forwarding to EWG via paper issue 1585 for approval.

EWG 2023-11-07

Accept the proposed resolution, forward to CWG for inclusion in C++26.




2783. Handling of deduction guides in global-module-fragment

Section: 10.4  [module.global.frag]     Status: DR     Submitter: Daniela Engert     Date: 2023-08-21

[Accepted as a DR at the November, 2023 meeting.]

Consider:

  // header "S.h"
  
  template<class T>
  struct S {
    S(const T*);
  };
  template<class T>
  S(T*) -> S<T>

  // translation unit
  module;
  #include "S.h"

  export module M;
  export using ::S;

Obviously, the using-declaration referring to the class template S is exported by M, but what about the deduction guide of S?

Proposed resolution (approved by CWG 2023-08-25) [SUPERSEDED]:

Add a new bullet after 10.4 [module.global.frag] bullet 3.5.7 as follows:

Proposed resolution (approved by CWG 2023-10-06):

Add a new bullet after 10.4 [module.global.frag] bullet 3.5.7 as follows:




2759. [[no_unique_address] and common initial sequence

Section: 11.4.1  [class.mem.general]     Status: DR     Submitter: Richard Smith     Date: 2020-11-10

[Accepted as a DR at the November, 2023 meeting.]

The interaction of [[no_unique_address]] and the definition of common initial sequence is still problematic. Subclause 11.4.1 [class.mem.general] bullet 23.3 specifies that corresponding members in a common initial sequence are not allowed to differ with respect to the presence or absence of a [[no_unique_address]] attribute. However, the Itanium ABI will not allocate two successive data members of the same empty class type at the same address, causing non-conforming behavior for the following example:

  struct A {};
  struct B {};

  struct C {
   [[no_unique_address]] A a;
   [[no_unique_address]] B b;
  };

  struct D {
   [[no_unique_address]] A a1;
   [[no_unique_address]] A a2;
  };

  static_assert(offsetof(C, b) == offsetof(D, a2));

See Itanium ABI issue 108.

Since "common initial sequence" and "layout compatible" are concepts mostly used for C compatibility, but [[no_unique_address]] does not exist in C, it seems reasonable to terminate a common initial sequence at the first data member that is declared [[no_unique_address]].

Another concern is the behavior of std::is_layout_compatible on implementations that ignore [[no_unique_address]]. On such an implementation, the following example would be considered layout-compatible, although it actually is not:

  struct E {};

  struct A {
    E e;
    int i;
  };

  struct B {
    [[no_unique_address]] E e;
    int i;
  };

  static_assert(
    std::is_layout_compatible_v<A, B>
  );

Alternative possible resolution [SUPERSEDED]:

Change in 11.4.1 [class.mem.general] paragraph 23 as follows:

The common initial sequence of two standard-layout struct (11.2 [class.prop]) types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that

Proposed resolution (approved by CWG 2023-08-25):

Change in 11.4.1 [class.mem.general] paragraph 23 as follows:

The common initial sequence of two standard-layout struct (11.2 [class.prop]) types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that



2595. "More constrained" for eligible special member functions

Section: 11.4.4  [special]     Status: DR     Submitter: Barry Revzin     Date: 2022-06-08

[Accepted as a DR at the November, 2023 meeting.]

Consider:

  #include <type_traits>

  template<typename T>
  concept Int = std::is_same_v<T, int>;

  template<typename T>
  concept Float = std::is_same_v<T, float>;

  template<typename T>
  struct Foo {
    Foo() requires Int<T> = default; // #1
    Foo() requires Int<T> || Float<T> = default; // #2
  };

Per the wording, #1 is not eligible for Foo<float>, because the constraints are not satisfied. But #2 also is not eligible, because #1 is more constrained than #2. The intent is that #2 is eligible.

Proposed resolution (approved by CWG 2023-06-17):

Change in 11.4.4 [special] paragraph 6 as follows:

An eligible special member function is a special member function for which:



2761. Implicitly invoking the deleted destructor of an anonymous union member

Section: 11.4.7  [class.dtor]     Status: DR     Submitter: Corentin Jabot     Date: 2023-07-11

[Accepted as a DR at the November, 2023 meeting.]

Consider:

  struct S{
    ~S() {}
  };

  struct A {
    union {
      S arr_;
    };
    ~A(); // user-provided!
  };

  auto foo() {
    return A{S()};
  }

Does the destructor of A attempt to destroy the (unnamed) data member that is the anonymous union? The latter has a deleted destructor per 11.4.7 [class.dtor]. For the default constructor, 11.9.3 [class.base.init] paragraph 9.2 prevents the corresponding construction.

Proposed resolution (approved by CWG 2023-08-25):

  1. Change in 9.4.2 [dcl.init.aggr] paragraph 9 as follows:

    The destructor for each element of class type other than an anonymous union member is potentially invoked (11.4.7 [class.dtor]) from the context where the aggregate initialization occurs
  2. Change in 11.4.7 [class.dtor] paragraph 13 as follows:

    After executing the body of the destructor and destroying any objects with automatic storage duration allocated within the body, a destructor for class X calls the destructors for X's direct non-variant non-static data members other than anonymous unions, the destructors for X's non-virtual direct base classes and, if X is the most derived class (11.9.3 [class.base.init]), its destructor calls the destructors for X's virtual base classes. All destructors are called as if they were referenced with a qualified name, that is, ignoring any possible virtual overriding destructors in more derived classes. Bases and members are destroyed in the reverse order of the completion of their constructor (see 11.9.3 [class.base.init]).
  3. Change in 14.3 [except.ctor] paragraph 3 as follows:

    A subobject is known to be initialized if it is not an anonymous union member and its initialization is specified
    • in 11.9.3 [class.base.init] for initialization by constructor,
    • in 11.4.5.3 [class.copy.ctor] for initialization by defaulted copy/move constructor,
    • in 11.9.4 [class.inhctor.init] for initialization by inherited constructor,
    • in 9.4.2 [dcl.init.aggr] for aggregate initialization,
    • in 7.5.5.3 [expr.prim.lambda.capture] for the initialization of the closure object when evaluating a lambda-expression,
    • in 9.4.1 [dcl.init.general] for default-initialization, value-initialization, or direct-initialization of an array.



2807. Destructors declared consteval

Section: 11.4.7  [class.dtor]     Status: DR     Submitter: Corentin Jabot     Date: 2023-09-07

[Accepted as a DR at the November, 2023 meeting.]

There is a conflict between 9.2.6 [dcl.constexpr] paragraph 2

A destructor, an allocation function, or a deallocation function shall not be declared with the consteval specifier.

and 11.4.7 [class.dtor] paragraph 1

Each decl-specifier of the decl-specifier-seq of a prospective destructor declaration (if any) shall be friend, inline, virtual, constexpr, or consteval.

Proposed resolution (approved by CWG 2023-10-20):

Change in 11.4.7 [class.dtor] paragraph 1 as follows:

Each decl-specifier of the decl-specifier-seq of a prospective destructor declaration (if any) shall be friend, inline, virtual, or constexpr, or consteval.



2591. Implicit change of active union member for anonymous union in union

Section: 11.5.1  [class.union.general]     Status: DR     Submitter: Richard Smith     Date: 2022-05-29

[Accepted as a DR at the November, 2023 meeting.]

Subclause 11.5.1 [class.union.general] paragraph 6 describes how union member subobjects are implicitly created by certain assignment operations that assign to union members. However, this description does not appear to properly handle the case of an anonymous union appearing within a union:

  union A {
    int x;
    union {
     int y;
    };
  };
  void f() {
    A a = {.x = 1};
    a.y = 2;
  }

Here, the expectation is that the assignment to a.y starts the lifetime of the anonymous union member subobject within A and also the int member subobject of the anonymous union member subobject. But the algorithm for computing S(a.y) determines that it is {a.y} and does not include the anonymous union member subobject.

Proposed resolution (approved by CWG 2023-06-17):

Change in 11.5.1 [class.union.general] paragraph 6 as follows:

In an assignment expression of the form E1 = E2 that uses either the built-in assignment operator (7.6.19 [expr.ass]) or a trivial assignment operator (11.4.6 [class.copy.assign]), for each element X of S(E1) and each anonymous union member X (11.5.2 [class.union.anon]) that is a member of a union and has such an element as an immediate subobject (recursively), if modification of X would have undefined behavior under 6.7.3 [basic.life], an object of the type of X is implicitly created in the nominated storage; no initialization is performed and the beginning of its lifetime is sequenced after the value computation of the left and right operands and before the assignment.

Editing note: Adding this rule into the definition of S would be more logical, but S(E) is a set of subexpressions of E and there is no form of expression that names an anonymous union member. Redefining S(E) to be a set of objects might be a better option.




2504. Inheriting constructors from virtual base classes

Section: 11.9.4  [class.inhctor.init]     Status: DR     Submitter: Hubert Tong     Date: 2021-11-03

[Accepted as a DR at the November, 2023 meeting.]

According to 11.9.4 [class.inhctor.init] paragraph 1,

When a constructor for type B is invoked to initialize an object of a different type D (that is, when the constructor was inherited (9.9 [namespace.udecl])), initialization proceeds as if a defaulted default constructor were used to initialize the D object and each base class subobject from which the constructor was inherited, except that the B subobject is initialized by the invocation of the inherited constructor. The complete initialization is considered to be a single function call; in particular, the initialization of the inherited constructor's parameters is sequenced before the initialization of any part of the Dobject.

First, this assumes that the base class constructor will be invoked from the derived class constructor, which will not be true if the base is virtual and initialized by a more-derived constructor.

If the call to the virtual base constructor is omitted, the last sentence is unclear whether the initialization of the base class constructor's parameters by the inheriting constructor occurs or not. There is implementation divergence in the initialization of V's parameter in the following example:

  struct NonTriv {
    NonTriv(int);
    ~NonTriv();
  };
  struct V { V() = default; V(NonTriv); };
  struct Q { Q(); };
  struct A : virtual V, Q {
    using V::V;
    A() : A(42) { }
  };
  struct B : A { };
  void foo() { B b; }

CWG telecon 2022-09-23:

Inheriting constructors from a virtual base class ought to be ill-formed. Inform EWG accordingly.

Possible resolution [SUPERSEDED]:

  1. Change in 9.9 [namespace.udecl] paragraph 3 as follows:

    ... If a using-declarator names a constructor, its nested-name-specifier shall name a direct non-virtual base class of the current class. If the immediate (class) scope is associated with a class template, it shall derive from the specified base class or have at least one dependent base class.
  2. Change the example in 11.9.4 [class.inhctor.init] paragraph 1 as follows:

    D2 f(1.0);  // error: B1 has a deleted no default constructor
    
    struct W { W(int); };
    struct X : virtual W { using W::W; X() = delete; };
    struct Y : X { using X::X; };
    struct Z : Y, virtual W { using Y::Y; };
    Z z(0);  // OK, initialization of Y does not invoke default constructor of X
    
  3. Change the example in 11.9.4 [class.inhctor.init] paragraph 2 as follows:

    struct V1 : virtual B { using B::B; };
    struct V2 : virtual B { using B::B; };
    
    struct D2 : V1, V2 {
      using V1::V1;
      using V2::V2;
    };
    D1 d1(0);  // error: ambiguous
    D2 d2(0);  // OK, initializes virtual B base class, which initializes the A base class
               // then initializes the V1 and V2 base classes as if by a defaulted default constructor
    

CWG telecon 2022-10-07:

Given that there are examples that discuss inheriting constructors from virtual base classes and given the existing normative wording, making it clear that NonTriv is not constructed, CWG felt that the implementation divergence is best addressed by amending the examples.

Possible resolution [SUPERSEDED]:

Add another example before 11.9.4 [class.inhctor.init] paragraph 2 as follows:

[ Example:

struct NonTriv {
  NonTriv(int);
  ~NonTriv();
};
struct V { V() = default; V(NonTriv); };
struct Q { Q(); };
struct A : virtual V, Q {
  using V::V;
  A() : A(42) { }    // #1, A(42) is equivalent to V(42)
};
struct B : A { };
void foo() { B b; }

In this example, the V subobject of b is constructed using the defaulted default constructor. The mem-initializer naming the constructor inherited from V at #1 is not evaluated and thus no object of type NonTriv is constructed. -- end example ]

If the constructor was inherited from multiple base class subobjects of type B, the program is ill-formed.

Proposed resolution (approved by CWG 2023-11-06):

  1. Change in 11.9.4 [class.inhctor.init] paragraph 1 as follows:

    When a constructor for type B is invoked to initialize an object of a different type D (that is, when the constructor was inherited (9.9 [namespace.udecl])), initialization proceeds as if a defaulted default constructor were used to initialize the D object and each base class subobject from which the constructor was inherited, except that the B subobject is initialized by the invocation of the inherited constructor if the base class subobject were to be initialized as part of the D object (11.9.3 [class.base.init]). The invocation of the inherited constructor, including the evaluation of any arguments, is omitted if the B subobject is not to be initialized as part of the D object. The complete initialization is considered to be a single function call; in particular, unless omitted, the initialization of the inherited constructor's parameters is sequenced before the initialization of any part of the Dobject.
  2. Add another example before 11.9.4 [class.inhctor.init] paragraph 2 as follows:

    [ Example:

    struct V { V() = default; V(int); };
    struct Q { Q(); };
    struct A : virtual V, Q {
      using V::V;
      A() = delete;
    };
    int bar() { return 42; }
    struct B : A {
      B() : A(bar()) {}  // ok
    };
    struct C : B {};
    void foo() { C c; } // bar is not invoked, because the V subobject is not initialized as part of B
    

    -- end example ]

CWG telecon 2022-10-21:

This is an ABI break for implementations when transitioning to the C++17 model for inheriting constructors.




2762. Type of implicit object parameter

Section: 12.2.2.1  [over.match.funcs.general]     Status: DR     Submitter: Jim X     Date: 2023-07-11

[Accepted as a DR at the November, 2023 meeting.]

Subclause 12.2.2.1 [over.match.funcs.general] paragraph 4 specifies:

For implicit object member functions, the type of the implicit object parameter is where X is the class of which the function is a member and cv is the cv-qualification on the member function declaration.

Since a member of some class C is also a member of any class derived from C, this specification is unclear.

Proposed resolution (approved by CWG 2023-08-25):

Change in 12.2.2.1 [over.match.funcs.general] paragraph 4 as follows:

For implicit object member functions, the type of the implicit object parameter is where X is the class of which the function is a direct member and cv is the cv-qualification on the member function declaration.



2628. Implicit deduction guides should propagate constraints

Section: 12.2.2.9  [over.match.class.deduct]     Status: DR     Submitter: Roy Jacobson     Date: 2022-09-11

[Accepted as a DR at the November, 2023 meeting.]

Consider:

template<class T> concept True = true;

template<class T> struct X {
  template<class U> requires True<T> X(T, U(&)[3]);
};
template<typename T, typename U> X(T, U(&)[3]) -> X<T>;
int arr3[3];
X z(3, arr3);     // #1

According to 12.2.2.9 [over.match.class.deduct] bullet 1.1, the requires-clause of the constructor is not propagated to the function template synthesized for the implicit deduction guide. Thus, instead of favoring the more-constrained implicit deduction guide per 12.2.4.1 [over.match.best.general] bullet 2.6, the user-declared deduction-guide is preferred per 12.2.4.1 [over.match.best.general] bullet 2.11.

Proposed resolution (approved by CWG 2023-10-20):

Change in 12.2.2.9 [over.match.class.deduct] bullet 1.1 as follows:




2789. Overload resolution with implicit and explicit object member functions

Section: 12.2.4.1  [over.match.best.general]     Status: DR     Submitter: Corentin Jabot     Date: 2023-08-08

[Accepted as a DR at the November, 2023 meeting.]

Consider:

  template <typename T = int>
  struct S {
    constexpr void f();                      // #1
    constexpr void f(this S&) requires true; // #2
  };

  void test() {
    S<> s;
    s.f();                 // #3
  }

With the current rules, the call at #3 is ambiguous, even though #2 is more constrainted.

Proposed resolution (approved by CWG 2023-11-07):

Change in 12.2.4.1 [over.match.best.general] bullet 2.6 as follows:




1038. Overload resolution of &x.static_func

Section: 12.3  [over.over]     Status: DR     Submitter: Mike Miller     Date: 2010-03-02

[Accepted as a DR at the November, 2023 meeting.]

The Standard is not clear whether the following example is well-formed or not:

    struct S {
        static void f(int);
        static void f(double);
    };
    S s;
    void (*pf)(int) = &s.f;

According to 7.6.1.5 [expr.ref] bullet 4.3, you do function overload resolution to determine whether x.f is a static or non-static member function. 7.6.2.2 [expr.unary.op] paragraph 6 says that you can only take the address of an overloaded function in a context that determines the overload to be chosen, and the initialization of a function pointer is such a context (12.3 [over.over] paragraph 1) . The problem is that 12.3 [over.over] is phrased in terms of “an overloaded function name,” and this is a member access expression, not a name.

There is variability among implementations as to whether this example is accepted; some accept it as written, some only if the & is omitted, and some reject it in both forms.

Additional note (October, 2010):

A related question concerns an example like

    struct S {
        static void g(int*) {}
        static void g(long) {}
    } s;

    void foo() {
        (&s.g)(0L);
    }

Because the address occurs in a call context and not in one of the contexts mentioned in 12.3 [over.over] paragraph 1, the call expression in foo is presumably ill-formed. Contrast this with the similar example

    void g1(int*) {}
    void g1(long) {}

    void foo1() {
        (&g1)(0L);
    }

This call presumably is well-formed because 12.2.2.2 [over.match.call] applies to “the address of a set of overloaded functions.” (This was clearer in the wording prior to the resolution of issue 704: “...in this context using &F behaves the same as using the name F by itself.”) It's not clear that there's any reason to treat these two cases differently.

This question also bears on the original question of this issue, since the original wording of 12.2.2.2 [over.match.call] also described the case of an ordinary member function call like s.g(0L) as involving the “name” of the function, even though the postfix-expression is a member access expression and not a “name.” Perhaps the reference to “name” in 12.3 [over.over] should be similarly understood as applying to member access expressions?

Additional notes (February, 2023)

This appears to be resolved, in part by P1787R6 (accepted November, 2020).

CWG 2023-06-12

The clarifications in P1787R6 did not address the core of this issue, so it is kept open. In order to avoid confusion, a wording change to clarify the treatment (regardless of direction) seems advisable. CWG felt that the first and second examples should be treated consistently, and expressed a mild preferences towards making those ill-formed. It was noted that the reference to id-expression in 12.3 [over.over] can be understood to refer to the id-expression of a class member access.

This issue is resolved by issue 2725.




2806. Make a type-requirement a type-only context

Section: 13.8.1  [temp.res.general]     Status: DR     Submitter: Barry Revzin     Date: 2023-10-10

[Accepted as a DR at the November, 2023 meeting.]

Consider:

  template <typename T>
  concept C = requires {
    typename T::type<void>;   // template required?
  };

There is implementation divergence: gcc accepts, clang and MSVC reject.

A type-requirement ought to be a type-only context.

Proposed resolution (approved by CWG 2023-10-20):

  1. Change in 7.5.7.3 [expr.prim.req.type] paragraph 1 as follows:

    A type-requirement asserts the validity of a type. The component names of a type-requirement are those of its nested-name-specifier (if any) and type-name. [Note 1: The enclosing requires-expression will evaluate to false if substitution of template arguments fails. —end note]
  2. Change in 13.8.1 [temp.res.general] paragraph 4 as follows:

    A qualified or unqualified name is said to be in a type-only context if it is the terminal name of
    • a typename-specifier, type-requirement, nested-name-specifier, elaborated-type-specifier, class-or-decltype, or
    • ...



2600. Type dependency of placeholder types

Section: 13.8.3.3  [temp.dep.expr]     Status: DR     Submitter: Hubert Tong     Date: 2022-06-18

[Accepted as a DR at the November, 2023 meeting.]

Subclause 13.8.3.2 [temp.dep.type] paragraph 7 has a list of types considered to be dependent. This list covers placeholder types only insofar as it has an entry about decltype(expression). Subclause 13.8.3.3 [temp.dep.expr] paragraph 3 has a list of expression forms not considered dependent unless specific types named by the expressions are dependent. This list includes forms where placeholder types are allowed. For example, the wording does not say that the new-expression at #1 (below) is dependent, but it ought to be:

  template <typename T> struct A { A(bool, T); };

  void g(...);

  template <typename T>
  auto f(T t) { return g(new A(t, 0)); }  // #1

  int g(A<int> *);
  int h() { return f<void *>(nullptr); }

Some implementation even treats an obviously non-dependent case as dependent:

  template <typename T, typename U> struct A { A(T, U); };

  void g(...); // #1

  template <typename T>
  auto f() { return g(new A(0, 0)); } // #1 or #2?

  int g(A<int, int> *); // #2
  void h() { return f<void *>(); }

A similar example that is non-dependent:

  template <typename T, typename U = T> struct A { A(T, U); };

  void g(...);

  template <typename T>
  auto f() { return g(new A(0, 0)); }

  int g(A<int> *);
  void h() { return f<void *>(); }

And another non-dependent one:

  template <typename T, typename U = T> struct A { A(T); };

  void g(...);

  template <typename T>
  auto f() { return g(new A(0)); }

  int g(A<int> *);
  void h() { return f<void *>(); }

And here is an example that is dependent:

  template<class T>
  struct S {
   template<class U = T> struct A { A(int); };

   auto f() { return new A(0); } // dependent return type
  };

Proposed resolution (November, 2022) [SUPERSEDED]:

  1. Change in 7.6.2.8 [expr.new] paragraph 2 as follows:

    If a placeholder type (9.2.9.7 [dcl.spec.auto]) or a placeholder for a deduced class type (9.2.9.8 [dcl.type.class.deduct]) appears in the type-specifier-seq of a new-type-id or type-id of a new-expression, the allocated type is deduced as follows: Let init be the new-initializer , if any, and T be the new-type-id or type-id of the new-expression, then the allocated type is the type deduced for the variable x in the invented declaration (9.2.9.7 [dcl.spec.auto]):
    T x init ;
    
  2. Insert new paragraphs before 13.8.3.2 [temp.dep.type] paragraph 7 and change the latter as follows:

    An initializer is dependent if any constituent expression (6.9.1 [intro.execution]) of the initializer is type-dependent. A placeholder type (9.2.9.7.1 [dcl.spec.auto.general]) is dependent if it designates a type deduced from a dependent initializer.

    A placeholder for a deduced class type (9.2.9.8 [dcl.type.class.deduct]) is dependent if

    • it has a dependent initializer or
    • any default template-argument of the primary class template named by the placeholder is dependent when considered in the scope enclosing the primary class template.

    A type is dependent if it is

    • ...
    • a function type whose exception specification is value-dependent,
    • denoted by a dependent placeholder type,
    • denoted by a dependent placeholder for a deduced class type,
    • ...

Proposed resolution (approved by CWG 2023-06-12):

  1. Change in 7.6.2.8 [expr.new] paragraph 2 as follows:

    If a placeholder type (9.2.9.7 [dcl.spec.auto]) or a placeholder for a deduced class type (9.2.9.8 [dcl.type.class.deduct]) appears in the type-specifier-seq of a new-type-id or type-id of a new-expression, the allocated type is deduced as follows: Let init be the new-initializer , if any, and T be the new-type-id or type-id of the new-expression, then the allocated type is the type deduced for the variable x in the invented declaration (9.2.9.7 [dcl.spec.auto]):
    T x init ;
    
  2. Insert new paragraphs before 13.8.3.2 [temp.dep.type] paragraph 7 and change the latter as follows:

    An initializer is dependent if any constituent expression (6.9.1 [intro.execution]) of the initializer is type-dependent. A placeholder type (9.2.9.7.1 [dcl.spec.auto.general]) is dependent if it designates a type deduced from a dependent initializer.

    A placeholder for a deduced class type (9.2.9.8 [dcl.type.class.deduct]) is dependent if

    • it has a dependent initializer, or
    • it refers to an alias template that is a member of the current instantiation and whose defining-type-id is dependent after class template argument deduction (12.2.2.9 [over.match.class.deduct]) and substitution (13.7.8 [temp.alias]).

    [ Example:

      template<class T, class V>
      struct S { S(T); };
    
      template<class U>
      struct A {
        template<class T> using X = S<T, U>;
        template<class T> using Y = S<T, int>;
        void f() {
          new X(1);    // dependent
          new Y(1);    // not dependent
        }
      };
    

    -- end example ]

    A type is dependent if it is

    • ...
    • a function type whose exception specification is value-dependent,
    • denoted by a dependent placeholder type,
    • denoted by a dependent placeholder for a deduced class type,
    • ...



2785. Type-dependence of requires-expression

Section: 13.8.3.3  [temp.dep.expr]     Status: DR     Submitter: CWG     Date: 2023-07-17

[Accepted as a DR at the November, 2023 meeting.]

(Split off from issue 2774.)

Subclause 13.8.3.3 [temp.dep.expr] is lacking specifiation about the type-dependence of requires-expressions.

Proposed resolution (approved by CWG 2023-08-25):

Change in 13.8.3.3 [temp.dep.expr] paragraph 4 as follows:

Expressions of the following forms are never type-dependent (because the type of the expression cannot be dependent):
  ...
  noexcept ( expression )
  requires-expression



2054. Missing description of class SFINAE

Section: 13.10.3  [temp.deduct]     Status: DR     Submitter: Ville Voutilainen     Date: 2014-12-07

[Accepted as a DR at the November, 2023 meeting.]

Presumably something like the following should be well-formed, where a deduction failure in a partial specialization is handled as a SFINAE case as it is with function templates and not a hard error:

  template <class T, class U> struct X   {
    typedef char member;
  };

  template<class T> struct X<T,
   typename enable_if<(sizeof(T)>sizeof(
     float)), float>::type>
  {
    typedef long long member;
  };

  int main() {
    cout << sizeof(X<double, float>::member);
  }

However, this does not appear to be described anywhere in the Standard.

Additional notes (January, 2023)

The section on SFINAE (13.10.3.1 [temp.deduct.general] paragraph 8) is not specific to function templates, and 13.7.6.2 [temp.spec.partial.match] paragraph 2 hands off the "matching" determination for partial specializations to 13.10.3 [temp.deduct] in general. However, the definition of deduction substitution loci in 13.10.3.1 [temp.deduct.general] paragraph 7 does not account for the template argument list of a partial specialization.

Proposed resolution (approved by CWG 2023-11-08):

Change in 13.10.3.1 [temp.deduct.general] paragraph 7 as follows:

The deduction substitution loci are The substitution occurs in all types and expressions that are used in the deduction substitution loci. ...



2672. Lambda body SFINAE is still required, contrary to intent and note

Section: 13.10.3.1  [temp.deduct.general]     Status: DR     Submitter: Richard Smith     Date: 2022-09-30

[Accepted as a DR at the November, 2023 meeting.]

Subclause 13.10.3.1 [temp.deduct.general] paragraph 9 specifies:

A lambda-expression appearing in a function type or a template parameter is not considered part of the immediate context for the purposes of template argument deduction. [Note 7: The intent is to avoid requiring implementations to deal with substitution failure involving arbitrary statements. ... -- end note ]

However, the intent of the note is not satisfied by the normative rule, because a lambda-expression appearing in a requires-expression has the same concerns as one in a function signature.

Suggested resolution: Change the rule to say that substitution into the body of a lambda is never in the immediate context of substitution into the lambda-expression and move the rule somewhere more general.

Possible resolution (reviewed by CWG 2023-08-25) [SUPERSEDED]:

  1. Change in 13.5.2.3 [temp.constr.atomic] paragraph 3 as follows:

    To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its expression. If substitution results in an invalid type or expression in the immediate context of the atomic constraint (13.10.3.1 [temp.deduct.general]), the constraint is not satisfied. Otherwise, the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is performed if necessary, and E shall be a constant expression of type bool. The constraint is satisfied if and only if evaluation of E results in true.
  2. Change in 13.10.3.1 [temp.deduct.general] paragraph 9 as follows:

    A lambda-expression appearing in a function type or a template parameter Substituting into the body of a lambda-expression is not considered part of never in the immediate context for the purposes of template argument deduction of substitution into the lambda-expression. [Note 7: The intent is to avoid requiring implementations to deal with substitution failure involving arbitrary statements.

Proposed resolution (approved by CWG 2023-11-09):

  1. Change in 7.5.7.1 [expr.prim.req.general] paragraph 5 as follows:

    The substitution of template arguments into a requires-expression may can result in the formation of invalid types or expressions in the immediate context of its requirements (13.10.3.1 [temp.deduct.general]) or the violation of the semantic constraints of those requirements. In such cases, the requires-expression evaluates to false; it does not cause the program to be ill-formed. The substitution and semantic constraint checking proceeds in lexical order and stops when a condition that determines the result of the requires-expression is encountered. If substitution (if any) and semantic constraint checking succeed, the requires-expression evaluates to true.
  2. Change in 13.5.2.3 [temp.constr.atomic] paragraph 3 as follows:

    To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its expression. If substitution results in an invalid type or expression in the immediate context of the atomic constraint (13.10.3.1 [temp.deduct.general]), the constraint is not satisfied. Otherwise, the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is performed if necessary, and E shall be a constant expression of type bool. The constraint is satisfied if and only if evaluation of E results in true.
  3. Change in 13.10.3.1 [temp.deduct.general] paragraph 9 as follows:

    A lambda-expression appearing in a function type or a template parameter is not considered part of the immediate context for the purposes of template argument deduction. When substituting into a lambda-expression, substitution into its body is not in the immediate context. [Note 7: The intent is to avoid requiring implementations to deal with substitution failure involving arbitrary statements.



2772. Missing Annex C entry for linkage effects of linkage-specification

Section: C.6.4  [diff.cpp03.dcl.dcl]     Status: DR     Submitter: Hubert Tong     Date: 2023-07-15

[Accepted as a DR at the November, 2023 meeting.]

With C++11, anonymous namespaces changed from external linkage (with a unique namespace name) to internal linkage. That implies that extern "C", which affects names with external linkage only, no longer has an effect inside anonymous namespaces.

However, a corresponding Annex C entry is missing.

Proposed resolution (approved by CWG 2023-09-15):

Add a new paragraph in C.6.4 [diff.cpp03.dcl.dcl] as follows:

Affected subclause: 9.11 [dcl.link]
Change: Names declared in an anonymous namespace changed from external linkage to internal linkage; language linkage applies to names with external linkage only.
Rationale: Alignment with user expectations.
Effect on original feature: Valid C++ 2003 code may violate the one-definition rule (6.3 [basic.def.odr]) in this revision of C++. For example:
  namespace { extern "C" { extern int x; } }  // #1, previously external linkage and C language linkage, now internal linkage and C++ language linkage
  namespace A { extern "C" int x = 42; }      // #2, external linkage and C language linkage
  int main(void) { return x; }
This code is valid in C++ 2003, but #2 is not a definition for #1 in this revision of C++, violating the one-definition rule.





Issues with "accepted" Status




Issues with "DRWP" Status


2573. Undefined behavior when splicing results in a universal-character-name

Section: 5.2  [lex.phases]     Status: DRWP     Submitter: US     Date: 2019-10-23     Liaison: SG12

[ Resolved by paper P2621R2 (Undefined behavior in the lexer), adopted in June, 2023. ]

(From National Body comments US 024 and US 025 on the C++20 DIS.)

Subclause 5.2 [lex.phases] bullet 1.2 specifies:

Except for splices reverted in a raw string literal, if a splice results in a character sequence that matches the syntax of a universal-character-name, the behavior is undefined.

Undefined behavior during lexing is not acceptable. The behavior ought to be well-defined, ill-formed, or conditionally-supported.

Additional notes (January, 2023):

Forwarded to SG12 with paper issue 1405, by decision of the CWG and SG12 chairs.




2574. Undefined behavior when lexing unmatched quotes

Section: 5.4  [lex.pptoken]     Status: DRWP     Submitter: US     Date: 2019-10-23     Liaison: SG12

[ Resolved by paper P2621R2 (Undefined behavior in the lexer), adopted in June, 2023. ]

(From National Body comment US 027 on the C++20 DIS.)

Subclause 5.4 [lex.pptoken] paragraph 2 specifies:

If a U+0027 apostrophe or a U+0022 quotation mark character matches the last category, the behavior is undefined.

Undefined behavior during lexing is not acceptable. This ought to be ill-formed.

Additional notes (January, 2023):

Forwarded to SG12 with paper issue 1406, by decision of the CWG and SG12 chairs.




2698. Using extended integer types with z suffix

Section: 5.13.2  [lex.icon]     Status: DRWP     Submitter: Mike Miller     Date: 2023-02-17     Liaison: WG14, EWG

[Accepted as a DR at the June, 2023 meeting.]

Subclause 5.13.2 [lex.icon] paragraph 4 specifies:

If an integer-literal cannot be represented by any type in its list and an extended integer type (6.8.2 [basic.fundamental]) can represent its value, it may have that extended integer type. If all of the types in the list for the integer-literal are signed, the extended integer type shall be signed. If all of the types in the list for the integer-literal are unsigned, the extended integer type shall be unsigned. If the list contains both signed and unsigned types, the extended integer type may be signed or unsigned. A program is ill-formed if one of its translation units contains an integer-literal that cannot be represented by any of the allowed types.

This implies that an integer-literal with a z suffix can be of extended integer type, if the literal is larger than what is representable in std::size_t.

According to the author of the paper P0330R8 (Literal Suffix for (signed) size_t) introducing the feature, this is unintentional; z should only yield std::size_t and its corresponding signed integer type.

See also the corresponding WG14 paper N2998 Literal Suffixes for size_t.

Proposed resolution (reviewed by CWG 2023-03-03, approved by CWG 2023-05-12):

Change in 5.13.2 [lex.icon] paragraph 4 as follows:

If Except for integer-literals containing a size-suffix, if the value of an integer-literal cannot be represented by any type in its list and an extended integer type (6.8.2 [basic.fundamental]) can represent its value, it may have that extended integer type. If all of the types in the list for the integer-literal are signed, the extended integer type shall be is signed. If all of the types in the list for the integer-literal are unsigned, the extended integer type shall be is unsigned. If the list contains both signed and unsigned types, the extended integer type may be signed or unsigned. A program is ill-formed if one of its translation units contains If an integer-literal that cannot be represented by any of the allowed types, the program is ill-formed. [ Note: An integer-literal with a z or Z suffix is ill-formed if it cannot be represented by std::size_t. -- end note ]

Additional notes (February, 2023)

Alerted the chair of SG22 (C/C++ Liaison).

Forwarded to EWG at the request of the EWG chair via cplusplus/papers#1467.

EWG 2023-05-11

The "z" suffixes mean std::size_t (or its corresponding signed type) only. The proposed resolution is accepted by EWG.




2721. When exactly is storage reused?

Section: 6.7.3  [basic.life]     Status: DRWP     Submitter: Richard Smith     Date: 2023-03-23

[Accepted as a DR at the June, 2023 meeting.]

Subclause 6.7.3 [basic.life] bullet 1.5 specifies:

The lifetime of an object o of type T ends when:

Consider the expression new (p) T(x). Does the lifetime of *p end when p is returned from the allocation function, before x is evaluated? Or does the lifetime end when the constructor body starts executing, after x is evaluated?

The second option is conceivable for initialization by constructor and non-class types; for aggregate initialization, the first option must be used, because evaluation of x directly initializes a part of the resulting object. The first option is simpler to implement for constant evaluation.

Proposed resolution (approved by CWG 2023-05-12):

Change in 6.7.3 [basic.life] paragraph 1 as follows:

The lifetime of an object o of type T ends when: When evaluating a new-expression, storage is considered reused after it is returned from the allocation function, but before the evaluation of the new-initializer (7.6.2.8 [expr.new]). [ Example:
  struct S {
    int m;
  };

  void f() {
    S x{1};
    new(&x) S(x.m);  // undefined behavior
  }
-- end example ]



2719. Creating objects in misaligned storage

Section: 6.7.6  [basic.align]     Status: DRWP     Submitter: Jiang An     Date: 2023-04-05

[Accepted as a DR at the June, 2023 meeting.]

A non-allocating form of operator new can be used to create an object in storage that is not suitably aligned for the type of the object. Such attempts ought to be undefined behavior.

Proposed resolution (approved by CWG 2023-04-28):

  1. Change in 6.7.6 [basic.align] paragraph 1 as follows:

    Object types have alignment requirements (6.8.2 [basic.fundamental], 6.8.4 [basic.compound]) which place restrictions on the addresses at which an object of that type may be allocated. An alignment is an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated. An object type imposes an alignment requirement on every object of that type; stricter alignment can be requested using the alignment specifier (9.12.2 [dcl.align]). Attempting to create an object (6.7.2 [intro.object]) in storage that does not meet the alignment requirements of the object's type is undefined behavior.
  2. Change in 7.6.2.8 [expr.new] paragraph 22 as follows:

    [Note 11: When the allocation function returns a value other than null, it must be a pointer to a block of storage in which space for the object has been reserved. The block of storage is assumed to be appropriately aligned (6.7.6 [basic.align]) and of the requested size. The address of the created object will not necessarily be the same as that of the block if the object is an array. —end note]



2519. Object representation of a bit-field

Section: 6.8.1  [basic.types.general]     Status: DRWP     Submitter: Jiang An     Date: 2022-01-20

[Accepted as a DR at the June, 2023 meeting.]

6.7.2 [intro.object] clearly implies that bit-fields are objects; paragraphs 8-9 contain phrases like “unless an object is a bit-field...” and “a non-bit-field subobject”. However, the definition of “object representation” in 6.8.1 [basic.types.general] paragraph 4 is,

The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T).

and thus fails to address bit-fields, which are not necessarily composed of a sequence of complete bytes.

The C Standard (6.2.6.1 paragraph 4) says,

Values stored in bit-fields consist of m bits, where m is the size specified for the bit-field. The object representation is the set of m bits the bit-field comprises in the addressable storage unit holding it.

Presumably similar wording could be adopted for C++.

Proposed resolution (approved by CWG 2023-01-06) [SUPERSEDED]:

Change in 6.8.1 [basic.types.general] paragraph 4 as follows:

The object representation of an object of a type T is the sequence of N unsigned char objects taken up by the a non-bit-field complete object of type T, where N equals sizeof(T). The value representation of an object of a type T is the set of bits in the object representation of T that participate in representing a value of type T. The object and value representation of a non-bit-field complete object of type T are the bytes and bits, respectively, of the object corresponding to the object and value representation of its type. The object representation of a bit-field object is the sequence of N bits taken up by the object, where N is the width of the bit-field (11.4.10 [class.bit]). The value representation of a bit-field object is the set of bits in the object representation that participate in representing its value. Bits in the object representation of a type or object that are not part of the value representation are padding bits. For trivially copyable types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values. [ Footnote: ... ]

CWG 2023-02-06

Additional drafting is needed to constrain the definition to complete object types.

Proposed resolution (approved for C++26 by CWG 2023-02-06):

Change in 6.8.1 [basic.types.general] paragraph 4 as follows:

The object representation of an object of a complete object type T is the sequence of N unsigned char objects taken up by the a non-bit-field complete object of type T, where N equals sizeof(T). The value representation of an object of a type T is the set of bits in the object representation of T that participate in representing a value of type T. The object and value representation of a non-bit-field complete object of type T are the bytes and bits, respectively, of the object corresponding to the object and value representation of its type. The object representation of a bit-field object is the sequence of N bits taken up by the object, where N is the width of the bit-field (11.4.10 [class.bit]). The value representation of a bit-field object is the set of bits in the object representation that participate in representing its value. Bits in the object representation of a type or object that are not part of the value representation are padding bits. For trivially copyable types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values. [ Footnote: ... ]



2723. Range of representable values for floating-point types

Section: 6.8.2  [basic.fundamental]     Status: DRWP     Submitter: Jiang An     Date: 2023-04-21

[Accepted as a DR at the June, 2023 meeting.]

The range of representable values is defined for integer types, but not for floating-point types. This term is used in 5.13.4 [lex.fcon] paragraph 3 as well as in the library, e.g. in 22.13.3 [charconv.from.chars] and 30.4.3.2.3 [facet.num.get.virtuals].

The C standard contains a suitable definition that we should inherit.

Proposed resolution (approved by CWG 2023-05-12):

Add a new paragraph after 6.8.2 [basic.fundamental] paragraph 12 as follows:

... Except as specified in 6.8.3 [basic.extended.fp], the object and value representations and accuracy of operations of floating-point types are implementation-defined.

The minimum range of representable values for a floating-point type is the most negative finite floating-point number representable in that type through the most positive finite floating-point number representable in that type. In addition, if negative infinity is representable in a type, the range of that type is extended to all negative real numbers; likewise, if positive infinity is representable in a type, the range of that type is extended to all positive real numbers. [ Note: Since negative and positive infinity are representable in ISO/IEC/IEEE 60559 formats, all real numbers lie within the range of representable values of a floating-point type adhering to ISO/IEC/IEEE 60559. ]




2485. Bit-fields in integral promotions

Section: 7.3.7  [conv.prom]     Status: DRWP     Submitter: Richard Smith     Date: 2021-04-01

[Accepted as a DR at the June, 2023 meeting.]

According to 7.3.7 [conv.prom] paragraph 5,

A prvalue for an integral bit-field (11.4.10 [class.bit]) can be converted to a prvalue of type int if int can represent all the values of the bit-field; otherwise, it can be converted to unsigned int if unsigned int can represent all the values of the bit-field. If the bit-field is larger yet, no integral promotion applies to it. If the bit-field has an enumerated type, it is treated as any other value of that type for promotion purposes.

This description has several problems. First, the “bit-field” semantic property only makes sense for glvalue expressions, so it's unclear why these rules are described as applying to a prvalue. Perhaps this should be rephrased as something like “An expression that was a bit-field glvalue prior to the application of the lvalue-to-rvalue conversion”?

Second, suppose that char32_t is wider than int. Per paragraph 2, a char32_t prvalue promotes to unsigned long (because unsigned long is necessarily at least 32 bits wide). But per paragraph 5, a char32_t : 32 bitfield does not promote. This seems inconsistent.

Finally, it is not clear that the usual integral promotions are not applied to bit-fields. This should be made explicit.

Proposed resolution (approved by CWG 2023-02-07):

  1. Insert a paragraph before 7.3.7 [conv.prom] paragraph 1 as follows:

    For the purposes of 7.3.7 [conv.prom], a converted bit-field is a prvalue that is the result of an lvalue-to-rvalue conversion (7.3.2 [conv.lval]) applied to a bit-field (11.4.10 [class.bit]).
  2. Change in 7.3.7 [conv.prom] paragraph 1 as follows:

    A prvalue of that is not a converted bit-field and has an integer type other than bool, char8_t, char16_t, char32_t, or wchar_t whose integer conversion rank (6.8.6 [conv.rank]) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.
  3. Change in 7.3.7 [conv.prom] paragraph 4 as follows:

    A prvalue of an unscoped enumeration type whose underlying type is fixed (9.7.1 [dcl.enum]) can be converted to a prvalue of its underlying type. Moreover, if integral promotion can be applied to its underlying type, a prvalue of an unscoped enumeration type whose underlying type is fixed can also be converted to a prvalue of the promoted underlying type. [ Note: A converted bit-field of enumeration type is treated as any other value of that type for promotion purposes. -- end note ]
  4. Change in 7.3.7 [conv.prom] paragraph 5 as follows:

    A prvalue for an integral bit-field (11.4.10 [class.bit]) converted bit-field of integral type can be converted to a prvalue of type int if int can represent all the values of the bit-field; otherwise, it can be converted to unsigned int if unsigned int can represent all the values of the bit-field. If the bit-field is larger yet, no integral promotion applies to it. If the bit-field has enumeration type, it is treated as any other value of that type for promotion purposes.
  5. Move 7.3.7 [conv.prom] paragraph 2 after paragraph 5 and change as follows:

    A prvalue of type char8_t, char16_t, char32_t, or wchar_t (6.8.2 [basic.fundamental]) (including a converted bit-field that was not already promoted to int or unsigned int according to the rules above) can be converted to a prvalue of the first of the following types that can represent all the values of its underlying type: int, unsigned int, long int, unsigned long int, long long int, or unsigned long long int. If none of the types in that list can represent all the values of its underlying type, a prvalue of type char8_t, char16_t, char32_t, or wchar_t can be converted to a prvalue of , or its underlying type.



170. Pointer-to-member conversions

Section: 7.3.13  [conv.mem]     Status: DRWP     Submitter: Mike Stump     Date: 16 Sep 1999

[Accepted as a DR at the June, 2023 meeting.]

The descriptions of explicit (7.6.1.9 [expr.static.cast] paragraph 9) and implicit (7.3.13 [conv.mem] paragraph 2) pointer-to-member conversions differ in two significant ways:

  1. In a static_cast, a conversion in which the class in the target pointer-to-member type is a base of the class in which the member is declared is permitted and required to work correctly, as long as the resulting pointer-to-member is eventually dereferenced with an object whose dynamic type contains the member. That is, the class of the target pointer-to-member type is not required to contain the member referred to by the value being converted. The specification of implicit pointer-to-member conversion is silent on this question.

    (This situation cannot arise in an implicit pointer-to-member conversion where the source value is something like &X::f, since you can only implicitly convert from pointer-to-base-member to pointer-to-derived-member. However, if the source value is the result of an explicit "up-cast," the target type of the conversion might still not contain the member referred to by the source value.)

  2. The target type in a static_cast is allowed to be more cv-qualified than the source type; in an implicit conversion, however, the cv-qualifications of the two types are required to be identical.

The first difference seems like an oversight. It is not clear whether the latter difference is intentional or not.

(See also issue 794.)

CWG 2022-11-09

The second concern is NAD; implicit conversions allow chaining a pointer-to-member conversion with a qualification conversion.

Proposed resolution (approved by CWG 2023-02-06):

  1. Change in 7.3.13 [conv.mem] paragraph 2 as follows:

    A prvalue of type “pointer to member of B of type cv T”, where B is a class type, can be converted to a prvalue of type “pointer to member of D of type cv T”, where D is a complete class derived (11.7 [class.derived]) from B. If B is an inaccessible (11.8 [class.access]), ambiguous (6.5.2 [class.member.lookup]), or virtual (11.7.2 [class.mi]) base class of D, or a base class of a virtual base class of D, a program that necessitates this conversion is ill-formed. If class D does not contain the original member and is not a base class of the class containing the original member, the behavior is undefined. Otherwise, The the result of the conversion refers to the same member as the pointer to member before the conversion took place, but it refers to the base class member as if it were a member of the derived class. The result refers to the member in D's instance of B. Since the result has type “pointer to member of D of type cv T”, indirection through it with a D object is valid. The result is the same as if indirecting through the pointer to member of B with the B subobject of D. The null member pointer value is converted to the null member pointer value of the destination type. [ Footnote: ... ]
  2. Change in 7.6.1.9 [expr.static.cast] paragraph 13 as follows:

    ... If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. ...



1973. Which parameter-declaration-clause in a lambda-expression?

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: DRWP     Submitter: Dinka Ranns     Date: 2014-07-16

[Accepted as a DR at the June, 2023 meeting.]

According to 7.5.5.2 [expr.prim.lambda.closure] paragraph 3,

The closure type for a lambda-expression has a public inline function call operator (for a non-generic lambda) or function call operator template (for a generic lambda) (12.4.4 [over.call]) whose parameters and return type are described by the lambda-expression's parameter-declaration-clause and trailing-return-type respectively, and whose template-parameter-list consists of the specified template-parameter-list, if any.

This is insufficiently precise because the trailing-return-type might itself contain a parameter-declaration-clause.

Suggested resolution [SUPERSEDED]:

Change in 7.5.5.1 [expr.prim.lambda.general] paragraph 5 as follows:

If a lambda-declarator does not include a start with a parenthesized parameter-declaration-clause, it is as if () were inserted at the start of the lambda-declarator. A lambda-expression's parameter-declaration-clause is the (possibly empty) parameter-declaration-clause of the lambda-expression's lambda-declarator. If the lambda-declarator does not include a trailing-return-type, the lambda return type is auto, which is deduced from return statements as described in 9.2.9.7 [dcl.spec.auto].

Proposed resolution (approved by CWG 2023-02-06):

  1. Change in 7.5.5.2 [expr.prim.lambda.closure] paragraph 3 as follows:

    The closure type for a lambda-expression has a public inline function call operator (for a non-generic lambda) or function call operator template (for a generic lambda) (12.4.4 [over.call]) whose parameters and return type are described by those of the lambda-expression's parameter-declaration-clause and trailing-return-type respectively, and whose template-parameter-list consists of the specified template-parameter-list, if any.
  2. Change in 7.5.5.1 [expr.prim.lambda.general] paragraph 5 as follows:

    If a lambda-declarator does not include a parameter-declaration-clause, it is as if () were inserted at the start of the lambda-declarator. A lambda-expression's parameter-declaration-clause is the parameter-declaration-clause of the lambda-expression's lambda-declarator, if any, or empty otherwise. If the lambda-declarator does not include a trailing-return-type, the lambda return type is auto, which is deduced from return statements as described in 9.2.9.7 [dcl.spec.auto].



2542. Is a closure type a structural type?

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: DRWP     Submitter: Zhihao Yuan     Date: 2022-03-01

[Accepted as a DR at the June, 2023 meeting.]

Consider:

  template <auto V>
  void foo() {}

  void bar() {
    foo<[i = 3] { return i; }>();
  }

It is unclear whether the data members of a closure type are public or private. This makes a difference, since it affects whether a closure type is a structural type or not (13.2 [temp.param] paragraph 7:

A structural type is one of the following:

Proposed resolution (approved by CWG 2023-03-30):

Change in 7.5.5.2 [expr.prim.lambda.closure] paragraph 2 as follows:

... The closure type is not an aggregate type (9.4.2 [dcl.init.aggr]) and not a structural type (13.2 [temp.param]). ...



1642. Missing requirements for prvalue operands

Section: 7.6  [expr.compound]     Status: DRWP     Submitter: Joseph Mansfield     Date: 2013-03-15

[Accepted as a DR at the June, 2023 meeting.]

Although the note in 7.2.1 [basic.lval] paragraph 1 states that

The discussion of each built-in operator in Clause 7 [expr] indicates the category of the value it yields and the value categories of the operands it expects

in fact, many of the operators that take prvalue operands do not make that requirement explicit. Possible approaches to address this failure could be a blanket statement that an operand whose value category is not stated is assumed to be a prvalue; adding prvalue requirements to each operand description for which it is missing; or changing the description of the usual arithmetic conversions to state that they imply the lvalue-to-rvalue conversion, which would cover the majority of the omissions.

(See also issue 1685, which deals with an inaccurately-specified value category.)

Proposed resolution (approved by CWG 2023-04-28):

  1. Change in 7.2.1 [basic.lval] paragraph 6 as follows:

    Whenever a glvalue appears as an operand of an operator that expects requires a prvalue for that operand, the lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), or function-to-pointer (7.3.4 [conv.func]) standard conversions are applied to convert the expression to a prvalue. ...
  2. Change in 7.3.1 [conv.general] paragraph 1 as follows:

    ... A standard conversion sequence will be applied to an expression if necessary to convert it to an expression having a required destination type and value category. ...
  3. Add to the bulleted list in 7.4 [expr.arith.conv] paragraph 1 as follows:

    ... This pattern is called the usual arithmetic conversions, which are defined as follows:
    • The lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is applied to each operand and the resulting prvalues are used in place of the original operands for the remainder of this section.
    • If either operand is of scoped enumeration type (9.7.1 [dcl.enum]), no conversions are performed; if the other operand does not have the same type, the expression is ill-formed.
    • ...
  4. Change in 7.6.1.3 [expr.call] paragraph 1 as follows:

    ... For a call to a non-member function or to a static member function, the postfix expression shall be either be an lvalue that refers to a function (in which case the function-to-pointer standard conversion (7.3.4 [conv.func]) is suppressed on the postfix expression), or have a prvalue of function pointer type.
  5. Change in 7.6.2.2 [expr.unary.op] paragraph 7 as follows:

    The operand of the unary + operator shall have be a prvalue of arithmetic, unscoped enumeration, or pointer type and the result is the value of the argument. Integral promotion is performed on integral or enumeration operands. ...
  6. Change in 7.6.2.2 [expr.unary.op] paragraph 8 as follows:

    The operand of the unary - operator shall have be a prvalue of arithmetic or unscoped enumeration type and the result is the negative of its operand. Integral promotion is performed on integral or enumeration operands. ...
  7. Change in 7.6.2.2 [expr.unary.op] paragraph 10 as follows:

    The operand of the ~ operator shall have be a prvalue of integral or unscoped enumeration type. Integral promotions are performed. ...
  8. Change in 7.6.2.9 [expr.delete] paragraph 1 as follows:

    ... The operand shall be of pointer to object type or of class type. If the operand is of class type, the operand it is contextually implicitly converted (7.3 [conv]) to a pointer to object type. [ Footnote: ... ] Otherwise, it shall be a prvalue of pointer to object type. The delete-expression has type void.
  9. Change in 7.6.4 [expr.mptr.oper] paragraph 2 and 3 as follows:

    The binary operator .* binds its second operand, which shall be a prvalue of type “pointer to member of T” to its first operand, which shall be ...

    The binary operator ->* binds its second operand, which shall be a prvalue of type “pointer to member of T” to its first operand, which shall be ...

  10. Change in 7.6.6 [expr.add] paragraph 1 as follows:

    The additive operators + and - group left-to-right. The Each operand shall be a prvalue. If both operands have arithmetic or unscoped enumeration type, the usual arithmetic conversions (7.4 [expr.arith.conv]) are performed for operands of arithmetic or enumeration type. Otherwise, if one operand has arithmetic or unscoped enumeration type, integral promotion is applied (7.3.7 [conv.prom]) to that operand. A converted or promoted operand is used in place of the corresponding original operand for the remainder of this section. ... For addition, either both operands shall have arithmetic or unscoped enumeration type, or one operand shall be a pointer to a completely-defined object type and the other shall have integral or unscoped enumeration type.
  11. Change in 7.6.6 [expr.add] paragraph 2 as follows:

    For subtraction, one of the following shall hold:
    • both operands have arithmetic or unscoped enumeration type; or
    • both operands are pointers to cv-qualified or cv-unqualified versions of the same completely-defined object type; or
    • the left operand is a pointer to a completely-defined object type and the right operand has integral or unscoped enumeration type.
  12. Change in 7.6.7 [expr.shift] paragraph 1 as follows:

    ... The operands shall be prvalues of integral or unscoped enumeration type and integral promotions are performed. ...
  13. Change in 9.4.1 [dcl.init.general] bullet 16.9 as follows:

    • ...
    • Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression. A standard conversion sequence (7.3 [conv]) will be is used, if necessary, to convert the initializer expression to a prvalue of the cv-unqualified version of the destination type; no user-defined conversions are considered. ...
    • ...



2715. "calling function" for parameter initialization may not exist

Section: 7.6.1.3  [expr.call]     Status: DRWP     Submitter: Brian Bi     Date: 2023-04-02

[Accepted as a DR at the June, 2023 meeting.]

Subclause 7.6.1.3 [expr.call] paragraph 6 specifies:

... The initialization and destruction of each parameter occurs within the context of the calling function. [Example 2: The access of the constructor, conversion functions or destructor is checked at the point of call in the calling function. If a constructor or destructor for a function parameter throws an exception, the search for a handler starts in the calling function; in particular, if the function called has a function-try-block (14.1 [except.pre]) with a handler that can handle the exception, this handler is not considered. —end example]

However, there is no calling function in the case where a function call appears in the initializer of a namespace-scope variable. Likewise, some constant expressions appearing in a type-id do not have calling functions, either. For example:

  class C {
   private:
    constexpr int C(int) {}
    friend void foo(int (*a)[1]) noexcept;
  };

  constexpr int bar(C) { return 1; }

  void foo(int (&a)[bar(1)]) noexcept(bar(2) > 0); // presumably OK because of friendship

Proposed resolution (approved by CWG 2023-04-28):

Change in 7.6.1.3 [expr.call] paragraph 6 as follows:

... The initialization and destruction of each parameter occurs within the context of the calling function full-expression (6.9.1 [intro.execution]) where the function call appears. [Example 2: The access (11.8.1 [class.access.general]) of the constructor, conversion functions, or destructor is checked at the point of call in the calling function. If a constructor or destructor for a function parameter throws an exception, the search for a handler starts in the calling function; in particular, if the function called has a any function-try-block (14.1 [except.pre]) of the called function with a handler that can handle the exception, this handler is not considered. —end example]



2718. Type completeness for derived-to-base conversions

Section: 7.6.1.9  [expr.static.cast]     Status: DRWP     Submitter: Jim X     Date: 2023-04-09

[Accepted as a DR at the June, 2023 meeting.]

Issue 2310 clarified class completeness requirements for derived-to-base pointer conversions, but neglected the corresponding lvalue conversion.

Proposed resolution (approved by CWG 2023-04-28):

Change in 7.6.1.9 [expr.static.cast] paragraph 2 as follows:

An lvalue of type “cv1 B”, where B is a class type, can be cast to type “reference to cv2 D”, where D is a complete class derived (11.7 [class.derived]) from B, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. ...



2722. Temporary materialization conversion for noexcept operator

Section: 7.6.2.7  [expr.unary.noexcept]     Status: DRWP     Submitter: Brian Bi     Date: 2023-04-24

[Accepted as a DR at the June, 2023 meeting.]

It is unclear whether noexcept(A()) applies the temporary materialization conversion to the prvalue A(). The resolution of issue 1354 suggests that it does so that the destructor is (notionally) invoked.

Proposed resolution (approved by CWG 2023-05-12):

Change in 7.6.2.7 [expr.unary.noexcept] paragraph 3 as follows:

If the operand is a prvalue, the temporary materialization conversion (7.3.5 [conv.rval]) is applied. The result of the noexcept operator is true unless the expression full-expression of the operand is potentially-throwing (14.5 [except.spec]).



2729. Meaning of new-type-id

Section: 7.6.2.8  [expr.new]     Status: DRWP     Submitter: Jim X     Date: 2023-02-06

[Accepted as a DR at the June, 2023 meeting.]

Subclause 7.6.2.8 [expr.new] paragraph 1 introduces the grammar non-terminal new-type-id, but never specifies its meaning.

Proposed resolution (approved by CWG 2023-06-12):

  1. Change in 7.6.2.8 [expr.new] paragraph 1 as follows:

    The new-expression attempts to create an object of the type-id (9.3.2 [dcl.name]) or new-type-id (9.3.2 [dcl.name]) to which it is applied. The type of that object is the allocated type. This type shall be a complete object type (6.8.1 [basic.types.general]), but not an abstract class type (11.7.4 [class.abstract]) or array thereof (6.7.2 [intro.object]).
  2. Change in 9.3.2 [dcl.name] paragraph 1 as follows:

    To specify type conversions explicitly, and as an argument of sizeof, alignof, new, or typeid, the name of a type shall be specified. This can be done with a type-id or new-type-id (7.6.2.8 [expr.new]), which is syntactically a declaration for a variable or function of that type that omits the name of the entity.



2724. Clarify rounding for arithmetic right shift

Section: 7.6.7  [expr.shift]     Status: DRWP     Submitter: Jan Schultke     Date: 2023-04-07

[Accepted as a DR at the June, 2023 meeting.]

(From editorial issue 6225.)

Subclause 7.6.7 [expr.shift] paragraph 3 specifies:

The value of E1 >> E2 is E1/2E2, rounded down.

It is unclear whether "rounded down" means "towards zero" or "towards negative infinity".

Proposed resolution (approved by CWG 2023-05-12):

Change in 7.6.7 [expr.shift] paragraph 3 as follows:

The value of E1 >> E2 is E1/2E2, rounded down towards negative infinity.



2699. Inconsistency of throw-expression specification

Section: 7.6.18  [expr.throw]     Status: DRWP     Submitter: Krystian Stasiowski     Date: 2020-04-06

[Accepted as a DR at the June, 2023 meeting.]

Subclause 7.6.18 [expr.throw] paragraph 2 and 3 specify:

Evaluating a throw-expression with an operand throws an exception (14.2 [except.throw]); the type of the exception object is determined by removing any top-level cv-qualifier s from the static type of the operand and adjusting the type from “array of T” or function type T to “pointer to T”.

A throw-expression with no operand rethrows the currently handled exception (14.4 [except.handle]). The exception is reactivated with the existing exception object; no new exception object is created. The exception is no longer considered to be caught.

This means that throwing a value of type const char[3] would throw a char* rather than const char*, which is not intended.

Proposed resolution (approved by CWG 2023-03-03):

Change in 7.6.18 [expr.throw] paragraph 2 through 4 as follows:

Evaluating a A throw-expression with an operand throws an exception (14.2 [except.throw]); the. The array-to-pointer (7.3.3 [conv.array]) and function-to-pointer (7.3.4 [conv.func]) standard conversions are performed on the operand. The type of the exception object is determined by removing any top-level cv-qualifiers from the static type of the (possibly converted) operand and adjusting the type from “array of T” or function type T to “pointer to T”.

A throw-expression with no operand rethrows the currently handled exception (14.4 [except.handle]). If no exception is presently being handled, the function std:: terminate is invoked (14.6.2 [except.terminate]). The Otherwise, the exception is reactivated with the existing exception object; no new exception object is created. The exception is no longer considered to be caught.

If no exception is presently being handled, evaluating a throw-expression with no operand calls std:: terminate() (14.6.2 [except.terminate]).




2711. Source for copy-initializing the exception object

Section: 7.6.18  [expr.throw]     Status: DRWP     Submitter: Brian Bi     Date: 2023-03-26

[Accepted as a DR at the June, 2023 meeting.]

Neither 14.2 [except.throw] nor 7.6.18 [expr.throw] specifiy the source for copy-initializing the exception object.

Proposed resolution (based on the resolution of issue 2699; approved by CWG 2023-06-12):

  1. Change in 7.6.18 [expr.throw] paragraph 2 as follows:

    Evaluating a throw-expression with an operand throws an exception (14.2 [except.throw]); the type of the exception object is determined by removing any top-level cv-qualifiers from the static type of the operand and adjusting the type from “array of T” or function type T to “pointer to T”. The exception object is copy-initialized (9.4.1 [dcl.init.general]) from the (possibly converted) operand.
  2. Change in 14.2 [except.throw] paragraph 3 as follows:

    Throwing an exception copy-initializes (9.4 [dcl.init], 11.4.5.3 [class.copy.ctor]) a temporary object, called the exception object. If the type of the exception object would be an incomplete type (6.8.1 [basic.types.general]), an abstract class type (11.7.4 [class.abstract]), or a pointer to an incomplete type other than cv void (6.8.4 [basic.compound]), the program is ill-formed.



2552. Constant evaluation of non-defining variable declarations

Section: 7.7  [expr.const]     Status: DRWP     Submitter: Hubert Tong     Date: 2022-03-21

[Accepted as a DR at the June, 2023 meeting.]

Paper P2242 (Non-literal variables (and labels and gotos) in constexpr functions) added 7.7 [expr.const] bullet 5.2:

It seems that block-scope extern (i.e. non-defining) declarations are covered by the above bullet, but only definitions should be in view here.

Proposed resolution (approved by CWG 2023-06-15):

  1. Change in 7.7 [expr.const] bullet 5.2 as follows:




2710. Loops in constant expressions

Section: 7.7  [expr.const]     Status: DRWP     Submitter: Daniel Krügler     Date: 2023-03-23

[Accepted as a DR at the June, 2023 meeting.]

Iteration statements such as while and for loops are specified by equivalent code involving goto (8.6.2 [stmt.while] paragraph 2, 8.6.4 [stmt.for] paragraph 1, 8.6.5 [stmt.ranged] paragraph 1). The goto statement cannot be evaluated in constant expressions (7.7 [expr.const] bullet 5.30), thus while and for loops cannot be evaluated in constant expressions. Similar concerns arise for continue (8.7.3 [stmt.cont] paragraph 1).

However, that is neither intended nor existing practice.

Suggested resolution [SUPERSEDED]:

Change in 7.7 [expr.const] bullet 5.30 as follows:

An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine (6.9.1 [intro.execution]), would evaluate one of the following:

CWG 2023-03-30

Keep the rule non-normative and non-exhaustive.

Proposed resolution (approved by CWG 2023-04-28):

Change in 7.7 [expr.const] bullet 5.30 as follows:

An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine (6.9.1 [intro.execution]), would evaluate one of the following:



2750. construct_at without constructor call

Section: 7.7  [expr.const]     Status: DRWP     Submitter: CWG     Date: 2023-06-16

[Accepted as a DR at the June, 2023 meeting.]

Subclause 7.7 [expr.const] paragraph 6 talks about invoking the "underlying constructor", but there is no constructor when creating an aggregate or a scalar type.

Proposed resolution:

For the purposes of determining whether an expression E is a core constant expression, the evaluation of the body of a member function of std::allocator<T> as defined in 20.2.10.2 [allocator.members], where T is a literal type, is ignored. Similarly, the evaluation of the body of std::construct_at or std::ranges::construct_at is considered to include only the underlying constructor call initialization of the T object if the first argument (of type T*) points to storage allocated with std::allocator<T> or to an object whose lifetime began within the evaluation of E.



2550. Type "reference to cv void" outside of a declarator

Section: 9.3.4.3  [dcl.ref]     Status: DRWP     Submitter: Jens Maurer     Date: 2022-03-13

[Accepted as a DR at the June, 2023 meeting.]

9.3.4.3 [dcl.ref] paragraph 1 specifies:

A declarator that specifies the type “reference to cv void” is ill-formed.

A declarator does not contain the leading decl-specifier-seq of a declaration, so the following example is not covered by the prohibition:

  void f(void& x);

Proposed resolution (approved by CWG 2023-06-15):

Change in 9.3.4.3 [dcl.ref] paragraph 1 as follows:

A declarator that specifies Forming the type “reference to cv void” is ill-formed.



2683. Default arguments for member functions of templated nested classes

Section: 9.3.4.7  [dcl.fct.default]     Status: DRWP     Submitter: Matthew House     Date: 2023-01-11

[Accepted as a DR at the June, 2023 meeting.]

Subclause 9.3.4.7 [dcl.fct.default] paragraph 6 specifies:

Except for member functions of class templates, the default arguments in a member function definition that appears outside of the class definition are added to the set of default arguments provided by the member function declaration in the class definition; the program is ill-formed if a default constructor (11.4.5.2 [class.default.ctor]), copy or move constructor (11.4.5.3 [class.copy.ctor]), or copy or move assignment operator (11.4.6 [class.copy.assign]) is so declared. Default arguments for a member function of a class template shall be specified on the initial declaration of the member function within the class template.

That rule appears to allow adding default arguments for member functions of classes that are nested within class templates, for example:

  template<class> struct A { struct B { void c(int); }; };
  template<class T> void A<T>::B::c(int = 0) {}

MSVC accepts; gcc and clang reject.

Proposed resolution (approved by CWG 2023-03-03):

Change in 9.3.4.7 [dcl.fct.default] paragraph 6 as follows:

Except for member functions of class templates templated classes, the default arguments in a member function definition that appears outside of the class definition are added to the set of default arguments provided by the member function declaration in the class definition; the program is ill-formed if a default constructor (11.4.5.2 [class.default.ctor]), copy or move constructor (11.4.5.3 [class.copy.ctor]), or copy or move assignment operator (11.4.6 [class.copy.assign]) is so declared. Default arguments for a member function of a templated class template shall be specified on the initial declaration of the member function within the templated class template.



2708. Parenthesized initialization of arrays

Section: 9.4.1  [dcl.init.general]     Status: DRWP     Submitter: Mike Miller     Date: 2023-03-14

[Accepted as a DR at the June, 2023 meeting.]

Consider:

  const int arr[2](1,2);

This is accepted by all major implementations, yet 9.4.1 [dcl.init.general] paragraph 13 prohibits it:

If the entity being initialized does not have class type, the expression-list in a parenthesized initializer shall be a single expression.

Presumably, this was an oversight when adding parenthesized aggregate initialization.

Proposed resolution (approved by CWG 2023-04-28):

Change in 9.4.1 [dcl.init.general] paragraph 13 as follows:

If the entity being initialized does not have class or array type, the expression-list in a parenthesized initializer shall be a single expression.



2713. Initialization of reference-to-aggregate from designated initializer list

Section: 9.4.5  [dcl.init.list]     Status: DRWP     Submitter: Richard Smith     Date: 2023-04-06

[Accepted as a DR at the June, 2023 meeting.]

Aggregates can be initialized by a designated initializer list, but references to aggregates cannot, although list-initialization of such with a regular braced-init-list is fine.

Subclause 9.4.5 [dcl.init.list] paragraph 3 specifies:

List-initialization of an object or reference of type T is defined as follows:

Subclause 12.2.4.2.6 [over.ics.list] paragraph 2 specifies:

If the initializer list is a designated-initializer-list, a conversion is only possible if the parameter has an aggregate type that can be initialized from the initializer list according to the rules for aggregate initialization (9.4.2 [dcl.init.aggr]), in which case the implicit conversion sequence is a user-defined conversion sequence whose second standard conversion sequence is an identity conversion.

Proposed resolution (approved by CWG 2023-04-28):

  1. Change in 9.4.5 [dcl.init.list] bullet 3.1 as follows:

    • If the braced-init-list contains a designated-initializer-list and T is not a reference type, T shall be an aggregate class. ...
    • ...
  2. Change in 9.4.5 [dcl.init.list] bullet 3.9 as follows:

    Otherwise, if the initializer list is not a designated-initializer-list and has a single element of type E and ...
  3. Change in 9.4.5 [dcl.init.list] bullet 3.10 as follows:

    [ Example:
      ...
      const B& b2{a};  // error: cannot copy-list-initialize B temporary from A
      struct C { int x; };
      C&& c = { .x = 1 };  // OK
    
    -- end example ]
  4. Change in 12.2.4.2.6 [over.ics.list] paragraph 2 as follows:

    If the initializer list is a designated-initializer-list and the parameter is not a reference, a conversion is only possible if the parameter has an aggregate type that can be initialized from the initializer list according to the rules for aggregate initialization (9.4.2 [dcl.init.aggr]), in which case the implicit conversion sequence is a user-defined conversion sequence whose second standard conversion sequence is an identity conversion.



2663. Example for member redeclarations with using-declarations

Section: 9.9  [namespace.udecl]     Status: DRWP     Submitter: Shafik Yaghmour     Date: 2022-11-28

[Accepted as a DR at the June, 2023 meeting.]

Issue 36 was resolved by P1787R6, but no example was added.

Proposed resolution (approved by CWG 2023-06-13):

  1. Add an example to 9.9 [namespace.udecl] paragraph 8 as follows:

    [ Example:
    struct C {
      int i;
    };
    
    struct D1 : C { };
    struct D2 : C { };
    
    struct D3 : D1, D2 {
      using D1::i;   // OK, equivalent to using C::i
      using D1::i;   // error: duplicate
      using D2::i;   // error: duplicate, also names C::i
    };
    
    -- end example ]
  2. Change the example in 9.9 [namespace.udecl] paragraph 10 as follows:

      using B::x;
      using A::x;              // OK, hides struct B::x
      using A::x;              // OK, does not conflict with previous using A::x
      x = 99;                  // assigns to A::x
      struct x x1;             // x1 has class type B::x
    }
    

CWG 2023-01-06

There is implementation divergence in handling this example.

CWG 2023-02-07

P1787R6 clarified that the example added to 9.9 [namespace.udecl] paragraph 10 is accepted, even in the non-function case.




2732. Can importable headers react to preprocessor state from point of import?

Section: 10.3  [module.import]     Status: DRWP     Submitter: Xu Chuanqi     Date: 2023-05-25

[Accepted as a DR at the June, 2023 meeting.]

Subclause 10.3 [module.import] paragraph 5 specifies:

A module-import-declaration that specifies a header-name H imports a synthesized header unit, which is a translation unit formed by applying phases 1 to 7 of translation (5.2 [lex.phases]) to the source file or header nominated by H, which shall not contain a module-declaration. [Note 2: All declarations within a header unit are implicitly exported (10.2 [module.interface]), and are attached to the global module (10.1 [module.unit]). —end note]

It is unclear whether the contents of header units can vary depending on the set of defined macros at the point where the import (or #include) appears.

Proposed resolution (approved by CWG 2023-06-13):

Change in 10.3 [module.import] paragraph 5 as follows:

[Note 2: A header unit is a separate translation unit with an independent set of defined macros. All declarations within a header unit are implicitly exported (10.2 [module.interface]), and are attached to the global module (10.1 [module.unit]). —end note]



1353. Array and variant members and deleted special member functions

Section: 11.4.5  [class.ctor]     Status: DRWP     Submitter: Sean Hunt     Date: 2011-08-16

[Accepted as a DR at the June, 2023 meeting.]

The specification of when a defaulted special member function is to be defined as deleted sometimes overlooks variant and array members.

Proposed resolution (approved by CWG 2023-02-07):

  1. Change in 11.4.5.2 [class.default.ctor] paragraph 2 as follows:

    A defaulted default constructor for class X is defined as deleted if:
    • X is a union that has a variant member with a non-trivial default constructor and no variant member of X has a default member initializer,
    • X is a non-union class that has a variant member M with a non-trivial default constructor and no variant member of the anonymous union containing M has a default member initializer,
    • any non-static data member with no default member initializer (11.4 [class.mem]) is of reference type,
    • any non-variant non-static data member of const-qualified type (or possibly multi-dimensional array thereof) with no brace-or-equal-initializer is not const-default-constructible (9.4 [dcl.init]),
    • X is a union and all of its variant members are of const-qualified type (or possibly multi-dimensional array thereof),
    • X is a non-union class and all members of any anonymous union member are of const-qualified type (or possibly multi-dimensional array thereof),
    • any potentially constructed subobject, except for a non-static data member with a brace-or-equal-initializer or a variant member of a union where another non-static data member has a brace-or-equal-initializer, has class type M (or possibly multi-dimensional array thereof) and either M has no default constructor or overload resolution (12.2 [over.match]) as applied to find M's corresponding constructor results in an ambiguity or in a function that is deleted or inaccessible from the defaulted default constructor either does not result in a usable candidate (12.2.1 [over.match.general]) or, in the case of a variant member, selects a non-trivial function, or
    • any potentially constructed subobject has a type with class type M (or possibly multi-dimensional array thereof) and M has a destructor that is deleted or inaccessible from the defaulted default constructor.
  2. Change in 11.4.5.3 [class.copy.ctor] paragraph 10 as follows:

    ... A defaulted copy/move constructor for a class X is defined as deleted (9.5.3 [dcl.fct.def.delete]) if X has:
    • a potentially constructed subobject of type M (or possibly multi-dimensional array thereof) that cannot be copied/moved because for which overload resolution (12.2 [over.match]), as applied to find M's corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor either does not result in a usable candidate (12.2.1 [over.match.general]) or, in the case of a variant member, selects a non-trivial function,
    • a variant member whose corresponding constructor as selected by overload resolution is non-trivial,
    • any potentially constructed subobject of a type with class type M (or possibly multi-dimensional array thereof) where M has a destructor that is deleted or inaccessible from the defaulted constructor, or,
    • for the copy constructor, a non-static data member of rvalue reference type.
  3. Change in 11.4.6 [class.copy.assign] paragraph 7 as follows:

    A defaulted copy/move assignment operator for class X is defined as deleted if X has:
    • a variant member with a non-trivial corresponding assignment operator and X is a union-like class, or
    • a non-static data member of const non-class type (or possibly multi-dimensional array thereof), or
    • a non-static data member of reference type, or
    • a direct non-static data member of class type M (or possibly multi-dimensional array thereof) or a direct base class M that cannot be copied/moved because overload resolution (12.2 [over.match]), as applied to find M's corresponding assignment operator, results in an ambiguity or a function that is deleted or inaccessible from the defaulted assignment operator either does not result in a usable candidate (12.2.1 [over.match.general]) or, in the case of a variant member, selects a non-trivial function.
  4. Change in 11.4.7 [class.dtor] paragraph 7 as follows:

    A defaulted destructor for a class X is defined as deleted if:
    • X is a union-like class that has a variant member with a non-trivial destructor,
    • any potentially constructed subobject has class type M (or possibly multi-dimensional array thereof) and M has a deleted destructor that is deleted or a destructor that is inaccessible from the defaulted destructor or, in the case of a variant member, is non-trivial,
    • or, for a virtual destructor, lookup of the non-array deallocation function results in an ambiguity or in a function that is deleted or inaccessible from the defaulted destructor.



2716. Rule about self-or-base conversion is normatively redundant

Section: 11.4.8.3  [class.conv.fct]     Status: DRWP     Submitter: Brian Bi     Date: 2023-04-11

[Accepted as a DR at the June, 2023 meeting.]

The rule in 11.4.8.3 [class.conv.fct] paragraph 4 is normatively redundant explanation:

A conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to cv void. [ Footnote: These conversions are considered as standard conversions for the purposes of overload resolution (12.2.4.2 [over.best.ics], 12.2.4.2.5 [over.ics.ref]) and therefore initialization (9.4 [dcl.init]) and explicit casts (7.6.1.9 [expr.static.cast]). A conversion to void does not invoke any conversion function (7.6.1.9 [expr.static.cast]). Even though never directly called to perform a conversion, such conversion functions can be declared and can potentially be reached through a call to a virtual conversion function in a base class. -- end footnote ]

Proposed resolution (approved by CWG 2023-04-28):

Change in 11.4.8.3 [class.conv.fct] paragraph 4 as follows:

[ Note: A conversion function is never used to convert invoked for implicit or explicit conversions of an a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to cv void. [ Footnote: These conversions are considered as standard conversions for the purposes of overload resolution (12.2.4.2 [over.best.ics], 12.2.4.2.5 [over.ics.ref]) and therefore initialization (9.4 [dcl.init]) and explicit casts (7.6.1.9 [expr.static.cast]). A conversion to void does not invoke any conversion function (7.6.1.9 [expr.static.cast]). Even though never directly called to perform a conversion, such conversion functions can be declared and can potentially be reached through a call to a virtual conversion function in a base class. -- end footnote ] -- end note ]

[ Example: ... ]




2712. Simplify restrictions on built-in assignment operator candidates

Section: 12.2.2.3  [over.match.oper]     Status: DRWP     Submitter: Brian Bi     Date: 2023-03-24

[Accepted as a DR at the June, 2023 meeting.]

Subclause 12.2.2.3 [over.match.oper] paragraph 5 specifies:

For the built-in assignment operators, conversions of the left operand are restricted as follows:

The first bullet is redundant, because standard conversion sequences cannot bind "vq T&" (the type of the first parameter of a built-in assignment operator) to a temporary.

Proposed resolution (approved by CWG 2023-03-30):

Change in 12.2.2.3 [over.match.oper] paragraph 5 as follows:

For the first parameter of the built-in assignment operators, only standard conversion sequences (12.2.4.2.2 [over.ics.scs]) are considered. conversions of the left operand are restricted as follows:



2697. Deduction guides using abbreviated function syntax

Section: 13.7.2.3  [temp.deduct.guide]     Status: DRWP     Submitter: CWG     Date: 2023-02-11     Liaison: EWG

[Accepted as a DR at the June, 2023 meeting.]

It is unclear whether deduction guides can be expressed using abbreviated function syntax. Subclause 13.7.2.3 [temp.deduct.guide] paragraph 3 refers to the restrictions of a function's parameter-declaration-clause:

The same restrictions apply to the parameter-declaration-clause of a deduction guide as in a function declaration (9.3.4.6 [dcl.fct]). ...

However, that subclause is silent on the meaning of abbreviated function syntax when used for deduction guides. Furthermore, 9.3.4.6 [dcl.fct] paragraph 22 explicitly restricts the definition to function templates, which deduction guides are not:

An abbreviated function template is a function declaration that has one or more generic parameter type placeholders (9.2.9.7 [dcl.spec.auto]). ...

Arguably, the lack of template parameter names in abbreviated function syntax makes it less suitable to specifiy deduction guides.

CWG 2023-02-11

CWG solicits input from EWG whether abbreviated function syntax is intended to be used for deduction guides. See cplusplus/papers#1465.

EWG 2023-05-11

CWG should clarify that abbreviated function syntax should not be permitted in deduction guides.

Proposed resolution (approved by CWG 2023-05-12):

Change in 13.7.2.3 [temp.deduct.guide] paragraph 3 as follows:

The same restrictions apply to the parameter-declaration-clause of a deduction guide as in a function declaration (9.3.4.6 [dcl.fct]), except that a generic parameter type placeholder (9.2.9.7 [dcl.spec.auto]) shall not appear in the parameter-declaration-clause of a deduction guide. The simple-template-id shall name a class template specialization. The template-name shall be the same identifier as the template-name of the simple-template-id. A deduction-guide shall inhabit the scope to which the corresponding class template belongs and, for a member class template, have the same access. Two deduction guide declarations for the same class template shall not have equivalent parameter-declaration-clauses if either is reachable from the other.



2717. Pack expansion for alignment-specifier

Section: 13.7.4  [temp.variadic]     Status: DRWP     Submitter: Jim X     Date: 2023-04-11

[Accepted as a DR at the June, 2023 meeting.]

Subclause 13.7.4 [temp.variadic] paragraph 11 specifies:

The instantiation of any other pack expansion produces a list of elements E1, E2, ... , EN.

Consider this example:

  template<class ...T>
  struct Align{
   alignas(T...) unsigned char buffer[128];
  };
  Align<int, short> a;

The pack expansion of the alignment-specifier yields alignas(int), alignas(short), an ill-formed list per the grammar in 9.12.1 [dcl.attr.grammar].

Proposed resolution (approved by CWG 2023-04-28):

Insert a new paragraph after 13.7.4 [temp.variadic] paragraph 9 as follows:

The instantiation of a sizeof... expression (7.6.2.5 [expr.sizeof]) produces an integral constant with value N.

The instantiation of an alignment-specifier with an ellipsis produces E1 E2 ... EN.




2720. Template validity rules for templated entities and alias templates

Section: 13.8.1  [temp.res.general]     Status: DRWP     Submitter: Richard Smith     Date: 2023-03-29

[Accepted as a DR at the June, 2023 meeting.]

Subclause 13.8.1 [temp.res.general] paragraph 6 specifies rules to determine when a template is valid, but the specification is in terms of "instantiation". However, some kinds of templates (namely alias templates) are not instantiated.

Further, the rule discusses only templates, but should apply to any templated entity.

Proposed resolution (approved by CWG 2023-05-12):

Change in 13.8.1 [temp.res.general] paragraph 6 as follows:

The validity of a template templated entity may be checked prior to any instantiation. [Note 3: Knowing which names are type names allows the syntax of every template to be checked in this way. —end note]

The program is ill-formed, no diagnostic required, if:






Issues with "WP" Status




Issues with "CD1" Status


663. Valid Cyrillic identifier characters

Section: _N2691_.E  [extendid]     Status: CD1     Submitter: Steve Clamage     Date: 30 November 2007

[Voted into the WP at the June, 2008 meeting.]

The C99 and C++ Standards disagree about the validity of two Cyrillic characters for use in identifiers. C++ (_N2691_.E [extendid]) says that 040d is valid in an identifier but that 040e is not; C99 (Annex D) says exactly the opposite. In fact, both characters should be accepted in identifiers; see the Unicode chart.

Proposed resolution (February, 2008):

The reference in paragraph 2 should be changed to ISO/IEC TR 10176:2003 and the table should be changed to conform to the one in that document (beginning on page 34).




122. template-ids as unqualified-ids

Section: _N4567_.5.1.1  [expr.prim.general]     Status: CD1     Submitter: Mike Miller     Date: 3 June 1999

[Moved to DR at 10/01 meeting.]

_N4567_.5.1.1 [expr.prim.general] paragraph 11 reads,

A template-id shall be used as an unqualified-id only as specified in 13.9.3 [temp.explicit] , 13.9 [temp.spec] , and 13.7.6 [temp.spec.partial] .

What uses of template-ids as unqualified-ids is this supposed to prevent? And is the list of referenced sections correct/complete? For instance, what about 13.10.2 [temp.arg.explicit], "Explicit template argument specification?" Does its absence from the list in _N4567_.5.1.1 [expr.prim.general] paragraph 11 mean that "f<int>()" is ill-formed?

This is even more confusing when you recall that unqualified-ids are contained in qualified-ids:

qualified-id: ::opt nested-name-specifier templateopt unqualified-id

Is the wording intending to say "used as an unqualified-id that is not part of a qualified-id?" Or something else?

Proposed resolution (10/00):

Remove the referenced sentence altogether.




125. Ambiguity in friend declaration syntax

Section: _N4567_.5.1.1  [expr.prim.general]     Status: CD1     Submitter: Martin von Loewis     Date: 7 June 1999

[Voted into WP at March 2004 meeting.]

The example below is ambiguous.

    struct A{
      struct B{};
    };

    A::B C();

    namespace B{
      A C();
    }

    struct Test {
      friend A::B ::C();
    };
Here, it is not clear whether the friend declaration denotes A B::C() or A::B C(), yet the standard does not resolve this ambiguity.

The ambiguity arises since both the simple-type-specifier (9.2.9.3 [dcl.type.simple] paragra 1) and an init-declararator (9.3 [dcl.decl] paragraph 1) contain an optional :: and an optional nested-name-specifier (_N4567_.5.1.1 [expr.prim.general] paragraph 1) . Therefore, two different ways to analyse this code are possible:

simple-type-specifier = A::B
init-declarator = ::C()
simple-declaration = friend A::B ::C();
or
simple-type-specifier = A
init-declarator = ::B::C()
simple-declaration = friend A ::B::C();
Since it is a friend declaration, the init-declarator may be qualified, and start with a global scope.

Suggested Resolution: In the definition of nested-name-specifier, add a sentence saying that a :: token immediately following a nested-name-specifier is always considered as part of the nested-name-specifier. Under this interpretation, the example is ill-formed, and should be corrected as either

    friend A (::B::C)();   //or
    friend A::B (::C)();

An alternate suggestion — changing 9.2 [dcl.spec] to say that

The longest sequence of tokens that could possibly be a type name is taken as the decl-specifier-seq of a declaration.

— is undesirable because it would make the example well-formed rather than requiring the user to disambiguate the declaration explicitly.

Proposed resolution (04/01):

(See below for problem with this, from 10/01 meeting.)

In _N4567_.5.1.1 [expr.prim.general] paragraph 7,

  1. Before the grammar for qualified-id, start a new paragraph 7a with the text

    A qualified-id is an id-expression that contains the scope resolution operator ::.
  2. Following the grammar fragment, insert the following:

    The longest sequence of tokens that could form a qualified-id constitutes a single qualified-id. [Example:

        // classes C, D; functions F, G, namespace N; non-class type T
        friend C ::D::F();   // ill-formed, means friend (C::D::F)();
        friend C (::D::F)(); // well-formed
        friend N::T ::G();   // ill-formed, means friend (N::T::G)();
        friend N::T (::G)(); // well-formed
    

    end example]

  3. Start a new paragraph 7b following the example.

(This resolution depends on that of issue 215.)

Notes from 10/01 meeting:

It was pointed out that the proposed resolution does not deal with cases like X::Y where X is a type but not a class type. The working group reaffirmed its decision that the disambiguation should be syntactic only, i.e., it should depend only on whether or not the name is a type.

Jason Merrill :

At the Seattle meeting, I suggested that a solution might be to change the class-or-namespace-name in the nested-name-specifier rule to just be "identifier"; there was some resistance to this idea. FWIW, I've tried this in g++. I had to revise the idea so that only the second and subsequent names were open to being any identifier, but that seems to work just fine.

So, instead of

it would be

Or some equivalent but right-associative formulation, if people feel that's important, but it seems irrelevant to me.

Clark Nelson :

Personally, I prefer the left-associative rule. I think it makes it easier to understand. I was thinking about this production a lot at the meeting, considering also some issues related to 301. My formulation was getting kind of ugly, but with a left-associative rule, it gets a lot nicer.

Your proposal isn't complete, however, as it doesn't allow template arguments without an explicit template keyword. You probably want to add an alternative for:

There is admittedly overlap between this alternative and

but I think they're both necessary.

Notes from the 4/02 meeting:

The changes look good. Clark Nelson will merge the two proposals to produce a single proposed resolution.

Proposed resolution (April 2003):

nested-name-specifier is currently defined in _N4567_.5.1.1 [expr.prim.general] paragraph 7 as:

The proposed definition is instead:

Issue 215 is addressed by using type-name instead of class-name in the first alternative. Issue 125 (this issue) is addressed by using identifier instead of anything more specific in the third alternative. Using left association instead of right association helps eliminate the need for class-or-namespace-name (or type-or-namespace-name, as suggested for issue 215).

It should be noted that this formulation also rules out the possibility of A::template B::, i.e. using the template keyword without any template arguments. I think this is according to the purpose of the template keyword, and that the former rule allowed such a construct only because of the difficulty of formulation of a right-associative rule that would disallow it. But I wanted to be sure to point out this implication.

Notes from April 2003 meeting:

See also issue 96.

The proposed change resolves only part of issue 215.




466. cv-qualifiers on pseudo-destructor type

Section: _N4778_.7.6.1.4  [expr.pseudo]     Status: CD1     Submitter: Mark Mitchell     Date: 18 Mar 2004

[Voted into WP at April, 2006 meeting.]

_N4778_.7.6.1.4 [expr.pseudo] paragraph 2 says both:

The type designated by the pseudo-destructor-name shall be the same as the object type.
and also:
The cv-unqualified versions of the object type and of the type designated by the pseudo-destructor-name shall be the same type.
Which is it? "The same" or "the same up to cv-qualifiers"? The second sentence is more generous than the first. Most compilers seem to implement the less restrictive form, so I guess that's what I think we should do.

See also issues 305 and 399.

Proposed resolution (October, 2005):

Change _N4778_.7.6.1.4 [expr.pseudo] paragraph 2 as follows:

The left-hand side of the dot operator shall be of scalar type. The left-hand side of the arrow operator shall be of pointer to scalar type. This scalar type is the object type. The type designated by the pseudo-destructor-name shall be the same as the object type. The cv-unqualified versions of the object type and of the type designated by the pseudo-destructor-name shall be the same type. Furthermore, the two type-names in a pseudo-destructor-name of the form shall designate the same scalar type. The cv-unqualified versions of the object type and of the type designated by the pseudo-destructor-name shall be the same type.



141. Non-member function templates in member access expressions

Section: _N4868_.6.5.6  [basic.lookup.classref]     Status: CD1     Submitter: fvali     Date: 31 July 1999

[Voted into the WP at the June, 2008 meeting.]

_N4868_.6.5.6 [basic.lookup.classref] paragraph 1 says,

In a class member access expression (7.6.1.5 [expr.ref] ), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (13.3 [temp.names] ) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class or function template.

There do not seem to be any circumstances in which use of a non-member template function would be well-formed as the id-expression of a class member access expression.

Proposed Resolution (November, 2006):

Change _N4868_.6.5.6 [basic.lookup.classref] paragraph 1 as follows:

In a class member access expression (7.6.1.5 [expr.ref]), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (13.3 [temp.names]) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class or function template...



305. Name lookup in destructor call

Section: _N4868_.6.5.6  [basic.lookup.classref]     Status: CD1     Submitter: Mark Mitchell     Date: 19 May 2001

[Voted into WP at the October, 2006 meeting.]

I believe this program is invalid:

    struct A {
    };

    struct C {
      struct A {};
      void f ();
    };

    void C::f () {
      ::A *a;
      a->~A ();
    }
The problem is that _N4868_.6.5.6 [basic.lookup.classref] says that you have to look up A in both the context of the pointed-to-type (i.e., ::A), and in the context of the postfix-expression (i.e., the body of C::f), and that if the name is found in both places it must name the same type in both places.

The EDG front end does not issue an error about this program, though.

Am I reading the standardese incorrectly?

John Spicer: I think you are reading it correctly. I think I've been hoping that this would get changed. Unlike other dual lookup contexts, this is one in which the compiler already knows the right answer (the type must match that of the left hand of the -> operator). So I think that if either of the types found matches the one required, it should be sufficient. You can't say a->~::A(), which means you are forced to say a->::A::~A(), which disables the virtual mechanism. So you would have to do something like create a local typedef for the desired type.

See also issues 244, 399, and 466.

Proposed resolution (April, 2006):

  1. Remove the indicated text from _N4868_.6.5.6 [basic.lookup.classref] paragraph 2:

    If the id-expression in a class member access (7.6.1.5 [expr.ref]) is an unqualified-id, and the type of the object expression is of a class type C (or of pointer to a class type C), the unqualified-id is looked up in the scope of class C...
  2. Change _N4868_.6.5.6 [basic.lookup.classref] paragraph 3 as indicated:

    If the unqualified-id is ~type-name, the type-name is looked up in the context of the entire postfix-expression. and If the type T of the object expression is of a class type C (or of pointer to a class type C), the type-name is also looked up in the context of the entire postfix-expression and in the scope of class C. The type-name shall refer to a class-name. If type-name is found in both contexts, the name shall refer to the same class type. If the type of the object expression is of scalar type, the type-name is looked up in the scope of the complete postfix-expression. At least one of the lookups shall find a name that refers to (possibly cv-qualified) T. [Example:
    
        struct A { };
    
        struct B {
          struct A { };
          void f(::A* a);
        };
    
        void B::f(::A* a) {
          a->~A();  // OK, lookup in *a finds the injected-class-name
        }
    
    end example]

[Note: this change also resolves issue 414.]




381. Incorrect example of base class member lookup

Section: _N4868_.6.5.6  [basic.lookup.classref]     Status: CD1     Submitter: Steve Adamczyk     Date: 8 Nov 2002

[Voted into WP at October 2004 meeting.]

The example in _N4868_.6.5.6 [basic.lookup.classref] paragraph 4 is wrong (see 11.8.3 [class.access.base] paragraph 5; the cast to the naming class can't be done) and needs to be corrected. This was noted when the final version of the algorithm for issue 39 was checked against it.

Proposed Resolution (October 2003):

Remove the entire note at the end of _N4868_.6.5.6 [basic.lookup.classref] paragraph 4, including the entire example.




414. Multiple types found on destructor lookup

Section: _N4868_.6.5.6  [basic.lookup.classref]     Status: CD1     Submitter: John Spicer     Date: 1 May 2003

[Voted into WP at the October, 2006 meeting.]

By _N4868_.6.5.6 [basic.lookup.classref] paragraph 3, the following is ill-formed because the two lookups of the destructor name (in the scope of the class of the object and in the surrounding context) find different Xs:

  struct X {};
  int main() {
    X x;
    struct X {};
    x.~X();  // Error?
  }

This is silly, because the compiler knows what the type has to be, and one of the things found matches that. The lookup should require only that one of the lookups finds the required class type.

Proposed resolution (April, 2005):

This issue is resolved by the resolution of issue 305.




452. Wording nit on description of this

Section: _N4868_.11.4.3.2  [class.this]     Status: CD1     Submitter: Gennaro Prota     Date: 8 Jan 2004

[Voted into WP at July, 2007 meeting.]

_N4868_.11.4.3.2 [class.this] paragraph 1, which specifies the meaning of the keyword 'this', seems to limit its usage to the *body* of non-static member functions. However 'this' is also usable in ctor-initializers which, according to the grammar in 9.5 [dcl.fct.def] par. 1, are not part of the body.

Proposed resolution: Changing the first part of _N4868_.11.4.3.2 [class.this] par. 1 to:

In the body of a nonstatic (9.3) member function or in a ctor-initializer (12.6.2), the keyword this is a non-lvalue expression whose value is the address of the object for which the function is called.

NOTE: I'm talking of constructors as functions that are "called"; there have been discussions on c.l.c++.m as to whether constructors are "functions" and to whether this terminology is correct or not; I think it is both intuitive and in agreement with the standard wording.

Steve Adamczyk: See also issue 397, which is defining a new syntax term for the body of a function including the ctor-initializers.

Notes from the March 2004 meeting:

This will be resolved when issue 397 is resolved.

Proposed resolution (October, 2005):

  1. Change 9.5 [dcl.fct.def] paragraph 1 as indicated:

  2. Function definitions have the form

    An informal reference to the body of a function should be interpreted as a reference to the nonterminal function-body.

  3. Change the definition of function-try-block in Clause 14 [except] paragraph 1:

  4. Change 6.4.7 [basic.scope.class] paragraph 1, point 1, as indicated:

  5. The potential scope of a name declared in a class consists not only of the declarative region following the name's point of declaration, but also of all function bodies, bodies and default arguments, and constructor ctor-initializers in that class (including such things in nested classes).
  6. Change 6.4.7 [basic.scope.class] paragraph 1, point 5, as indicated:

  7. The potential scope of a declaration that extends to or past the end of a class definition also extends to the regions defined by its member definitions, even if the members are defined lexically outside the class (this includes static data member definitions, nested class definitions, member function definitions (including the member function body and, for constructor functions (11.4.5 [class.ctor]), the ctor-initializer (11.9.3 [class.base.init])) and any portion of the declarator part of such definitions which follows the identifier, including a parameter-declaration-clause and any default arguments (9.3.4.7 [dcl.fct.default]). [Example:...
  8. Change footnote 32 in 6.5.3 [basic.lookup.unqual] paragraph 8 as indicated:

  9. That is, an unqualified name that occurs, for instance, in a type or default argument expression in the parameter-declaration-clause, parameter-declaration-clause or in the function body, or in an expression of a mem-initializer in a constructor definition.
  10. Change _N4567_.5.1.1 [expr.prim.general] paragraph 3 as indicated:

  11. ...The keyword this shall be used only inside a non-static class member function body (11.4.2 [class.mfct]) or in a constructor mem-initializer (11.9.3 [class.base.init])...
  12. Change 11.4 [class.mem] paragraph 2 as indicated:

  13. ...Within the class member-specification, the class is regarded as complete within function bodies, default arguments, and exception-specifications, and constructor ctor-initializers (including such things in nested classes)...
  14. Change 11.4 [class.mem] paragraph 9 as indicated:

  15. Each occurrence in an expression of the name of a non-static data member or non-static member function of a class shall be expressed as a class member access (7.6.1.5 [expr.ref]), except when it appears in the formation of a pointer to member (7.6.2.2 [expr.unary.op]), or or when it appears in the body of a non-static member function of its class or of a class derived from its class (11.4.3 [class.mfct.non.static]), or when it appears in a mem-initializer for a constructor for its class or for a class derived from its class (11.9.3 [class.base.init]).
  16. Change the note in 11.4.2 [class.mfct] paragraph 5 as indicated:

  17. [Note: a name used in a member function definition (that is, in the parameter-declaration-clause including the default arguments (9.3.4.7 [dcl.fct.default]), or or in the member function body, or, for a constructor function (11.4.5 [class.ctor]), in a mem-initializer expression (11.9.3 [class.base.init])) is looked up as described in 6.5 [basic.lookup]. —end note]
  18. Change 11.4.3 [class.mfct.non.static] paragraph 1 as indicated:

  19. ...A non-static member function may also be called directly using the function call syntax (7.6.1.3 [expr.call], 12.2.2.2 [over.match.call]) from within the body of a member function of its class or of a class derived from its class.

  20. Change 11.4.3 [class.mfct.non.static] paragraph 3 as indicated:

  21. When an id-expression (_N4567_.5.1.1 [expr.prim.general]) that is not part of a class member access syntax (7.6.1.5 [expr.ref]) and not used to form a pointer to member (7.6.2.2 [expr.unary.op]) is used in the body of a non-static member function of class X or used in the mem-initializer for a constructor of class X, if name lookup (6.5.3 [basic.lookup.unqual]) resolves the name in the id-expression to a non-static non-type member of class X or of a base class of X, the id-expression is transformed into a class member access expression (7.6.1.5 [expr.ref]) using (*this) (_N4868_.11.4.3.2 [class.this]) as the postfix-expression to the left of the . operator...
  22. Change 11.4.5 [class.ctor] paragraph 7 as indicated:

  23. ...The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with an empty mem-initializer-list no ctor-initializer (11.9.3 [class.base.init]) and an empty function body compound-statement...
  24. Change 11.9.3 [class.base.init] paragraph 4 as indicated:

  25. ...After the call to a constructor for class X has completed, if a member of X is neither specified in the constructor's mem-initializers, nor default-initialized, nor value-initialized, nor given a value during execution of the compound-statement of the body of the constructor, the member has indeterminate value.
  26. Change the last bullet of 11.9.3 [class.base.init] paragraph 5 as indicated:

  27. Change Clause 14 [except] paragraph 4 as indicated:

  28. A function-try-block associates a handler-seq with the ctor-initializer, if present, and the function-body compound-statement. An exception thrown during the execution of the initializer expressions in the ctor-initializer or during the execution of the function-body compound-statement transfers control to a handler in a function-try-block in the same way as an exception thrown during the execution of a try-block transfers control to other handlers. [Example:

        int f(int);
        class C {
            int i;
            double d;
        public:
            C(int, double);
        };
    
        C::C(int ii, double id)
        try
            : i(f(ii)), d(id)
        {
            // constructor function body statements
        }
        catch (...)
        {
            // handles exceptions thrown from the ctor-initializer
            // and from the constructor function body statements
        }
    

    end example]

  29. Change 14.3 [except.ctor] paragraph 2 as indicated:

  30. When an exception is thrown, control is transferred to the nearest handler with a matching type (14.4 [except.handle]); “nearest” means the handler for which the compound-statement, compound-statement or ctor-initializer, or function-body following the try keyword was most recently entered by the thread of control and not yet exited.



387. Errors in example in 14.6.5

Section: _N4868_.13.8.6  [temp.inject]     Status: CD1     Submitter: Aleksey Gurtovoy     Date: 27 Oct 2002

[Voted into WP at March 2004 meeting.]

The example in _N4868_.13.8.6 [temp.inject] paragraph 2 is incorrect:

  template<typename T> class number {
      number(int);
      //...
      friend number gcd(number& x, number& y) { /* ... */ }
      //...
  };

  void g()
  {
      number<double> a(3), b(4);
      //...
      a = gcd(a,b);   // finds gcd because number<double> is an
                      // associated class, making gcd visible
                      // in its namespace (global scope)
      b = gcd(3,4);   // ill-formed; gcd is not visible
  }

Regardless of the last statement ("b = gcd(3,4);"), the above code is ill-formed:

a) number's constructor is private;

b) the definition of (non-void) friend 'gcd' function does not contain a return statement.

Proposed resolution (April 2003):

Replace the example in _N4868_.13.8.6 [temp.inject] paragraph 2

  template<typename T> class number {
          number(int);
          //...
          friend number gcd(number& x, number& y) { /* ... */ }
          //...
  };

  void g()
  {
          number<double> a(3), b(4);
          //...
          a = gcd(a,b);           //  finds  gcd  because  number<double>  is an
                                  //  associated class, making  gcd  visible
                                  //  in its namespace (global scope)
          b = gcd(3,4);           //  ill-formed;  gcd  is not visible
  }
by
  template<typename T> class number {
     public:
          number(int);
          //...
          friend number gcd(number x, number y) { return 0; }
     private:
          //...
  };

  void g()
  {
          number<double> a(3), b(4);
          //...
          a = gcd(a,b);           //  finds  gcd  because  number<double>  is an
                                  //  associated class, making  gcd  visible
                                  //  in its namespace (global scope)
          b = gcd(3,4);           //  ill-formed;  gcd  is not visible
  }

Drafting note: Added "return" to the friend function, removed references in gcd arguments, added access specifiers.




357. Definition of signature should include name

Section: Clause 3  [intro.defs]     Status: CD1     Submitter: Steve Clamage     Date: 26 May 2002

[Voted into WP at April, 2007 meeting.]

Section Clause 3 [intro.defs], definition of "signature" omits the function name as part of the signature. Since the name participates in overload resolution, shouldn't it be included in the definition? I didn't find a definition of signature in the ARM, but I might have missed it.

Fergus Henderson: I think so. In particular, _N4140_.17.6.4.3.2 [global.names] reserves certain "function signatures" for use by the implementation, which would be wrong unless the signature includes the name.

-2- Each global function signature declared with external linkage in a header is reserved to the implementation to designate that function signature with external linkage.

-5- Each function signature from the Standard C library declared with external linkage is reserved to the implementation for use as a function signature with both extern "C" and extern "C++" linkage, or as a name of namespace scope in the global namespace.

Other uses of the term "function signature" in the description of the standard library also seem to assume that it includes the name.

James Widman:

Names don't participate in overload resolution; name lookup is separate from overload resolution. However, the word “signature” is not used in Clause 12 [over]. It is used in linkage and declaration matching (e.g., 13.7.7.2 [temp.over.link]). This suggests that the name and scope of the function should be part of its signature.

Proposed resolution (October, 2006):

  1. Replace Clause 3 [intro.defs] “signature” with the following:

  2. the name and the parameter-type-list (9.3.4.6 [dcl.fct]) of a function, as well as the class or namespace of which it is a member. If a function or function template is a class member its signature additionally includes the cv-qualifiers (if any) on the function or function template itself. The signature of a function template additionally includes its return type and its template parameter list. The signature of a function template specialization includes the signature of the template of which it is a specialization and its template arguments (whether explicitly specified or deduced). [Note: Signatures are used as a basis for name-mangling and linking. —end note]
  3. Delete paragraph 3 and replace the first sentence of 13.7.7.2 [temp.over.link] as follows:

  4. The signature of a function template specialization consists of the signature of the function template and of the actual template arguments (whether explicitly specified or deduced).

    The signature of a function template consists of its function signature, its return type and its template parameter list is defined in Clause 3 [intro.defs]. The names of the template parameters are significant...

(See also issue 537.)




537. Definition of “signature”

Section: Clause 3  [intro.defs]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 12 October 2005

[Voted into WP at April, 2007 meeting.]

The standard defines “signature” in two places: Clause 3 [intro.defs] and 13.7.7.2 [temp.over.link] paragraphs 3-4. The former seems to be meant as a formal definition (I think it's the only place covering the nontemplate case), yet it lacks some bits mentioned in the latter (specifically, the notion of a “signature of a function template,” which is part of every signature of the associated function template specializations).

Also, I think the Clause 3 [intro.defs] words “the information about a function that participates in overload resolution” isn't quite right either. Perhaps, “the information about a function that distinguishes it in a set of overloaded functions?”

Eric Gufford:

In Clause 3 [intro.defs] the definition states that “Function signatures do not include return type, because that does not participate in overload resolution,” while 13.7.7.2 [temp.over.link] paragraph 4 states “The signature of a function template consists of its function signature, its return type and its template parameter list.” This seems inconsistent and potentially confusing. It also seems to imply that two identical function templates with different return types are distinct signatures, which is in direct violation of 12.2 [over.match]. 13.7.7.2 [temp.over.link] paragraph 4 should be amended to include verbiage relating to overload resolution.

Either return types are included in function signatures, or they're not, across the board. IMHO, they should be included as they are an integral part of the function declaration/definition irrespective of overloads. Then verbiage should be added about overload resolution to distinguish between signatures and overload rules. This would help clarify things, as it is commonly understood that overload resolution is based on function signature.

In short, the term “function signature” should be made consistent, and removed from its (implicit, explicit or otherwise) linkage to overload resolution as it is commonly understood.

James Widman:

The problem is that (a) if you say the return type is part of the signature of a non-template function, then you have overloading but not overload resolution on return types (i.e., what we have now with function templates). I don't think anyone wants to make the language uglier in that way. And (b) if you say that the return type is not part of the signature of a function template, you will break code. Given those alternatives, it's probably best to maintain the status quo (which the implementors appear to have rendered faithfully).

Proposed resolution (September, 2006):

This issue is resolved by the resolution of issue 357.




362. Order of initialization in instantiation units

Section: 5.2  [lex.phases]     Status: CD1     Submitter: Mark Mitchell     Date: 2 July 2002

[Voted into WP at March 2004 meeting.]

Should this program do what its author obviously expects? As far as I can tell, the standard says that the point of instantiation for Fib<n-1>::Value is the same as the point of instantiation as the enclosing specialization, i.e., Fib<n>::Value. What in the standard actually says that these things get initialized in the right order?

  template<int n>
  struct Fib { static int Value; };

  template <>
  int Fib<0>::Value = 0;

  template <>
  int Fib<1>::Value = 1;

  template<int n>
  int Fib<n>::Value = Fib<n-1>::Value + Fib<n-2>::Value;

  int f ()
  {
    return Fib<40>::Value;
  }

John Spicer: My opinion is that the standard does not specify the behavior of this program. I thought there was a core issue related to this, but I could not find it. The issue that I recall proposed tightening up the static initialization rules to make more cases well defined.

Your comment about point of instantiation is correct, but I don't think that really matters. What matters is the order of execution of the initialization code at execution time. Instantiations don't really live in "translation units" according to the standard. They live in "instantiation units", and the handling of instantiation units in initialization is unspecified (which should probably be another core issue). See 5.2 [lex.phases] paragraph 8.

Notes from October 2002 meeting:

We discussed this and agreed that we really do mean the the order is unspecified. John Spicer will propose wording on handling of instantiation units in initialization.

Proposed resolution (April 2003):

TC1 contains the following text in 6.9.3.2 [basic.start.static] paragraph 1:

Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.

This was revised by issue 270 to read:

Dynamic initialization of an object is either ordered or unordered. Explicit specializations and definitions of class template static data members have ordered initialization. Other class template static data member instances have unordered initialization. Other objects defined in namespace scope have ordered initialization. Objects defined within a single translation unit and with ordered initialization shall be initialized in the order of their definitions in the translation unit. The order of initialization is unspecified for objects with unordered initialization and for objects defined in different translation units.

This addresses this issue but while reviewing this issue some additional changes were suggested for the above wording:

Dynamic initialization of an object is either ordered or unordered. Definitions of explicitly specialized Explicit specializations and definitions of class template static data members have ordered initialization. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) instances have unordered initialization. Other objects defined in namespace scope have ordered initialization. Objects defined within a single translation unit and with ordered initialization shall be initialized in the order of their definitions in the translation unit. The order of initialization is unspecified for objects with unordered initialization and for objects defined in different translation units.



558. Excluded characters in universal character names

Section: 5.3  [lex.charset]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 8 February 2006

[Moved to DR at October 2007 meeting.]

C99 and C++ differ in their approach to universal character names (UCNs).

Issue 248 already covers the differences in UCNs allowed for identifiers, but a more fundamental issue is that of UCNs that correspond to codes reserved by ISO 10676 for surrogate pair forms.

Specifically, C99 does not allow UCNs whose short names are in the range 0xD800 to 0xDFFF. I think C++ should have the same constraint. If someone really wants to place such a code in a character or string literal, they should use a hexadecimal escape sequence instead, for example:

    wchar_t  w1 = L'\xD900'; // Okay.
    wchar_t  w2 = L'\uD900'; // Error, not a valid character.

(Compare 6.4.3 paragraph 2 in ISO/IEC 9899/1999 with 5.3 [lex.charset] paragraph 2 in the C++ standard.)

Proposed resolution (October, 2007):

This issue is resolved by the adoption of paper J16/07-0030 = WG21 N2170.




505. Conditionally-supported behavior for unknown character escapes

Section: 5.13.3  [lex.ccon]     Status: CD1     Submitter: Mike Miller     Date: 14 Apr 2005

[Voted into WP at the October, 2006 meeting.]

The current wording of 5.13.3 [lex.ccon] paragraph 3 states,

If the character following a backslash is not one of those specified, the behavior is undefined.

Paper J16/04-0167=WG21 N1727 suggests that such character escapes be ill-formed. In discussions at the Lillehammer meeting, however, the CWG felt that the newly-approved category of conditionally-supported behavior would be more appropriate.

Proposed resolution (April, 2006):

Change the next-to-last sentence of 5.13.3 [lex.ccon] paragraph 3 from:

If the character following a backslash is not one of those specified, the behavior is undefined.

to:

Escape sequences in which the character following the backslash is not listed in Table 6 are conditionally-supported, with implementation-defined semantics.



309. Linkage of entities whose names are not simply identifiers, in introduction

Section: 6.1  [basic.pre]     Status: CD1     Submitter: Mike Miller     Date: 17 Sep 2001

[Voted into the WP at the June, 2008 meeting.]

6.1 [basic.pre] paragraph 10, while not incorrect, does not allow for linkage of operators and conversion functions. It says:

An identifier used in more than one translation unit can potentially refer to the same entity in these translation units depending on the linkage (6.6 [basic.link]) of the identifier specified in each translation unit.

Proposed Resolution (November, 2006):

This issue is resolved by the proposed resolution of issue 485.




485. What is a “name”?

Section: 6.1  [basic.pre]     Status: CD1     Submitter: Gabriel Dos Reis     Date: 9 Nov 2004

[Voted into the WP at the June, 2008 meeting.]

6.1 [basic.pre] paragraph 4 says:

A name is a use of an identifier (5.10 [lex.name]) that denotes an entity or label (8.7.6 [stmt.goto], 8.2 [stmt.label]).

Just three paragraphs later, it says

Two names are the same if

The last two bullets contradict the definition of name in paragraph 4 because they are not identifiers.

This definition affects other parts of the Standard, as well. For example, in 6.5.4 [basic.lookup.argdep] paragraph 1,

When an unqualified name is used as the postfix-expression in a function call (7.6.1.3 [expr.call]), other namespaces not considered during the usual unqualified lookup (6.5.3 [basic.lookup.unqual]) may be searched, and in those namespaces, namespace-scope friend function declarations (11.8.4 [class.friend]) not otherwise visible may be found.

With the current definition of name, argument-dependent lookup apparently does not apply to function-notation calls to overloaded operators.

Another related question is whether a template-id is a name or not and thus would trigger an argument-dependent lookup. Personally, I have always viewed a template-id as a name, just like operator+.

Proposed Resolution (November, 2006):

  1. Change 6.1 [basic.pre] paragraphs 3-8 as follows:

    1. An entity is a value, object, subobject, base class subobject, array element, variable, reference, function, instance of a function, enumerator, type, class member, template, template specialization, namespace, or parameter pack.

    2. A name is a use of an identifier identifier (5.10 [lex.name]), operator-function-id (12.4 [over.oper]), conversion-function-id (11.4.8.3 [class.conv.fct]), or template-id (13.3 [temp.names]) that denotes an entity or label (8.7.6 [stmt.goto], 8.2 [stmt.label]). A variable is introduced by the declaration of an object. The variable's name denotes the object.

    3. Every name that denotes an entity is introduced by a declaration. Every name that denotes a label is introduced either by a goto statement (8.7.6 [stmt.goto]) or a labeled-statement (8.2 [stmt.label]).

    4. A variable is introduced by the declaration of an object. The variable's name denotes the object.

    5. Some names denote types, classes, enumerations, or templates. In general, it is necessary to determine whether or not a name denotes one of these entities before parsing the program that contains it. The process that determines this is called name lookup (6.5 [basic.lookup]).

    6. Two names are the same if

      • they are identifiers identifiers composed of the same character sequence; or

      • they are the names of overloaded operator functions operator-function-ids formed with the same operator; or

      • they are the names of user-defined conversion functions conversion-function-ids formed with the same type., or

      • they are template-ids that refer to the same class or function (13.6 [temp.type]).

    7. An identifier A name used in more than one translation unit can potentially refer to the same entity in these translation units depending on the linkage (6.6 [basic.link]) of the identifier name specified in each translation unit.

  2. Change 6.4.7 [basic.scope.class] paragraph 1 item 5 as follows:

    The potential scope of a declaration that extends to or past the end of a class definition also extends to the regions defined by its member definitions, even if the members are defined lexically outside the class (this includes static data member definitions, nested class definitions, member function definitions (including the member function body and any portion of the declarator part of such definitions which follows the identifier declarator-id, including a parameter-declaration-clause and any default arguments (9.3.4.7 [dcl.fct.default]).

    [Drafting note: This last change is not really mandated by the issue, but it's another case of “identifier” confusion.]

(This proposed resolution also resolves issue 309.)




261. When is a deallocation function "used?"

Section: 6.3  [basic.def.odr]     Status: CD1     Submitter: Mike Miller     Date: 7 Nov 2000

[Moved to DR at October 2002 meeting.]

6.3 [basic.def.odr] paragraph 2 says that a deallocation function is "used" by a new-expression or delete-expression appearing in a potentially-evaluated expression. 6.3 [basic.def.odr] paragraph 3 requires only that "used" functions be defined.

This wording runs afoul of the typical implementation technique for polymorphic delete-expressions in which the deallocation function is invoked from the virtual destructor of the most-derived class. The problem is that the destructor must be defined, because it's virtual, and if it contains an implicit reference to the deallocation function, the deallocation function must also be defined, even if there are no relevant new-expressions or delete-expressions in the program.

For example:

        struct B { virtual ~B() { } };

        struct D: B {
            void operator delete(void*);
            ~D() { }
        };

Is it required that D::operator delete(void*) be defined, even if no B or D objects are ever created or deleted?

Suggested resolution: Add the words "or if it is found by the lookup at the point of definition of a virtual destructor (11.4.7 [class.dtor])" to the specification in 6.3 [basic.def.odr] paragraph 2.

Notes from 04/01 meeting:

The consensus was in favor of requiring that any declared non-placement operator delete member function be defined if the destructor for the class is defined (whether virtual or not), and similarly for a non-placement operator new if a constructor is defined.

Proposed resolution (10/01):

In 6.3 [basic.def.odr] paragraph 2, add the indicated text:

An allocation or deallocation function for a class is used by a new expression appearing in a potentially-evaluated expression as specified in 7.6.2.8 [expr.new] and 11.4.11 [class.free]. A deallocation function for a class is used by a delete expression appearing in a potentially-evaluated expression as specified in 7.6.2.9 [expr.delete] and 11.4.11 [class.free]. A non-placement allocation or deallocation function for a class is used by the definition of a constructor of that class. A non-placement deallocation function for a class is used by the definition of the destructor of that class, or by being selected by the lookup at the point of definition of a virtual destructor (11.4.7 [class.dtor]). [Footnote: An implementation is not required to call allocation and deallocation functions from constructors or destructors; however, this is a permissible implementation technique.]




289. Incomplete list of contexts requiring a complete type

Section: 6.3  [basic.def.odr]     Status: CD1     Submitter: Mike Miller     Date: 25 May 2001

[Moved to DR at October 2002 meeting.]

6.3 [basic.def.odr] paragraph 4 has a note listing the contexts that require a class type to be complete. It does not list use as a base class as being one of those contexts.

Proposed resolution (10/01):

In 6.3 [basic.def.odr] paragraph 4 add a new bullet at the end of the note as the next-to-last bullet:




433. Do elaborated type specifiers in templates inject into enclosing namespace scope?

Section: 6.4.2  [basic.scope.pdecl]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 2 September 2003

[Voted into WP at March 2004 meeting.]

Consider the following translation unit:

  template<class T> struct S {
    void f(union U*);  // (1)
  };
  template<class T> void S<T>::f(union U*) {}  // (2)
  U *p;  // (3)

Does (1) introduce U as a visible name in the surrounding namespace scope?

If not, then (2) could presumably be an error since the "union U" in that definition does not find the same type as the declaration (1).

If yes, then (3) is OK too. However, we have gone through much trouble to allow template implementations that do not pre-parse the template definitions, but requiring (1) to be visible would change that.

A slightly different case is the following:

  template<typename> void f() { union U *p; }
  U *q;  // Should this be valid?

Notes from October 2003 meeting:

There was consensus that example 1 should be allowed. (Compilers already parse declarations in templates; even MSVC++ 6.0 accepts this case.) The vote was 7-2.

Example 2, on the other hand, is wrong; the union name goes into a block scope anyway.

Proposed resolution:

In 6.4.2 [basic.scope.pdecl] change the second bullet of paragraph 5 as follows:

for an elaborated-type-specifier of the form
   class-key identifier
if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest non-class, non-function-prototype scope that contains the declaration. [Note: These rules also apply within templates.] [Note: ...]



432. Is injected class name visible in base class specifier list?

Section: 6.4.7  [basic.scope.class]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 29 August 2003

[Voted into WP at March 2004 meeting.]

Consider the following example (inspired by a question from comp.lang.c++.moderated):

  template<typename> struct B {};
  template<typename T> struct D: B<D> {};

Most (all?) compilers reject this code because D is handled as a template name rather than as the injected class name.

Clause 11 [class]/2 says that the injected class name is "inserted into the scope of the class."

6.4.7 [basic.scope.class]/1 seems to be the text intended to describe what "scope of a class" means, but it assumes that every name in that scope was introduced using a "declarator". For an implicit declaration such as the injected-class name it is not clear what that means.

So my questions:

  1. Should the injected class name be available in the base class specifiers?
    John Spicer: I do not believe the injected class name should be available in the base specifier. I think the semantics of injected class names should be as if a magic declaration were inserted after the opening "{" of the class definition. The injected class name is a member of the class and members don't exist at the point where the base specifiers are scanned.
  2. Do you agree the wording should be clarified whatever the answer to the first question?
    John Spicer: I believe the 6.4.7 [basic.scope.class] wording should be updated to reflect the fact that not all names come from declarators.

Notes from October 2003 meeting:

We agree with John Spicer's suggested answers above.

Proposed Resolution (October 2003):

The answer to question 1 above is No and no change is required.

For question 1, change 6.4.7 [basic.scope.class] paragraph 1 rule 1 to:

1) The potential scope of a name declared in a class consists not only of the declarative region following the name's point of declaration declarator, but also of all function bodies, default arguments, and constructor ctor-initializers in that class (including such things in nested classes). The point of declaration of an injected-class-name (Clause 11 [class]) is immediately following the opening brace of the class definition.

(Note that this change overlaps a change in issue 417.)

Also change 6.4.2 [basic.scope.pdecl] by adding a new paragraph 8 for the injected-class-name case:

The point of declaration for an injected-class-name (Clause 11 [class]) is immediately following the opening brace of the class definition.

Alternatively this paragraph could be added after paragraph 5 and before the two note paragraphs (i.e. it would become paragraph 5a).




39. Conflicting ambiguity rules

Section: 6.5.2  [class.member.lookup]     Status: CD1     Submitter: Neal M Gafter     Date: 20 Aug 1998

[Voted into WP at April 2005 meeting.]

The ambiguity text in 6.5.2 [class.member.lookup] may not say what we intended. It makes the following example ill-formed:

    struct A {
        int x(int);
    };
    struct B: A {
        using A::x;
        float x(float);
    };

    int f(B* b) {
        b->x(3);  // ambiguous
    }
This is a name lookup ambiguity because of 6.5.2 [class.member.lookup] paragraph 2:
... Each of these declarations that was introduced by a using-declaration is considered to be from each sub-object of C that is of the type containing the declaration designated by the using-declaration. If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed.
This contradicts the text and example in paragraph 12 of 9.9 [namespace.udecl] .

Proposed Resolution (10/00):

  1. Replace the two cited sentences from 6.5.2 [class.member.lookup] paragraph 2 with the following:

    The resulting set of declarations shall all be from sub-objects of the same type, or there shall be a set of declarations from sub-objects of a single type that contains using-declarations for the declarations found in all other sub-object types. Furthermore, for nonstatic members, the resulting set of declarations shall all be from a single sub-object, or there shall be a set of declarations from a single sub-object that contains using-declarations for the declarations found in all other sub-objects. Otherwise, there is an ambiguity and the program is ill-formed.
  2. Replace the examples in 6.5.2 [class.member.lookup] paragraph 3 with the following:

        struct A {
            int x(int);
            static int y(int);
        };
        struct V {
            int z(int);
        };
        struct B: A, virtual V {
            using A::x;
            float x(float);
            using A::y;
            static float y(float);
            using V::z;
            float z(float);
        };
        struct C: B, A, virtual V {
        };
    
        void f(C* c) {
            c->x(3);    // ambiguous -- more than one sub-object A
            c->y(3);    // not ambiguous
            c->z(3);    // not ambiguous
        }
    

Notes from 04/01 meeting:

The following example should be accepted but is rejected by the wording above:

    struct A { static void f(); };

    struct B1: virtual A {
        using A::f;
    };

    struct B2: virtual A {
        using A::f;
    };

    struct C: B1, B2 { };

    void g() {
        C::f();        // OK, calls A::f()
    }

Notes from 10/01 meeting (Jason Merrill):

The example in the issues list:

    struct A {
        int x(int);
    };
    struct B: A {
        using A::x;
        float x(float);
    };

    int f(B* b) {
        b->x(3);  // ambiguous
    }
Is broken under the existing wording:
... Each of these declarations that was introduced by a using-declaration is considered to be from each sub-object of C that is of the type containing the declaration designated by the using-declaration. If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed.
Since the two x's are considered to be "from" different objects, looking up x produces a set including declarations "from" different objects, and the program is ill-formed. Clearly this is wrong. The problem with the existing wording is that it fails to consider lookup context.

The first proposed solution:

The resulting set of declarations shall all be from sub-objects of the same type, or there shall be a set of declarations from sub-objects of a single type that contains using-declarations for the declarations found in all other sub-object types. Furthermore, for nonstatic members, the resulting set of declarations shall all be from a single sub-object, or there shall be a set of declarations from a single sub-object that contains using-declarations for the declarations found in all other sub-objects. Otherwise, there is an ambiguity and the program is ill-formed.
breaks this testcase:
    struct A { static void f(); };

    struct B1: virtual A {
        using A::f;
    };

    struct B2: virtual A {
        using A::f;
    };

    struct C: B1, B2 { };

    void g() {
        C::f();        // OK, calls A::f()
    }
because it considers the lookup context, but not the definition context; under this definition of "from", the two declarations found are the using-declarations, which are "from" B1 and B2.

The solution is to separate the notions of lookup and definition context. I have taken an algorithmic approach to describing the strategy.

Incidentally, the earlier proposal allows one base to have a superset of the declarations in another base; that was an extension, and my proposal does not do that. One algorithmic benefit of this limitation is to simplify the case of a virtual base being hidden along one arm and not another ("domination"); if we allowed supersets, we would need to remember which subobjects had which declarations, while under the following resolution we need only keep two lists, of subobjects and declarations.

Proposed resolution (October 2002):

Replace 6.5.2 [class.member.lookup] paragraph 2 with:

The following steps define the result of name lookup for a member name f in a class scope C.

The lookup set for f in C, called S(f,C), consists of two component sets: the declaration set, a set of members named f; and the subobject set, a set of subobjects where declarations of these members (possibly including using-declarations) were found. In the declaration set, using-declarations are replaced by the members they designate, and type declarations (including injected-class-names) are replaced by the types they designate. S(f,C) is calculated as follows.

If C contains a declaration of the name f, the declaration set contains every declaration of f in C (excluding bases), the subobject set contains C itself, and calculation is complete.

Otherwise, S(f,C) is initially empty. If C has base classes, calculate the lookup set for f in each direct base class subjobject Bi, and merge each such lookup set S(f,Bi) in turn into S(f,C).

The following steps define the result of merging lookup set S(f,Bi) into the intermediate S(f,C):

The result of name lookup for f in C is the declaration set of S(f,C). If it is an invalid set, the program is ill-formed.

[Example:

    struct A { int x; };                    // S(x,A) = {{ A::x }, { A }}
    struct B { float x; };                  // S(x,B) = {{ B::x }, { B }}
    struct C: public A, public B { };       // S(x,C) = { invalid, { A in C, B in C }}
    struct D: public virtual C { };         // S(x,D) = S(x,C)
    struct E: public virtual C { char x; }; // S(x,E) = {{ E::x }, { E }}
    struct F: public D, public E { };       // S(x,F) = S(x,E)

    int main() {
      F f;
      f.x = 0;   // OK, lookup finds { E::x }
    }
S(x,F) is unambiguous because the A and B base subobjects of D are also base subobjects of E, so S(x,D) is discarded in the first merge step. --end example]

Turn 6.5.2 [class.member.lookup] paragraphs 5 and 6 into notes.

Notes from October 2003 meeting:

Mike Miller raised some new issues in N1543, and we adjusted the proposed resolution as indicated in that paper.

Further information from Mike Miller (January 2004):

Unfortunately, I've become aware of a minor glitch in the proposed resolution for issue 39 in N1543, so I'd like to suggest a change that we can discuss in Sydney.

A brief review and background of the problem: the major change we agreed on in Kona was to remove detection of multiple-subobject ambiguity from class lookup (6.5.2 [class.member.lookup]) and instead handle it as part of the class member access expression. It was pointed out in Kona that 11.8.3 [class.access.base]/5 has this effect:

If a class member access operator, including an implicit "this->," is used to access a nonstatic data member or nonstatic member function, the reference is ill-formed if the left operand (considered as a pointer in the "." operator case) cannot be implicitly converted to a pointer to the naming class of the right operand.

After the meeting, however, I realized that this requirement is not sufficient to handle all the cases. Consider, for instance,

    struct B {
        int i;
    };

    struct I1: B { };
    struct I2: B { };

    struct D: I1, I2 {
        void f() {
            i = 0;    // not ill-formed per 11.2p5
        }
    };

Here, both the object expression ("this") and the naming class are "D", so the reference to "i" satisfies the requirement in 11.8.3 [class.access.base]/5, even though it involves a multiple-subobject ambiguity.

In order to address this problem, I proposed in N1543 to add a paragraph following 7.6.1.5 [expr.ref]/4:

If E2 is a non-static data member or a non-static member function, the program is ill-formed if the class of E1 cannot be unambiguously converted (10.2) to the class of which E2 is directly a member.

That's not quite right. It does diagnose the case above as written; however, it breaks the case where qualification is used to circumvent the ambiguity:

    struct D2: I1, I2 {
        void f() {
            I2::i = 0;    // ill-formed per proposal
        }
    };

In my proposed wording, the class of "this" can't be converted to "B" (the qualifier is ignored), so the access is ill-formed. Oops.

I think the following is a correct formulation, so the proposed resolution we discuss in Sydney should contain the following paragraph instead of the one in N1543:

If E2 is a nonstatic data member or a non-static member function, the program is ill-formed if the naming class (11.2) of E2 cannot be unambiguously converted (10.2) to the class of which E2 is directly a member.

This reformulation also has the advantage of pointing readers to 11.8.3 [class.access.base], where the the convertibility requirement from the class of E1 to the naming class is located and which might otherwise be overlooked.

Notes from the March 2004 meeting:

We discussed this further and agreed with these latest recommendations. Mike Miller has produced a paper N1626 that gives just the final collected set of changes.

(This resolution also resolves isssue 306.)




306. Ambiguity by class name injection

Section: 6.5.2  [class.member.lookup]     Status: CD1     Submitter: Clark Nelson     Date: 19 Jul 2001

[Voted into WP at April 2005 meeting.]

Is the following well-formed?

    struct A {
        struct B { };
    };
    struct C : public A, public A::B {
        B *p;
    };
The lookup of B finds both the struct B in A and the injected B from the A::B base class. Are they the same thing? Does the standard say so?

What if a struct is found along one path and a typedef to that struct is found along another path? That should probably be valid, but does the standard say so?

This is resolved by issue 39

February 2004: Moved back to "Review" status because issue 39 was moved back to "Review".




139. Error in friend lookup example

Section: 6.5.3  [basic.lookup.unqual]     Status: CD1     Submitter: Mike Miller     Date: 14 Jul 1999

[Moved to DR at 10/01 meeting.]

The example in 6.5.3 [basic.lookup.unqual] paragraph 3 is incorrect:

    typedef int f;
    struct A {
        friend void f(A &);
        operator int();
        void g(A a) {
            f(a);
        }
    };
Regardless of the resolution of other issues concerning the lookup of names in friend declarations, this example is ill-formed (the function and the typedef cannot exist in the same scope).

One possible repair of the example would be to make f a class with a constructor taking either A or int as its parameter.

(See also issues 95, 136, 138, 143, 165, and 166.)

Proposed resolution (04/01):

  1. Change the example in 6.5.3 [basic.lookup.unqual] paragraph 3 to read:

        typedef int f;
        namespace N {
            struct A {
                friend int f(A &);
                operator int();
                void g(A a) {
                    int i = f(a);
                          // f is the typedef, not the friend function:
                          // equivalent to int(a)
                }
            };
        }
    
  2. Delete the sentence immediately following the example:

    The expression f(a) is a cast-expression equivalent to int(a).



514. Is the initializer for a namespace member in the scope of the namespace?

Section: 6.5.3  [basic.lookup.unqual]     Status: CD1     Submitter: Mike Miller     Date: 24 Mar 2005

[Voted into WP at the October, 2006 meeting.]

Is the following code well-formed?

    namespace N {
      int i;
      extern int j;
    }
    int N::j = i;

The question here is whether the lookup for i in the initializer of N::j finds the declaration in namespace N or not. Implementations differ on this question.

If N::j were a static data member of a class, the answer would be clear: both 6.5.3 [basic.lookup.unqual] paragraph 12 and 9.4 [dcl.init] paragraph 11 say that the initializer “is in the scope of the member's class.” There is no such provision for namespace members defined outside the namespace, however.

The reasoning given in 6.5.3 [basic.lookup.unqual] may be instructive:

A name used in the definition of a static data member of class X (11.4.9.3 [class.static.data]) (after the qualified-id of the static member) is looked up as if the name was used in a member function of X.

It is certainly the case that a name used in a function that is a member of a namespace is looked up in that namespace (6.5.3 [basic.lookup.unqual] paragraph 6), regardless of whether the definition is inside or outside that namespace. Initializers for namespace members should probably be looked up the same way.

Proposed resolution (April, 2006):

Add a new paragraph following 6.5.3 [basic.lookup.unqual] paragraph 12:

If a variable member of a namespace is defined outside of the scope of its namespace then any name used in the definition of the variable member (after the declarator-id) is looked up as if the definition of the variable member occurred in its namespace. [Example:

    namespace N {
      int i = 4;
      extern int j;
    }

    int i = 2;

    int N::j = i;	// N::j == 4

end example]




143. Friends and Koenig lookup

Section: 6.5.4  [basic.lookup.argdep]     Status: CD1     Submitter: Mike Miller     Date: 21 Jul 1999

[Moved to DR at 4/02 meeting.]

Paragraphs 1 and 2 of 6.5.4 [basic.lookup.argdep] say, in part,

When an unqualified name is used as the postfix-expression in a function call (7.6.1.3 [expr.call] )... namespace-scope friend function declarations (11.8.4 [class.friend] ) not otherwise visible may be found... the set of declarations found by the lookup of the function name [includes] the set of declarations found in the... classes associated with the argument types.
The most straightforward reading of this wording is that if a function of namespace scope (as opposed to a class member function) is declared as a friend in a class, and that class is an associated class in a function call, the friend function will be part of the overload set, even if it is not visible to normal lookup.

Consider the following example:

    namespace A {
	class S;
    };
    namespace B {
	void f(A::S);
    };
    namespace A {
	class S {
	    int i;
	    friend void B::f(S);
	};
    }
    void g() {
	A::S s;
	f(s); // should find B::f(A::S)
    }
This example would seem to satisfy the criteria from 6.5.4 [basic.lookup.argdep] : A::S is an associated class of the argument, and A::S has a friend declaration of the namespace-scope function B::f(A::S), so Koenig lookup should include B::f(A::S) as part of the overload set in the call.

Another interpretation is that, instead of finding the friend declarations in associated classes, one only looks for namespace-scope functions, visible or invisible, in the namespaces of which the the associated classes are members; the only use of the friend declarations in the associated classes is to validate whether an invisible function declaration came from an associated class or not and thus whether it should be included in the overload set or not. By this interpretation, the call f(s) in the example will fail, because B::f(A::S) is not a member of namespace A and thus is not found by the lookup.

Notes from 10/99 meeting: The second interpretation is correct. The wording should be revised to make clear that Koenig lookup works by finding "invisible" declarations in namespace scope and not by finding friend declarations in associated classes.

Proposed resolution (04/01): The "associated classes" are handled adequately under this interpretation by 6.5.4 [basic.lookup.argdep] paragraph 3, which describes the lookup in the associated namespaces as including the friend declarations from the associated classes. Other mentions of the associated classes should be removed or qualified to avoid the impression that there is a lookup in those classes:

  1. In 6.5.4 [basic.lookup.argdep], change

    When an unqualified name is used as the postfix-expression in a function call (7.6.1.3 [expr.call]), other namespaces not considered during the usual unqualified lookup (6.5.3 [basic.lookup.unqual]) may be searched, and namespace-scope friend function declarations (11.8.4 [class.friend]) not otherwise visible may be found.

    to

    When an unqualified name is used as the postfix-expression in a function call (7.6.1.3 [expr.call]), other namespaces not considered during the usual unqualified lookup (6.5.3 [basic.lookup.unqual]) may be searched, and in those namespaces, namespace-scope friend function declarations (11.8.4 [class.friend]) not otherwise visible may be found.
  2. In 6.5.4 [basic.lookup.argdep] paragraph 2, delete the words and classes in the following two sentences:

    If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated namespaces and classes are not considered. Otherwise the set of declarations found by the lookup of the function name is the union of the set of declarations found using ordinary unqualified lookup and the set of declarations found in the namespaces and classes associated with the argument types.

(See also issues 95, 136, 138, 139, 165, 166, and 218.)




218. Specification of Koenig lookup

Section: 6.5.4  [basic.lookup.argdep]     Status: CD1     Submitter: Hyman Rosen     Date: 28 Mar 2000

[Voted into WP at April, 2007 meeting.]

The original intent of the Committee when Koenig lookup was added to the language was apparently something like the following:

  1. The name in the function call expression is looked up like any other unqualified name.
  2. If the ordinary unqualified lookup finds nothing or finds the declaration of a (non-member) function, function template, or overload set, argument-dependent lookup is done and any functions found in associated namespaces are added to the result of the ordinary lookup.

This approach is not reflected in the current wording of the Standard. Instead, the following appears to be the status quo:

  1. Lookup of an unqualified name used as the postfix-expression in the function call syntax always performs Koenig lookup (6.5.3 [basic.lookup.unqual] paragraph 3).
  2. Unless ordinary lookup finds a class member function, the result of Koenig lookup always includes the declarations found in associated namespaces (6.5.4 [basic.lookup.argdep] paragraph 2), regardless of whether ordinary lookup finds a declaration and, if so, what kind of entity is found.
  3. The declarations from associated namespaces are not limited to functions and template functions by anything in 6.5.4 [basic.lookup.argdep]. However, if Koenig lookup results in more than one declaration and at least one of the declarations is a non-function, the program is ill-formed (9.8.4 [namespace.udir], paragraph 4; although this restriction is in the description of the using-directive, the wording applies to any lookup that spans namespaces).

John Spicer: Argument-dependent lookup was created to solve the problem of looking up function names within templates where you don't know which namespace to use because it may depend on the template argument types (and was then expanded to permit use in nontemplates). The original intent only concerned functions. The safest and simplest change is to simply clarify the existing wording to that effect.

Bill Gibbons: I see no reason why non-function declarations should not be found. It would take a special rule to exclude "function objects", as well as pointers to functions, from consideration. There is no such rule in the standard and I see no need for one.

There is also a problem with the wording in 6.5.4 [basic.lookup.argdep] paragraph 2:

If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated namespaces and classes are not considered.

This implies that if the ordinary lookup of the name finds the declaration of a data member which is a pointer to function or function object, argument-dependent lookup is still done.

My guess is that this is a mistake based on the incorrect assumption that finding any member other than a member function would be an error. I would just change "class member function" to "class member" in the quoted sentence.

Mike Miller: In light of the issue of "short-circuiting" Koenig lookup when normal lookup finds a non-function, perhaps it should be written as "...finds the declaration of a class member, an object, or a reference, the associated namespaces..."?

Andy Koenig: I think I have to weigh in on the side of extending argument-dependent lookup to include function objects and pointers to functions. I am particularly concerned about [function objects], because I think that programmers should be able to replace functions by function objects without changing the behavior of their programs in fundamental ways.

Bjarne Stroustrup: I don't think we could seriously argue from first principles that [argument-dependent lookup should find only function declarations]. In general, C++ name lookup is designed to be independent of type: First we find the name(s), then, we consider its(their) meaning. 6.5 [basic.lookup] states "The name lookup rules apply uniformly to all names ..." That is an important principle.

Thus, I consider text that speaks of "function call" instead of plain "call" or "application of ()" in the context of koenig lookup an accident of history. I find it hard to understand how 7.6.1.3 [expr.call] doesn't either disallow all occurrences of x(y) where x is a class object (that's clearly not intended) or requires koenig lookup for x independently of its type (by reference from 6.5 [basic.lookup]). I suspect that a clarification of 7.6.1.3 [expr.call] to mention function objects is in order. If the left-hand operand of () is a name, it should be looked up using koenig lookup.

John Spicer: This approach causes otherwise well-formed programs to be ill-formed, and it does so by making names visible that might be completely unknown to the author of the program. Using-directives already do this, but argument-dependent lookup is different. You only get names from using-directives if you actually use using-directives. You get names from argument-dependent lookup whether you want them or not.

This basically breaks an important reason for having namespaces. You are not supposed to need any knowledge of the names used by a namespace.

But this example breaks if argument-dependent lookup finds non-functions and if the translation unit includes the <list> header somewhere.

    namespace my_ns {
        struct A {};
        void list(std::ostream&, A&);

        void f() {
            my_ns::A a;
            list(cout, a);
        }
    }

This really makes namespaces of questionable value if you still need to avoid using the same name as an entity in another namespace to avoid problems like this.

Erwin Unruh: Before we really decide on this topic, we should have more analysis on the impact on programs. I would also like to see a paper on the possibility to overload functions with function surrogates (no, I won't write one). Since such an extension is bound to wait until the next official update, we should not preclude any outcome of the discussion.

I would like to have a change right now, which leaves open several outcomes later. I would like to say that:

Koenig lookup will find non-functions as well. If it finds a variable, the program is ill-formed. If the primary lookup finds a variable, Koenig lookup is done. If the result contains both functions and variables, the program is ill-formed. [Note: A future standard will assign semantics to such a program.]

I myself are not comfortable with this as a long-time result, but it prepares the ground for any of the following long term solutions:

The note is there to prevent compiler vendors to put their own extensions in here.

(See also issues 113 and 143.)

Notes from 04/00 meeting:

Although many agreed that there were valid concerns motivating a desire for Koenig lookup to find non-function declarations, there was also concern that supporting this capability would be more dangerous than helpful in the absence of overload resolution for mixed function and non-function declarations.

A straw poll of the group revealed 8 in favor of Koenig lookup finding functions and function templates only, while 3 supported the broader result.

Notes from the 10/01 meeting:

There was unanimous agreement on one less controversial point: if the normal lookup of the identifier finds a non-function, argument-dependent lookup should not be done.

On the larger issue, the primary point of consensus is that making this change is an extension, and therefore it should wait until the point at which we are considering extensions (which could be very soon). There was also consensus on the fact that the standard as it stands is not clear: some introductory text suggests that argument-dependent lookup finds only functions, but the more detailed text that describes the lookup does not have any such restriction.

It was also noted that some existing implementations (e.g., g++) do find some non-functions in some cases.

The issue at this point is whether we should (1) make a small change to make the standard clear (presumably in the direction of not finding the non-functions in the lookup), and revisit the issue later as an extension, or (2) leave the standard alone for now and make any changes only as part of considering the extension. A straw vote favored option (1) by a strong majority.

Additional Notes (September, 2006):

Recent discussion of this issue has emphasized the following points:

  1. The concept of finding function pointers and function objects as part of argument-dependent lookup is not currently under active discussion in the Evolution Working Group.

  2. The major area of concern with argument-dependent lookup is finding functions in unintended namespaces. There are current proposals to deal with this concern either by changing the definition of “associated namespace” so that fewer namespaces are considered or to provide a mechanism for enabling or disabling ADL altogether. Although this concern is conceptually distinct from the question of whether ADL finds function pointers and function objects, it is related in the sense that the current rules are perceived as finding too many functions (because of searching too many namespaces), and allowing function pointers and function objects would also increase the number of entities found by ADL.

  3. Any expansion of ADL to include function pointers and function objects must necessarily update the overloading rules to specify how they interact with functions and function templates in the overload set. Current implementation experience (g++) is not helpful in making this decision because, although it performs a uniform lookup and finds non-function entities, it diagnoses an error in overload resolution if non-function entities are in the overload set.

  4. There is a possible problem if types are found by ADL: it is not clear that overloading between callable entities (functions, function templates, function pointers, and function objects) and types (where the postfix syntax means a cast or construction of a temporary) is reasonable or useful.

James Widman:

There is a larger debate here about whether ADL should find object names; the proposed wording below is only intended to answer the request for wording to clarify the status quo (option 1 above) and not to suggest the outcome of the larger debate.

Proposed Resolution (October, 2006):

  1. Replace the normative text in 6.5.4 [basic.lookup.argdep] paragraph 3 with the following (leaving the text of the note and example unchanged):

    Let X be the lookup set produced by unqualified lookup (6.5.3 [basic.lookup.unqual]) and let Y be the lookup set produced by argument dependent lookup (defined as follows). If X contains

    • a declaration of a class member, or
    • a block-scope function declaration that is not a using-declaration, or
    • a declaration that is neither a function nor a function template

    then Y is empty. Otherwise Y is the set of declarations found in the namespaces associated with the argument types as described below. The set of declarations found by the lookup of the name is the union of X and Y.

  2. Change 6.5.3 [basic.lookup.unqual] paragraph 4 as indicated:

    When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier (6.5.5.3 [namespace.qual]) except that:

    • Any using-directives in the associated namespace are ignored.
    • Any namespace-scope friend functions or friend function templates declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup (11.8.4 [class.friend]).
    • All names except those of (possibly overloaded) functions and function templates are ignored.




403. Reference to a type as a template-id

Section: 6.5.4  [basic.lookup.argdep]     Status: CD1     Submitter: John Spicer     Date: 18 Sep 2003

[Voted into WP at March 2004 meeting.]

Spun off from issue 384.

6.5.4 [basic.lookup.argdep] says:

If T is a template-id, its associated namespaces and classes are the namespace in which the template is defined; for member templates, the member template's class; the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces in which any template template arguments are defined; and the classes in which any member templates used as template template arguments are defined. [Note: non-type template arguments do not contribute to the set of associated namespaces. ]
There is a problem with the term "is a template-id". template-id is a syntactic construct and you can't really talk about a type being a template-id. Presumably, this is intended to mean "If T is the type of a class template specialization ...".

Proposed Resolution (October 2003):

In 6.5.4 [basic.lookup.argdep], paragraph 2, bullet 8, replace

If T is a template-id ...
with
If T is a class template specialization ...




557. Does argument-dependent lookup cause template instantiation?

Section: 6.5.4  [basic.lookup.argdep]     Status: CD1     Submitter: Mike Miller     Date: 8 February 2006

[Voted into WP at the October, 2006 meeting.]

One might assume from 13.9.2 [temp.inst] paragraph 1 that argument-dependent lookup would require instantiation of any class template specializations used in argument types:

Unless a class template specialization has been explicitly instantiated (13.9.3 [temp.explicit]) or explicitly specialized (13.9.4 [temp.expl.spec]), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program.

A complete class type is required to determine the associated classes and namespaces for the argument type (to determine the class's bases) and to determine the friend functions declared by the class, so the completeness of the class type certainly “affects the semantics of the program.”

This conclusion is reinforced by the second bullet of 6.5.4 [basic.lookup.argdep] paragraph 2:

A class template specialization is a class type, so the second bullet would appear to apply, requiring the specialization to be instantiated in order to determine its base classes.

However, bullet 8 of that paragraph deals explicitly with class template specializations:

Note that the class template specialization itself is not listed as an associated class, unlike other class types, and there is no mention of base classes. If bullet 8 were intended as a supplement to the treatment of class types in bullet 2, one would expect phrasing along the lines of, “In addition to the associated namespaces and classes for all class types...” or some such; instead, bullet 8 reads like a self-contained and complete specification.

If argument-dependent lookup does not cause implicit instantiation, however, examples like the following fail:

    template <typename T> class C {
        friend void f(C<T>*) { }
    };
    void g(C<int>* p) {
        f(p);    // found by ADL??
    }

Implementations differ in whether this example works or not.

Proposed resolution (April, 2006):

  1. Change bullet 2 of 6.5.4 [basic.lookup.argdep] paragraph 2 as indicated:

  2. Delete bullet 8 of 6.5.4 [basic.lookup.argdep] paragraph 2:




298. T::x when T is cv-qualified

Section: 6.5.5.2  [class.qual]     Status: CD1     Submitter: Steve Adamczyk     Date: 7 Jul 2001

[Voted into WP at April 2003 meeting.]

Can a typedef T to a cv-qualified class type be used in a qualified name T::x?

    struct A { static int i; };
    typedef const A CA;
    int main () {
      CA::i = 0;  // Okay?
    }

Suggested answer: Yes. All the compilers I tried accept the test case.

Proposed resolution (10/01):

In 6.5.5.2 [class.qual] paragraph 1 add the indicated text:

If the nested-name-specifier of a qualified-id nominates a class, the name specified after the nested-name-specifier is looked up in the scope of the class (6.5.2 [class.member.lookup]), except for the cases listed below. The name shall represent one or more members of that class or of one of its base classes (11.7 [class.derived]). If the class-or-namespace-name of the nested-name-specifier names a cv-qualified class type, it nominates the underlying class (the cv-qualifiers are ignored).

Notes from 4/02 meeting:

There is a problem in that class-or-namespace-name does not include typedef names for cv-qualified class types. See 9.2.4 [dcl.typedef] paragraph 4:

Argument and text removed from proposed resolution (October 2002):

9.2.4 [dcl.typedef] paragraph 5:

Here's a good question: in this example, should X be used as a name-for-linkage-purposes (FLP name)?

  typedef class { } const X;

Because a type-qualifier is parsed as a decl-specifier, it isn't possible to declare cv-qualified and cv-unqualified typedefs for a type in a single declaration. Also, of course, there's no way to declare a typedef for the cv-unqualified version of a type for which only a cv-qualified version has a name. So, in the above example, if X isn't used as the FLP name, then there can be no FLP name. Also note that a FLP name usually represents a parameter type, where top-level cv-qualifiers are usually irrelevant anyway.

Data points: for the above example, Microsoft uses X as the FLP name; GNU and EDG do not.

My recommendation: for consistency with the direction we're going on this issue, for simplicity of description (e.g., "the first class-name declared by the declaration"), and for (very slightly) increased utility, I think Microsoft has this right.

If the typedef declaration defines an unnamed class type (or enum type), the first typedef-name declared by the declaration to be have that class type (or enum type) or a cv-qualified version thereof is used to denote the class type (or enum type) for linkage purposes only (6.6 [basic.link]). [Example: ...

Proposed resolution (October 2002):

6.5.6 [basic.lookup.elab] paragraphs 2 and 3:

This sentence is deleted twice:

... If this name lookup finds a typedef-name, the elaborated-type-specifier is ill-formed. ...

Note that the above changes are included in N1376 as part of the resolution of issue 245.

_N4567_.5.1.1 [expr.prim.general] paragraph 7:

This is only a note, and it is at least incomplete (and quite possibly inaccurate), despite (or because of) its complexity. I propose to delete it.

... [Note: a typedef-name that names a class is a class-name (11.3 [class.name]). Except as the identifier in the declarator for a constructor or destructor definition outside of a class member-specification (11.4.5 [class.ctor], 11.4.7 [class.dtor]), a typedef-name that names a class may be used in a qualified-id to refer to a constructor or destructor. ]

9.2.4 [dcl.typedef] paragraph 4:

My first choice would have been to make this the primary statement about the equivalence of typedef-name and class-name, since the equivalence comes about as a result of a typedef declaration. Unfortunately, references to class-name point to 11.3 [class.name], so it would seem that the primary statement should be there instead. To avoid the possiblity of conflicts in the future, I propose to make this a note.

[Note: A typedef-name that names a class type, or a cv-qualified version thereof, is also a class-name (11.3 [class.name]). If a typedef-name is used following the class-key in an elaborated-type-specifier (9.2.9.5 [dcl.type.elab]), or in the class-head of a class declaration (Clause 11 [class]), or is used as the identifier in the declarator for a constructor or destructor declaration (11.4.5 [class.ctor], 11.4.7 [class.dtor]), to identify the subject of an elaborated-type-specifier (9.2.9.5 [dcl.type.elab]), class declaration (Clause 11 [class]), constructor declaration (11.4.5 [class.ctor]), or destructor declaration (11.4.7 [class.dtor]), the program is ill-formed. ] [Example: ...

9.2.9.5 [dcl.type.elab] paragraph 2:

This is the only remaining (normative) statement that a typedef-name can't be used in an elaborated-type-specifier. The reference to template type-parameter is deleted by the resolution of issue 283.

... If the identifier resolves to a typedef-name or a template type-parameter, the elaborated-type-specifier is ill-formed. [Note: ...

9.3 [dcl.decl] grammar rule declarator-id:

When I looked carefully into the statement of the rule prohibiting a typedef-name in a constructor declaration, it appeared to me that this grammar rule (inadvertently?) allows something that's always forbidden semantically.

11.3 [class.name] paragraph 5:

Unlike the prohibitions against appearing in an elaborated-type-specifier or constructor or destructor declarator, each of which was expressed more than once, the prohibition against a typedef-name appearing in a class-head was previously stated only in 9.2.4 [dcl.typedef]. It seems to me that that prohibition belongs here instead. Also, it seems to me important to clarify that a typedef-name that is a class-name is still a typedef-name. Otherwise, the various prohibitions can be argued around easily, if perversely ("But that isn't a typedef-name, it's a class-name; it says so right there in 11.3 [class.name].")

A typedef-name (9.2.4 [dcl.typedef]) that names a class type or a cv-qualified version thereof is also a class-name, but shall not be used in an elaborated-type-specifier; see also 9.2.4 [dcl.typedef]. as the identifier in a class-head.

11.4.5 [class.ctor] paragraph 3:

The new nonterminal references are needed to really nail down what we're talking about here. Otherwise, I'm just eliminating redundancy. (A typedef-name that doesn't name a class type is no more valid here than one that does.)

A typedef-name that names a class is a class-name (9.2.4 [dcl.typedef]); however, a A typedef-name that names a class shall not be used as the identifier class-name in the declarator declarator-id for a constructor declaration.

11.4.7 [class.dtor] paragraph 1:

The same comments apply here as to 11.4.5 [class.ctor].

... A typedef-name that names a class is a class-name (7.1.3); however, a A typedef-name that names a class shall not be used as the identifier class-name following the ~ in the declarator for a destructor declaration.



318. struct A::A should not name the constructor of A

Section: 6.5.5.2  [class.qual]     Status: CD1     Submitter: John Spicer     Date: 18 Oct 2001

[Voted into WP at April 2003 meeting.]

A use of an injected-class-name in an elaborated-type-specifier should not name the constructor of the class, but rather the class itself, because in that context we know that we're looking for a type. See issue 147.

Proposed Resolution (revised October 2002):

This clarifies the changes made in the TC for issue 147.

In 6.5.5.2 [class.qual] paragraph 1a replace:

If the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected class name of C (Clause 11 [class]), the name is instead considered to name the constructor of class C.

with

In a lookup in which the constructor is an acceptable lookup result, if the nested-name-specifier nominates a class C and the name specified after the nested-name-specifier, when looked up in C, is the injected class name of C (Clause 11 [class]), the name is instead considered to name the constructor of class C. [Note: For example, the constructor is not an acceptable lookup result in an elaborated type specifier so the constructor would not be used in place of the injected class name.]

Note that issue 263 updates a part of the same paragraph.

Append to the example:

  struct A::A a2;  // object of type A



400. Using-declarations and the "struct hack"

Section: 6.5.5.3  [namespace.qual]     Status: CD1     Submitter: Mark Mitchell     Date: 22 Jan 2003

[Voted into WP at March 2004 meeting.]

Consider this code:

  struct A { int i; struct i {}; };
  struct B { int i; struct i {}; };
  struct D : public A, public B { using A::i; void f (); };
  void D::f () { struct i x; }

I can't find anything in the standard that says definitively what this means. 9.9 [namespace.udecl] says that a using-declaration shall name "a member of a base class" -- but here we have two members, the data member A::i and the class A::i.

Personally, I'd find it more attractive if this code did not work. I'd like "using A::i" to mean "lookup A::i in the usual way and bind B::i to that", which would mean that while "i = 3" would be valid in D::f, "struct i x" would not be. However, if there were no A::i data member, then "A::i" would find the struct and the code in D::f would be valid.

John Spicer: I agree with you, but unfortunately the standard committee did not.

I remembered that this was discussed by the committee and that a resolution was adopted that was different than what I hoped for, but I had a hard time finding definitive wording in the standard.

I went back though my records and found the paper that proposed a resolution and the associated committee motion that adopted the proposed resolution The paper is N0905, and "option 1" from that paper was adopted at the Stockholm meeting in July of 1996. The resolution is that "using A::i" brings in everything named i from A.

6.5.5.3 [namespace.qual] paragraph 2 was modified to implement this resolution, but interestingly that only covers the namespace case and not the class case. I think the class case was overlooked when the wording was drafted. A core issue should be opened to make sure the class case is handled properly.

Notes from April 2003 meeting:

This is related to issue 11. 9.9 [namespace.udecl] paragraph 10 has an example for namespaces.

Proposed resolution (October 2003):

Add a bullet to the end of 6.5.5.2 [class.qual] paragraph 1:

Change the beginning of 9.9 [namespace.udecl] paragraph 4 from

A using-declaration used as a member-declaration shall refer to a member of a base class of the class being defined, shall refer to a member of an anonymous union that is a member of a base class of the class being defined, or shall refer to an enumerator for an enumeration type that is a member of a base class of the class being defined.

to

In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined. Such a using-declaration introduces the set of declarations found by member name lookup (6.5.2 [class.member.lookup], 6.5.5.2 [class.qual]).



245. Name lookup in elaborated-type-specifiers

Section: 6.5.6  [basic.lookup.elab]     Status: CD1     Submitter: Jack Rouse     Date: 14 Sep 2000

[Voted into WP at April 2003 meeting.]

I have some concerns with the description of name lookup for elaborated type specifiers in 6.5.6 [basic.lookup.elab]:

  1. Paragraph 2 has some parodoxical statements concerning looking up names that are simple identifers:

    If the elaborated-type-specifier refers to an enum-name and this lookup does not find a previously declared enum-name, the elaborated-type-specifier is ill-formed. If the elaborated-type-specifier refers to an [sic] class-name and this lookup does not find a previously declared class-name... the elaborated-type-specifier is a declaration that introduces the class-name as described in 6.4.2 [basic.scope.pdecl]."

    It is not clear how an elaborated-type-specifier can refer to an enum-name or class-name given that the lookup does not find such a name and that class-name and enum-name are not part of the syntax of an elaborated-type-specifier.

  2. The second sentence quoted above seems to suggest that the name found will not be used if it is not a class name. typedef-name names are ill-formed due to the sentence preceding the quote. If lookup finds, for instance, an enum-name then a new declaration will be created. This differs from C, and from the enum case, and can have surprising effects:

        struct S {
           enum E {
               one = 1
           };
           class E* p;     // declares a global class E?
        };
    

    Was this really the intent? If this is the case then some more work is needed on 6.5.6 [basic.lookup.elab]. Note that the section does not make finding a type template formal ill-formed, as is done in 9.2.9.5 [dcl.type.elab]. I don't see anything that makes a type template formal name a class-name. So the example in 9.2.9.5 [dcl.type.elab] of friend class T; where T is a template type formal would no longer be ill-formed with this interpretation because it would declare a new class T.

(See also issue 254.)

Notes from the 4/02 meeting:

This will be consolidated with the changes for issue 254. See also issue 298.

Proposed resolution (October 2002):

As given in N1376=02-0034. Note that the inserts and strikeouts in that document do not display correctly in all browsers; <del> --> <strike> and <ins> --> <b>, and the similar changes for the closing delimiters, seem to do the trick.




254. Definitional problems with elaborated-type-specifiers

Section: 6.5.6  [basic.lookup.elab]     Status: CD1     Submitter: Clark Nelson     Date: 26 Oct 2000

[Voted into WP at April 2003 meeting.]

  1. The text in 6.5.6 [basic.lookup.elab] paragraph 2 twice refers to the possibility that an elaborated-type-specifier might have the form

            class-key identifier ;
    

    However, the grammar for elaborated-type-specifier does not include a semicolon.

  2. In both 6.5.6 [basic.lookup.elab] and 9.2.9.5 [dcl.type.elab], the text asserts that an elaborated-type-specifier that refers to a typedef-name is ill-formed. However, it is permissible for the form of elaborated-type-specifier that begins with typename to refer to a typedef-name.

    This problem is the result of adding the typename form to the elaborated-type-name grammar without changing the verbiage correspondingly. It could be fixed either by updating the verbiage or by moving the typename syntax into its own production and referring to both nonterminals when needed.

(See also issue 180. If this issue is resolved in favor of a separate nonterminal in the grammar for the typename forms, the wording in that issue's resolution must be changed accordingly.)

Notes from 04/01 meeting:

The consensus was in favor of moving the typename forms out of the elaborated-type-specifier grammar.

Notes from the 4/02 meeting:

This will be consolidated with the changes for issue 245.

Proposed resolution (October 2002):

As given in N1376=02-0034.




216. Linkage of nameless class-scope enumeration types

Section: 6.6  [basic.link]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 13 Mar 2000

[Moved to DR at 10/01 meeting.]

6.6 [basic.link] paragraph 4 says (among other things):
A name having namespace scope has external linkage if it is the name of
That prohibits for example:
    typedef enum { e1 } *PE;
    void f(PE) {}  // Cannot declare a function (with linkage) using a
		   // type with no linkage.

However, the same prohibition was not made for class scope types. Indeed, 6.6 [basic.link] paragraph 5 says:

In addition, a member function, static data member, class or enumeration of class scope has external linkage if the name of the class has external linkage.

That allows for:

    struct S {
       typedef enum { e1 } *MPE;
       void mf(MPE) {}
    };

My guess is that this is an unintentional consequence of 6.6 [basic.link] paragraph 5, but I would like confirmation on that.

Proposed resolution:

Change text in 6.6 [basic.link] paragraph 5 from:

In addition, a member function, static data member, class or enumeration of class scope has external linkage if the name of the class has external linkage.
to:
In addition, a member function, a static data member, a named class or enumeration of class scope, or an unnamed class or enumeration defined in a class-scope typedef declaration such that the class or enumeration has the typedef name for linkage purposes (9.2.4 [dcl.typedef]), has external linkage if the name of the class has external linkage.



319. Use of names without linkage in declaring entities with linkage

Section: 6.6  [basic.link]     Status: CD1     Submitter: Clark Nelson     Date: 29 Oct 2001

[Voted into WP at October 2004 meeting.]

According to 6.6 [basic.link] paragraph 8, "A name with no linkage ... shall not be used to declare an entity with linkage." This would appear to rule out code such as:

  typedef struct {
    int i;
  } *PT;
  extern "C" void f(PT);
[likewise]
  static enum { a } e;
which seems rather harmless to me.

See issue 132, which dealt with a closely related issue.

Andrei Iltchenko submitted the same issue via comp.std.c++ on 17 Dec 2001:

Paragraph 8 of Section 6.6 [basic.link] contains the following sentences: "A name with no linkage shall not be used to declare an entity with linkage. If a declaration uses a typedef name, it is the linkage of the type name to which the typedef refers that is considered."

The problem with this wording is that it doesn't cover cases where the type to which a typedef-name refers has no name. As a result it's not clear whether, for example, the following program is well-formed:

#include <vector>

int  main()
{
   enum  {   sz = 6u   };
   typedef int  (* aptr_type)[sz];
   typedef struct  data  {
      int   i,  j;
   }  * elem_type;
   std::vector<aptr_type>   vec1;
   std::vector<elem_type>   vec2;
}

Suggested resolution:

My feeling is that the rules for whether or not a typedef-name used in a declaration shall be treated as having or not having linkage ought to be modelled after those for dependent types, which are explained in 13.8.3.2 [temp.dep.type].

Add the following text at the end of Paragraph 8 of Section 6.6 [basic.link] and replace the following example:

In case of the type referred to by a typedef declaration not having a name, the newly declared typedef-name has linkage if and only if its referred type comprises no names of no linkage excluding local names that are eligible for appearance in an integral constant-expression (7.7 [expr.const]). [Note: if the referred type contains a typedef-name that does not denote an unnamed class, the linkage of that name is established by the recursive application of this rule for the purposes of using typedef names in declarations.] [Example:
  void f()
  {
     struct A { int x; };        // no linkage
     extern A a;                 // ill-formed
     typedef A Bl
     extern B b;                 // ill-formed

     enum  {   sz = 6u   };
     typedef int  (* C)[sz];     // C has linkage because sz can
                                 // appear in a constant expression
  }
--end example.]

Additional issue (13 Jan 2002, from Andrei Iltchenko):

Paragraph 2 of Section 13.4.2 [temp.arg.type] is inaccurate and unnecessarily prohibits a few important cases; it says "A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template-parameter." The inaccuracy stems from the fact that it is not a type but its name that can have a linkage.

For example based on the current wording of 13.4.2 [temp.arg.type], the following example is ill-formed.

  #include <vector>
  struct  data  {
    int   i,  j;
  };
  int  main()
  {
    enum  {   sz = 6u   };
    std::vector<int(*)[sz]>   vec1; // The types 'int(*)[sz]' and 'data*'
    std::vector<data*>        vec2; // have no names and are thus illegal
                                    // as template type arguments.
  }

Suggested resolution:

Replace the whole second paragraph of Section 13.4.2 [temp.arg.type] with the following wording:

A type whose name does not have a linkage or a type compounded from any such type shall not be used as a template-argument for a template-parameter. In case of a type T used as a template type argument not having a name, T constitutes a valid template type argument if and only if the name of an invented typedef declaration referring to T would have linkage; see 3.5. [Example:
  template <class T> class X { /* ... */ };
  void f()
  {
    struct S { /* ... */ };
    enum  {   sz = 6u   };

    X<S> x3;                     // error: a type name with no linkage
                                 // used as template-argument
    X<S*> x4;                    // error: pointer to a type name with
                                 // no linkage used as template-argument
    X<int(*)[sz]> x5;            // OK: since the name of typedef int
                                 // (*pname)[sz] would have linkage
  }
--end example] [Note: a template type argument may be an incomplete type (6.8 [basic.types]).]

Proposed resolution:

This is resolved by the changes for issue 389. The present issue was moved back to Review status in February 2004 because 389 was moved back to Review.




389. Unnamed types in entities with linkage

Section: 6.6  [basic.link]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 31 Oct 2002

[Voted into WP at October 2004 meeting.]

6.6 [basic.link] paragraph 8 says (among other things):

A name with no linkage (notably, the name of a class or enumeration declared in a local scope (6.4.3 [basic.scope.block])) shall not be used to declare an entity with linkage. If a declaration uses a typedef name, it is the linkage of the type name to which the typedef refers that is considered.

I would expect this to catch situations such as the following:

  // File 1:
  typedef struct {} *UP;
  void f(UP) {}

  // File 2:
  typedef struct {} *UP; // Or: typedef struct {} U, *UP;
  void f(UP);

The problem here is that most implementations must generate the same mangled name for "f" in two translation units. The quote from the standard above isn't quite clear, unfortunately: There is no type name to which the typedef refers.

A related situation is the following:

  enum { no, yes } answer;
The variable "answer" is declared as having external linkage, but it is declared with an unnamed type. Section 6.6 [basic.link] talks about the linkage of names, however, and does therefore not prohibit this. There is no implementation issue for most compilers because they do not ordinarily mangle variable names, but I believe the intent was to allow that implementation technique.

Finally, these problems are much less relevant when declaring names with internal linkage. For example, I would expect there to be few problems with:

  typedef struct {} *UP;
  static void g(UP);

I recently tried to interpret 6.6 [basic.link] paragraph 8 with the assumption that types with no names have no linkage. Surprisingly, this resulted in many diagnostics on variable declarations (mostly like "answer" above).

I'm pretty sure the standard needs clarifying words in this matter, but which way should it go?

See also issue 319.

Notes from April 2003 meeting:

There was agreement that this check is not needed for variables and functions with extern "C" linkage, and a change there is desirable to allow use of legacy C headers. The check is also not needed for entities with internal linkage, but there was no strong sentiment for changing that case.

We also considered relaxing this requirement for extern "C++" variables but decided that we did not want to change that case.

We noted that if extern "C" functions are allowed an additional check is needed when such functions are used as arguments in calls of function templates. Deduction will put the type of the extern "C" function into the type of the template instance, i.e., there would be a need to mangle the name of an unnamed type. To plug that hole we need an additional requirement on the template created in such a case.

Proposed resolution (April 2003, revised slightly October 2003 and March 2004):

In 6.6 [basic.link] paragraph 8, change

A name with no linkage (notably, the name of a class or enumeration declared in a local scope (6.4.3 [basic.scope.block])) shall not be used to declare an entity with linkage. If a declaration uses a typedef name, it is the linkage of the type name to which the typedef refers that is considered.

to

A type is said to have linkage if and only if A type without linkage shall not be used as the type of a variable or function with linkage, unless the variable or function has extern "C" linkage (9.11 [dcl.link]). [Note: in other words, a type without linkage contains a class or enumeration that cannot be named outside of its translation unit. An entity with external linkage declared using such a type could not correspond to any other entity in another translation unit of the program and is thus not permitted. Also note that classes with linkage may contain members whose types do not have linkage, and that typedef names are ignored in the determination of whether a type has linkage.]

Change 13.4.2 [temp.arg.type] paragraph 2 from (note: this is the wording as updated by issue 62)

The following types shall not be used as a template-argument for a template type-parameter:

to

A type without linkage (6.6 [basic.link]) shall not be used as a template-argument for a template type-parameter.

Once this issue is ready, issue 319 should be moved back to ready as well.




474. Block-scope extern declarations in namespace members

Section: 6.6  [basic.link]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 23 Jul 2004

[Voted into WP at October 2005 meeting.]

Consider the following bit of code:

    namespace N {
      struct S {
        void f();
      };
    }
    using namespace N;
    void S::f() {
      extern void g();  // ::g or N::g?
    }

In 6.6 [basic.link] paragraph 7 the Standard says (among other things),

When a block scope declaration of an entity with linkage is not found to refer to some other declaration, then that entity is a member of the innermost enclosing namespace.

The question then is whether N is an “enclosing namespace” for the local declaration of g()?

Proposed resolution (October 2004):

Add the following text as a new paragraph at the end of 9.8.2 [namespace.def]:

The enclosing namespaces of a declaration are those namespaces in which the declaration lexically appears, except for a redeclaration of a namespace member outside its original namespace (e.g., a definition as specified in _N4868_.9.8.2.3 [namespace.memdef]). Such a redeclaration has the same enclosing namespaces as the original declaration. [Example:
  namespace Q {
    namespace V {
      void f(); // enclosing namespaces are the global namespace, Q, and Q::V
      class C { void m(); };
    }
    void V::f() { // enclosing namespaces are the global namespace, Q, and Q::V
      extern void h(); // ... so this declares Q::V::h
    }
    void V::C::m() { // enclosing namespaces are the global namespace, Q, and Q::V
    }
  }

end example]




513. Non-class “most-derived” objects

Section: 6.7.2  [intro.object]     Status: CD1     Submitter: Marc Schoolderman     Date: 20 Mar 2005

[Voted into WP at April, 2006 meeting.]

The standard uses “most derived object” in some places (for example, Clause 3 [intro.defs] “dynamic type,” 7.6.2.9 [expr.delete]) to refer to objects of both class and non-class type. However, 6.7.2 [intro.object] only formally defines it for objects of class type.

Possible fix: Change the wording in 6.7.2 [intro.object] paragraph 4 from

an object of a most derived class type is called a most derived object

to

an object of a most derived class type, or of non-class type, is called a most derived object

Proposed resolution (October, 2005):

Add the indicated words to 6.7.2 [intro.object] paragraph 4:

If a complete object, a data member (11.4 [class.mem]), or an array element is of class type, its type is considered the most derived class, to distinguish it from the class type of any base class subobject; an object of a most derived class type, or of a non-class type, is called a most derived object.



119. Object lifetime and aggregate initialization

Section: 6.7.3  [basic.life]     Status: CD1     Submitter: Jack Rouse     Date: 20 May 1999

[Moved to DR at 4/02 meeting.]

Jack Rouse: 6.7.3 [basic.life] paragraph 1 includes:

The lifetime of an object is a runtime property of the object. The lifetime of an object of type T begins when:
Consider the code:
    struct B {
        B( int = 0 );
        ~B();
    };

    struct S {
        B b1;
    };

    int main()
    {
        S s = { 1 };
        return 0;
    }
In the code above, class S does have a non-trivial constructor, the default constructor generated by the compiler. According the text above, the lifetime of the auto s would never begin because a constructor for S is never called. I think the second case in the text needs to include aggregate initialization.

Mike Miller: I see a couple of ways of fixing the problem. One way would be to change "the constructor call has completed" to "the object's initialization is complete."

Another would be to add following "a class type with a non-trivial constructor" the phrase "that is not initialized with the brace notation (9.4.2 [dcl.init.aggr] )."

The first formulation treats aggregate initialization like a constructor call; even POD-type members of an aggregate could not be accessed before the aggregate initialization completed. The second is less restrictive; the POD-type members of the aggregate would be usable before the initialization, and the members with non-trivial constructors (the only way an aggregate can acquire a non-trivial constructor) would be protected by recursive application of the lifetime rule.

Proposed resolution (04/01):

In 6.7.3 [basic.life] paragraph 1, change

If T is a class type with a non-trivial constructor (11.4.5 [class.ctor]), the constructor call has completed.

to

If T is a class type with a non-trivial constructor (11.4.5 [class.ctor]), the initialization is complete. [Note: the initialization can be performed by a constructor call or, in the case of an aggregate with an implicitly-declared non-trivial default constructor, an aggregate initialization (9.4.2 [dcl.init.aggr]).]



274. Cv-qualification and char-alias access to out-of-lifetime objects

Section: 6.7.3  [basic.life]     Status: CD1     Submitter: Mike Miller     Date: 14 Mar 2001

[Voted into WP at April 2003 meeting.]

The wording in 6.7.3 [basic.life] paragraph 6 allows an lvalue designating an out-of-lifetime object to be used as the operand of a static_cast only if the conversion is ultimately to "char&" or "unsigned char&". This description excludes the possibility of using a cv-qualified version of these types for no apparent reason.

Notes on 04/01 meeting:

The wording should be changed to allow cv-qualified char types.

Proposed resolution (04/01):

In 6.7.3 [basic.life] paragraph 6 change the third bullet:

to read:




404. Unclear reference to construction with non-trivial constructor

Section: 6.7.3  [basic.life]     Status: CD1     Submitter: Mike Miller     Date: 8 Apr 2003

[Voted into WP at March 2004 meeting.]

6.7.3 [basic.life] paragraph 1 second bullet says:

if T is a class type with a non-trivial constructor (12.1), the constructor call has completed.

This is confusing; what was intended is probably something like

if T is a class type and the constructor invoked to create the object is non-trivial (12.1), the constructor call has completed.

Proposed Resolution (October 2003):

As given above.




594. Coordinating issues 119 and 404 with delegating constructors

Section: 6.7.3  [basic.life]     Status: CD1     Submitter: Tom Plum     Date: 30 August 2006

[Voted into the WP at the September, 2008 meeting.]

In ISO/IEC 14882:2003, the second bullet of 6.7.3 [basic.life] paragraph 1 reads,

if T is a class type with a non-trivial constructor (11.4.5 [class.ctor]), the constructor call has completed.

Issue 119 pointed out that aggregate initialization can be used with some classes with a non-trivial implicitly-declared default constructor, and that in such cases there is no call to the object's constructor. The resolution for that issue was to change the previously-cited wording to read,

If T is a class type with a non-trivial constructor (11.4.5 [class.ctor], the initialization is complete.

Later (but before the WP was revised with the wording from the resolution of issue 119), issue 404 changed the 2003 wording to read,

If T is a class type and the constructor invoked to create the object is non-trivial (11.4.5 [class.ctor]), the constructor call has completed.

thus reversing the effect of issue 119, whose whole purpose was to cover objects with non-trivial constructors that are not invoked.

Through an editorial error, the post-Redmond draft (N1905) still contained the original 2003 wording that should have been replaced by the resolution of issue 119, in addition to the new wording from the resolution:

if T is a class type and the constructor invoked to create the object is non-trivial (11.4.5 [class.ctor]), the constructor call has completed. the initialization is complete.

Finally, during the application of the edits for delegating constructors (N1986), this editing error was “fixed” by retaining the original 2003 wording (which was needed for the application of the change specified in N1986), so that the current draft (N2009) reads,

if T is a class type and the constructor invoked to create the object is non-trivial (11.4.5 [class.ctor]), the principal constructor call 11.9.3 [class.base.init]) has completed.

Because the completion of the call to the principal constructor corresponds to the point at which the object is “fully constructed” (14.3 [except.ctor] paragraph 2), i.e., its initialization is complete, I believe that the exact wording of the issue 119 resolution would be correct and should be restored verbatim.

Proposed resolution (June, 2008):

Change 6.7.3 [basic.life] paragraph 1 as follows:

The lifetime of an object is a runtime property of the object. An object is said to have non-trivial initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor. [Note: Initialization by a trivial copy constructor is non-trivial initialization. —end note] The lifetime of an object of type T begins when:

The lifetime of an object of type T ends when...




521. Requirements for exceptions thrown by allocation functions

Section: 6.7.5.5.2  [basic.stc.dynamic.allocation]     Status: CD1     Submitter: Alisdair Meredith     Date: 22 May 2005

[Voted into WP at the October, 2006 meeting.]

According to 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 3,

Any other allocation function that fails to allocate storage shall only indicate failure by throwing an exception of class std::bad_alloc (17.6.4.1 [bad.alloc]) or a class derived from std::bad_alloc.

Shouldn't this statement have the usual requirements for an unambiguous and accessible base class?

Proposed resolution (April, 2006):

Change the last sentence of 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 3 as indicated:

Any other allocation function that fails to allocate storage shall only indicate failure only by throwing an exception of class std::bad_alloc (17.6.4.1 [bad.alloc]) or a class derived from std::bad_alloc a type that would match a handler (14.4 [except.handle]) of type std::bad_alloc (17.6.4.1 [bad.alloc]).



220. All deallocation functions should be required not to throw

Section: 6.7.5.5.3  [basic.stc.dynamic.deallocation]     Status: CD1     Submitter: Herb Sutter     Date: 31 Mar 2000

[Voted into the WP at the September, 2008 meeting (resolution in paper N2757).]

[Picked up by evolution group at October 2002 meeting.]

The default global operators delete are specified to not throw, but there is no requirement that replacement global, or class-specific, operators delete must not throw. That ought to be required.

In particular:

We already require that all versions of an allocator's deallocate() must not throw, so that part is okay.

Rationale (04/00):

  1. Replacement deallocation functions are already required not to throw an exception (cf 16.4.5.8 [res.on.functions] paragraph 2, as applied to 17.6.3.2 [new.delete.single] paragraph 12 and 17.6.3.3 [new.delete.array] paragraph 11).
  2. Section 16.4.5.6 [replacement.functions] is describing the signatures of the functions to be replaced; exception specfications are not part of the signature.
  3. There does not appear to be any pressing need to require that class member deallocation functions not throw.

Note (March, 2008):

The Evolution Working Group has accepted the intent of this issue and referred it to CWG for action for C++0x (see paper J16/07-0033 = WG21 N2173).

Proposed resolution (March, 2008):

Change 6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 3 as follows:

A deallocation function shall not terminate by throwing an exception. The value of the first argument supplied to a deallocation function...



348. delete and user-written deallocation functions

Section: 6.7.5.5.3  [basic.stc.dynamic.deallocation]     Status: CD1     Submitter: Ruslan Abdikeev     Date: 1 April 2002

[Voted into WP at October 2005 meeting.]

Standard is clear on behaviour of default allocation/deallocation functions. However, it is surpisingly vague on requirements to the behaviour of user-defined deallocation function and an interaction between delete-expression and deallocation function. This caused a heated argument on fido7.su.c-cpp newsgroup.

Resume:

It is not clear if user-supplied deallocation function is called from delete-expr when the operand of delete-expr is the null pointer (7.6.2.9 [expr.delete]). If it is, standard does not specify what user-supplied deallocation function shall do with the null pointer operand (17.6.3 [new.delete]). Instead, Standard uses the term "has no effect", which meaning is too vague in context given (7.6.2.9 [expr.delete]).

Description:

Consider statements

   char* p= 0; //result of failed non-throwing ::new char[]
   ::delete[] p;
Argument passed to delete-expression is valid - it is the result of a call to the non-throwing version of ::new, which has been failed. 7.6.2.9 [expr.delete] paragraph 1 explicitly prohibit us to pass 0 without having the ::new failure.

Standard does NOT specify whether user-defined deallocation function should be called in this case, or not.

Specifically, standard says in 7.6.2.9 [expr.delete] paragraph 2:

...if the value of the operand of delete is the null pointer the operation has no effect.
Standard doesn't specify term "has no effect". It is not clear from this context, whether the called deallocation function is required to have no effect, or delete-expression shall not call the deallocation function.

Furthermore, in para 4 standard says on default deallocation function:

If the delete-expression calls the implementation deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation]), if the operand of the delete expression is not the null pointer constant, ...
Why it is so specific on interaction of default deallocation function and delete-expr?

If "has no effect" is a requirement to the deallocation function, then it should be stated in 6.7.5.5.3 [basic.stc.dynamic.deallocation], or in 17.6.3.2 [new.delete.single] and 17.6.3.3 [new.delete.array], and it should be stated explicitly.

Furthermore, standard does NOT specify what actions shall be performed by user-supplied deallocation function if NULL is given (17.6.3.2 [new.delete.single] paragraph 12):

Required behaviour: accept a value of ptr that is null or that was returned by an earlier call to the default operator new(std::size_t) or operator new(std::size_t, const std::nothrow_t&).

The same corresponds to ::delete[] case.

Expected solution:

  1. Make it clear that delete-expr will not call deallocation function if null pointer is given (in 7.6.2.9 [expr.delete]).
  2. Specify what user deallocation function shall do when null is given (either in 6.7.5.5.3 [basic.stc.dynamic.deallocation], or in 17.6.3.2 [new.delete.single], and 17.6.3.3 [new.delete.array]).

Notes from October 2002 meeting:

We believe that study of 17.6.3.2 [new.delete.single] paragraphs 12 and 13, 17.6.3.3 [new.delete.array] paragraphs 11 and 12, and 6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 3 shows that the system-provided operator delete functions must accept a null pointer and ignore it. Those sections also show that a user-written replacement for the system-provided operator delete functions must accept a null pointer. There is no requirement that such functions ignore a null pointer, which is okay -- perhaps the reason for replacing the system-provided functions is to do something special with null pointer values (e.g., log such calls and return).

We believe that the standard should not require an implementation to call a delete function with a null pointer, but it must allow that. For the system-provided delete functions or replacements thereof, the standard already makes it clear that the delete function must accept a null pointer. For class-specific delete functions, we believe the standard should require that such functions accept a null pointer, though it should not mandate what they do with null pointers.

7.6.2.9 [expr.delete] needs to be updated to say that it is unspecified whether or not the operator delete function is called with a null pointer, and 6.7.5.5.3 [basic.stc.dynamic.deallocation] needs to be updated to say that any deallocation function must accept a null pointer.

Proposed resolution (October, 2004):

  1. Change 7.6.2.9 [expr.delete] paragraph 2 as indicated:

    If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, and the converted operand is used in place of the original operand for the remainder of this section. In either alternative, if the value of the operand of delete is the null pointer the operation has no effect may be a null pointer value. If it is not a null pointer value, in In the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a sub-object (6.7.2 [intro.object]) representing a base class of such an object (11.7 [class.derived])...
  2. Change 7.6.2.9 [expr.delete] paragraph 4 as follows (note that the old wording reflects the changes proposed by issue 442:

    The cast-expression in a delete-expression shall be evaluated exactly once. If the delete-expression calls the implementation deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation]), and if the value of the operand of the delete expression is not a null pointer, the deallocation function will deallocate the storage referenced by the pointer thus rendering the pointer invalid. [Note: the value of a pointer that refers to deallocated storage is indeterminate. —end note]

  3. Change 7.6.2.9 [expr.delete] paragraphs 6-7 as follows:

    The If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted. In the case of an array, the elements will be destroyed in order of decreasing address (that is, in reverse order of the completion of their constructor; see 11.9.3 [class.base.init]).

    The If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will call a deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation]). Otherwise, it is unspecified whether the deallocation function will be called. [Note: The deallocation function is called regardless of whether the destructor for the object or some element of the array throws an exception. —end note]

  4. Change 6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 3 as indicated:

    The value of the first argument supplied to one of the a deallocation functions provided in the standard library may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call to the deallocation function has no effect. Otherwise, the value supplied to operator delete(void*) in the standard library shall be one of the values returned by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, const std::nothrow_t&) in the standard library, and the value supplied to operator delete[](void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[](std::size_t) or operator new[](std::size_t, const std::nothrow_t&) in the standard library.

[Note: this resolution also resolves issue 442.]




649. Optionally ill-formed extended alignment requests

Section: 6.7.6  [basic.align]     Status: CD1     Submitter: Mike Miller     Date: 12 Aug 2007

[Voted into the WP at the September, 2008 meeting.]

The requirements on an implementation when presented with an alignment-specifier not supported by that implementation in that context are contradictory: 6.7.6 [basic.align] paragraph 9 says,

If a request for a specific extended alignment in a specific context is not supported by an implementation, the implementation may reject the request as ill-formed. The implementation may also silently ignore the requested alignment.

In contrast, 9.12.2 [dcl.align] paragraph 2, bullet 4 says simply,

with no provision to “silently ignore” the requested alignment. These two passages need to be reconciled.

If the outcome of the reconciliation is to grant implementations the license to accept and ignore extended alignment requests, the specification should be framed in terms of mechanisms that already exist in the Standard, such as undefined behavior and/or conditionally-supported constructs; “ill-formed” is a category that is defined by the Standard, not something that an implementation can decide.

Notes from the February, 2008 meeting:

The consensus was that such requests should be ill-formed and require a diagnostic. However, it was also observed that an implementation need not reject an ill-formed program; the only requirement is that it issue a diagnostic. It would thus be permissible for an implementation to “noisily ignore” (as opposed to “silently ignoring”) an unsupported alignment request.

Proposed resolution (June, 2008):

Change 6.7.6 [basic.align] paragraph 9 as follows:

If a request for a specific extended alignment in a specific context is not supported by an implementation, the implementation may reject the request as program is ill-formed. The implementation may also silently ignore the requested alignment. [Note: aAdditionally, a request for runtime allocation of dynamic memory storage for which the requested alignment cannot be honored may shall be treated as an allocation failure. end note]



86. Lifetime of temporaries in query expressions

Section: 6.7.7  [class.temporary]     Status: CD1     Submitter: Steve Adamczyk     Date: Jan 1999

[Voted into WP at April, 2006 meeting.]

In 6.7.7 [class.temporary] paragraph 5, should binding a reference to the result of a "?" operation, each of whose branches is a temporary, extend both temporaries?

Here's an example:

    const SFileName &C = noDir ? SFileName("abc") : SFileName("bcd");

Do the temporaries created by the SFileName conversions survive the end of the full expression?

Notes from 10/00 meeting:

Other problematic examples include cases where the temporary from one branch is a base class of the temporary from the other (i.e., where the implementation must remember which type of temporary must be destroyed), or where one branch is a temporary and the other is not. Similar questions also apply to the comma operator. The sense of the core language working group was that implementations should be required to support these kinds of code.

Notes from the March 2004 meeting:

We decided that the cleanest model is one in which any "?" operation that returns a class rvalue always copies one of its operands to a temporary and returns the temporary as the result of the operation. (Note that this may involve slicing.) An implementation would be free to optimize this using the rules in 11.4.5.3 [class.copy.ctor] paragraph 15, and in fact we would expect that in many cases compilers would do such optimizations. For example, the compiler could construct both rvalues in the above example into a single temporary, and thus avoid a copy.

See also issue 446.

Proposed resolution (October, 2004):

This issue is resolved by the resolutions of issue 446.

Note (October, 2005):

This issue was overlooked when issue 446 was moved to “ready” status and was thus inadvertently omitted from the list of issues accepted as Defect Reports at the October, 2005 meeting.




124. Lifetime of temporaries in default initialization of class arrays

Section: 6.7.7  [class.temporary]     Status: CD1     Submitter: Jack Rouse     Date: 3 June 1999

[Moved to DR at 4/01 meeting.]

Jack Rouse: 6.7.7 [class.temporary] states that temporary objects will normally be destroyed at the end of the full expression in which they are created. This can create some unique code generation requirements when initializing a class array with a default constructor that uses a default argument. Consider the code:

    struct T {
       int i;
       T( int );
       ~T();
    };

    struct S {
       S( int = T(0).i );
       ~S();
    };

    S* f( int n )
    {
       return new S[n];
    }
The full expression allocating the array in f(int) includes the default constructor for S. Therefore according to 6.9.1 [intro.execution] paragraph 14, it includes the default argument expression for S(int). So evaluation of the full expression should include evaluating the default argument "n" times and creating "n" temporaries of type T. But the destruction of the temporaries must be delayed until the end of the full expression so this requires allocating space at runtime for "n" distinct temporaries. It is unclear how these temporaries are supposed to be allocated and deallocated. They cannot readily be autos because a variable allocation is required.

I believe that many existing implementations will destroy the temporaries needed by the default constructor after each array element is initialized. But I can't find anything in the standard that allows the temporaries to be destroyed early in this case.

I think the standard should allow the early destruction of temporaries used in the default initialization of class array elements. I believe early destruction is the status quo, and I don't think the users of existing C++ compilers have been adversely impacted by it.

Proposed resolution (04/01):

The proposed resolution is contained in the proposal for issue 201.




199. Order of destruction of temporaries

Section: 6.7.7  [class.temporary]     Status: CD1     Submitter: Alan Nash     Date: 27 Jan 2000

[Voted into the WP at the April, 2007 meeting as part of paper J16/07-0099 = WG21 N2239.]

6.7.7 [class.temporary] paragraph 3 simply states the requirement that temporaries created during the evaluation of an expression

are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created.
There is nothing said about the relative order in which these temporaries are destroyed.

Paragraph 5, dealing with temporaries bound to references, says

the temporaries created during the evaluation of the expression initializing the reference, except the temporary to which the reference is bound, are destroyed at the end of the full-expression in which they are created and in the reverse order of the completion of their construction.
Is this difference intentional? May temporaries in expressions other than those initializing references be deleted in non-LIFO order?

Notes from 04/00 meeting:

Steve Adamczyk expressed concern about constraining implementations that are capable of fine-grained parallelism -- they may be unable to determine the order of construction without adding undesirable overhead.

Proposed resolution (April, 2007):

As specified in paper J16/07-0099 = WG21 N2239.




201. Order of destruction of temporaries in initializers

Section: 6.7.7  [class.temporary]     Status: CD1     Submitter: Alan Nash     Date: 31 Jan 2000

[Moved to DR at 4/01 meeting.]

According to 6.7.7 [class.temporary] paragraph 4, an expression appearing as the initializer in an object definition constitutes a context "in which temporaries are destroyed at a different point than the end of the full-expression." It goes on to say that the temporary containing the value of the expression persists until after the initialization is complete (see also issue 117). This seems to presume that the end of the full-expression is a point earlier than the completion of the initialization.

However, according to 6.9.1 [intro.execution] paragraphs 12-13, the full-expression in such cases is, in fact, the entire initialization. If this is the case, the behavior described for temporaries in an initializer expression is simply the normal behavior of temporaries in any expression, and treating it as an exception to the general rule is both incorrect and confusing.

Proposed resolution (04/01):

[Note: this proposal also addresses issue 124.]

  1. Add to the end of 6.9.1 [intro.execution] paragraph 12:

    If the initializer for an object or sub-object is a full-expression, the initialization of the object or sub-object (e.g., by calling a constructor or copying an expression value) is considered to be part of the full-expression.
  2. Replace 6.7.7 [class.temporary] paragraph 4 with:

    There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context is when a default constructor is called to initialize an element of an array. If the constructor has one or more default arguments, any temporaries created in the default argument expressions are destroyed immediately after return from the constructor.



320. Question on copy constructor elision example

Section: 6.7.7  [class.temporary]     Status: CD1     Submitter: Steve Clamage     Date: 2 Nov 2001

[Voted into WP at April 2005 meeting.]

Section 6.7.7 [class.temporary] paragraph 2, abridged:

  X f(X);
  void g()
  {
	X a;
	a = f(a);
  }

a=f(a) requires a temporary for either the argument a or the result of f(a) to avoid undesired aliasing of a.

The note seems to imply that an implementation is allowed to omit copying "a" to f's formal argument, or to omit using a temporary for the return value of f. I don't find that license in normative text.

Function f returns an X by value, and in the expression the value is assigned (not copy-constructed) to "a". I don't see how that temporary can be omitted. (See also 11.4.5.3 [class.copy.ctor] p 15)

Since "a" is an lvalue and not a temporary, I don't see how copying "a" to f's formal parameter can be avoided.

Am I missing something, or is 6.7.7 [class.temporary] p 2 misleading?

Proposed resolution (October, 2004):

In 6.7.7 [class.temporary] paragraph 2, change the last sentence as indicated:

On the other hand, the expression a=f(a) requires a temporary for either the argument a or the result of f(a) to avoid undesired aliasing of a the result of f(a), which is then assigned to a.



392. Use of full expression lvalue before temporary destruction

Section: 6.7.7  [class.temporary]     Status: CD1     Submitter: Stephen Clamage     Date: 21 Nov 2002

[Voted into WP at March 2004 meeting.]

class C {
public:
    C();
    ~C();
    int& get() { return p; } // reference return
private:
    int p;
};

int main ()
{
    if ( C().get() ) // OK?
}

Section 6.7.7 [class.temporary] paragraph 3 says a temp is destroyed as the last step in evaluating the full expression. But the expression C().get() has a reference type. Does 6.7.7 [class.temporary] paragraph 3 require that the dereference to get a boolean result occur before the destructor runs, making the code valid? Or does the code have undefined behavior?

Bill Gibbons: It has undefined behavior, though clearly this wasn't intended. The lvalue-to-rvalue conversion that occurs in the "if" statement is not currently part of the full-expression.

From section 6.7.7 [class.temporary] paragraph 3:

Temporary objects are destroyed as the last step in evaluating the full-expression (6.9.1 [intro.execution]) that (lexically) contains the point where they were created.

From section 6.9.1 [intro.execution] paragraph 12:

A full-expression is an expression that is not a subexpression of another expression. If a language construct is defined to produce an implicit call of a function, a use of the language construct is considered to be an expression for the purposes of this definition.

The note in section 6.9.1 [intro.execution] paragraph 12 goes on to explain that this covers expressions used as initializers, but it does not discuss lvalues within temporaries.

It is a small point but it is probably worth correcting 6.9.1 [intro.execution] paragraph 12. Instead of the "implicit call of a function" wording, it might be better to just say that a full-expression includes any implicit use of the expression value in the enclosing language construct, and include a note giving implicit calls and lvalue-to-rvalue conversions as examples.

Offhand the places where this matters include: initialization (including member initializers), selection statements, iteration statements, return, throw

Proposed resolution (April 2003):

Change 6.9.1 [intro.execution] paragraph 12-13 to read:

A full-expression is an expression that is not a subexpression of another expression. If a language construct is defined to produce an implicit call of a function, a use of the language construct is considered to be an expression for the purposes of this definition. Conversions applied to the result of an expression in order to satisfy the requirements of the language construct in which the expression appears are also considered to be part of the full-expression.

[Note: certain contexts in C++ cause the evaluation of a full-expression that results from a syntactic construct other than expression (7.6.20 [expr.comma]). For example, in 9.4 [dcl.init] one syntax for initializer is

but the resulting construct is a function call upon a constructor function with expression-list as an argument list; such a function call is a full-expression. For example, in 9.4 [dcl.init], another syntax for initializer is but again the resulting construct might be a function call upon a constructor function with one assignment-expression as an argument; again, the function call is a full-expression. ] [Example:

  struct S {
      S(int i): I(i) { }
      int& v() { return I; }
    private:
      int I;
  };

  S s1(1);           // full-expression is call of S::S(int)
  S s2 = 2;          // full-expression is call of S::S(int)

  void f() {
      if (S(3).v())  // full-expression includes lvalue-to-rvalue and
                     // int to bool conversions, performed before
                     // temporary is deleted at end of full-expression
      { }
  }

end example]



443. Wording nit in description of lifetime of temporaries

Section: 6.7.7  [class.temporary]     Status: CD1     Submitter: Matthias Hofmann     Date: 2 Dec 2003

[Voted into WP at April 2005 meeting.]

There seems to be a typo in 6.7.7 [class.temporary]/5, which says "The temporary to which the reference is bound or the temporary that is the complete object TO a subobject OF which the TEMPORARY is bound persists for the lifetime of the reference except as specified below."

I think this should be "The temporary to which the reference is bound or the temporary that is the complete object OF a subobject TO which the REFERENCE is bound persists for the lifetime of the reference except as specified below."

I used upper-case letters for the parts I think need to be changed.

Proposed resolution (October, 2004):

Change 6.7.7 [class.temporary] paragraph 5 as indicated:

The temporary to which the reference is bound or the temporary that is the complete object to of a subobject of to which the temporary reference is bound persists for the lifetime of the reference except as specified below.



464. Wording nit on lifetime of temporaries to which references are bound

Section: 6.7.7  [class.temporary]     Status: CD1     Submitter: Allan Odgaard     Date: 21 Feb 2004

[Voted into WP at April, 2006 meeting.]

Section 6.7.7 [class.temporary] paragraph 5 ends with this "rule":

[...] if obj2 is an object with static or automatic storage duration created after the temporary is created, the temporary shall be destroyed after obj2 is destroyed.

For the temporary to be destroyed after obj2 is destroyed, when obj2 has static storage, I would say that the reference to the temporary should also have static storage, but that is IMHO not clear from the paragraph.

Example:

    void f ()
    {
       const T1& ref = T1();
       static T2 obj2;
       ...
    }

Here the temporary would be destoyed before obj2, contrary to the rule above.

Steve Adamczyk: I agree there's a minor issue here. I think the clause quoted above meant for obj1 and obj2 to have the same storage duration. Replacing "obj2 is an object with static or automatic storage duration" by "obj2 is an object with the same storage duration as obj1" would, I believe, fix the problem.

Notes from October 2004 meeting:

We agreed with Steve Adamczyk's suggestion.

Proposed resolution (October, 2005):

Change 6.7.7 [class.temporary] paragraph 5 as follows:

... In addition, the destruction of temporaries bound to references shall take into account the ordering of destruction of objects with static or automatic storage duration (6.7.5.2 [basic.stc.static], 6.7.5.4 [basic.stc.auto]); that is, if obj1 is an object with static or automatic storage duration created before the temporary is created with the same storage duration as the temporary, the temporary shall be destroyed before obj1 is destroyed; if obj2 is an object with static or automatic storage duration created after the temporary is created with the same storage duration as the temporary, the temporary shall be destroyed after obj2 is destroyed...



644. Should a trivial class type be a literal type?

Section: 6.8  [basic.types]     Status: CD1     Submitter: Alisdair Meredith     Date: 8 Aug 2007

[Voted into the WP at the June, 2008 meeting.]

The original proposed wording for 6.8 [basic.types] paragraph 11 required a constexpr constructor for a literal class only “if the class has at least one user-declared constructor.” This wording was dropped during the review by CWG out of a desire to ensure that literal types not have any uninitialized members. Thus, a class like

    struct pixel {
        int x, y;
    };

is not a literal type. However, if an object of that type is aggregate-initialized or value-initialized, there can be no uninitialized members; the missing wording should be restored in order to permit use of expressions like pixel().x as constant expressions.

Proposed resolution (February, 2008):

Change 6.8 [basic.types] paragraph 10 as follows:

A type is a literal type if it is:



637. Sequencing rules and example disagree

Section: 6.9.1  [intro.execution]     Status: CD1     Submitter: Ofer Porat     Date: 2 June 2007

[Voted into the WP at the September, 2008 meeting.]

In 6.9.1 [intro.execution] paragraph 16, the following expression is still listed as an example of undefined behavior:

    i = ++i + 1;

However, it appears that the new sequencing rules make this expression well-defined:

  1. The assignment side-effect is required to be sequenced after the value computations of both its LHS and RHS (7.6.19 [expr.ass] paragraph 1).

  2. The LHS (i) is an lvalue, so its value computation involves computing the address of i.

  3. In order to value-compute the RHS (++i + 1), it is necessary to first value-compute the lvalue expression ++i and then do an lvalue-to-rvalue conversion on the result. This guarantees that the incrementation side-effect is sequenced before the computation of the addition operation, which in turn is sequenced before the assignment side effect. In other words, it yields a well-defined order and final value for this expression.

It should be noted that a similar expression

    i = i++ + 1;

is still not well-defined, since the incrementation side-effect remains unsequenced with respect to the assignment side-effect.

It's unclear whether making the expression in the example well-defined was intentional or just a coincidental byproduct of the new sequencing rules. In either case either the example should be fixed, or the rules should be changed.

Clark Nelson: In my opinion, the poster's argument is perfectly correct. The rules adopted reflect the CWG's desired outcome for issue 222. At the Portland meeting, I presented (and still sympathize with) Tom Plum's case that these rules go a little too far in nailing down required behavior; this is a consequence of that.

One way or another, a change needs to be made, and I think we should seriously consider weakening the resolution of issue 222 to keep this example as having undefined behavior. This could be done fairly simply by having the sequencing requirements for an assignment expression depend on whether it appears in an lvalue context.

James Widman: How's this for a possible re-wording?

In all cases, the side effect of the assignment expression is sequenced after the value computations of the right and left operands. Furthermore, if the assignment expression appears in a context where an lvalue is required, the side effect of the assignment expression is sequenced before its value computation.

Notes from the February, 2008 meeting:

There was no real support in the CWG for weakening the resolution of issue 222 and returning the example to having undefined behavior. No one knew of an implementation that doesn't already do the (newly) right thing for such an example, so there was little motivation to go out of our way to increase the domain of undefined behavior. So the proposed resolution is to change the example to one that definitely does have undependable behavior in existing practice, and undefined behavior under the new rules.

Also, the new formulation of the sequencing rules approved in Oxford contained the wording that by and large resolved issue 222, so with the resolution of this issue, we can also close issue 222.

Proposed resolution (March, 2008):

Change the example in 6.9.1 [intro.execution] paragraph 16 as follows:

    i = v[i++];             // the behavior is undefined
    i = 7, i++, i++;        // i becomes 9
    i = ++i i++ + 1;        // the behavior is undefined
    i = i + 1;              // the value of i is incremented

This resolution also resolves issue 222.




639. What makes side effects “different” from one another?

Section: 6.9.1  [intro.execution]     Status: CD1     Submitter: James Widman     Date: 26 July 2007

[Voted into the WP at the September, 2008 meeting.]

Is the behavior undefined in the following example?

    void f() {
         int n = 0;
         n = --n;
    }

6.9.1 [intro.execution] paragraph 16 says,

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

It's not clear to me whether the two side-effects in n=--n are “different.” As far as I can tell, it seems that both side-effects involve the assignment of -1 to n, which in a sense makes them non-“different.” But I don't know if that's the intent. Would it be better to say “another” instead of “a different?”

On a related note, can we include this example to illustrate?

    void f( int, int );
    void g( int a ) { f( a = -1, a = -1 ); } // Undefined?

Proposed resolution (March, 2008):

Change 6.9.1 [intro.execution] paragraph 16 as follows:

...If a side effect on a scalar object is unsequenced relative to either a different another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. [Example:

    void f(int, int);
    void g(int i, int* v) {
        i = v[i++];         // the behavior is undefined
        i = 7, i++, i++;    // i becomes 9

        i = ++i + 1;        // the behavior is undefined
        i = i + 1;          // the value of i is incremented

        f(i = -1, i = -1);  // the behavior is undefined
    }

end example] When calling...




270. Order of initialization of static data members of class templates

Section: 6.9.3.2  [basic.start.static]     Status: CD1     Submitter: Jonathan H. Lundquist     Date: 9 Feb 2001

[Moved to DR at 4/02 meeting.]

The Standard does not appear to address how the rules for order of initialization apply to static data members of class templates.

Suggested resolution: Add the following verbiage to either 6.9.3.2 [basic.start.static] or 11.4.9.3 [class.static.data]:

Initialization of static data members of class templates shall be performed during the initialization of static data members for the first translation unit to have static initialization performed for which the template member has been instantiated. This requirement shall apply to both the static and dynamic phases of initialization.

Notes from 04/01 meeting:

Enforcing an order of initialization on static data members of class templates will result in substantial overhead on access to such variables. The problem is that the initialization be required as the result of instantiation in a function used in the initialization of a variable in another translation unit. In current systems, the order of initialization of static data data members of class templates is not predictable. The proposed resolution is to state that the order of initialization is undefined.

Proposed resolution (04/01, updated slightly 10/01):

Replace the following sentence in 6.9.3.2 [basic.start.static] paragraph 1:

Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.

with

Dynamic initialization of an object is either ordered or unordered. Explicit specializations and definitions of class template static data members have ordered initialization. Other class template static data member instances have unordered initialization. Other objects defined in namespace scope have ordered initialization. Objects defined within a single translation unit and with ordered initialization shall be initialized in the order of their definitions in the translation unit. The order of initialization is unspecified for objects with unordered initialization and for objects defined in different translation units.

Note that this wording is further updated by issue 362.

Note (07/01):

Brian McNamara argues against the proposed resolution. The following excerpt captures the central point of a long message on comp.std.c++:

I have a class for representing linked lists which looks something like
    template <class T>
    class List {
       ...  static List<T>* sentinel; ...
    };

    template <class T>
    List<T>* List<T>::sentinel( new List<T> ); // static member definition

The sentinel list node is used to represent "nil" (the null pointer cannot be used with my implementation, for reasons which are immaterial to this discussion). All of the List's non-static member functions and constructors depend upon the value of the sentinel. Under the proposed resolution for issue #270, Lists cannot be safely instantiated before main() begins, as the sentinel's initialization is "unordered".

(Some readers may propose that I should use the "singleton pattern" in the List class. This is undesirable, for reasons I shall describe at the end of this post at the location marked "[*]". For the moment, indulge me by assuming that "singleton" is not an adequate solution.)

Though this is a particular example from my own experience, I believe it is representative of a general class of examples. It is common to use static data members of a class to represent the "distinguished values" which are important to instances of that class. It is imperative that these values be initialized before any instances of the class are created, as the instances depend on the values.

In a comp.std.c++ posting on 28 Jul 2001, Brian McNamara proposes the following alternative resolution:

Replace the following sentence in 6.9.3.2 [basic.start.static] paragraph 1:

Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.
with
Objects with static storage duration defined in namespace scope shall be initialized in the order described below.
and then after paragraph 1, add this text:
Dynamic initialization is either ordered or quasi-ordered. Explicit specializations of class template static data members have ordered initialization. Other class template static data member instances have quasi-ordered initialization. All other objects defined in namespace scope have ordered initialization. The order of initialization is specified as follows:
along with a non-normative note along the lines of
[ Note: The intention is that translation units can each be compiled separately with no knowledge of what objects may be re-defined in other translation units. Each translation unit can contain a method which initializes all objects (both quasi-ordered and ordered) as though they were ordered. When these translation units are linked together to create an executable program, all of these objects can be initialized by simply calling the initialization methods (one from each translation unit) in any order. Quasi-ordered objects require some kind of guard to ensure that they are not initialized more than once (the first attempt to initialize such an object should succeed; any subsequent attempts should simply be ignored). ]

Erwin Unruh replies: There is a point which is not mentioned with this posting. It is the cost for implementing the scheme. It requires that each static template variable is instantiated in ALL translation units where it is used. There has to be a flag for each of these variables and this flag has to be checked in each TU where the instantiation took place.

I would reject this idea and stand with the proposed resolution of issue 270.

There just is no portable way to ensure the "right" ordering of construction.

Notes from 10/01 meeting:

The Core Working Group reaffirmed its previous decision.




441. Ordering of static reference initialization

Section: 6.9.3.2  [basic.start.static]     Status: CD1     Submitter: Mike Miller     Date: 1 Dec 2003

[Voted into WP at April 2005 meeting.]

I have a couple of questions about 6.9.3.2 [basic.start.static], "Initialization of non-local objects." I believe I recall some discussion of related topics, but I can't find anything relevant in the issues list.

The first question arose when I discovered that different implementations treat reference initialization differently. Consider, for example, the following (namespace-scope) code:

  int i;
  int& ir = i;
  int* ip = &i;
Both initializers, "i" and "&i", are constant expressions, per 7.7 [expr.const] paragraph 4-5 (a reference constant expression and an address constant expression, respectively). Thus, both initializations are categorized as static initialization, according to 6.9.3.2 [basic.start.static] paragraph 1:
Zero-initialization and initialization with a constant expression are collectively called static initialization; all other initialization is dynamic initialization.

However, that does not mean that both ir and ip must be initialized at the same time:

Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.

Because "int&" is not a POD type, there is no requirement that it be initialized before dynamic initialization is performed, and implementations differ in this regard. Using a function called during dynamic initialization to print the values of "ip" and "&ir", I found that g++, Sun, HP, and Intel compilers initialize ir before dynamic initialization and the Microsoft compiler does not. All initialize ip before dynamic initialization. I believe this is conforming (albeit inconvenient :-) behavior.

So, my first question is whether it is intentional that a reference of static duration, initialized with a reference constant expression, need not be initialized before dynamic initialization takes place, and if so, why?

The second question is somewhat broader. As 6.9.3.2 [basic.start.static] is currently worded, it appears that there are no requirements on when ir is initialized. In fact, there is a whole category of objects -- non-POD objects initialized with a constant expression -- for which no ordering is specified. Because they are categorized as part of "static initialization," they are not subject to the requirement that they "shall be initialized in the order in which their definition appears in the translation unit." Because they are not POD types, they are not required to be initialized before dynamic initialization occurs. Am I reading this right?

My preference would be to change 6.9.3.2 [basic.start.static] paragraph 1 so that 1) references are treated like POD objects with respect to initialization, and 2) "static initialization" applies only to POD objects and references. Here's some sample wording to illustrate:

Suggested resolution:

Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. Initializing a reference, or an object of POD type, of static storage duration with a constant expression (5.19) is called constant initialization. Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place. [Remainder unchanged.]

Proposed Resolution:

Change 6.9.3.2 [basic.start.static] paragraph 1 as follows:

Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. Initializing a reference, or an object of POD type, of static storage duration with a constant expression (5.19) is called constant initialization. Together, zero-initialization and constant initialization are Zero-initialization and initialization with a constant expression are collectively called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.



688. Constexpr constructors and static initialization

Section: 6.9.3.2  [basic.start.static]     Status: CD1     Submitter: Peter Dimov     Date: 26 March, 2008

[Voted into the WP at the September, 2008 meeting (resolution in paper N2757).]

Given this literal type,

    struct X {
        constexpr X() { }
    };

and this definition,

    static X x;

the current specification does not require that x be statically initialized because it is not “initialized with a constant expression” (6.9.3.1 [basic.start.main] paragraph 1) .

Lawrence Crowl:

This guarantee is essential for atomics.

Jens Maurer:

Suggestion:

A reference with static storage duration or an object of literal type with static storage duration can be initialized with a constant expression (7.7 [expr.const]) or with a constexpr constructor; this is called constant initialization.

(Not spelling out “default constructor” makes it easier to handle multiple-parameter constexpr constructors, where there isn't “a” constant expression but several.)

Peter Dimov:

In addition, there is a need to enforce static initialization for non-literal types: std::shared_ptr, std::once_flag, and std::atomic_* all have nontrivial copy constructors, making them non-literal types. However, we need a way to ensure that a constexpr constructor called with constant expressions will guarantee static initialization, regardless of the nontriviality of the copy constructor.

Proposed resolution (April, 2008):

  1. Change 6.9.3.2 [basic.start.static] paragraph 1 as follows:

  2. ...A reference with static storage duration and an object of trivial or literal type with static storage duration can be initialized with a constant expression (7.7 [expr.const]); this If a reference with static storage duration is initialized with a constant expression (7.7 [expr.const]) or if the initialization of an object with static storage duration satisfies the requirements for the object being declared with constexpr (9.2.6 [dcl.constexpr]), that initialization is called constant initialization...
  3. Change 8.8 [stmt.dcl] paragraph 4 as follows:

  4. ...A local object of trivial or literal type (6.8 [basic.types]) with static storage duration initialized with constant-expressions is initialized Constant initialization (6.9.3.2 [basic.start.static]) of a local entity with static storage duration is performed before its block is first entered...
  5. Change 9.2.6 [dcl.constexpr] paragraph 7 as follows:

  6. A constexpr specifier used in an object declaration declares the object as const. Such an object shall be initialized, and every expression that appears in its initializer (9.4 [dcl.init]) shall be a constant expression. Every implicit conversion used in converting the initializer expressions and every constructor call used for the initialization shall be one of those allowed in a constant expression (7.7 [expr.const])...
  7. Replace 9.4.2 [dcl.init.aggr] paragraph 14 as follows:

  8. When an aggregate with static storage duration is initialized with a brace-enclosed initializer-list, if all the member initializer expressions are constant expressions, and the aggregate is a trivial type, the initialization shall be done during the static phase of initialization (6.9.3.2 [basic.start.static]); otherwise, it is unspecified whether the initialization of members with constant expressions takes place during the static phase or during the dynamic phase of initialization. [Note: The order of initialization for aggregates with static storage duration is specified in 6.9.3.2 [basic.start.static] and 8.8 [stmt.dcl]. —end note]

(Note: the change to 6.9.3.2 [basic.start.static] paragraph 1 needs to be reconciled with the conflicting change in issue 684.)




28. 'exit', 'signal' and static object destruction

Section: 6.9.3.3  [basic.start.dynamic]     Status: CD1     Submitter: Martin J. O'Riordan     Date: 19 Oct 1997

[Voted into the WP at the June, 2008 meeting.]

The C++ standard has inherited the definition of the 'exit' function more or less unchanged from ISO C.

However, when the 'exit' function is called, objects of static extent which have been initialised, will be destructed if their types posses a destructor.

In addition, the C++ standard has inherited the definition of the 'signal' function and its handlers from ISO C, also pretty much unchanged.

The C standard says that the only standard library functions that may be called while a signal handler is executing, are the functions 'abort', 'signal' and 'exit'.

This introduces a bit of a nasty turn, as it is not at all unusual for the destruction of static objects to have fairly complex destruction semantics, often associated with resource release. These quite commonly involve apparently simple actions such as calling 'fclose' for a FILE handle.

Having observed some very strange behaviour in a program recently which in handling a SIGTERM signal, called the 'exit' function as indicated by the C standard.

But unknown to the programmer, a library static object performed some complicated resource deallocation activities, and the program crashed.

The C++ standard says nothing about the interaction between signals, exit and static objects. My observations, was that in effect, because the destructor called a standard library function other than 'abort', 'exit' or 'signal', while transitively in the execution context of the signal handler, it was in fact non-compliant, and the behaviour was undefined anyway.

This is I believe a plausible judgement, but given the prevalence of this common programming technique, it seems to me that we need to say something a lot more positive about this interaction.

Curiously enough, the C standard fails to say anything about the analogous interaction with functions registered with 'atexit' ;-)

Proposed Resolution (10/98):

The current Committee Draft of the next version of the ISO C standard specifies that the only standard library function that may be called while a signal handler is executing is 'abort'. This would solve the above problem.

[This issue should remain open until it has been decided that the next version of the C++ standard will use the next version of the C standard as the basis for the behavior of 'signal'.]

Notes (November, 2006):

C89 is slightly contradictory here: It allows any signal handler to terminate by calling abort, exit, longjmp, but (for asynchronous signals, i.e. not those produced by abort or raise) then makes calling any library function other than signal with the current signal undefined behavior (C89 7.7.1.1). For synchronous signals, C99 forbids calls to raise, but imposes no other restrictions. For asynchronous signals, C99 allows only calls to abort, _Exit, and signal with the current signal (C99 7.14.1.1). The current C++ WP refers to “plain old functions” and “conforming C programs” (17.13 [support.runtime] paragraph 6).

Proposed Resolution (November, 2006):

Change the footnote in 17.13 [support.runtime] paragraph 6 as follows:

In particular, a signal handler using exception handling is very likely to have problems. Also, invoking std::exit may cause destruction of objects, including those of the standard library implementation, which, in general, yields undefined behavior in a signal handler (see 6.9.1 [intro.execution]).



222. Sequence points and lvalue-returning operators

Section: Clause 7  [expr]     Status: CD1     Submitter: Andrew Koenig     Date: 20 Dec 1999

[Voted into the WP at the September, 2008 meeting.]

I believe that the committee has neglected to take into account one of the differences between C and C++ when defining sequence points. As an example, consider

    (a += b) += c;

where a, b, and c all have type int. I believe that this expression has undefined behavior, even though it is well-formed. It is not well-formed in C, because += returns an rvalue there. The reason for the undefined behavior is that it modifies the value of `a' twice between sequence points.

Expressions such as this one are sometimes genuinely useful. Of course, we could write this particular example as

    a += b; a += c;

but what about

    void scale(double* p, int n, double x, double y) {
        for (int i = 0; i < n; ++i) {
            (p[i] *= x) += y;
        }
    }

All of the potential rewrites involve multiply-evaluating p[i] or unobvious circumlocations like creating references to the array element.

One way to deal with this issue would be to include built-in operators in the rule that puts a sequence point between evaluating a function's arguments and evaluating the function itself. However, that might be overkill: I see no reason to require that in

    x[i++] = y;

the contents of `i' must be incremented before the assignment.

A less stringent alternative might be to say that when a built-in operator yields an lvalue, the implementation shall not subsequently change the value of that object as a consequence of that operator.

I find it hard to imagine an implementation that does not do this already. Am I wrong? Is there any implementation out there that does not `do the right thing' already for (a += b) += c?

7.6.19 [expr.ass] paragraph 1 says,

The result of the assignment operation is the value stored in the left operand after the assignment has taken place; the result is an lvalue.

What is the normative effect of the words "after the assignment has taken place"? I think that phrase ought to mean that in addition to whatever constraints the rules about sequence points might impose on the implementation, assignment operators on built-in types have the additional constraint that they must store the left-hand side's new value before returning a reference to that object as their result.

One could argue that as the C++ standard currently stands, the effect of x = y = 0; is undefined. The reason is that it both fetches and stores the value of y, and does not fetch the value of y in order to compute its new value.

I'm suggesting that the phrase "after the assignment has taken place" should be read as constraining the implementation to set y to 0 before yielding the value of y as the result of the subexpression y = 0.

Note that this suggestion is different from asking that there be a sequence point after evaluation of an assignment. In particular, I am not suggesting that an order constraint be imposed on any side effects other than the assignment itself.

Francis Glassborow:

My understanding is that for a single variable:

  1. Multiple read accesses without a write are OK
  2. A single read access followed by a single write (of a value dependant on the read, so that the read MUST happen first) is OK
  3. A write followed by an actual read is undefined behaviour
  4. Multiple writes have undefined behaviour

It is the 3) that is often ignored because in practice the compiler hardly ever codes for the read because it already has that value but in complicated evaluations with a shortage of registers, that is not always the case. Without getting too close to the hardware, I think we both know that a read too close to a write can be problematical on some hardware.

So, in x = y = 0;, the implementation must NOT fetch a value from y, instead it has to "know" what that value will be (easy because it has just computed that in order to know what it must, at some time, store in y). From this I deduce that computing the lvalue (to know where to store) and the rvalue to know what is stored are two entirely independent actions that can occur in any order commensurate with the overall requirements that both operands for an operator be evaluated before the operator is.

Erwin Unruh:

C distinguishes between the resulting value of an assignment and putting the value in store. So in C a compiler might implement the statement x=y=0; either as x=0;y=0; or as y=0;x=0; In C the statement (x += 5) += 7; is not allowed because the first += yields an rvalue which is not allowed as left operand to +=. So in C an assignment is not a sequence of write/read because the result is not really "read".

In C++ we decided to make the result of assignment an lvalue. In this case we do not have the option to specify the "value" of the result. That is just the variable itself (or its address in a different view). So in C++, strictly speaking, the statement x=y=0; must be implemented as y=0;x=y; which makes a big difference if y is declared volatile.

Furthermore, I think undefined behaviour should not be the result of a single mentioning of a variable within an expression. So the statement (x +=5) += 7; should NOT have undefined behaviour.

In my view the semantics could be:

  1. if the result of an assignment is used as an rvalue, its value is that of the variable after assignment. The actual store takes place before the next sequence point, but may be before the value is used. This is consistent with C usage.
  2. if the result of an assignment is used as an lvalue to store another value, then the new value will be stored in the variable before the next sequence point. It is unspecified whether the first assigned value is stored intermediately.
  3. if the result of an assignment is used as an lvalue to take an address, that address is given (it doesn't change). The actual store of the new value takes place before the next sequence point.

Jerry Schwarz:

My recollection is different from Erwin's. I am confident that the intention when we decided to make assignments lvalues was not to change the semantics of evaluation of assignments. The semantics was supposed to remain the same as C's.

Ervin seems to assume that because assignments are lvalues, an assignment's value must be determined by a read of the location. But that was definitely not our intention. As he notes this has a significant impact on the semantics of assignment to a volatile variable. If Erwin's interpretation were correct we would have no way to write a volatile variable without also reading it.

Lawrence Crowl:

For x=y=0, lvalue semantics implies an lvalue to rvalue conversion on the result of y=0, which in turn implies a read. If y is volatile, lvalue semantics implies both a read and a write on y.

The standard apparently doesn't state whether there is a value dependence of the lvalue result on the completion of the assignment. Such a statement in the standard would solve the non-volatile C compatibility issue, and would be consistent with a user-implemented operator=.

Another possible approach is to state that primitive assignment operators have two results, an lvalue and a corresponding "after-store" rvalue. The rvalue result would be used when an rvalue is required, while the lvalue result would be used when an lvalue is required. However, this semantics is unsupportable for user-defined assignment operators, or at least inconsistent with all implementations that I know of. I would not enjoy trying to write such two-faced semantics.

Erwin Unruh:

The intent was for assignments to behave the same as in C. Unfortunately the change of the result to lvalue did not keep that. An "lvalue of type int" has no "int" value! So there is a difference between intent and the standard's wording.

So we have one of several choices:

I think the last one has the least impact on existing programs, but it is an ugly solution.

Andrew Koenig:

Whatever we may have intended, I do not think that there is any clean way of making

    volatile int v;
    int i;

    i = v = 42;
have the same semantics in C++ as it does in C. Like it or not, the subexpression v = 42 has the type ``reference to volatile int,'' so if this statement has any meaning at all, the meaning must be to store 42 in v and then fetch the value of v to assign it to i.

Indeed, if v is volatile, I cannot imagine a conscientious programmer writing a statement such as this one. Instead, I would expect to see

    v = 42;
    i = v;
if the intent is to store 42 in v and then fetch the (possibly changed) value of v, or
    v = 42;
    i = 42;
if the intent is to store 42 in both v and i.

What I do want is to ensure that expressions such as ``i = v = 42'' have well-defined semantics, as well as expressions such as (i = v) = 42 or, more realistically, (i += v) += 42 .

I wonder if the following resolution is sufficient:

Append to 7.6.19 [expr.ass] paragraph 1:

There is a sequence point between assigning the new value to the left operand and yielding the result of the assignment expression.

I believe that this proposal achieves my desired effect of not constraining when j is incremented in x[j++] = y, because I don't think there is a constraint on the relative order of incrementing j and executing the assignment. However, I do think it allows expressions such as (i += v) += 42, although with different semantics from C if v is volatile.

Notes on 10/01 meeting:

There was agreement that adding a sequence point is probably the right solution.

Notes from the 4/02 meeting:

The working group reaffirmed the sequence-point solution, but we will look for any counter-examples where efficiency would be harmed.

For drafting, we note that ++x is defined in 7.6.2.3 [expr.pre.incr] as equivalent to x+=1 and is therefore affected by this change. x++ is not affected. Also, we should update any list of all sequence points.

Notes from October 2004 meeting:

Discussion centered around whether a sequence point “between assigning the new value to the left operand and yielding the result of the expression” would require completion of all side effects of the operand expressions before the value of the assignment expression was used in another expression. The consensus opinion was that it would, that this is the definition of a sequence point. Jason Merrill pointed out that adding a sequence point after the assignment is essentially the same as rewriting

    b += a

as

    b += a, b

Clark Nelson expressed a desire for something like a “weak” sequence point that would force the assignment to occur but that would leave the side effects of the operands unconstrained. In support of this position, he cited the following expression:

    j = (i = j++)

With the proposed addition of a full sequence point after the assignment to i, the net effect is no change to j. However, both g++ and MSVC++ behave differently: if the previous value of j is 5, the value of the expression is 5 but j gets the value 6.

Clark Nelson will investigate alternative approaches and report back to the working group.

Proposed resolution (March, 2008):

This issue is resolved by the adoption of the sequencing rules and the resolution of issue 637.




351. Sequence point error: unspecified or undefined?

Section: Clause 7  [expr]     Status: CD1     Submitter: Andrew Koenig     Date: 23 April 2002

[Voted into WP at March 2004 meeting.]

I have found what looks like a bug in Clause 7 [expr], paragraph 4:

Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined. Example:
        i = v[i++];                     // the behavior is unspecified
        i = 7, i++, i++;                // i becomes 9

        i = ++i + 1;                    // the behavior is unspecified
        i = i + 1;                      // the value of i is incremented
--end example]

So which is it, unspecified or undefined?

Notes from October 2002 meeting:

We should find out what C99 says and do the same thing.

Proposed resolution (April 2003):

Change the example in Clause 7 [expr], paragraph 4 from

[Example:
i = v[i++];                     //  the behavior is unspecified
i = 7, i++, i++;                //   i  becomes  9

i = ++i + 1;                    //  the behavior is unspecified
i = i + 1;                      //  the value of  i  is incremented
--- end example]

to (changing "unspecified" to "undefined" twice)

[Example:
i = v[i++];                     //  the behavior is undefined
i = 7, i++, i++;                //   i  becomes  9

i = ++i + 1;                    //  the behavior is undefined
i = i + 1;                      //  the value of  i  is incremented
--- end example]



451. Expressions with invalid results and ill-formedness

Section: Clause 7  [expr]     Status: CD1     Submitter: Gennaro Prota     Date: 19 Jan 2004

[Voted into WP at October 2005 meeting.]

Clause 7 [expr] par. 5 of the standard says:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression is a constant expression (5.19), in which case the program is ill-formed.

Well, we do know that except in some contexts (e.g. controlling expression of a #if, array bounds), a compiler is not required to evaluate constant-expressions in compile time, right?

Now, let us consider, the following simple snippet:

  if (a && 1/0)
      ...
with a, to fix our attention, being *not* a constant expression. The quote above seems to say that since 1/0 is a constant (sub-)expression, the program is ill-formed. So, is it the intent that such ill-formedness is diagnosable at run-time? Or is it the intent that the above gives undefined behavior (if 1/0 is evaluated) and is not ill-formed?

I think the intent is actually the latter, so I propose the following rewording of the quoted section:

If an expression is evaluated but its result is not mathematically defined or not in the range of representable values for its type the behavior is undefined, unless such an expression is a constant expression (5.19) that shall be evaluated during program translation, in which case the program is ill-formed.

Rationale (March, 2004):

We feel the standard is clear enough. The quoted sentence does begin "If during the evaluation of an expression, ..." so the rest of the sentence does not apply to an expression that is not evaluated.

Note (September, 2004):

Gennaro Prota feels that the CWG missed the point of his original comment: unless a constant expression appears in a context that requires a constant expression, an implementation is permitted to defer its evaluation to runtime. An evaluation that fails at runtime cannot affect the well-formedness of the program; only expressions that are evaluated at compile time can make a program ill-formed.

The status has been reset to “open” to allow further discussion.

Proposed resolution (October, 2004):

Change paragraph 5 of Clause 7 [expr] as indicated:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression is a constant expression appears where an integral constant expression is required (7.7 [expr.const]), in which case the program is ill-formed.



158. Aliasing and qualification conversions

Section: 7.2.1  [basic.lval]     Status: CD1     Submitter: Mike Stump     Date: 20 Aug 1999

[Moved to DR at 4/02 meeting.]

7.2.1 [basic.lval] paragraph 15 lists the types via which an lvalue can be used to access the stored value of an object; using an lvalue type that is not listed results in undefined behavior. It is permitted to add cv-qualification to the actual type of the object in this access, but only at the top level of the type ("a cv-qualified version of the dynamic type of the object").

However, 7.3.6 [conv.qual] paragraph 4 permits a "conversion [to] add cv-qualifiers at levels other than the first in multi-level pointers." The combination of these two rules allows creation of pointers that cannot be dereferenced without causing undefined behavior. For instance:

    int* jp;
    const int * const * p1 = &jp;
    *p1;    // undefined behavior!

The reason that *p1 results in undefined behavior is that the type of the lvalue is const int * const", which is not "a cv-qualified version of" int*.

Since the conversion is permitted, we must give it defined semantics, hence we need to fix the wording in 7.2.1 [basic.lval] to include all possible conversions of the type via 7.3.6 [conv.qual].

Proposed resolution (04/01):

Add a new bullet to 7.2.1 [basic.lval] paragraph 15, following "a cv-qualified version of the dynamic type of the object:"




519. Null pointer preservation in void* conversions

Section: 7.3.12  [conv.ptr]     Status: CD1     Submitter: comp.std.c++     Date: 19 May 2005

[Voted into WP at April, 2006 meeting.]

The C standard says in 6.3.2.3, paragraph 4:

Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal.

C++ appears to be incompatible with the first sentence in only two areas:

    A *a = 0;
    void *v = a;

C++ (7.3.12 [conv.ptr] paragraph 2) says nothing about the value of v.

    void *v = 0;
    A *b = (A*)v; // aka static_cast<A*>(v)

C++ (7.6.1.9 [expr.static.cast] paragraph 10) says nothing about the value of b.

Suggested changes:

  1. Add the following sentence to 7.3.12 [conv.ptr] paragraph 2:

  2. The null pointer value is converted to the null pointer value of the destination type.
  3. Add the following sentence to 7.6.1.9 [expr.static.cast] paragraph 10:

  4. The null pointer value (7.3.12 [conv.ptr]) is converted to the null pointer value of the destination type.

Proposed resolution (October, 2005):

  1. Add the indicated words to 7.3.12 [conv.ptr] paragraph 2:

  2. An rvalue of type “pointer to cv T,” where T is an object type, can be converted to an rvalue of type “pointer to cv void”. The result of converting a “pointer to cv T” to a “pointer to cv void” points to the start of the storage location where the object of type T resides, as if the object is a most derived object (6.7.2 [intro.object]) of type T (that is, not a base class subobject). The null pointer value is converted to the null pointer value of the destination type.
  3. Add the indicated words to 7.6.1.9 [expr.static.cast] paragraph 11:

  4. An rvalue of type “pointer to cv1 void” can be converted to an rvalue of type “pointer to cv2 T,” where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. The null pointer value is converted to the null pointer value of the destination type. A value of type pointer to object converted to “pointer to cv void” and back, possibly with different cv-qualification, shall have its original value...



654. Conversions to and from nullptr_t

Section: 7.3.12  [conv.ptr]     Status: CD1     Submitter: Jason Merrill     Date: 7 October 2007

[Voted into the WP at the June, 2008 meeting as paper N2656.]

In the interest of promoting use of nullptr instead of the integer literal 0 as the null pointer constant, the proposal accepted by the Committee does not provide for converting a zero-valued integral constant to type std::nullptr_t. However, this omission reduces the utility of the feature for use in the library for smart pointers. In particular, the addition of that conversion (along with a converting constructor accepting a std::nullptr_t) would allow smart pointers to be used just like ordinary pointers in expressions like:

    if (p == 0) { }
    if (0 == p) { }
    if (p != 0) { }
    if (0 != p) { }
    p = 0;

The existing use of the “unspecified bool type” idiom supports this usage, but being able to use std::nullptr_t instead would be simpler and more elegant.

Jason Merrill: I have another reason to support the conversion as well: it seems to me very odd for nullptr_t to be more restrictive than void*. Anything we can do with an arbitrary pointer, we ought to be able to do with nullptr_t as well. Specifically, since there is a standard conversion from literal 0 to void*, and there is a standard conversion from void* to bool, nullptr_t should support the same conversions.

This changes two of the example lines in the proposal as adopted:

    if (nullptr) ;      // error, no conversion to bool
    if (nullptr == 0) ; // error

become

    if (nullptr) ;      // evaluates to false
    if( nullptr == 0 ); // evaluates to true

And later,

    char* ch3 = expr ? nullptr : nullptr; // ch3 is the null pointer value
    char* ch4 = expr ? 0 : nullptr;       // ch4 is the null pointer value
    int n3 = expr ? nullptr : nullptr;    // error, nullptr_t can't be converted to int
    int n4 = expr ? 0 : nullptr;          // error, nullptr_t can't be converted to int

I would also allow reinterpret_cast from nullptr_t to integral type, with the same semantics as a reinterpret_cast from the null pointer value to integral type.

Basically, I would like nullptr_t to act like a void* which is constrained to always be (void*)0.




480. Is a base of a virtual base also virtual?

Section: 7.3.13  [conv.mem]     Status: CD1     Submitter: Mark Mitchell     Date: 18 Oct 2004

[Voted into WP at the October, 2006 meeting.]

When the Standard refers to a virtual base class, it should be understood to include base classes of virtual bases. However, the Standard doesn't actually say this anywhere, so when 7.3.13 [conv.mem] (for example) forbids casting to a derived class member pointer from a virtual base class member pointer, it could be read as meaning:

  struct B {};
  struct D : public B {};
  struct D2 : virtual public D {};

  int B::*p;
  int D::*q;

  void f() {
    static_cast<int D2::*>(p);  // permitted
    static_cast<int D2::*>(q);  // forbidden
  }

Proposed resolution (October, 2005):

  1. Change 7.3.13 [conv.mem] paragraph 2 as indicated:

  2. ...If B is an inaccessible (11.8 [class.access]), ambiguous (6.5.2 [class.member.lookup]) or virtual (11.7.2 [class.mi]) base class of D, or a base class of a virtual base class of D, a program that necessitates this conversion is ill-formed...
  3. Change 7.6.1.9 [expr.static.cast] paragraph 2 as indicated:

  4. ...and B is not neither a virtual base class of D nor a base class of a virtual base class of D...
  5. Change 7.6.1.9 [expr.static.cast] paragraph 9 as indicated:

  6. ...and B is not neither a virtual base class of D nor a base class of a virtual base class of D...



113. Visibility of called function

Section: 7.6.1.3  [expr.call]     Status: CD1     Submitter: Christophe de Dinechin     Date: 5 May 1999

[Moved to DR at 10/01 meeting.]

Christophe de Dinechin: In 7.6.1.3 [expr.call] , paragraph 2 reads:

If no declaration of the called function is visible from the scope of the call the program is ill-formed.
I think nothing there or in the previous paragraph indicates that this does not apply to calls through pointer or virtual calls.

Mike Miller: "The called function" is unfortunate phraseology; it makes it sound as if it's referring to the function actually called, as opposed to the identifier in the postfix expression. It's wrong with respect to Koenig lookup, too (the declaration need not be visible if it can be found in a class or namespace associated with one or more of the arguments).

In fact, this paragraph should be a note. There's a general rule that says you have to find an unambiguous declaration of any name that is used (6.5 [basic.lookup] paragraph 1) ; the only reason this paragraph is here is to contrast with C's implicit declaration of called functions.

Proposed resolution:

Change section 7.6.1.3 [expr.call] paragraph 2 from:
If no declaration of the called function is visible from the scope of the call the program is ill-formed.
to:
[Note: if a function or member function name is used, and name lookup (6.5 [basic.lookup]) does not find a declaration of that name, the program is ill-formed. No function is implicitly declared by such a call. ]

(See also issue 218.)




118. Calls via pointers to virtual member functions

Section: 7.6.1.3  [expr.call]     Status: CD1     Submitter: Martin O'Riordan     Date: 17 May 1999

[Voted into the WP at the June, 2008 meeting.]

Martin O'Riordan: Having gone through all the relevant references in the IS, it is not conclusive that a call via a pointer to a virtual member function is polymorphic at all, and could legitimately be interpreted as being static.

Consider 7.6.1.3 [expr.call] paragraph 1:

The function called in a member function call is normally selected according to the static type of the object expression ( 11.7 [class.derived] ), but if that function is virtual and is not specified using a qualified-id then the function actually called will be the final overrider (11.7.3 [class.virtual] ) of the selected function in the dynamic type of the object expression.
Here it is quite specific that you get the polymorphic call only if you use the unqualified syntax. But, the address of a member function is "always" taken using the qualified syntax, which by inference would indicate that call with a PMF is static and not polymorphic! Not what was intended.

Yet other references such as 7.6.4 [expr.mptr.oper] paragraph 4:

If the dynamic type of the object does not contain the member to which the pointer refers, the behavior is undefined.
indicate that the opposite may have been intended, by stating that it is the dynamic type and not the static type that matters. Also, 7.6.4 [expr.mptr.oper] paragraph 6:
If the result of .* or ->* is a function, then that result can be used only as the operand for the function call operator (). [Example:
        (ptr_to_obj->*ptr_to_mfct)(10);
calls the member function denoted by ptr_to_mfct for the object pointed to by ptr_to_obj. ]
which also implies that it is the object pointed to that determines both the validity of the expression (the static type of 'ptr_to_obj' may not have a compatible function) and the implicit (polymorphic) meaning. Note too, that this is stated in the non-normative example text.

Andy Sawyer: Assuming the resolution is what I've assumed it is for the last umpteen years (i.e. it does the polymorphic thing), then the follow on to that is "Should there also be a way of selecting the non-polymorphic behaviour"?

Mike Miller: It might be argued that the current wording of 7.6.1.3 [expr.call] paragraph 1 does give polymorphic behavior to simple calls via pointers to members. (There is no qualified-id in obj.*pmf, and the IS says that if the function is not specified using a qualified-id, the final overrider will be called.) However, it clearly says the wrong thing when the pointer-to-member itself is specified using a qualified-id (obj.*X::pmf).

Bill Gibbons: The phrase qualified-id in 7.6.1.3 [expr.call] paragraph 1 refers to the id-expression and not to the "pointer-to-member expression" earlier in the paragraph:

For a member function call, the postfix expression shall be an implicit (11.4.3 [class.mfct.non.static] , 11.4.9 [class.static] ) or explicit class member access (7.6.1.5 [expr.ref] ) whose id-expression is a function member name, or a pointer-to-member expression (7.6.4 [expr.mptr.oper] ) selecting a function member.

Mike Miller: To be clear, here's an example:

    struct S {
	virtual void f();
    };
    void (S::*pmf)();
    void g(S* sp) {
	sp->f();         // 1: polymorphic
	sp->S::f();      // 2: non-polymorphic
	(sp->S::f)();    // 3: non-polymorphic
	(sp->*pmf)();    // 4: polymorphic
	(sp->*&S::f)();  // 5: polymorphic
    }

Notes from October 2002 meeting:

This was moved back to open for lack of a champion. Martin O'Riordan is not expected to be attending meetings.

Proposed resolution (February, 2008):

  1. Change 7.6.1.3 [expr.call] paragraph 1 as follows:

    ... For a member function call, the postfix expression shall be an implicit (11.4.3 [class.mfct.non.static], 11.4.9 [class.static]) or explicit class member access (7.6.1.5 [expr.ref]) whose id-expression is a function member name, or a pointer-to-member expression (7.6.4 [expr.mptr.oper]) selecting a function member. The first expression in the postfix expression is then called the object expression, and; the call is as a member of the object pointed to or referred to by the object expression (7.6.1.5 [expr.ref], 7.6.4 [expr.mptr.oper]). In the case of an implicit class member access, the implied object is the one pointed to by this. [Note: a member function call of the form f() is interpreted as (*this).f() (see 11.4.3 [class.mfct.non.static]). —end note] If a function or member function name is used, the name can be overloaded ( Clause 12 [over]), in which case the appropriate function shall be selected according to the rules in 12.2 [over.match]. The function called in a member function call is normally selected according to the static type of the object expression (11.7 [class.derived]), but if that function is virtual and is not specified using a qualified-id then the function actually called will be the final overrider (11.7.3 [class.virtual]) of the selected function in the dynamic type of the object expression If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called. Otherwise, its final overrider (11.7.3 [class.virtual]) in the dynamic type of the object expression is called. ...
  2. Change 7.6.4 [expr.mptr.oper] paragraph 4 as follows:

    The first operand is called the object expression. If the dynamic type of the object expression does not contain the member to which the pointer refers, the behavior is undefined.



506. Conditionally-supported behavior for non-POD objects passed to ellipsis

Section: 7.6.1.3  [expr.call]     Status: CD1     Submitter: Mike Miller     Date: 14 Apr 2005

[Voted into WP at the October, 2006 meeting.]

The current wording of 7.6.1.3 [expr.call] paragraph 7 states:

When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg (17.13 [support.runtime]). The lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are performed on the argument expression. After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed. If the argument has a non-POD class type ( Clause 11 [class]), the behavior is undefined.

Paper J16/04-0167=WG21 N1727 suggests that passing a non-POD object to ellipsis be ill-formed. In discussions at the Lillehammer meeting, however, the CWG felt that the newly-approved category of conditionally-supported behavior would be more appropriate.

Proposed resolution (October, 2005):

Change 7.6.1.3 [expr.call] paragraph 7 as indicated:

...After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed. If the argument has a non-POD class type (clause 9), the behavior is undefined. Passing an argument of non-POD class type (clause 9) with no corresponding parameter is conditionally-supported, with implementation-defined semantics.



634. Conditionally-supported behavior for non-POD objects passed to ellipsis redux

Section: 7.6.1.3  [expr.call]     Status: CD1     Submitter: Howard Hinnant     Date: 6 Jun 2007

[Voted into the WP at the September, 2008 meeting.]

Issue 506 changed passing a non-POD class type to an ellipsis from undefined behavior to conditionally-supported behavior. As a result, an implementation could conceivably reject code like the following:

    struct two {char _[2];};

    template <class From, class To>
    struct is_convertible
    {
    private:
            static From f;

            template <class U> static char test(const U&);
            template <class U> static two test(...);
    public:
            static const bool value = sizeof(test<To>(f)) == 1;
    };

    struct A
    {
         A();
    };

    int main()
    {
         const bool b = is_convertible<A,int>::value;  // b == false
    }

This technique has become popular in template metaprogramming, and no non-POD object is actually passed at runtime. Concepts will eliminate much (perhaps not all) of the need for this kind of programming, but legacy code will persist.

Perhaps this technique should be officially supported by allowing implementations to reject passing a non-POD type to ellipsis only if it appears in a potentially-evaluated expression?

Notes from the July, 2007 meeting:

The CWG agreed with the suggestion to allow such calls in unevaluated contexts.

Proposed resolution (September, 2007):

Change 7.6.1.3 [expr.call] paragraph 7 as follows:

...Passing an a potentially-evaluated argument of non-trivial class type (Clause 11 [class]) with no corresponding parameter is conditionally-supported, with implementation-defined semantics...



421. Is rvalue.field an rvalue?

Section: 7.6.1.5  [expr.ref]     Status: CD1     Submitter: Gabriel Dos Reis     Date: 15 June 2003

[Voted into WP at March 2004 meeting.]

Consider

  typedef
    struct {
      int a;
    } A;

  A f(void)
  {
    A a;
    return a;
  }

  int main(void)
  {
    int* p = &f().a;   // #1
  }

Should #1 be rejected? The standard is currently silent.

Mike Miller: I don't believe the Standard is silent on this. I will agree that the wording of 7.6.1.5 [expr.ref] bullet 4.2 is unfortunate, as it is subject to misinterpretation. It reads,

If E1 is an lvalue, then E1.E2 is an lvalue.
The intent is, "and not otherwise."

Notes from October 2003 meeting:

We agree the reference should be an rvalue, and a change along the lines of that recommended by Mike Miller is reasonable.

Proposed Resolution (October 2003):

Change the second bullet of 7.6.1.5 [expr.ref] paragraph 4 to read:

If E1 is an lvalue, then E1.E2 is an lvalue; otherwise, it is an rvalue.



492. typeid constness inconsistent with example

Section: 7.6.1.8  [expr.typeid]     Status: CD1     Submitter: Ron Natalie     Date: 15 Dec 2004

[Voted into WP at April, 2006 meeting.]

There is an inconsistency between the normative text in section 7.6.1.8 [expr.typeid] and the example that follows.

Here is the relevant passage (starting with paragraph 4):

When typeid is applied to a type-id, the result refers to a std::type_info object representing the type of the type-id. If the type of the type-id is a reference type, the result of the typeid expression refers to a std::type_info object representing the referenced type.

The top-level cv-qualifiers of the lvalue expression or the type-id that is the operand of typeid are always ignored.

and the example:

    typeid(D) == typeid(const D&); // yields true

The second paragraph above says the “type-id that is the operand”. This would be const D&. In this case, the const is not at the top-level (i.e., applied to the operand itself).

By a strict reading, the above should yield false.

My proposal is that the strict reading of the normative test is correct. The example is wrong. Different compilers here give different answers.

Proposed resolution (April, 2005):

Change the second sentence of 7.6.1.8 [expr.typeid] paragraph 4 as follows:

If the type of the type-id is a reference to a possibly cv-qualified type, the result of the typeid expression refers to a std::type_info object representing the cv-unqualified referenced type.



54. Static_cast from private base to derived class

Section: 7.6.1.9  [expr.static.cast]     Status: CD1     Submitter: Steve Adamczyk     Date: 13 Oct 1998

[Voted into WP at October 2004 meeting.]

Is it okay to use a static_cast to cast from a private base class to a derived class? That depends on what the words "valid standard conversion" in paragraph 8 mean — do they mean the conversion exists, or that it would not get an error if it were done? I think the former was intended — and therefore a static_cast from a private base to a derived class would be allowed.

Rationale (04/99): A static_cast from a private base to a derived class is not allowed outside a member from the derived class, because 7.3.12 [conv.ptr] paragraph 3 implies that the conversion is not valid. (Classic style casts work.)

Reopened September 2003:

Steve Adamczyk: It makes some sense to disallow the inverse conversion that is pointer-to-member of derived to pointer-to-member of private base. There's less justification for the pointer-to-private-base to pointer-to-derived case. EDG, g++ 3.2, and MSVC++ 7.1 allow the pointer case and disallow the pointer-to-member case. Sun disallows the pointer case as well.

  struct B {};
  struct D : private B {};
  int main() {
    B *p = 0;
    static_cast<D *>(p);  // Pointer case: should be allowed
    int D::*pm = 0;
    static_cast<int B::*>(pm);  // Pointer-to-member case: should get error
  }

There's a tricky case with old-style casts: because the static_cast interpretation is tried first, you want a case like the above to be considered a static_cast, but then issue an error, not be rejected as not a static cast; if you did the latter, you would then try the cast as a reinterpret_cast.

Ambiguity and casting to a virtual base should likewise be errors after the static_cast interpretation is selected.

Notes from the October 2003 meeting:

There was lots of sentiment for making things symmetrical: the pointer case should be the same as the pointer-to-member case. g++ 3.3 now issues errors on both cases.

We decided an error should be issued on both cases. The access part of the check should be done later; by some definition of the word the static_cast is valid, and then later an access error is issued. This is similar to the way standard conversions work.

Proposed Resolution (October 2003):

Replace paragraph 7.6.1.9 [expr.static.cast]/6:

The inverse of any standard conversion sequence ( 7.3 [conv]), other than the lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), function-to-pointer (7.3.4 [conv.func]), and boolean (7.3.14 [conv.fctptr]) conversions, can be performed explicitly using static_cast. The lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) conversions are applied to the operand. Such a static_cast is subject to the restriction that the explicit conversion does not cast away constness (7.6.1.11 [expr.const.cast]), and the following additional rules for specific cases:

with two paragraphs:

The inverse of any standard conversion sequence ( 7.3 [conv]), other than the lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), function-to-pointer (7.3.4 [conv.func]), and boolean (7.3.14 [conv.fctptr]) conversions, can be performed explicitly using static_cast. A program is ill-formed if it uses static_cast to perform the inverse of an ill-formed standard conversion sequence.[Example:

struct B {};
struct D : private B {};
void f() {
  static_cast<D*>((B*)0); // Error: B is a private base of D.
  static_cast<int B::*>((int D::*)0); // Error: B is a private base of D.
}
--- end example]

The lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) conversions are applied to the operand. Such a static_cast is subject to the restriction that the explicit conversion does not cast away constness (7.6.1.11 [expr.const.cast]), and the following additional rules for specific cases:

In addition, modify the second sentence of 7.6.3 [expr.cast]/5. The first two sentences of 7.6.3 [expr.cast]/5 presently read:

The conversions performed by can be performed using the cast notation of explicit type conversion. The same semantic restrictions and behaviors apply.

Change the second sentence to read:

The same semantic restrictions and behaviors apply, with the exception that in performing a static_cast in the following situations the conversion is valid even if the base class is inaccessible:

Remove paragraph 7.6.3 [expr.cast]/7, which presently reads:

In addition to those conversions, the following static_cast and reinterpret_cast operations (optionally followed by a const_cast operation) may be performed using the cast notation of explicit type conversion, even if the base class type is not accessible:



427. static_cast ambiguity: conversion versus cast to derived

Section: 7.6.1.9  [expr.static.cast]     Status: CD1     Submitter: Mark Mitchell     Date: 5 July 2003

[Voted into WP at October 2004 meeting.]

Consider this code:

  struct B {};

  struct D : public B {
    D(const B&);
  };

  extern B& b;

  void f() {
    static_cast<const D&>(b);
  }

The rules for static_cast permit the conversion to "const D&" in two ways:

  1. D is derived from B, and b is an lvalue, so a cast to D& is OK.
  2. const D& t = b is valid, using the constructor for D. [Ed. note: actually, this should be parenthesized initialization.]

The first alternative is 7.6.1.9 [expr.static.cast]/5; the second is 7.6.1.9 [expr.static.cast]/2.

Presumably the first alternative is better -- it's the "simpler" conversion. The standard does not seem to make that clear.

Steve Adamczyk: I take the "Otherwise" at the beginning of 7.6.1.9 [expr.static.cast]/3 as meaning that the paragraph 2 interpretation is used if available, which means in your example above interpretation 2 would be used. However, that's not what EDG's compiler does, and I agree that it's not the "simpler" conversion.

Proposed Resolution (October 2003):

Move paragraph 5.2.9/5:

An lvalue of type ``cv1 B'', where B is a class type, can be cast to type ``reference to cv2 D'', where D is a class derived ( 11.7 [class.derived]) from B, if a valid standard conversion from ``pointer to D'' to ``pointer to B'' exists (7.3.12 [conv.ptr]), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is not a virtual base class of D. The result is an lvalue of type ``cv2 D.'' If the lvalue of type ``cv1 B'' is actually a sub-object of an object of type D, the lvalue refers to the enclosing object of type D. Otherwise, the result of the cast is undefined. [Example:

  struct B {};
  struct D : public B {};
  D d;
  B &br = d;

  static_cast<D&>(br);            //  produces lvalue to the original  d  object
--- end example]

before paragraph 7.6.1.9 [expr.static.cast]/2.

Insert Otherwise, before the text of paragraph 7.6.1.9 [expr.static.cast]/2 (which will become 7.6.1.9 [expr.static.cast]/3 after the above insertion), so that it reads:

Otherwise, an expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration "T t(e);" is well-formed, for some invented temporary variable t (9.4 [dcl.init]). The effect of such an explicit conversion is the same as performing the declaration and initialization and then using the temporary variable as the result of the conversion. The result is an lvalue if T is a reference type (9.3.4.3 [dcl.ref]), and an rvalue otherwise. The expression e is used as an lvalue if and only if the initialization uses it as an lvalue.




439. Guarantees on casting pointer back to cv-qualified version of original type

Section: 7.6.1.9  [expr.static.cast]     Status: CD1     Submitter: Mark Mitchell     Date: 30 Oct 2003

[Voted into WP at April 2005 meeting.]

Paragraph 7.6.1.9 [expr.static.cast] paragraph 10 says that:

A value of type pointer to object converted to "pointer to cv void" and back to the original pointer type will have its original value.

That guarantee should be stronger. In particular, given:

  T* p1 = new T;
  const T* p2 = static_cast<const T*>(static_cast<void *>(p1));
  if (p1 != p2)
    abort ();
there should be no call to "abort". The last sentence of Paragraph 7.6.1.9 [expr.static.cast] paragraph 10 should be changed to read:

A value of type pointer to object converted to "pointer to cv void" and back to the original pointer type (or a variant of the original pointer type that differs only in the cv-qualifiers applied to the object type) will have its original value. [Example:
T* p1 = new T;
const T* p2 = static_cast<const T*>(static_cast<void *>(p1));
bool b = p1 == p2; // b will have the value true.
---end example.]

Proposed resolution:

Change 7.6.1.9 [expr.static.cast] paragraph 10 as indicated:

A value of type pointer to object converted to "pointer to cv void" and back to the original pointer type, possibly with different cv-qualification, will have its original value. [Example:

  T* p1 = new T;
  const T* p2 = static_cast<const T*>(static_cast<void *>(p1));
  bool b = p1 == p2; // b will have the value true.

---end example]

Rationale: The wording "possibly with different cv-qualification" was chosen over the suggested wording to allow for changes in cv-qualification at different levels in a multi-level pointer, rather than only at the object type level.




671. Explicit conversion from a scoped enumeration type to integral type

Section: 7.6.1.9  [expr.static.cast]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 22 December 2007

[Voted into the WP at the September, 2008 meeting.]

There appears to be no provision in the Standard for explicit conversion of a value of a scoped enumeration type to an integral type, even though the inverse conversion is permitted. That is,

    enum class E { e };
    static_cast<E>(0);       // #1: OK
    static_cast<int>(E::e);  // #2: error

This is because values of scope enumeration types (intentionally) cannot be implicitly converted to integral types (7.3.7 [conv.prom] and 7.3.9 [conv.integral]) and 7.6.1.9 [expr.static.cast] was not updated to permit #2, although #1 is covered by paragraph 8.

Proposed resolution (June, 2008):

Add the following as a new paragraph following 7.6.1.9 [expr.static.cast] paragraph 8:

A value of a scoped enumeration type (9.7.1 [dcl.enum]) can be explicitly converted to an integral type. The value is unchanged if the original value can be represented by the specified type. Otherwise, the resulting value is unspecified.



195. Converting between function and object pointers

Section: 7.6.1.10  [expr.reinterpret.cast]     Status: CD1     Submitter: Steve Clamage     Date: 12 Jan 2000

[Voted into WP at April 2005 meeting.]

It is currently not permitted to cast directly between a pointer to function type and a pointer to object type. This conversion is not listed in 7.6.1.9 [expr.static.cast] and 7.6.1.10 [expr.reinterpret.cast] and thus requires a diagnostic to be issued. However, if a sufficiently long integral type exists (as is the case in many implementations), it is permitted to cast between pointer to function types and pointer to object types using that integral type as an intermediary.

In C the cast results in undefined behavior and thus does not require a diagnostic, and Unix C compilers generally do not issue one. This fact is used in the definition of the standard Unix function dlsym, which is declared to return void* but in fact may return either a pointer to a function or a pointer to an object. The fact that C++ compilers are required to issue a diagnostic is viewed as a "competitive disadvantage" for the language.

Suggested resolution: Add wording to 7.6.1.10 [expr.reinterpret.cast] allowing conversions between pointer to function and pointer to object types, if the implementation has an integral data type that can be used as an intermediary.

Several points were raised in opposition to this suggestion:


  1. Early C++ supported this conversion and it was deliberately removed during the standardization process.
  2. The existence of an appropriate integral type is irrelevant to whether the conversion is "safe." The condition should be on whether information is lost in the conversion or not.
  3. There are numerous ways to address the problem at an implementation level rather than changing the language. For example, the compiler could recognize the specific case of dlsym and omit the diagnostic, or the C++ binding to dlsym could be changed (using templates, for instance) to circumvent the violation.
  4. The conversion is, in fact, not supported by C; the dlsym function is simply relying on non-mandated characteristics of C implementations, and we would be going beyond the requirements of C compatibility in requiring (some) implementations to support the conversion.
  5. This issue is in fact not a defect (omitted or self-contradictory requirements) in the current Standard; the proposed change would actually be an extension and should not be considered until the full review of the IS.
  6. dlsym appears not to be used very widely, and the declaration in the header file is not problematic, only calls to it. Since C code generally requires some porting to be valid C++ anyway, this should be considered one of those items that requires porting.

Martin O'Riordan suggested an alternative approach:


The advantage of this approach is that it would permit writing portable, well-defined programs involving such conversions. However, it breaks the current degree of compatibility between old and new casts, and it adds functionality to dynamic_cast which is not obviously related to its current meaning.

Notes from 04/00 meeting:

Andrew Koenig suggested yet another approach: specify that "no diagnostic is required" if the implementation supports the conversion.

Later note:

It was observed that conversion between function and data pointers is listed as a "common extension" in C99.

Notes on the 10/01 meeting:

It was decided that we want the conversion defined in such a way that it always exists but is always undefined (as opposed to existing only when the size relationship is appropriate, and being implementation-defined in that case). This would allow an implementation to issue an error at compile time if the conversion does not make sense.

Bill Gibbons notes that the definitions of the other similar casts are inconsistent in this regard. Perhaps they should be updated as well.

Proposed resolution (April 2003):

After 7.6.1.10 [expr.reinterpret.cast] paragraph 6, insert:

A pointer to a function can be explicitly converted to a pointer to a function of a different type. The effect of calling a function through a pointer to a function type (9.3.4.6 [dcl.fct]) that is not the same as the type used in the definition of the function is undefined. Except that converting an rvalue of type ``pointer to T1'' to the type ``pointer to T2'' (where T1 and T2 are function types) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified. [Note: see also 7.3.12 [conv.ptr] for more details of pointer conversions. ] It is implementation defined whether a conversion from pointer to object to pointer to function and/or a conversion from pointer to function to pointer to object exist.
and in paragraph 10:
An lvalue expression of type T1 can be cast to the type ``reference to T2'' if T1 and T2 are object types and an expression of type ``pointer to T1'' can be explicitly converted to the type ``pointer to T2'' using a reinterpret_cast. That is, a reference cast reinterpret_cast< T& >(x) has the same effect as the conversion *reinterpret_cast< T* >(&x) with the built-in & and * operators. The result is an lvalue that refers to the same object as the source lvalue, but with a different type. No temporary is created, no copy is made, and constructors (11.4.5 [class.ctor]) or conversion functions (11.4.8 [class.conv]) are not called.

Drafting Note:

If either conversion exists, the implementation already has to define the behavior (paragraph 3).

Notes from April 2003 meeting:

The new consensus is that if the implementation allows this cast, pointer-to-function converted to pointer-to-object converted back to the original pointer-to-function should work; anything else is undefined behavior. If the implementation does not allow the cast, it should be ill-formed.

Tom Plum is investigating a new concept, that of a "conditionally-defined" feature, which may be applicable here.

Proposed Resolution (October, 2004):

(See paper J16/04-0067 = WG21 N1627 for background material and rationale for this resolution. The resolution proposed here differs only editorially from the one in the paper.)

  1. Insert the following into Clause 3 [intro.defs] and renumber all following definitions accordingly:

    1.3.2  conditionally-supported behavior

    behavior evoked by a program construct that is not a mandatory requirement of this International Standard. If a given implementation supports the construct, the behavior shall be as described herein; otherwise, the implementation shall document that the construct is not supported and shall treat a program containing an occurrence of the construct as ill-formed (Clause 3 [intro.defs]).

  2. Add the indicated words to 4.1 [intro.compliance] paragraph 2, bullet 2:

  3. Insert the following as a new paragraph following 7.6.1.10 [expr.reinterpret.cast] paragraph 7:

    Converting a pointer to a function to a pointer to an object type or vice versa evokes conditionally-supported behavior. In any such conversion supported by an implementation, converting from an rvalue of one type to the other and back (possibly with different cv-qualification) shall yield the original pointer value; mappings between pointers to functions and pointers to objects are otherwise implementation-defined.
  4. Change 9.10 [dcl.asm] paragraph 1 as indicated:

    The meaning of an An asm declaration evokes conditionally-supported behavior. If supported, its meaning is implementation-defined.
  5. Change 9.11 [dcl.link] paragraph 2 as indicated:

    The string-literal indicates the required language linkage. The meaning of the string-literal is implementation-defined. A linkage-specification with a string that is unknown to the implementation is ill-formed. This International Standard specifies the semantics of C and C++ language linkage. Other values of the string-literal evoke conditionally-supported behavior, with implementation-defined semantics. [Note: Therefore, a linkage-specification with a string-literal that is unknown to the implementation requires a diagnostic. When the string-literal in a linkage-specification names a programming language, the spelling of the programming language's name is implementation-defined. [Note: It is recommended that the spelling be taken from the document defining that language, for example Ada (not ADA) and Fortran or FORTRAN (depending on the vintage). The semantics of a language linkage other than C++ or C are implementation-defined. ]
  6. Change Clause 13 [temp] paragraph 4 as indicated:

    A template, a template explicit specialization (13.9.4 [temp.expl.spec]), or a class template partial specialization shall not have C linkage. If the linkage of one of these is something other than C or C++, the behavior is implementation-defined result is conditionally-supported behavior, with implementation-defined semantics.



463. reinterpret_cast<T*>(0)

Section: 7.6.1.10  [expr.reinterpret.cast]     Status: CD1     Submitter: Gennaro Prota     Date: 14 Feb 2004

[Voted into WP at April, 2006 meeting.]

Is reinterpret_cast<T*>(null_pointer_constant) guaranteed to yield the null pointer value of type T*?

I think a committee clarification is needed. Here's why: 7.6.1.10 [expr.reinterpret.cast] par. 8 talks of "null pointer value", not "null pointer constant", so it would seem that

  reinterpret_cast<T*>(0)
is a normal int->T* conversion, with an implementation-defined result.

However a little note to 7.6.1.10 [expr.reinterpret.cast] par. 5 says:

Converting an integral constant expression (5.19) with value zero always yields a null pointer (4.10), but converting other expressions that happen to have value zero need not yield a null pointer.
Where is this supported in normative text? It seems that either the footnote or paragraph 8 doesn't reflect the intent.

SUGGESTED RESOLUTION: I think it would be better to drop the footnote #64 (and thus the special case for ICEs), for two reasons:

a) it's not normative anyway; so I doubt anyone is relying on the guarantee it hints at, unless that guarantee is given elsewhere in a normative part

b) users expect reinterpret_casts to be almost always implementation dependent, so this special case is a surprise. After all, if one wants a null pointer there's static_cast. And if one wants reinterpret_cast semantics the special case requires doing some explicit cheat, such as using a non-const variable as intermediary:

   int v = 0;
   reinterpret_cast<T*>(v); // implementation defined

   reinterpret_cast<T*>(0); // null pointer value of type T*
   const int w = 0;
   reinterpret_cast<T*>(w); // null pointer value of type T*

It seems that not only that's providing a duplicate functionality, but also at the cost to hide what seems the more natural one.

Notes from October 2004 meeting:

This footnote was added in 1996, after the invention of reinterpret_cast, so the presumption must be that it was intentional. At this time, however, the CWG feels that there is no reason to require that reinterpret_cast<T*>(0) produce a null pointer value as its result.

Proposed resolution (April, 2005):

  1. Delete the footnote in 7.6.1.10 [expr.reinterpret.cast] paragraph 5 reading,

    Converting an integral constant expression (7.7 [expr.const]) with value zero always yields a null pointer (7.3.12 [conv.ptr]), but converting other expressions that happen to have value zero need not yield a null pointer.
  2. Add the indicated note to 7.6.1.10 [expr.reinterpret.cast] paragraph 8:

    The null pointer value (7.3.12 [conv.ptr]) is converted to the null pointer value of the destination type. [Note: A null pointer constant, which has integral type, is not necessarily converted to a null pointer value. —end note]



324. Can "&" be applied to assignment to bit-field?

Section: 7.6.2.2  [expr.unary.op]     Status: CD1     Submitter: Alasdair Grant     Date: 27 Nov 2001

[Voted into WP at October 2003 meeting.]

An assignment returns an lvalue for its left operand. If that operand refers to a bit field, can the "&" operator be applied to the assignment? Can a reference be bound to it?

  struct S { int a:3; int b:3; int c:3; };

  void f()
  {
    struct S s;
    const int *p = &(s.b = 0);     // (a)
    const int &r = (s.b = 0);      // (b)
          int &r2 = (s.b = 0);     // (c)
  }

Notes from the 4/02 meeting:

The working group agreed that this should be an error.

Proposed resolution (October 2002):

In 7.6.2.3 [expr.pre.incr] paragraph 1 (prefix "++" and "--" operators), change

The value is the new value of the operand; it is an lvalue.
to
The result is the updated operand; it is an lvalue, and it is a bit-field if the operand is a bit-field.

In 7.6.16 [expr.cond] paragraph 4 ("?" operator), add the indicated text:

If the second and third operands are lvalues and have the same type, the result is of that type and is an lvalue and it is a bit-field if the second or the third operand is a bit-field, or if both are bit-fields.

In 7.6.19 [expr.ass] paragraph 1 (assignment operators) add the indicated text (the original text is as updated by issue 221, which is DR but not in TC1):

The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue with the type and value of the left operand after the assignment has taken place. The result in all cases is a bit-field if the left operand is a bit-field.

Note that issue 222 adds (non-conflicting) text at the end of this same paragraph (7.6.19 [expr.ass] paragraph 1).

In 7.6.20 [expr.comma] paragraph 1 (comma operator), change:

The type and value of the result are the type and value of the right operand; the result is an lvalue if its right operand is.
to
The type and value of the result are the type and value of the right operand; the result is an lvalue if the right operand is an lvalue, and is a bit-field if the right operand is an lvalue and a bit-field.

Relevant related text (no changes required):

7.6.2.2 [expr.unary.op] paragraph 4:

The operand of & shall not be a bit-field.

9.4.4 [dcl.init.ref] paragraph 5, bullet 1, sub-bullet 1 (regarding binding a reference to an lvalue):

... is an lvalue (but is not a bit-field) ...




659. Alignment of function types

Section: 7.6.2.6  [expr.alignof]     Status: CD1     Submitter: Alisdair Meredith     Date: 7 November 2007

[Voted into the WP at the September, 2008 meeting.]

The specification for the alignof operator (7.6.2.6 [expr.alignof]) does not forbid function types as operands, although it probably should.

Proposed resolution (March, 2008):

The issue, as described, is incorrect. The requirement in 7.6.2.6 [expr.alignof] is for “a complete object type,” so a function type is already forbidden. However, the existing text does have a problem in this requirement in that it does not allow a reference type, as anticipated by paragraph 3. Consequently, the proposal is to change 7.6.2.6 [expr.alignof] paragraph 1 as indicated:

An alignof expression yields the alignment requirement of its operand type. The operand shall be a type-id representing a complete object type or a reference to a complete object type.



256. Overflow in size calculations

Section: 7.6.2.8  [expr.new]     Status: CD1     Submitter: James Kanze     Date: 15 Oct 2000

[Voted into the WP at the September, 2008 meeting.]

[Picked up by evolution group at October 2002 meeting.]

(See also issue 476.)

The size requested by an array allocation is computed by multiplying the number of elements requested by the size of each element and adding an implementation-specific amount for overhead. It is possible for this calculation to overflow. Is an implementation required to detect this situation and, for instance, throw std::bad_alloc?

On one hand, the maximum allocation size is one of the implementation limits specifically mentioned in Annex Clause Annex B [implimits], and, according to 4.1 [intro.compliance] paragraph 2, an implementation is only required to "accept and correctly execute" programs that do not violate its resource limits.

On the other hand, it is difficult or impossible for user code to detect such overflows in a portable fashion, especially given that the array allocation overhead is not fixed, and it would be a service to the user to handle this situation gracefully.

Rationale (04/01):

Each implementation is required to document the maximum size of an object (Annex Clause Annex B [implimits]). It is not difficult for a program to check array allocations to ensure that they are smaller than this quantity. Implementations can provide a mechanism in which users concerned with this problem can request extra checking before array allocations, just as some implementations provide checking for array index and pointer validity. However, it would not be appropriate to require this overhead for every array allocation in every program.

(See issue 624 for a request to reconsider this resolution.)

Note (March, 2008):

The Evolution Working Group has accepted the intent of this issue and referred it to CWG for action for C++0x (see paper J16/07-0033 = WG21 N2173).

Proposed resolution (September, 2008):

This issue is resolved by the resolution of issue 624, given in paper N2757.




299. Conversion on array bound expression in new

Section: 7.6.2.8  [expr.new]     Status: CD1     Submitter: Mark Mitchell     Date: 19 Jul 2001

[Voted into WP at October 2005 meeting.]

In 7.6.2.8 [expr.new], the standard says that the expression in an array-new has to have integral type. There's already a DR (issue 74) that says it should also be allowed to have enumeration type. But, it should probably also say that it can have a class type with a single conversion to integral type; in other words the same thing as in 8.5.3 [stmt.switch] paragraph 2.

Suggested resolution:

In 7.6.2.8 [expr.new] paragraph 6, replace "integral or enumeration type (6.8.2 [basic.fundamental])" with "integral or enumeration type (6.8.2 [basic.fundamental]), or a class type for which a single conversion function to integral or enumeration type exists".

Proposed resolution (October, 2004):

Change 7.6.2.8 [expr.new] paragraph 6 as follows:

Every constant-expression in a direct-new-declarator shall be an integral constant expression (7.7 [expr.const]) and evaluate to a strictly positive value. The expression in a direct-new-declarator shall have be of integral type, or enumeration type (3.9.1), or a class type for which a single conversion function to integral or enumeration type exists (11.4.8 [class.conv]). If the expression is of class type, the expression is converted by calling the conversion function, and the result of the conversion is used in place of the original expression. The value of the expression shall bewith a non-negative value. [Example: ...

Proposed resolution (April, 2005):

Change 7.6.2.8 [expr.new] paragraph 6 as follows:

Every constant-expression in a direct-new-declarator shall be an integral constant expression (7.7 [expr.const]) and evaluate to a strictly positive value. The expression in a direct-new-declarator shall have integral or enumeration type (6.8.2 [basic.fundamental]) with a non-negative value be of integral type, enumeration type, or a class type for which a single conversion function to integral or enumeration type exists (11.4.8 [class.conv]). If the expression is of class type, the expression is converted by calling that conversion function, and the result of the conversion is used in place of the original expression. If the value of the expression is negative, the behavior is undefined. [Example: ...



429. Matching deallocation function chosen based on syntax or signature?

Section: 7.6.2.8  [expr.new]     Status: CD1     Submitter: John Wilkinson     Date: 18 July 2003

[Voted into WP at October 2004 meeting.]

What does this example do?

  #include <stdio.h>
  #include <stdlib.h>

  struct A {
        void* operator new(size_t alloc_size, size_t dummy=0) {
                printf("A::operator new()\n");
                return malloc(alloc_size);
        };

        void operator delete(void* p, size_t s) {
                printf("A::delete %d\n", s);
        };


        A()  {printf("A constructing\n"); throw 2;};

  };

  int
  main() {
    try {
        A* ap = new A;
        delete ap;
    }
    catch(int) {printf("caught\n"); return 1;}
  }

The fundamental issue here is whether the deletion-on-throw is driven by the syntax of the new (placement or non-placement) or by signature matching. If the former, the operator delete would be called with the second argument equal to the size of the class. If the latter, it would be called with the second argument 0.

Core issue 127 (in TC1) dealt with this topic. It removed some wording in 14.3 [except.ctor] paragraph 2 that implied a syntax-based interpretation, leaving wording in 7.6.2.8 [expr.new] paragraph 19 that is signature-based. But there is no accompanying rationale to confirm an explicit choice of the signature-based approach.

EDG and g++ get 0 for the second argument, matching the presumed core issue 127 resolution. But maybe this should be revisited.

Notes from October 2003 meeting:

There was widespread agreement that the compiler shouldn't just silently call the delete with either of the possible values. In the end, we decided it's smarter to issue an error on this case and force the programmer to say what he means.

Mike Miller's analysis of the status quo: 6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 2 says that "operator delete(void*, std::size_t)" is a "usual (non-placement) deallocation function" if the class does not declare "operator delete(void*)." 6.7.5.5.2 [basic.stc.dynamic.allocation] does not use the same terminology for allocation functions, but the most reasonable way to understand the uses of the term "placement allocation function" in the Standard is as an allocation function that has more than one parameter and thus can (but need not) be called using the "new-placement" syntax described in 7.6.2.8 [expr.new]. (In considering issue 127, the core group discussed and endorsed the position that, "If a placement allocation function has default arguments for all its parameters except the first, it can be called using non-placement syntax.")

7.6.2.8 [expr.new] paragraph 19 says that any non-placement deallocation function matches a non-placement allocation function, and that a placement deallocation function matches a placement allocation function with the same parameter types after the first -- i.e., a non-placement deallocation function cannot match a placement allocation function. This makes sense, because non-placement ("usual") deallocation functions expect to free memory obtained from the system heap, which might not be the case for storage resulting from calling a placement allocation function.

According to this analysis, the example shows a placement allocation function and a non-placement deallocation function, so the deallocation function should not be invoked at all, and the memory will just leak.

Proposed Resolution (October 2003):

Add the following text at the end of 7.6.2.8 [expr.new] paragraph 19:

If the lookup finds the two-parameter form of a usual deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation]), and that function, considered as a placement deallocation function, would have been selected as a match for the allocation function, the program is ill-formed. [Example:
struct S {
  // Placement allocation function:
  static void* operator new(std::size_t, std::size_t);

  // Usual (non-placement) deallocation function:
  static void operator delete(void*, std::size_t);
};

S* p = new (0) S; // ill-formed: non-placement deallocation function matches 
                  // placement allocation function 
--- end example]



624. Overflow in calculating size of allocation

Section: 7.6.2.8  [expr.new]     Status: CD1     Submitter: Jens Maurer     Date: 8 March 2007

[Voted into the WP at the September, 2008 meeting (resolution in paper N2757).]

Issue 256 was closed without action, principally on the the grounds that an implementation could provide a means (command-line option, #pragma, etc.) for requesting that the allocation size be checked for validity, but that “it would not be appropriate to require this overhead for every array allocation in every program.”

This rationale may be giving too much weight to the overhead such a check would add, especially when compared to the likely cost of actually doing the storage allocation. In particular, the test essentially amounts to something like

    if (max_allocation_size / sizeof(T) < num_elements)
        throw std::bad_alloc();

(noting that max_allocation_size/sizeof(T) is a compile-time constant). It might make more sense to turn the rationale around and require the check, assuming that implementations could provide a mechanism for suppressing it if needed.

Suggested resolution:

In 7.6.2.8 [expr.new] paragraph 7, add the following words before the example:

If the value of the expression is such that the size of the allocated object would exceed the implementation-defined limit, an exception of type std::bad_alloc is thrown and no storage is obtained.

Note (March, 2008):

The Evolution Working Group has accepted the intent of issue 256 and referred it to CWG for action for C++0x (see paper J16/07-0033 = WG21 N2173).

Proposed resolution (March, 2008):

As suggested.

Notes from the June, 2008 meeting:

The CWG felt that this situation should not be treated like an out-of-memory situation and thus an exception of type std::bad_alloc (or, alternatively, returning a null pointer for a throw() allocator) would not be appropriate.

Proposed resolution (June, 2008):

Change 7.6.2.8 [expr.new] paragraph 8 as follows:

If the value of the expression in a direct-new-declarator is such that the size of the allocated object would exceed the implementation-defined limit, no storage is obtained and the new-expression terminates by throwing an exception of a type that would match a handler (14.4 [except.handle]) of type std::length_error (19.2.6 [length.error]). Otherwise, if When the value of the that expression in a direct-new-declarator is zero, the allocation function is called to allocate an array with no elements.

[Drafting note: std::length_error is thrown by std::string and std::vector and thus appears to be the right choice for the exception to be thrown here.]




288. Misuse of "static type" in describing pointers

Section: 7.6.2.9  [expr.delete]     Status: CD1     Submitter: James Kuyper     Date: 19 May 2001

[Voted into the WP at the June, 2008 meeting.]

For delete expressions, 7.6.2.9 [expr.delete] paragraph 1 says

The operand shall have a pointer type, or a class type having a single conversion function to a pointer type.

However, paragraph 3 of that same section says:

if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand's dynamic type and the static type shall have a virtual destructor or the behavior is undefined.

Since the operand must be of pointer type, its static type is necessarily the same as its dynamic type. That clause is clearly referring to the object being pointed at, and not to the pointer operand itself.

Correcting the wording gets a little complicated, because dynamic and static types are attributes of expressions, not objects, and there's no sub-expression of a delete-expression which has the relevant types.

Suggested resolution:

then there is a static type and a dynamic type that the hypothetical expression (* const-expression) would have. If that static type is different from that dynamic type, then that static type shall be a base class of that dynamic type, and that static type shall have a virtual destructor, or the behavior is undefined.

There's precedent for such use of hypothetical constructs: see 7.6.10 [expr.eq] paragraph 2, and 9.3.2 [dcl.name] paragraph 1.

11.7.3 [class.virtual] paragraph 3 has a similar problem. It refers to

the type of the pointer or reference denoting the object (the static type).

The type of the pointer is different from the type of the reference, both of which are different from the static type of '*pointer', which is what I think was actually intended. Paragraph 6 contains the exact same wording, in need of the same correction. In this case, perhaps replacing "pointer or reference" with "expression" would be the best fix. In order for this fix to be sufficient, pointer->member must be considered equivalent to (*pointer).member, in which case the "expression" referred to would be (*pointer).

11.4.11 [class.free] paragraph 4 says that
if a delete-expression is used to deallocate a class object whose static type has...

This should be changed to

if a delete-expression is used to deallocate a class object through a pointer expression whose dereferenced static type would have...

The same problem occurs later, when it says that the

static and dynamic types of the object shall be identical

In this case you could replace "object" with "dereferenced pointer expression".

Footnote 104 says that

7.6.2.9 [expr.delete] requires that ... the static type of the delete-expression's operand be the same as its dynamic type.

This would need to be changed to

the delete-expression's dereferenced operand

Proposed resolution (December, 2006):

  1. Change 7.6.2.9 [expr.delete] paragraph 3 as follows:

  2. In the first alternative (delete object), if the static type of the operand object to be deleted is different from its dynamic type, the static type shall be a base class of the operand's dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.
  3. Change the footnote in 11.4.11 [class.free] paragraph 4 as follows:

  4. A similar provision is not needed for the array version of operator delete because 7.6.2.9 [expr.delete] requires that in this situation, the static type of the delete-expression's operand object to be deleted be the same as its dynamic type.
  5. Change the footnote in 11.4.11 [class.free] paragraph 5 as follows:

  6. If the static type in the delete-expression of the object to be deleted is different from the dynamic type and the destructor is not virtual the size might be incorrect, but that case is already undefined; see 7.6.2.9 [expr.delete].

[Drafting notes: No change is required for 11.7.3 [class.virtual] paragraph 7 because “the type of the pointer” includes the pointed-to type. No change is required for 11.4.11 [class.free] paragraph 4 because “...used to deallocate a class object whose static type...” already refers to the object (and not the operand expression).]




353. Is deallocation routine called if destructor throws exception in delete?

Section: 7.6.2.9  [expr.delete]     Status: CD1     Submitter: Duane Smith     Date: 30 April 2002

[Voted into WP at April 2003 meeting.]

In a couple of comp.std.c++ threads, people have asked whether the Standard guarantees that the deallocation function will be called in a delete-expression if the destructor throws an exception. Most/all people have expressed the opinion that the deallocation function must be called in this case, although no one has been able to cite wording in the Standard supporting that view.

#include <new.h>
#include <stdio.h>
#include <stdlib.h>

static int flag = 0;

inline
void operator delete(void* p) throw()
{
   if (flag)
        printf("in deallocation function\n");
   free(p);
}

struct S {
    ~S() { throw 0; }
};

void f() {
    try {
        delete new S;
    }
    catch(...) { }
}

int main() {
       flag=1;
       f();
       flag=0;
       return 0;
}

Proposed resolution (October 2002):

Add to 7.6.2.9 [expr.delete] paragraph 7 the highlighted text:

The delete-expression will call a deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation]) [Note: The deallocation function is called regardless of whether the destructor for the object or some element of the array throws an exception. ]



442. Incorrect use of null pointer constant in description of delete operator

Section: 7.6.2.9  [expr.delete]     Status: CD1     Submitter: Matthias Hofmann     Date: 2 Dec 2003

[Voted into WP at October 2005 meeting.]

After some discussion in comp.lang.c++.moderated we came to the conclusion that there seems to be a defect in 7.6.2.9 [expr.delete]/4, which says:

The cast-expression in a delete-expression shall be evaluated exactly once. If the delete-expression calls the implementation deallocation function (3.7.3.2), and if the operand of the delete expression is not the null pointer constant, the deallocation function will deallocate the storage referenced by the pointer thus rendering the pointer invalid. [Note: the value of a pointer that refers to deallocated storage is indeterminate. ]

In the second sentence, the term "null pointer constant" should be changed to "null pointer". In its present form, the passage claims that the deallocation function will deallocate the storage refered to by a null pointer that did not come from a null pointer constant in the delete expression. Besides, how can the null pointer constant be the operand of a delete expression, as "delete 0" is an error because delete requires a pointer type or a class type having a single conversion function to a pointer type?

See also issue 348.

Proposed resolution:

Change the indicated sentence of 7.6.2.9 [expr.delete] paragraph 4 as follows:

If the delete-expression calls the implementation deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation]), and if the value of the operand of the delete expression is not the a null pointer constant, the deallocation function will deallocate the storage referenced by the pointer thus rendering the pointer invalid.

Notes from October 2004 meeting:

This wording is superseded by, and this issue will be resolved by, the resolution of issue 348.

Proposed resolution (April, 2005):

This issue is resolved by the resolution of issue 348.




520. Old-style casts between incomplete class types

Section: 7.6.3  [expr.cast]     Status: CD1     Submitter: comp.std.c++     Date: 19 May 2005

[Voted into WP at April, 2007 meeting.]

7.6.3 [expr.cast] paragraph 6 says,

The operand of a cast using the cast notation can be an rvalue of type “pointer to incomplete class type”. The destination type of a cast using the cast notation can be “pointer to incomplete class type”. In such cases, even if there is a inheritance relationship between the source and destination classes, whether the static_cast or reinterpret_cast interpretation is used is unspecified.

The wording seems to allow the following:

  1. casting from void pointer to incomplete type

  2.     struct A;
        struct B;
    
        void *v;
        A *a = (A*)v; // allowed to choose reinterpret_cast
    
  3. variant application of static or reinterpret casting

  4.     B *b = (B*)a;    // compiler can choose static_cast here
        A *aa = (A*)b;   // compiler can choose reinterpret_cast here
        assert(aa == a); // might not hold
    
  5. ability to somehow choose static_cast

  6. It's not entirely clear how a compiler can choose static_cast as 7.6.3 [expr.cast] paragraph 6 seems to allow. I believe the intent of 7.6.3 [expr.cast] paragraph 6 is to force the use of reinterpret_cast when either are incomplete class types and static_cast iff the compiler knows both types and there is a non-ambiguous hierarchy-traversal between that cast (or maybe not, core issue 242 talks about this). I cannot see any other interpretation because it isn't intuitive, every compiler I've tried agrees with me, and neither standard pointer conversions (7.3.12 [conv.ptr] paragraph 3) nor static_cast (7.6.1.9 [expr.static.cast] paragraph 5) talk about incomplete class types. If the committee agrees with me, I would like to see 7.3.12 [conv.ptr] paragraph 3 and 7.6.1.9 [expr.static.cast] paragraph 5 explicitly disallow incomplete class types and the wording of 7.6.3 [expr.cast] paragraph 6 changed to not allow any other interpretation.

Proposed resolution (April, 2006):

Change 7.6.3 [expr.cast] paragraph 6 as indicated:

The operand of a cast using the cast notation can be an rvalue of type “pointer to incomplete class type.” The destination type of a cast using the cast notation can be “pointer to incomplete class type.” In such cases, even if there is a inheritance relationship between the source and destination classes, whether the static_cast or reinterpret_cast interpretation is used is unspecified. If both the operand and destination types are class types and one or both are incomplete, it is unspecified whether the static_cast or the reinterpret_cast interpretation is used, even if there is an inheritance relationship between the two classes. [Note: For example, if the classes were defined later in the translation unit, a multi-pass compiler would be permitted to interpret a cast between pointers to the classes as if the class types were complete at that point. —end note]



497. Missing required initialization in example

Section: 7.6.4  [expr.mptr.oper]     Status: CD1     Submitter: Giovanni Bajo     Date: 03 Jan 2005

[Voted into WP at October 2005 meeting.]

7.6.4 [expr.mptr.oper] paragraph 5 contains the following example:

    struct S {
        mutable int i;
    };
    const S cs;
    int S::* pm = &S::i;   // pm refers to mutable member S::i
    cs.*pm = 88;           // ill-formed: cs is a const object

The const object cs is not explicitly initialized, and class S does not have a user-declared default constructor. This makes the code ill-formed as per 9.4 [dcl.init] paragraph 9.

Proposed resolution (April, 2005):

Change the example in 7.6.4 [expr.mptr.oper] paragraph 5 to read as follows:

    struct S {
        S() : i(0) { }
        mutable int i;
    };
    void f()
    {
        const S cs;
        int S::* pm = &S::i;   // pm refers to mutable member S::i
        cs.*pm = 88;           // ill-formed: cs is a const object
    }



614. Results of integer / and %

Section: 7.6.5  [expr.mul]     Status: CD1     Submitter: Gabriel Dos Reis     Date: 15 January 2007

[Voted into the WP at the September, 2008 meeting as part of paper N2757.]

The current Standard leaves it implementation-defined whether integer division rounds the result toward 0 or toward negative infinity and thus whether the result of % may be negative. C99, apparently reflecting (nearly?) unanimous hardware practice, has adopted the rule that integer division rounds toward 0, thus requiring that the result of -1 % 5 be -1. Should the C++ Standard follow suit?

On a related note, does INT_MIN % -1 invoke undefined behavior? The % operator is defined in terms of the / operator, and INT_MIN / -1 overflows, which by Clause 7 [expr] paragraph 5 causes undefined behavior; however, that is not the “result” of the % operation, so it's not clear. The wording of 7.6.5 [expr.mul] paragraph 4 appears to allow % to cause undefined behavior only when the second operand is 0.

Proposed resolution (August, 2008):

Change 7.6.5 [expr.mul] paragraph 4 as follows:

The binary / operator yields the quotient, and the binary % operator yields the remainder from the division of the first expression by the second. If the second operand of / or % is zero the behavior is undefined; otherwise (a/b)*b + a%b is equal to a. If both operands are nonnegative then the remainder is nonnegative; if not, the sign of the remainder is implementation-defined. [Footnote: According to work underway toward the revision of ISO C, the preferred algorithm for integer division follows the rules defined in the ISO Fortran standard, ISO/IEC 1539:1991, in which the quotient is always rounded toward zero. —end footnote]. For integral operands, the / operator yields the algebraic quotient with any fractional part discarded; [Footnote: This is often called “truncation towards zero.” —end footnote] if the quotient a/b is representable in the type of the result, (a/b)*b + a%b is equal to a.

[Drafting note: see C99 6.5.5 paragraph 6.]




661. Semantics of arithmetic comparisons

Section: 7.6.9  [expr.rel]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 27 November 2007

[Voted into the WP at the June, 2008 meeting.]

The actual semantics of arithmetic comparison — e.g., whether 1 < 2 yields true or false — appear not to be specified anywhere in the Standard. The C Standard has a general statement that

Each of the operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) shall yield 1 if the specified relation is true and 0 if it is false.

There is no corresponding statement in the C++ Standard.

Proposed resolution (February, 2008):

  1. Append the following paragraph to the end of 7.6.9 [expr.rel]:

  2. If both operands (after conversions) are of arithmetic type, each of the operators shall yield true if the specified relation is true and false if it is false.
  3. Append the following paragraph to the end of 7.6.10 [expr.eq]:

  4. Each of the operators shall yield true if the specified relation is true and false if it is false.



446. Does an lvalue-to-rvalue conversion on the "?" operator produce a temporary?

Section: 7.6.16  [expr.cond]     Status: CD1     Submitter: John Potter     Date: 31 Dec 2003

[Voted into WP at October 2005 meeting.]

The problem occurs when the value of the operator is determined to be an rvalue, the selected argument is an lvalue, the type is a class type and a non-const member is invoked on the modifiable rvalue result.

    struct B {
        int v;
        B (int v) : v(v) { }
        void inc () { ++ v; }
        };
    struct D : B {
        D (int v) : B(v) { }
        };

    B b1(42);
    (0 ? B(13) : b1).inc();
    assert(b1.v == 42);

The types of the second and third operands are the same and one is an rvalue. Nothing changes until p6 where an lvalue to rvalue conversion is performed on the third operand. 6.7.7 [class.temporary] states that an lvalue to rvalue conversion produces a temporary and there is nothing to remove it. It seems clear that the assertion must pass, yet most implementations fail.

There seems to be a defect in p3 b2 b1. First, the conditions to get here and pass the test.

If E1 and E2 have class type, and the underlying class types are the same or one is a base class of the other: E1 can be converted to match E2 if the class of T2 is the same type as, or a base class of, the class of T1, and the cv-qualification of T2 is the same cv-qualification as, or a greater cv-qualification than, the cv-qualification of T1.

If both E1 and E2 are lvalues, passing the conditions here also passes the conditions for p3 b1. Thus, at least one is an rvalue. The case of two rvalues is not interesting and the action is covered by the case when E1 is an rvalue.

    (0 ? D(13) : b1).inc();
    assert(b1.v == 42);
E1 is changed to an rvalue of type T2 that still refers to the original source class object (or the appropriate subobject thereof). [Note: that is, no copy is made. ]

Having changed the rvalue to base type, we are back to the above case where an lvalue to rvalue conversion is required on the third operand at p6. Again, most implementations fail.

The remaining case, E1 an lvalue and E2 an rvalue, is the defect.

    D d1(42);
    (0 ? B(13) : d1).inc();
    assert(d1.v == 42);

The above quote states that an lvalue of type T1 is changed to an rvalue of type T2 without creating a temporary. This is in contradiction to everything else in the standard about lvalue to rvalue conversions. Most implementations pass in spite of the defect.

The usual accessible and unambiguous is missing from the base class.

There seems to be two possible solutions. Following other temporary creations would produce a temporary rvalue of type T1 and change it to an rvalue of type T2. Keeping the no copy aspect of this bullet intact would change the lvalue of type T1 to an lvalue of type T2. In this case the lvalue to rvalue conversion would happen in p6 as usual.

Suggested wording for p3 b2 b1

The base part:

If E1 and E2 have class type, and the underlying class types are the same or one is a base class of the other: E1 can be converted to match E2 if the class of T2 is the same type as, or an accessible and unambiguous base class of, the class of T1, and the cv-qualification of T2 is the same cv-qualification as, or a greater cv-qualification than, the cv-qualification of T1. If the conversion is applied:

The same type temporary version:

If E1 is an lvalue, an lvalue to rvalue conversion is applied. The resulting or original rvalue is changed to an rvalue of type T2 that refers to the same class object (or the appropriate subobject thereof). [Note: that is, no copy is made in changing the type of the rvalue. ]

The never copy version:

The lvalue(rvalue) E1 is changed to an lvalue(rvalue) of type T2 that refers to the original class object (or the appropriate subobject thereof). [Note: that is, no copy is made. ]

The test case was posted to clc++m and results for implementations were reported.

#include <cassert>
struct B {
    int v;
    B (int v) : v(v) { }
    void inc () { ++ v; }
    };
struct D : B {
    D (int v) : B(v) { }
    };
int main () {
    B b1(42);
    D d1(42);
    (0 ? B(13) : b1).inc();
    assert(b1.v == 42);
    (0 ? D(13) : b1).inc();
    assert(b1.v == 42);
    (0 ? B(13) : d1).inc();
    assert(d1.v == 42);
    }

// CbuilderX(EDG301) FFF  Rob Williscroft
// ICC-8.0           FFF  Alexander Stippler
// COMO-4.301        FFF  Alexander Stippler

// BCC-5.4           FFP  Rob Williscroft
// BCC32-5.5         FFP  John Potter
// BCC32-5.65        FFP  Rob Williscroft
// VC-6.0            FFP  Stephen Howe
// VC-7.0            FFP  Ben Hutchings
// VC-7.1            FFP  Stephen Howe
// OpenWatcom-1.1    FFP  Stephen Howe

// Sun C++-6.2       PFF  Ron Natalie

// GCC-3.2           PFP  John Potter
// GCC-3.3           PFP  Alexander Stippler

// GCC-2.95          PPP  Ben Hutchings
// GCC-3.4           PPP  Florian Weimer

I see no defect with regards to lvalue to rvalue conversions; however, there seems to be disagreement about what it means by implementers. It may not be surprising because 5.16 and passing a POD struct to an ellipsis are the only places where an lvalue to rvalue conversion applies to a class type. Most lvalue to rvalue conversions are on basic types as operands of builtin operators.

Notes from the March 2004 meeting:

We decided all "?" operators that return a class rvalue should copy the second or third operand to a temporary. See issue 86.

Proposed resolution (October 2004):

  1. Change 7.6.16 [expr.cond] bullet 3.2 sub-bullet 1 as follows:

    if E1 and E2 have class type, and the underlying class types are the same or one is a base class of the other: E1 can be converted to match E2 if the class of T2 is the same type as, or a base class of, the class of T1, and the cv-qualification of T2 is the same cv-qualification as, or a greater cv-qualification than, the cv-qualification of T1. If the conversion is applied, E1 is changed to an rvalue of type T2 that still refers to the original source class object (or the appropriate subobject thereof). [Note: that is, no copy is made. —end note] by copy-initializing a temporary of type T2 from E1 and using that temporary as the converted operand.
  2. Change 7.6.16 [expr.cond] bullet 6.1 as follows:

    The second and third operands have the same type; the result is of that type. If the operands have class type, the result is an rvalue temporary of the result type, which is copy-initialized from either the second operand or the third operand depending on the value of the first operand.
  3. Change 7.3.2 [conv.lval] paragraph 2 as follows:

    The value contained in the object indicated by the lvalue is the rvalue result. When an lvalue-to-rvalue conversion occurs within the operand of sizeof (7.6.2.5 [expr.sizeof]) the value contained in the referenced object is not accessed, since that operator does not evaluate its operand. Otherwise, if the lvalue has a class type, the conversion copy-initializes a temporary of type T from the lvalue and the result of the conversion is an rvalue for the temporary. Otherwise, the value contained in the object indicated by the lvalue is the rvalue result.

[Note: this wording partially resolves issue 86. See also issue 462.]




339. Overload resolution in operand of sizeof in constant expression

Section: 7.7  [expr.const]     Status: CD1     Submitter: Steve Adamczyk     Date: 11 Mar 2002

[Voted into the WP at the June, 2008 meeting as paper N2634.]

I've seen some pieces of code recently that put complex expressions involving overload resolution inside sizeof operations in constant expressions in templates.

7.7 [expr.const] paragraph 1 implies that some kinds of nonconstant expressions are allowed inside a sizeof in a constant expression, but it's not clear that this was intended to extend all the way to things like overload resolution. Allowing such things has some hidden costs. For example, name mangling has to be able to represent all operators, including calls, and not just the operators that can appear in constant expressions.

  template <int I> struct A {};

  char xxx(int);
  char xxx(float);

  template <class T> A<sizeof(xxx((T)0))> f(T){}

  int main()
  {
    f(1);
  }

If complex expressions are indeed allowed, it should be because of an explicit committee decision rather than because of some looseness in this section of the standard.

Notes from the 4/02 meeting:

Any argument for restricting such expressions must involve a cost/benefit ratio: a restriction would be palatable only if it causes minimum hardship for users and allows a substantial reduction in implementation cost. If we propose a restriction, it must be one that library writers can live with.

Lots of these cases fail with current compilers, so there can't be a lot of existing code using them. We plan to find out what cases there are in libraries like Loki and Boost.

We noted that in many cases one can move the code into a class to get the same result. The implementation problem comes up when the expression-in-sizeof is in a template deduction context or part of a template signature. The problem cases are ones where an error causes deduction to fail, as opposed to contexts where an error causes a diagnostic. The latter contexts are easier to handle; however, there are situations where "fail deduction" may be the desired behavior.

Notes from the April 2003 meeting:

Here is a better example:

  extern "C" int printf(const char *, ...);
  char f(int);
  int f(...);
  // Approach 1 -- overload resolution in template class
  // No problem
  template <class T> struct conv_int {
    static const bool value = (sizeof(f(T())) == 1);
  };
  // Approach 2 -- overload resolution in type deduction
  // Difficult
  template <int I> struct A {
    static const int value = I;
  };
  template <class T> bool conv_int2(A<sizeof(f(T()))> p) {
    return p.value == 1;
  }

  template<typename T>
  A<sizeof(f(T()))> make_A() {
    return A<sizeof(f(T()))>();
  }

  int main() {
    printf("short: %d\n", conv_int<short>::value);
    printf("int *: %d\n", conv_int<int *>::value);
    printf("short: %d\n", conv_int2<short>(make_A<short>()));
    printf("int *: %d\n", conv_int2<int *>(make_A<int*>()));
  }

The core working group liked the idea of a restriction that says that expressions inside sizeof in template signature contexts must be otherwise valid as nontype template argument expressions (i.e., integer operations only, limited casts). This of course is subject to whether users can live with that restriction. This topic was brought up in full committee, but there was limited feedback from other groups.

It was also noted that if typeof (whatever it is called) is added, there may be a similar issue there.

Note (March, 2005):

Dave Abrahams (quoting a Usenet posting by Vladimir Marko): The decltype and auto proposal (revision 3: N1607) presents

    template <class T,class U>
    decltype((*(T*)0)+(*(U*)0)) add(const T& t,const U& u);

as a valid declaration (if the proposal is accepted). If [the restrictions in the April, 2003 note] really applied to decltype, the declaration above would be invalid. AFAICT every non-trivial use of decltype in a template function declaration would be invalid. And for me this would render my favorite proposal useless.

I would propose to allow any kind of expression inside sizeof (and decltype) and explicitly add sizeof (and decltype) expressions involving template-parameters to non-deduced contexts (add a bullet to 13.10.3.5 [temp.deduct.partial] paragraph 4).

Jaakko Jarvi: Just reinforcing that this is important and hope for insights. The topic is discussed a bit on page 10 of the latest revision of the proposal (N1705). Here's a quote from the proposal:

However, it is crucial that no restrictions are placed on what kinds of expressions are allowed inside decltype, and therefore also inside sizeof. We suggest that issue 339 is resolved to require the compiler to fail deduction (apply the SFINAE principle), and not produce an error, for as large set of invalid expressions in operands of sizeof or decltype as is possible to comfortably implement. We wish that implementors aid in classifying the kinds of expressions that should produce errors, and the kinds that should lead to failure of deduction.

Notes from the April, 2007 meeting:

The CWG is pursuing a compromise proposal, to which the EWG has tentatively agreed, which would allow arbitrary expressions in the return types of function templates but which would restrict the expressions that participate in the function signature (and thus in overload resolution) to those that can be used as non-type template arguments. During deduction and overload resolution, these complex return types would be ignored; that is, there would be no substitution of the deduced template arguments into the return type at this point. If such a function were selected by overload resolution, however, a substitution failure in the return type would produce a diagnostic rather than a deduction failure.

This approach works when doing overload resolution in the context of a function call, but additional tricks (still being defined) are needed in other contexts such as friend function declaration matching and taking the address of a function, in which the return type does play a part.

Notes from the July, 2007 meeting:

The problem is whether arbitrary expressions (for example, ones that include overload resolution) are allowed in template deduction contexts, and, if so, which expression errors are SFINAE failures and which are hard errors.

This issue deals with arbitrary expressions inside sizeof in deduction contexts. That's a fringe case right now (most compilers don't accept them). decltype makes the problem worse, because the standard use case is one that involves overload resolution. Generalized constant expressions make it worse yet, because they allow overload resolution and class types to show up in any constant expression in a deduction context.

Why is this an issue? Why don't we just say everything is allowed and be done with it?

At the April, 2007 meeting, we were headed toward a solution that imposed a restriction on expressions in deduction contexts, but such a restriction seems to really hamper uses of constexpr functions. So we're now proposing that fully general expressions be allowed, and that most errors in such expressions be treated as SFINAE failures rather than errors.

One issue with writing Standard wording for that is how to define “most.” There's a continuum of errors, some errors being clearly SFINAE failures, and some clearly “real” errors, with lots of unclear cases in between. We decided it's easier to write the definition by listing the errors that are not treated as SFINAE failures, and the list we came up with is as follows:

  1. errors that occur while processing some entity external to the expression, e.g., an instantiation of a template or the generation of the definition of an implicitly-declared copy constructor
  2. errors due to implementation limits
  3. errors due to access violations (this is a judgment call, but the philosophy of access has always been that it doesn't affect visibility)

Everything else produces a SFINAE failure rather than a hard error.

There was broad consensus that this felt like a good solution, but that feeling was mixed with trepidation on several fronts:

We will be producing wording for the Working Draft for the October, 2007 meeting.

(See also issue 657.)




366. String literal allowed in integral constant expression?

Section: 7.7  [expr.const]     Status: CD1     Submitter: Martin v. Loewis     Date: 29 July 2002

[Voted into WP at October 2003 meeting.]

According to 15.2 [cpp.cond] paragraph 1, the if-group

#if "Hello, world"

is well-formed, since it is an integral constant expression. Since that may not be obvious, here is why:

7.7 [expr.const] paragraph 1 says that an integral constant expression may involve literals (5.13 [lex.literal]); "Hello, world" is a literal. It restricts operators to not use certain type conversions; this expression does not use type conversions. It further disallows functions, class objects, pointers, ... - this expression is none of those, since it is an array.

However, 15.2 [cpp.cond] paragraph 6 does not explain what to do with this if-group, since the expression evaluates neither to false(zero) nor true(non-zero).

Proposed resolution (October 2002):

Change the beginning of the second sentence of 7.7 [expr.const] paragraph 1 which currently reads

An integral constant-expression can involve only literals (5.13 [lex.literal]), ...
to say
An integral constant-expression can involve only literals of arithmetic types (5.13 [lex.literal], 6.8.2 [basic.fundamental]), ...




367. throw operator allowed in constant expression?

Section: 7.7  [expr.const]     Status: CD1     Submitter: Martin v. Loewis     Date: 29 July 2002

[Voted into WP at the October, 2006 meeting.]

The following translation unit appears to be well-formed.

int x[true?throw 4:5];

According to 7.7 [expr.const], this appears to be an integral constant expression: it is a conditional expression, involves only literals, and no assignment, increment, decrement, function-call, or comma operators. However, if this is well-formed, the standard gives no meaning to this declaration, since the array bound (9.3.4.5 [dcl.array] paragraph 1) cannot be computed.

I believe the defect is that throw expressions should also be banned from constant expressions.

Notes from October 2002 meeting:

We should also check on new and delete.

Notes from the April, 2005 meeting:

Although it could be argued that all three of these operators potentially involve function calls — throw to std::terminate, new and delete to the corresponding allocation and deallocation functions — and thus would already be excluded from constant expressions, this reasoning was considered to be too subtle to allow closing the issue with no change. A modification that explicitly clarifies the status of these operators will be drafted.

Proposed resolution (October, 2005):

Change the last sentence of 7.7 [expr.const] as indicated:

In particular, except in sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call function call (including new-expressions and delete-expressions), or comma operators, or throw-expressions shall not be used.

Note: this sentence is also changed by the resolution of issue 530.




457. Wording nit on use of const variables in constant expressions

Section: 7.7  [expr.const]     Status: CD1     Submitter: Mark Mitchell     Date: 03 Feb 2004

[Voted into WP at April 2005 meeting.]

I'm looking at 7.7 [expr.const]. I see:

An integral constant-expression can involve only ... const variables or static data members of integral or enumeration types initialized with constant expressions ...

Shouldn't that be "const non-volatile"?

It seems weird to say that:

  const volatile int i = 3;
  int j[i];
is valid.

Steve Adamczyk: See issue 76, which made the similar change to 9.2.9.2 [dcl.type.cv] paragraph 2, and probably should have changed this one as well.

Proposed resolution (October, 2004):

Change the first sentence in the second part of 7.7 [expr.const] paragraph 1 as follows:

An integral constant-expression can involve only literals of arithmetic types (5.13 [lex.literal], 6.8.2 [basic.fundamental]), enumerators, non-volatile const variables or static data members of integral or enumeration types initialized with constant expressions (9.4 [dcl.init]), non-type template parameters of integral or enumeration types, and sizeof expressions.



530. Nontype template arguments in constant expressions

Section: 7.7  [expr.const]     Status: CD1     Submitter: Mark Mitchell     Date: 21 August 2005

[Voted into the WP at the April, 2007 meeting as part of paper J16/07-0095 = WG21 N2235.]

Consider:

    template <int* p> struct S {
        static const int I = 3;
    };
    int i;
    int a[S<&i>::I];

Clearly this should be valid, but a pedantic reading of 7.7 [expr.const] would suggest that this is invalid because “&i” is not permitted in integral constant expressions.

Proposed resolution (October, 2005):

Change the last sentence of 7.7 [expr.const] paragraph 1 as indicated:

In particular, except in non-type template-arguments or sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call, or comma operators shall not be used.

(Note: the same text is changed by the resolution of issue 367.)

Notes from April, 2006 meeting:

The proposed resolution could potentially be read as saying that the prohibited operations and operators would be permitted in integral constant expressions that are non-type template-arguments. John Spicer is investigating an alternate approach, to say that expressions in non-type template arguments are not part of the expression in which the template-id appears (in contrast to the operand of sizeof, which is part of the containing expression).

Additional note (May, 2008):

This issue is resolved by the rewrite of 7.7 [expr.const] that was done in conjunction with the constexpr proposal, paper N2235.




684. Constant expressions involving the address of an automatic variable

Section: 7.7  [expr.const]     Status: CD1     Submitter: Jens Maurer     Date: 13 March, 2008

[Voted into the WP at the September, 2008 meeting (resolution in paper N2757).]

The expressions that are excluded from being constant expressions in 7.7 [expr.const] paragraph 2 does not address an example like the following:

    void f() {
       int a;
       constexpr int* p = &a;    // should be ill-formed, currently isn't
    }

Suggested resolution:

Add the following bullet to the list in 7.7 [expr.const] paragraph 2:

Proposed resolution (June, 2008):

  1. Change 6.9.3.2 [basic.start.static] paragraph 1 as follows:

  2. Objects with static storage duration (6.7.5.2 [basic.stc.static]) or thread storage duration (3.7.2) shall be zero-initialized (9.4 [dcl.init]) before any other initialization takes place. A reference with static or thread storage duration and an object of trivial or literal type with static or thread storage duration can be initialized with a constant expression (7.7 [expr.const]); this is called constant initialization. Constant initialization is performed:
    • if an object of trivial or literal type with static or thread storage duration is initialized with a constant expression (7.7 [expr.const]), or

    • if a reference with static or thread storage duration is initialized with a constant expression that is not an lvalue designating an object with thread or automatic storage duration.

    Together, zero-initialization and constant initialization...
  3. Add the following in 7.7 [expr.const] paragraph 2:

(Note: the change to 6.9.3.2 [basic.start.static] paragraph 1 needs to be reconciled with the conflicting change in issue 688.)




276. Order of destruction of parameters and temporaries

Section: 8.7  [stmt.jump]     Status: CD1     Submitter: James Kanze     Date: 28 Mar 2001

[Voted into the WP at the June, 2008 meeting.]

According to 8.7 [stmt.jump] paragraph 2,

On exit from a scope (however accomplished), destructors (11.4.7 [class.dtor]) are called for all constructed objects with automatic storage duration (6.7.5.4 [basic.stc.auto]) (named objects or temporaries) that are declared in that scope, in the reverse order of their declaration.

This wording is problematic for temporaries and for parameters. First, temporaries are not "declared," so this requirement does not apply to them, in spite of the assertion in the quoted text that it does.

Second, although the parameters of a function are declared in the called function, they are constructed and destroyed in the calling context, and the order of evaluation of the arguments is unspecified (cf 7.6.1.3 [expr.call] paragraphs 4 and 8). The order of destruction of the parameters might, therefore, be different from the reverse order of their declaration.

Notes from 04/01 meeting:

Any resolution of this issue should be careful not to introduce requirements that are redundant or in conflict with those of other parts of the IS. This is especially true in light of the pending issues with respect to the destruction of temporaries (see issues 86, 124, 199, and 201). If possible, the wording of a resolution should simply reference the relevant sections.

It was also noted that the temporary for a return value is also destroyed "out of order."

Note that issue 378 picks a nit with the wording of this same paragraph.

Proposed Resolution (November, 2006):

Change 8.7 [stmt.jump] paragraph 2 as follows:

On exit from a scope (however accomplished), destructors (11.4.7 [class.dtor]) are called for all constructed objects with automatic storage duration (6.7.5.4 [basic.stc.auto]) (named objects or temporaries) that are declared in that scope, in the reverse order of their declaration. variables with automatic storage duration (6.7.5.4 [basic.stc.auto]) that have been constructed in that scope are destroyed in the reverse order of their construction. [Note: For temporaries, see 6.7.7 [class.temporary]. —end note] Transfer out of a loop...



378. Wording that says temporaries are declared

Section: 8.7  [stmt.jump]     Status: CD1     Submitter: Gennaro Prota     Date: 07 September 2002

Paragraph 8.7 [stmt.jump] paragraph 2 of the standard says:

On exit from a scope (however accomplished), destructors (11.4.7 [class.dtor]) are called for all constructed objects with automatic storage duration (6.7.5.4 [basic.stc.auto]) (named objects or temporaries) that are declared in that scope.

It refers to objects "that are declared" but the text in parenthesis also mentions temporaries, which cannot be declared. I think that text should be removed.

This is related to issue 276.

Proposed Resolution (November, 2006):

This issue is resolved by the resolution of issue 276.




281. inline specifier in friend declarations

Section: 9.2.3  [dcl.fct.spec]     Status: CD1     Submitter: John Spicer     Date: 24 Apr 2001

[Moved to DR at October 2002 meeting.]

There is currently no restriction on the use of the inline specifier in friend declarations. That would mean that the following usage is permitted:

    struct A {
        void f();
    };

    struct B {
        friend inline void A::f();
    };

    void A::f(){}

I believe this should be disallowed because a friend declaration in one class should not be able to change attributes of a member function of another class.

More generally, I think that the inline attribute should only be permitted in friend declarations that are definitions.

Notes from the 04/01 meeting:

The consensus agreed with the suggested resolution. This outcome would be similar to the resolution of issue 136.

Proposed resolution (10/01):

Add to the end of 9.2.3 [dcl.fct.spec] paragraph 3:

If the inline specifier is used in a friend declaration, that declaration shall be a definition or the function shall have previously been declared inline.



317. Can a function be declared inline after it has been called?

Section: 9.2.3  [dcl.fct.spec]     Status: CD1     Submitter: Steve Clamage     Date: 14 Oct 2001

[Voted into WP at October 2005 meeting.]

Steve Clamage: Consider this sequence of declarations:

  void foo() { ... }
  inline void foo();
The non-inline definition of foo precedes the inline declaration. It seems to me this code should be ill-formed, but I could not find anything in the standard to cover the situation.

Bjarne Stroustrup: Neither could I, so I looked in the ARM, which addressed this case (apparently for member function only) in some detail in 7.1.2 (pp103-104).

The ARM allows declaring a function inline after its initial declaration, as long as it has not been called.

Steve Clamage: If the above code is valid, how about this:

  void foo() { ... }    // define foo
  void bar() { foo(); } // use foo
  inline void foo();    // declare foo inline

Bjarne Stroustrup: ... and [the ARM] disallows declaring a function inline after it has been called.

This may still be a good resolution.

Steve Clamage: But the situation in the ARM is the reverse: Declare a function inline, and define it later (with no intervening call). That's a long-standing rule in C++, and allows you to write member function definitions outside the class.

In my example, the compiler could reasonably process the entire function as out-of-line, and not discover the inline declaration until it was too late to save the information necessary for inline generation. The equivalent of another compiler pass would be needed to handle this situation.

Bjarne Stroustrup: I see, and I think your argument it conclusive.

Steve Clamage: I'd like to open a core issue on this point, and I recommend wording along the lines of: "A function defined without an inline specifier shall not be followed by a declaration having an inline specifier."

I'd still like to allow the common idiom

  class T {
    int f();
  };
  inline int T::f() { ... }

Martin Sebor: Since the inline keyword is just a hint to the compiler, I don't see any harm in allowing the construct. Your hypothetical compiler can simply ignore the inline on the second declaration. On the other hand, I feel that adding another special rule will unnecessarily complicate the language.

Steve Clamage: The inline specifier is more than a hint. You can have multiple definitions of inline functions, but only one definition of a function not declared inline. In particular, suppose the above example were in a header file, and included multiple times in a program.

Proposed resolution (October, 2004):

Add the indicated words to 9.2.3 [dcl.fct.spec] paragraph 4:

An inline function shall be defined in every translation unit in which it is used and shall have exactly the same definition in every case (6.3 [basic.def.odr]). [Note: a call to the inline function may be encountered before its definition appears in the translation unit. —end note] If the definition of a function appears in a translation unit before its first declaration as inline, the program is ill-formed. If a function with external linkage is declared inline in one translation unit...



396. Misleading note regarding use of auto for disambiguation

Section: 9.2.3  [dcl.fct.spec]     Status: CD1     Submitter: Herb Sutter     Date: 3 Jan 2003

[Voted into WP at March 2004 meeting.]

BTW, I noticed that the following note in 9.2.2 [dcl.stc] paragraph 2 doesn't seem to have made it onto the issues list or into the TR:

[Note: hence, the auto specifier is almost always redundant and not often used; one use of auto is to distinguish a declaration-statement from an expression-statement (stmt.ambig) explicitly. --- end note]

I thought that this was well known to be incorrect, because using auto does not disambiguate this. Writing:

  auto int f();
is still a declaration of a function f, just now with an error since the function's return type may not use an auto storage class specifier. I suppose an error is an improvement over a silent ambiguity going the wrong way, but it's still not a solution for the user who wants to express the other in a compilable way.

Proposed resolution: Replace that note with the following note:

[Note: hence, the auto specifier is always redundant and not often used. --- end note]

John Spicer: I support the proposed change, but I think the disambiguation case is not the one that you describe. An example of the supposed disambiguation is:

  int i;
  int j;
  int main()
  {
    int(i);  // declares i, not reference to ::i
    auto int(j);  // declares j, not reference to ::j
  }

cfront would take "int(i)" as a cast of ::i, so the auto would force what it would otherwise treat as a statement to be considered a declaration (cfront 3.0 warned that this would change in the future).

In a conforming compiler the auto is always redundant (as you say) because anything that could be considered a valid declaration should be treated as one.

Proposed resolution (April 2003):

Replace 9.2.2 [dcl.stc] paragraph 2

[Note: hence, the auto specifier is almost always redundant and not often used; one use of auto is to distinguish a declaration-statement from an expression-statement (8.9 [stmt.ambig]) explicitly. --- end note]
with
[Note: hence, the auto specifier is always redundant and not often used. One use of auto is to distinguish a declaration-statement from an expression-statement explicitly rather than relying on the disambiguation rules (8.9 [stmt.ambig]), which may aid readers. --- end note]




397. Same address for string literals from default arguments in inline functions?

Section: 9.2.3  [dcl.fct.spec]     Status: CD1     Submitter: Mark Mitchell     Date: 13 Jan 2003

[Voted into WP at April, 2007 meeting.]

Are string literals from default arguments used in extern inlines supposed to have the same addresses across all translation units?

  void f(const char* = "s")
  inline g() {
    f();
  }

Must the "s" strings be the same in all copies of the inline function?

Steve Adamczyk: The totality of the standard's wisdom on this topic is (9.2.3 [dcl.fct.spec] paragraph 4):

A string literal in an extern inline function is the same object in different translation units.

I'd hazard a guess that a literal in a default argument expression is not "in" the extern inline function (it doesn't appear in the tokens of the function), and therefore it need not be the same in different translation units.

I don't know that users would expect such strings to have the same address, and an equally valid (and incompatible) expectation would be that the same string literal would be used for every expansion of a given default argument in a single translation unit.

Notes from April 2003 meeting:

The core working group feels that the address of a string literal should be guaranteed to be the same only if it actually appears textually within the body of the inline function. So a string in a default argument expression in a block extern declaration inside the body of a function would be the same in all instances of the function. On the other hand, a string in a default argument expression in the header of the function (i.e., outside of the body) would not be the same.

Proposed resolution (April 2003):

Change the last sentence and add the note to the end of 9.2.3 [dcl.fct.spec] paragraph 4:

A string literal in the body of an extern inline function is the same object in different translation units. [Note: A string literal that is encountered only in the context of a function call (in the default argument expression of the called function), is not “in” the extern inline function.]

Notes from October 2003 meeting:

We discussed ctor-initializer lists and decided that they are also part of the body. We've asked Clark Nelson to work on syntax changes to give us a syntax term for the body of a function so we can refer to it here. See also issue 452, which could use this term.

(October, 2005: moved to “review” status in concert with issue 452. With that resolution, the wording above needs no further changes.)

Proposed resolution (April, 2006):

Change the last sentence and add the note to the end of 9.2.3 [dcl.fct.spec] paragraph 4:

A string literal in the body of an extern inline function is the same object in different translation units. [Note: A string literal appearing in a default argument expression is not considered to be “in the body” of an inline function merely by virtue of the expression’s use in a function call from that inline function. —end note]



477. Can virtual appear in a friend declaration?

Section: 9.2.3  [dcl.fct.spec]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 23 Sep 2004

[Voted into WP at the October, 2006 meeting.]

I couldn't find wording that makes it invalid to say friend virtual... The closest seems to be 9.2.3 [dcl.fct.spec] paragraph 5, which says:

The virtual specifier shall only be used in declarations of nonstatic class member functions that appear within a member-specification of a class definition; see 11.7.3 [class.virtual].

I don't think that excludes a friend declaration (which is a valid member-specification by 11.4 [class.mem]).

John Spicer: I agree that virtual should not be allowed on friend declarations. I think the wording in 9.2.3 [dcl.fct.spec] is intended to be the declaration of a function within its class, although I think the wording should be improved to make it clearer.

Proposed resolution (October, 2005):

Change 9.2.3 [dcl.fct.spec] paragraphs 5-6 as indicated:

The virtual specifier shall only be used only in declarations the initial declaration of a non-static class member functions that appear within a member-specification of a class definition function; see 11.7.3 [class.virtual].

The explicit specifier shall be used only in declarations the declaration of constructors a constructor within a its class definition; see 11.4.8.2 [class.conv.ctor].




424. Wording problem with issue 56 resolution on redeclaring typedefs in class scope

Section: 9.2.4  [dcl.typedef]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 25 June 2003

[Voted into WP at March 2004 meeting.]

I wonder if perhaps the core issue 56 change in 9.2.4 [dcl.typedef] paragraph 2 wasn't quite careful enough. The intent was to remove the allowance for:

  struct S {
    typedef int I;
    typedef int I;
  };

but I think it also disallows the following:

  class B {
    typedef struct A {} A;
    void f(struct B::A*p);
  };

See also issue 407.

Proposed resolution (October 2003):

At the end of 9.2.4 [dcl.typedef] paragraph 2, add the following:

In a given class scope, a typedef specifier can be used to redefine any class-name declared in that scope that is not also a typedef-name to refer to the type to which it already refers. [Example:
  struct S {
    typedef struct A {} A;  // OK
    typedef struct B B;     // OK
    typedef A A;            // error
  };
]



647. Non-constexpr instances of constexpr constructor templates

Section: 9.2.6  [dcl.constexpr]     Status: CD1     Submitter: Mike Miller     Date: 12 Aug 2007

[Voted into the WP at the September, 2008 meeting.]

According to 9.2.6 [dcl.constexpr] paragraph 5,

If the instantiated template specialization of a constexpr function template would fail to satisfy the requirements for a constexpr function, the constexpr specifier is ignored and the specialization is not a constexpr function.

One would expect to see a similar provision for an instantiated constructor template (because the requirements for a constexpr function [paragraph 3] are different from the requirements for a constexpr constructor [paragraph 4]), but there is none; constexpr constructor templates are not mentioned.

Suggested resolution:

Change the wording of 9.2.6 [dcl.constexpr] paragraph 5 as indicated:

If the instantiated template specialization of a constexpr function template would fail to satisfy the requirements for a constexpr function or constexpr constructor, as appropriate to the function template, the constexpr specifier is ignored and the specialization is not a constexpr function or constexpr constructor.

Proposed resolution (June, 2008):

[Drafting note: This resolution goes beyond the problem described in the issue discussion, which is one aspect of the general failure of the existing wording to deal consistently with the distinctions between constexpr functions and constexpr constructors. The wording below attempts to rectify that problem systematically.]

  1. Change 9.2.6 [dcl.constexpr] paragraph 2 as follows:

  2. A constexpr specifier used in a function declaration the declaration of a function that is not a constructor declares that function to be a constexpr function. Similarly, a constexpr specifier used in a constructor declaration declares that constructor to be a constexpr constructor. Constexpr functions and constexpr constructors are implicitly inline (9.2.3 [dcl.fct.spec]). A constexpr function shall not be virtual (10.3).
  3. Change 9.2.6 [dcl.constexpr] paragraph 3 as follows:

  4. The definition of a constexpr function shall satisfy the following constraints:

    [Example:...

  5. Change 9.2.6 [dcl.constexpr] paragraph 4 as follows:

  6. The definition of a constexpr constructor shall satisfy the following constraints:

    A trivial copy constructor is also a constexpr constructor. [Example: ...

  7. Change 9.2.6 [dcl.constexpr] paragraph 5 as follows:

  8. If the instantiated template specialization of a constexpr function template would fail to satisfy the requirements for a constexpr function or constexpr constructor, the constexpr specifier is ignored and the specialization is not a constexpr function.
  9. Change 9.2.6 [dcl.constexpr] paragraph 6 as follows:

  10. A constexpr specifier used in for a non-static member function definition that is not a constructor declares that member function to be const (11.4.3 [class.mfct.non.static]). [Note: ...



648. Constant expressions in constexpr initializers

Section: 9.2.6  [dcl.constexpr]     Status: CD1     Submitter: Mike Miller     Date: 12 Aug 2007

[Voted into the WP at the September, 2008 meeting.]

The current wording of 9.2.6 [dcl.constexpr] paragraph 7 seems not quite correct. It reads,

A constexpr specifier used in an object declaration declares the object as const. Such an object shall be initialized, and every expression that appears in its initializer (9.4 [dcl.init]) shall be a constant expression.

The phrase “every expression” is intended to cover multiple arguments to a constexpr constructor and multiple expressions in an aggregate initializer. However, it could be read (incorrectly) as saying that non-constant expressions cannot appear as subexpressions in such initializers, even in places where they do not render the full-expression non-constant (i.e., as unevaluated operands and in the unselected branches of &&, ||, and ?:). Perhaps this problem could be remedied by replacing “every expression” with “every full-expression?”

Proposed resolution (June, 2008):

Change 9.2.6 [dcl.constexpr] paragraph 7 as follows:

A constexpr specifier used in an object declaration declares the object as const. Such an object shall be initialized, and every expression that appears in its initializer (8.5) initialized. If it is initialized by a constructor call, the constructor shall be a constexpr constructor and every argument to the constructor shall be a constant expression. Otherwise, every full-expression that appears in its initializer shall be a constant expression. Every implicit conversion used...



283. Template type-parameters are not syntactically type-names

Section: 9.2.9.3  [dcl.type.simple]     Status: CD1     Submitter: Clark Nelson     Date: 01 May 2001

[Voted into WP at April 2003 meeting.]

Although 13.2 [temp.param] paragraph 3 contains an assertion that

A type-parameter defines its identifier to be a type-name (if declared with class or typename)

the grammar in 9.2.9.3 [dcl.type.simple] paragraph 1 says that a type-name is either a class-name, an enum-name, or a typedef-name. The identifier in a template type-parameter is none of those. One possibility might be to equate the identifier with a typedef-name instead of directly with a type-name, which would have the advantage of not requiring parallel treatment of the two in situations where they are treated the same (e.g., in elaborated-type-specifiers, see issue 245). See also issue 215.

Proposed resolution (Clark Nelson, March 2002):

In 13.2 [temp.param] paragraph 3, change "A type-parameter defines its identifier to be a type-name" to "A type-parameter defines its identifier to be a typedef-name"

In 9.2.9.5 [dcl.type.elab] paragraph 2, change "If the identifier resolves to a typedef-name or a template type-parameter" to "If the identifier resolves to a typedef-name".

This has been consolidated with the edits for some other issues. See N1376=02-0034.




516. Use of signed in bit-field declarations

Section: 9.2.9.3  [dcl.type.simple]     Status: CD1     Submitter: comp.std.c++     Date: 25 Apr 2005

[Voted into WP at the October, 2006 meeting.]

9.2.9.3 [dcl.type.simple] paragraph 3 reads,

It is implementation-defined whether bit-fields and objects of char type are represented as signed or unsigned quantities. The signed specifier forces char objects and bit-fields to be signed; it is redundant with other integral types.

The last sentence in that quote is misleading w.r.t. bit-fields. The first sentence in that quote is correct but incomplete.

Proposed fix: change the two sentences to read:

It is implementation-defined whether objects of char type are represented as signed or unsigned quantities. The signed specifier forces char objects signed; it is redundant with other integral types except when declaring bit-fields (11.4.10 [class.bit]).

Proposed resolution (October, 2005):

Change 9.2.9.3 [dcl.type.simple] paragraph 3 as indicated:

When multiple simple-type-specifiers are allowed, they can be freely intermixed with other decl-specifiers in any order. [Note: It is implementation-defined whether bit-fields and objects of char type and certain bit-fields (11.4.10 [class.bit]) are represented as signed or unsigned quantities. The signed specifier forces bit-fields and char objects and bit-fields to be signed; it is redundant with other integral types in other contexts. end note]



651. Problems in decltype specification and examples

Section: 9.2.9.3  [dcl.type.simple]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 16 Aug 2007

[Voted into the WP at the September, 2008 meeting.]

The second bullet of 9.2.9.3 [dcl.type.simple] paragraph 4 reads,

The reference to “that function” is imprecise; it is not the actual function called at runtime but the statically chosen function (ignoring covariant return types in virtual functions).

Also, the examples in this paragraph have errors:

  1. The declaration of struct A should end with a semicolon.

  2. The lines of the form decltype(...); are ill-formed; they need a declarator.

Proposed Resolution (October, 2007):

Change 9.2.9.3 [dcl.type.simple] paragraph 4 as follows:

The type denoted by decltype(e) is defined as follows:

The operand of the decltype specifier is an unevaluated operand (Clause 7 [expr]).

[Example:

    const int&& foo();
    int i;
    struct A { double x; };
    const A* a = new A();
    decltype(foo()) x1;      // type is const int&&
    decltype(i) x2;          // type is int
    decltype(a->x) x3;       // type is double
    decltype((a->x)) x4;     // type is const double&

end example]




629. auto parsing ambiguity

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 14 March 2007

[Voted into the WP at the February, 2008 meeting as paper J16/08-0056 = WG21 N2546.]

We've found an interesting parsing ambiguity with the new meaning of auto. Consider:

    typedef int T;
    void f() {
        auto T = 42;  // Valid or not?
    }

The question here is whether T should be a type specifier or a storage class? 9.2.9.7 [dcl.spec.auto] paragraph 1 says,

The auto type-specifier has two meanings depending on the context of its use. In a decl-specifier-seq that contains at least one type-specifier (in addition to auto) that is not a cv-qualifier, the auto type-specifier specifies that the object named in the declaration has automatic storage duration.

In this case, T is a type-specifier, so the declaration is ill-formed: there is no declarator-id. Many, however, would like to see auto work “just like int,” i.e., forcing T to be redeclared in the inner scope. Concerns cited included hijacking of the name in templates and inline function bodies over the course of time if a program revision introduces a type with that name in the surrounding context. Although it was pointed out that enclosing the name in parentheses in the inner declaration would prevent any such problems, this was viewed as unacceptably ugly.

Notes from the April, 2007 meeting:

The CWG wanted to avoid a rule like, “if auto can be a type-specifier, it is” (similar to the existing “if it can be a declaration, it is” rule) because of the lookahead and backtracking difficulties such an approach would pose for certain kinds of parsing techniques. It was noted that the difficult lookahead cases all involve parentheses, which would not be a problem if only the “=” form of initializer were permitted in auto declarations; only very limited lookahead is required in that case. It was also pointed out that the “if it can be a type-specifier, it is” approach results in a quiet change of meaning for cases like

    typedef int T;
    int n = 3;
    void f() {
        auto T(n);
    }

This currently declares n to be an int variable in the inner scope but would, under the full lookahead approach, declare T to be a variable, quitely changing uses of n inside f() to refer to the outer variable.

The consensus of the CWG was to pursue the change to require the “=” form of initializer for auto.

Notes from the July, 2007 meeting:

See paper J16/07-0197 = WG21 N2337. There was no consensus among the CWG for either of the approaches recommended in the paper; additional input and direction is required.




686. Type declarations/definitions in type-specifier-seqs and type-ids

Section: 9.3.2  [dcl.name]     Status: CD1     Submitter: Jens Maurer     Date: 21 March, 2008

[Voted into the WP at the September, 2008 meeting.]

The restrictions on declaring and/or defining classes inside type-specifier-seqs and type-ids are inconsistent throughout the Standard. This is probably due to the fact that nearly all of the sections that deal with them attempt to state the restriction afresh. There are three cases:

  1. 7.6.2.8 [expr.new], 8.5 [stmt.select], and 11.4.8.3 [class.conv.fct] prohibit “declarations” of classes and enumerations. That means that

        while (struct C* p = 0) ;
    

    is ill-formed unless a prior declaration of C has been seen. These appear to be cases that should have been fixed by issue 379, changing “class declaration” to “class definition,” but were overlooked.

  2. 7.5.5 [expr.prim.lambda], 9.1 [dcl.pre], and 9.3.4.6 [dcl.fct] (late-specified return types) do not contain any restriction at all.

  3. All the remaining cases prohibit “type definitions,” apparently referring to classes and enumerations.

Suggested resolution:

Add something like, “A class or enumeration shall not be defined in a type-specifier-seq or in a type-id,” to a single place in the Standard and remove all other mentions of that restriction (allowing declarations via elaborated-type-specifier).

Mike Miller:

An alias-declaration is just a different syntax for a typedef declaration, which allows definitions of a class in the type; I would expect the same to be true of an alias-declaration. I don't have any particularly strong attachment to allowing a class definition in an alias-declaration. My only concern is introducing an irregularity into what are currently exact-match semantics with typedefs.

There's a parallel restriction in many (but not all?) of these places on typedef declarations.

Jens Maurer:

Those are redundant, as typedef is not a type-specifier, and should be removed as well.

Proposed resolution (March, 2008):

  1. Delete the indicated words from 7.6.1.7 [expr.dynamic.cast] paragraph 1:

  2. ...Types shall not be defined in a dynamic_cast....
  3. Delete the indicated words from 7.6.1.8 [expr.typeid] paragraph 4:

  4. ...Types shall not be defined in the type-id....
  5. Delete the indicated words from 7.6.1.9 [expr.static.cast] paragraph 1:

  6. ...Types shall not be defined in a static_cast....
  7. Delete the indicated words from 7.6.1.10 [expr.reinterpret.cast] paragraph 1:

  8. ...Types shall not be defined in a reinterpret_cast....
  9. Delete the indicated words from 7.6.1.11 [expr.const.cast] paragraph 1:

  10. ...Types shall not be defined in a const_cast....
  11. Delete paragraph 5 of 7.6.2.5 [expr.sizeof]:

  12. Types shall not be defined in a sizeof expression.
  13. Delete paragraph 5 of 7.6.2.8 [expr.new]:

  14. The type-specifier-seq shall not contain class declarations, or enumeration declarations.
  15. Delete paragraph 4 of 7.6.2.6 [expr.alignof]:

  16. A type shall not be defined in an alignof expression.
  17. Delete paragraph 3 of 7.6.3 [expr.cast]:

  18. Types shall not be defined in casts.
  19. Delete the indicated words from 8.5 [stmt.select] paragraph 2:

  20. ...The type-specifier-seq shall not contain typedef and shall not declare a new class or enumeration....
  21. Add the indicated words to 9.2.9 [dcl.type] paragraph 3:

  22. At least one type-specifier that is not a cv-qualifier is required in a declaration unless it declares a constructor, destructor or conversion function. [Footnote: ... ] A type-specifier-seq shall not define a class or enumeration unless it appears in the type-id of an alias-declaration (9.2.4 [dcl.typedef]).
  23. Delete the indicated words from 11.4.8.3 [class.conv.fct] paragraph 1:

  24. ...Classes, enumerations, and typedef-names shall not be declared in the type-specifier-seq....
  25. Delete the indicated words from 14.4 [except.handle] paragraph 1:

  26. ...Types shall not be defined in an exception-declaration.
  27. Delete paragraph 6 of 14.5 [except.spec]:

  28. Types shall not be defined in exception-specifications.

[Drafting note: no changes are required to 7.5.5 [expr.prim.lambda], 9.2.4 [dcl.typedef], 9.12.2 [dcl.align], 9.7.1 [dcl.enum], 9.3.4.6 [dcl.fct], 13.2 [temp.param], or 13.3 [temp.names].]




160. Missing std:: qualification

Section: 9.3.3  [dcl.ambig.res]     Status: CD1     Submitter: Al Stevens     Date: 23 Aug 1999

[Moved to DR at 10/01 meeting.]

9.3.3 [dcl.ambig.res] paragraph 3 shows an example that includes <cstddef> with no using declarations or directives and refers to size_t without the std:: qualification.

Many references to size_t throughout the document omit the std:: namespace qualification.

This is a typical case. The use of std:: is inconsistent throughout the document.

In addition, the use of exception specifications should be examined for consistency.

(See also issue 282.)

Proposed resolution:

In 6.9.1 [intro.execution] paragraph 9, replace all two instances of "sig_atomic_t" by "std::sig_atomic_t".

In 6.2 [basic.def] paragraph 4, replace all three instances of "string" by "std::string" in the example and insert "#include <string>" at the beginning of the example code.

In 6.9.3.1 [basic.start.main] paragraph 4, replace

Calling the function
void exit(int);
declared in <cstdlib>...

by

Calling the function std::exit(int) declared in <cstdlib>...

and also replace "exit" by "std::exit" in the last sentence of that paragraph.

In 6.9.3.1 [basic.start.main] first sentence of paragraph 5, replace "exit" by "std::exit".

In 6.9.3.2 [basic.start.static] paragraph 4, replace "terminate" by "std::terminate".

In 6.9.3.3 [basic.start.dynamic] paragraph 1, replace "exit" by "std::exit" (see also issue 28).

In 6.9.3.3 [basic.start.dynamic] paragraph 3, replace all three instances of "atexit" by "std::atexit" and both instances of "exit" by "std::exit" (see also issue 28).

In 6.9.3.3 [basic.start.dynamic] paragraph 4, replace

Calling the function
void abort();
declared in <cstdlib>...

by

Calling the function std::abort() declared in <cstdlib>...
and "atexit" by "std::atexit" (see also issue 28).

In 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 1 third sentence, replace "size_t" by "std::size_t".

In 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 3, replace "new_handler" by "std::new_handler". Furthermore, replace "set_new_handler" by "std::set_new_handler" in the note.

In 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 4, replace "type_info" by "std::type_info" in the note.

In 6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 3, replace all four instances of "size_t" by "std::size_t".

In 6.7.3 [basic.life] paragraph 5, replace "malloc" by "std::malloc" in the example code and insert "#include <cstdlib>" at the beginning of the example code.

In 6.8 [basic.types] paragraph 2, replace "memcpy" by "std::memcpy" (the only instance in the footnote and both instances in the example) and replace "memmove" by "std::memmove" in the footnote (see also issue 43).

In 6.8 [basic.types] paragraph 3, replace "memcpy" by "std::memcpy", once in the normative text and once in the example (see also issue 43).

In 6.8.2 [basic.fundamental] paragraph 8 last sentence, replace "numeric_limits" by "std::numeric_limits".

In 7.6.1.7 [expr.dynamic.cast] paragraph 9 second sentence, replace "bad_cast" by "std::bad_cast".

In 7.6.1.8 [expr.typeid] paragraph 2, replace "type_info" by "std::type_info" and "bad_typeid" by "std::bad_typeid".

In 7.6.1.8 [expr.typeid] paragraph 3, replace "type_info" by "std::type_info".

In 7.6.1.8 [expr.typeid] paragraph 4, replace both instances of "type_info" by "std::type_info".

In 7.6.2.5 [expr.sizeof] paragraph 6, replace both instances of "size_t" by "std::size_t".

In 7.6.2.8 [expr.new] paragraph 11 last sentence, replace "size_t" by "std::size_t".

In 7.6.6 [expr.add] paragraph 6, replace both instances of "ptrdiff_t" by "std::ptrdiff_t".

In 7.6.6 [expr.add] paragraph 8, replace "ptrdiff_t" by "std::ptrdiff_t".

In 8.7 [stmt.jump] paragraph 2, replace "exit" by "std::exit" and "abort" by "std::abort" in the note.

In 9.3.3 [dcl.ambig.res] paragraph 3, replace "size_t" by "std::size_t" in the example.

In 9.5 [dcl.fct.def] paragraph 5, replace "printf" by "std::printf" in the note.

In 11.4.7 [class.dtor] paragraph 13, replace "size_t" by "std::size_t" in the example.

In 11.4.11 [class.free] paragraph 2, replace all four instances of "size_t" by "std::size_t" in the example.

In 11.4.11 [class.free] paragraph 6, replace both instances of "size_t" by "std::size_t" in the example.

In 11.4.11 [class.free] paragraph 7, replace all four instances of "size_t" by "std::size_t" in the two examples.

In 11.9.5 [class.cdtor] paragraph 4, replace "type_info" by "std::type_info".

In 12.5 [over.built] paragraph 13, replace all five instances of "ptrdiff_t" by "std::ptrdiff_t".

In 12.5 [over.built] paragraph 14, replace "ptrdiff_t" by "std::ptrdiff_t".

In 12.5 [over.built] paragraph 21, replace both instances of "ptrdiff_t" by "std::ptrdiff_t".

In 13.3 [temp.names] paragraph 4, replace both instances of "size_t" by "std::size_t" in the example. (The example is quoted in issue 96.)

In 13.4 [temp.arg] paragraph 1, replace "complex" by "std::complex", once in the example code and once in the comment.

In 13.9.4 [temp.expl.spec] paragraph 8, issue 24 has already corrected the example.

In 14.2 [except.throw] paragraph 6, replace "uncaught_exception" by "std::uncaught_exception".

In 14.2 [except.throw] paragraph 7, replace "terminate" by "std::terminate" and both instances of "unexpected" by "std::unexpected".

In 14.2 [except.throw] paragraph 8, replace "terminate" by "std::terminate".

In 14.3 [except.ctor] paragraph 3, replace "terminate" by "std::terminate".

In 14.4 [except.handle] paragraph 9, replace "terminate" by "std::terminate".

In 14.5 [except.spec] paragraph 8, replace "unexpected" by "std::unexpected".

In 14.5 [except.spec] paragraph 9, replace "unexpected" by "std::unexpected" and "terminate" by "std::terminate".

In 14.6 [except.special] paragraph 1, replace "terminate" by "std::terminate" and "unexpected" by "std::unexpected".

In the heading of 14.6.2 [except.terminate], replace "terminate" by "std::terminate".

In 14.6.2 [except.terminate] paragraph 1, footnote in the first bullet, replace "terminate" by "std::terminate". In the same paragraph, fifth bullet, replace "atexit" by "std::atexit". In the same paragraph, last bullet, replace "unexpected_handler" by "std::unexpected_handler".

In 14.6.2 [except.terminate] paragraph 2, replace

In such cases,
void terminate();
is called...

by

In such cases, std::terminate() is called...

and replace all three instances of "terminate" by "std::terminate".

In the heading of _N4606_.15.5.2 [except.unexpected], replace "unexpected" by "std::unexpected".

In _N4606_.15.5.2 [except.unexpected] paragraph 1, replace

...the function
void unexpected();
is called...

by

...the function std::unexpected() is called...
.

In _N4606_.15.5.2 [except.unexpected] paragraph 2, replace "unexpected" by "std::unexpected" and "terminate" by "std::terminate".

In _N4606_.15.5.2 [except.unexpected] paragraph 3, replace "unexpected" by "std::unexpected".

In the heading of 14.6.3 [except.uncaught], replace "uncaught_exception" by "std::uncaught_exception".

In 14.6.3 [except.uncaught] paragraph 1, replace

The function
bool uncaught_exception()
returns true...

by

The function std::uncaught_exception() returns true...
.

In the last sentence of the same paragraph, replace "uncaught_exception" by "std::uncaught_exception".




112. Array types and cv-qualifiers

Section: 9.3.4.5  [dcl.array]     Status: CD1     Submitter: Steve Clamage     Date: 4 May 1999

[Moved to DR at 10/01 meeting.]

Steve Clamage: Section 9.3.4.5 [dcl.array] paragraph 1 reads in part as follows:

Any type of the form "cv-qualifier-seq array of N T" is adjusted to "array of N cv-qualifier-seq T," and similarly for "array of unknown bound of T." [Example:
    typedef int A[5], AA[2][3];
    typedef const A CA;     // type is "array of 5 const int"
    typedef const AA CAA;   // type is "array of 2 array of 3 const int"
end example] [Note: an "array of N cv-qualifier-seq T" has cv-qualified type; such an array has internal linkage unless explicitly declared extern (9.2.9.2 [dcl.type.cv] ) and must be initialized as specified in 9.4 [dcl.init] . ]
The Note appears to contradict the sentence that precedes it.

Mike Miller: I disagree; all it says is that whether the qualification on the element type is direct ("const int x[5]") or indirect ("const A CA"), the array itself is qualified in the same way the elements are.

Steve Clamage: In addition, section 6.8.5 [basic.type.qualifier] paragraph 2 says:

A compound type (6.8.4 [basic.compound] ) is not cv-qualified by the cv-qualifiers (if any) of the types from which it is compounded. Any cv-qualifiers applied to an array type affect the array element type, not the array type (9.3.4.5 [dcl.array] )."
The Note appears to contradict that section as well.

Mike Miller: Yes, but consider the last two sentences of 6.8.5 [basic.type.qualifier] paragraph 5:

Cv-qualifiers applied to an array type attach to the underlying element type, so the notation "cv T," where T is an array type, refers to an array whose elements are so-qualified. Such array types can be said to be more (or less) cv-qualified than other types based on the cv-qualification of the underlying element types.
I think this says essentially the same thing as 9.3.4.5 [dcl.array] paragraph 1 and its note: the qualification of an array is (bidirectionally) equivalent to the qualification of its members.

Mike Ball: I find this a very far reach. The text in 9.3.4.5 [dcl.array] is essentially that which is in the C standard (and is a change from early versions of C++). I don't see any justification at all for the bidirectional equivalence. It seems to me that the note is left over from the earlier version of the language.

Steve Clamage: Finally, the Note seems to say that the declaration

    volatile char greet[6] = "Hello";
gives "greet" internal linkage, which makes no sense.

Have I missed something, or should that Note be entirely removed?

Mike Miller: At least the wording in the note should be repaired not to indicate that volatile-qualification gives an array internal linkage. Also, depending on how the discussion goes, either the wording in 6.8.5 [basic.type.qualifier] paragraph 2 or in paragraph 5 needs to be amended to be consistent regarding whether an array type is considered qualified by the qualification of its element type.

Steve Adamczyk pointed out that the current state of affairs resulted from the need to handle reference binding consistently. The wording is intended to define the question, "Is an array type cv-qualified?" as being equivalent to the question, "Is the element type of the array cv-qualified?"

Proposed resolution (10/00):

Replace the portion of the note in 9.3.4.5 [dcl.array] paragraph 1 reading

such an array has internal linkage unless explicitly declared extern (9.2.9.2 [dcl.type.cv]) and must be initialized as specified in 9.4 [dcl.init].

with

see 6.8.5 [basic.type.qualifier].



140. Agreement of parameter declarations

Section: 9.3.4.6  [dcl.fct]     Status: CD1     Submitter: Steve Clamage     Date: 15 Jul 1999

[Moved to DR at 10/01 meeting.]

9.3.4.6 [dcl.fct] paragraph 3 says,

All declarations for a function with a given parameter list shall agree exactly both in the type of the value returned and in the number and type of parameters.
It is not clear what this requirement means with respect to a pair of declarations like the following:
    int f(const int);
    int f(int x) { ... }
Do they violate this requirement? Is x const in the body of the function declaration?

Tom Plum: I think the FDIS quotation means that the pair of decls are valid. But it doesn't clearly answer whether x is const inside the function definition. As to intent, I know the intent was that if the function definition wants to specify that x is const, the const must appear specifically in the defining decl, not just on some decl elsewhere. But I can't prove that intent from the drafted words.

Mike Miller: I think the intent was something along the following lines:

Two function declarations denote the same entity if the names are the same and the function signatures are the same. (Two function declarations with C language linkage denote the same entity if the names are the same.) All declarations of a given function shall agree exactly both in the type of the value returned and in the number and type of parameters; the presence or absence of the ellipsis is considered part of the signature.
(See 6.6 [basic.link] paragraph 9. That paragraph talks about names in different scopes and says that function references are the same if the "types are identical for purposes of overloading," i.e., the signatures are the same. See also 9.11 [dcl.link] paragraph 6 regarding C language linkage, where only the name is required to be the same for declarations in different namespaces to denote the same function.)

According to this paragraph, the type of a parameter is determined by considering its decl-specifier-seq and declarator and then applying the array-to-pointer and function-to-pointer adjustments. The cv-qualifier and storage class adjustments are performed for the function type but not for the parameter types.

If my interpretation of the intent of the second sentence of the paragraph is correct, the two declarations in the example violate that restriction — the parameter types are not the same, even though the function types are. Since there's no dispensation mentioned for "no diagnostic required," an implementation presumably must issue a diagnostic in this case. (I think "no diagnostic required" should be stated if the declarations occur in different translation units — unless there's a blanket statement to that effect that I have forgotten?)

(I'd also note in passing that, if my interpretation is correct,

    void f(int);
    void f(register int) { }
is also an invalid pair of declarations.)

Proposed resolution (10/00):

  1. In Clause 3 [intro.defs] “signature,” change "the types of its parameters" to "its parameter-type-list (9.3.4.6 [dcl.fct])".

  2. In the third bullet of 6.6 [basic.link] paragraph 9 change "the function types are identical for the purposes of overloading" to "the parameter-type-lists of the functions (9.3.4.6 [dcl.fct]) are identical."

  3. In the sub-bullets of the third bullet of 7.6.1.5 [expr.ref] paragraph 4, change all four occurrences of "function of (parameter type list)" to "function of parameter-type-list."

  4. In 9.3.4.6 [dcl.fct] paragraph 3, change

    All declarations for a function with a given parameter list shall agree exactly both in the type of the value returned and in the number and type of parameters; the presence or absence of the ellipsis is considered part of the function type.
    to
    All declarations for a function shall agree exactly in both the return type and the parameter-type-list.

  5. In 9.3.4.6 [dcl.fct] paragraph 3, change

    The resulting list of transformed parameter types is the function's parameter type list.
    to
    The resulting list of transformed parameter types and the presence or absence of the ellipsis is the function's parameter-type-list.

  6. In 9.3.4.6 [dcl.fct] paragraph 4, change "the parameter type list" to "the parameter-type-list."

  7. In the second bullet of _N4868_.12.2 [over.load] paragraph 2, change all occurrences of "parameter types" to "parameter-type-list."

  8. In 12.2 [over.match] paragraph 1, change "the types of the parameters" to "the parameter-type-list."

  9. In the last sub-bullet of the third bullet of 12.2.2.3 [over.match.oper] paragraph 3, change "parameter type list" to "parameter-type-list."

Note, 7 Sep 2001:

Editorial changes while putting in issue 147 brought up the fact that injected-class-name is not a syntax term and therefore perhaps shouldn't be written with hyphens. The same can be said of parameter-type-list.




262. Default arguments and ellipsis

Section: 9.3.4.6  [dcl.fct]     Status: CD1     Submitter: Jamie Schmeiser     Date: 13 Nov 2000

[Voted into WP at April 2003 meeting.]

The interaction of default arguments and ellipsis is not clearly spelled out in the current wording of the Standard. 9.3.4.7 [dcl.fct.default] paragraph 4 says,

In a given function declaration, all parameters subsequent to a parameter with a default argument shall have default arguments supplied in this or previous declarations.

Strictly speaking, ellipsis isn't a parameter, but this could be clearer. Also, in 9.3.4.6 [dcl.fct] paragraph 2,

If the parameter-declaration-clause terminates with an ellipsis, the number of arguments shall be equal to or greater than the number of parameters specified.

This could be interpreted to refer to the number of arguments after the addition of default arguments to the argument list given in the call expression, but again it could be clearer.

Notes from 04/01 meeting:

The consensus opinion was that an ellipsis is not a parameter and that default arguments should be permitted preceding an ellipsis.

Proposed Resolution (4/02):

Change the following sentence in 9.3.4.6 [dcl.fct] paragraph 2 from

If the parameter-declaration-clause terminates with an ellipsis, the number of arguments shall be equal to or greater than the number of parameters specified.

to

If the parameter-declaration-clause terminates with an ellipsis, the number of arguments shall be equal to or greater than the number of parameters that do not have a default argument.

As noted in the defect, section 9.3.4.7 [dcl.fct.default] is correct but could be clearer.

In 9.3.4.7 [dcl.fct.default], add the following as the first line of the example in paragraph 4.

  void g(int = 0, ...);  // okay, ellipsis is not a parameter so it can follow
                         // a parameter with a default argument



295. cv-qualifiers on function types

Section: 9.3.4.6  [dcl.fct]     Status: CD1     Submitter: Nathan Sidwell     Date: 29 Jun 2001

[Moved to DR at October 2002 meeting.]

This concerns the inconsistent treatment of cv qualifiers on reference types and function types. The problem originated with GCC bug report c++/2810. The bug report is available at http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view&pr=2810&database=gcc

9.3.4.3 [dcl.ref] describes references. Of interest is the statement (my emphasis)

Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef or of a template type argument, in which case the cv-qualifiers are ignored.

Though it is strange to ignore 'volatile' here, that is not the point of this defect report. 9.3.4.6 [dcl.fct] describes function types. Paragraph 4 states,

In fact, if at any time in the determination of a type a cv-qualified function type is formed, the program is ill-formed.

No allowance for typedefs or template type parameters is made here, which is inconsistent with the equivalent reference case.

The GCC bug report was template code which attempted to do,

    template <typename T> void foo (T const &);
    void baz ();
    ...
    foo (baz);

in the instantiation of foo, T is `void ()' and an attempt is made to const qualify that, which is ill-formed. This is a surprise.

Suggested resolution:

Replace the quoted sentence from paragraph 4 in 9.3.4.6 [dcl.fct] with

cv-qualified functions are ill-formed, except when the cv-qualifiers are introduced through the use of a typedef or of a template type argument, in which case the cv-qualifiers are ignored.

Adjust the example following to reflect this.

Proposed resolution (10/01):

In 9.3.4.6 [dcl.fct] paragraph 4, replace

The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type, i.e., it does not create a cv-qualified function type. In fact, if at any time in the determination of a type a cv-qualified function type is formed, the program is ill-formed. [Example:
  typedef void F();
  struct S {
    const F f;          // ill-formed
  };
-- end example]
by
The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. In the latter case, the cv-qualifiers are ignored. [Example:
  typedef void F();
  struct S {
    const F f;          // ok; equivalent to void f();
  };
-- end example]

Strike the last bulleted item in 13.10.3 [temp.deduct] paragraph 2, which reads

Attempting to create a cv-qualified function type.

Nathan Sidwell comments (18 Dec 2001 ): The proposed resolution simply states attempts to add cv qualification on top of a function type are ignored. There is no mention of whether the function type was introduced via a typedef or template type parameter. This would appear to allow

  void (const *fptr) ();
but, that is not permitted by the grammar. This is inconsistent with the wording of adding cv qualifiers to a reference type, which does mention typedefs and template parameters, even though
  int &const ref;
is also not allowed by the grammar.

Is this difference intentional? It seems needlessly confusing.

Notes from 4/02 meeting:

Yes, the difference is intentional. There is no way to add cv-qualifiers other than those cases.

Notes from April 2003 meeting:

Nathan Sidwell pointed out that some libraries use the inability to add const to a type T as a way of testing that T is a function type. He will get back to us if he has a proposal for a change.




681. Restrictions on declarators with late-specified return types

Section: 9.3.4.6  [dcl.fct]     Status: CD1     Submitter: Mike Miller     Date: 10 March, 2008

[Voted into the WP at the September, 2008 meeting as part of paper N2757.]

The wording added to 9.3.4.6 [dcl.fct] for declarators with late-specified return types says,

In a declaration T D where D has the form

and the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T,” T shall be the single type-specifier auto and the derived-declarator-type-list shall be empty.

These restrictions were intended to ensure that the return type of the function is exactly the specified type-id following the ->, not modified by declarator operators and cv-qualification.

Unfortunately, the requirement for an empty derived-declarator-type-list does not achieve this goal but instead forbids declarations like

    auto (*fp)() -> int;    // pointer to function returning int

while allowing declarations like

    auto *f() -> int;       // function returning pointer to int

The reason for this is that, according to the grammar in 9.3 [dcl.decl] paragraph 4, the declarator *f() -> int is parsed as a ptr-operator applied to the direct-declarator f() -> int; that is, the declarator D1 seen in 9.3.4.6 [dcl.fct] is just f, and the derived-declarator-type-list is thus empty.

By contrast, the declarator (*fp)() -> int is parsed as the direct-declarator (*fp) followed by the parameter-declaration-clause, etc. In this case, D1 in 9.3.4.6 [dcl.fct] is (*fp) and the derived-declarator-type-list is “pointer to,” i.e., not empty.

My personal view is that there is no reason to forbid the (*fp)() -> int form, and that doing so is problematic. For example, this restriction would require users desiring the late-specified return type syntax to write function parameters as function types and rely on parameter type transformations rather than writing them as pointer-to-function types, as they will actually turn out to be:

    void f(auto (*fp)() -> int);  // ill-formed
    void f(auto fp() -> int);     // OK (but icky)

It may be helpful in deciding whether to allow this form to consider the example of a function returning a pointer to a function. With the current restriction, only one of the three plausible forms is allowed:

    auto (*f())() -> int;           // Disallowed
    auto f() -> int (*)();          // Allowed
    auto f() -> auto (*)() -> int;  // Disallowed
Suggested resolution:
  1. Delete the words “and the derived-declarator-type-list shall be empty” from 9.3.4.6 [dcl.fct] paragraph 2.

  2. Add a new paragraph following 9.3 [dcl.decl] paragraph 4:

  3. A ptr-operator shall not be applied, directly or indirectly, to a function declarator with a late-specified return type (9.3.4.6 [dcl.fct]).

Proposed resolution (June, 2008):

  1. Change the grammar in 9.3 [dcl.decl] paragraph 4 as follows:

  2. Change the grammar in 9.3.2 [dcl.name] paragraph 1 as follows:

  3. Change 9.3.4.6 [dcl.fct] paragraph 2 as follows:

  4. ... T shall be the single type-specifier auto and the derived-declarator-type-list shall be empty. Then the type...
  5. Change all occurrences of direct-new-declarator in 7.6.2.8 [expr.new] to noptr-new-declarator. These changes appear in the grammar in paragraph 1 and in the text of paragraphs 6-8, as follows:

  6. When the allocated object is an array (that is, the direct-noptr-new-declarator syntax is used or the new-type-id or type-id denotes an array type), the new-expression yields a pointer to the initial element (if any) of the array. [Note: both new int and new int[10] have type int* and the type of new int[i][10] is int (*)[10]end note]

    Every constant-expression in a direct-noptr-new-declarator shall be an integral constant expression (7.7 [expr.const]) and evaluate to a strictly positive value. The expression in a direct-noptr-new-declarator shall be of integral type, enumeration type, or a class type for which a single non-explicit conversion function to integral or enumeration type exists (11.4.8 [class.conv]). If the expression is of class type, the expression is converted by calling that conversion function, and the result of the conversion is used in place of the original expression. If the value of the expression is negative, the behavior is undefined. [Example: given the definition int n = 42, new float[n][5] is well-formed (because n is the expression of a direct-noptr-new-declarator), but new float[5][n] is ill-formed (because n is not a constant expression). If n is negative, the effect of new float[n][5] is undefined. —end example]

    When the value of the expression in a direct-noptr-new-declarator is zero, the allocation function is called to allocate an array with no elements.




136. Default arguments and friend declarations

Section: 9.3.4.7  [dcl.fct.default]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 9 July 1999

[Moved to DR at 10/01 meeting.]

9.3.4.7 [dcl.fct.default] paragraph 4 says,

For non-template functions, default arguments can be added in later declarations of a function in the same scope. Declarations in different scopes have completely distinct sets of default arguments. That is, declarations in inner scopes do not acquire default arguments from declarations in outer scopes, and vice versa.
It is unclear how this wording applies to friend function declarations. For example,
    void f(int, int, int=0);             // #1
    class C {
        friend void f(int, int=0, int);  // #2
    };
    void f(int=0, int, int);             // #3
Does the declaration at #2 acquire the default argument from #1, and does the one at #3 acquire the default arguments from #2?

There are several related questions involved with this issue:

  1. Is the friend declaration in the scope of class C or in the surrounding namespace scope?

    Mike Miller: 9.3.4.7 [dcl.fct.default] paragraph 4 is speaking about the lexical location of the declaration... The friend declaration occurs in a different declarative region from the declaration at #1, so I would read [this paragraph] as saying that it starts out with a clean slate of default arguments.

    Bill Gibbons: Yes. It occurs in a different region, although it declares a name in the same region (i.e. a redeclaration). This is the same as with local externs and is intended to work the same way. We decided that local extern declarations cannot add (beyond the enclosing block) new default arguments, and the same should apply to friend declarations.

    John Spicer: The question is whether [this paragraph] does (or should) mean declarations that appear in the same lexical scope or declarations that declare names in the same scope. In my opinion, it really needs to be the latter. It seems somewhat paradoxical to say that a friend declaration declares a function in namespace scope yet the declaration in the class still has its own attributes. To make that work I think you'd have to make friends more like block externs that really do introduce a name into the scope in which the declaration is contained.

  2. Should default arguments be permitted in friend function declarations, and what effect should they have?

    Bill Gibbons: In the absence of a declaration visible in class scope to which they could be attached, default arguments on friend declarations do not make sense. [They should be] ill-formed, to prevent surprises.

    John Spicer: It is important that the following case work correctly:

            class X {
                    friend void f(X x, int i = 1){}
            };
    
            int main()
            {
                    X x;
                    f(x);
            }
    

    In other words, a function first declared in a friend declaration must be permitted to have default arguments and those default arguments must be usable when the function is found by argument dependent lookup. The reason that this is important is that it is common practice to define functions in friend declarations in templates, and that definition is the only place where the default arguments can be specified.

  3. What restrictions should be placed on default argument usage with friend declarations?

    John Spicer: We want to avoid instantiation side effects. IMO, the way to do this would be to prohibit a friend declaration from providing default arguments if a declaration of that function is already visible. Once a function has had a default specified in a friend declaration it should not be possible to add defaults in another declaration be it a friend or normal declaration.

    Mike Miller: The position that seems most reasonable to me is to allow default arguments in friend declarations to be used in Koenig lookup, but to say that they are completely unrelated to default arguments in declarations in the surrounding scope; and to forbid use of a default argument in a call if more than one declaration in the overload set has such a default, as in the proposed resolution for issue 1.

(See also issues 21, 95, 138, 139, 143, 165, and 166.)

Notes from 10/99 meeting:

Four possible outcomes were identified:

  1. If a friend declaration declares a default parameter, allow no other declarations of that function in the translation unit.
  2. Same as preceding, but only allow the friend declaration if it is also a definition.
  3. Disallow default arguments in friend declarations.
  4. Treat the default arguments in each friend declaration as a distinct set, causing an error if the call would be ambiguous.

The core group eliminated the first and fourth options from consideration, but split fairly evenly between the remaining two.

A straw poll of the full committee yielded the following results (given as number favoring/could live with/"over my dead body"):

  1. 0/14/5
  2. 8/13/5
  3. 11/7/14
  4. 7/10/9

Additional discussion is recorded in the "Record of Discussion" for the meeting, J16/99-0036 = WG21 N1212. See also paper J16/00-0040 = WG21 N1263.

Proposed resolution (10/00):

In 9.3.4.7 [dcl.fct.default], add following paragraph 4:

If a friend declaration specifies a default argument expression, that declaration must be a definition and shall be the only declaration of the function or function template in the translation unit.



5. CV-qualifiers and type conversions

Section: 9.4  [dcl.init]     Status: CD1     Submitter: Josee Lajoie     Date: unknown

[Moved to DR at 4/01 meeting.]

The description of copy-initialization in 9.4 [dcl.init] paragraph 14 says:

Should "destination type" in this last bullet refer to "cv-unqualified destination type" to make it clear that the destination type excludes any cv-qualifiers? This would make it clearer that the following example is well-formed:
     struct A {
       A(A&);
     };
     struct B : A { };

     struct C {
       operator B&();
     };

     C c;
     const A a = c; // allowed?

The temporary created with the conversion function is an lvalue of type B. If the temporary must have the cv-qualifiers of the destination type (i.e. const) then the copy-constructor for A cannot be called to create the object of type A from the lvalue of type const B. If the temporary has the cv-qualifiers of the result type of the conversion function, then the copy-constructor for A can be called to create the object of type A from the lvalue of type const B. This last outcome seems more appropriate.

Steve Adamczyk:

Because of late changes to this area, the relevant text is now the third sub-bullet of the fourth bullet of 9.4 [dcl.init] paragraph 14:

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated... The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the destination type. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization.

The issue still remains whether the wording should refer to "the cv-unqualified version of the destination type." I think it should.

Notes from 10/00 meeting:

The original example does not illustrate the remaining problem. The following example does:

    struct C { };
    C c;
    struct A {
        A(const A&);
        A(const C&);
    };
    const volatile A a = c;    // Okay

Proposed Resolution (04/01):

In 9.4 [dcl.init], paragraph 14, bullet 4, sub-bullet 3, change

if the function is a constructor, the call initializes a temporary of the destination type.

to

if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type.



78. Section 8.5 paragraph 9 should state it only applies to non-static objects

Section: 9.4  [dcl.init]     Status: CD1     Submitter: Judy Ward     Date: 15 Dec 1998

Paragraph 9 of 9.4 [dcl.init] says:

If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for an object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.
It should be made clear that this paragraph does not apply to static objects.

Proposed resolution (10/00): In 9.4 [dcl.init] paragraph 9, replace

Otherwise, if no initializer is specified for an object..."
with
Otherwise, if no initializer is specified for a non-static object...



177. Lvalues vs rvalues in copy-initialization

Section: 9.4  [dcl.init]     Status: CD1     Submitter: Steve Adamczyk     Date: 25 October 1999

[Moved to DR at 4/02 meeting.]

Is the temporary created during copy-initialization of a class object treated as an lvalue or an rvalue? That is, is the following example well-formed or not?

    struct B { };
    struct A {
        A(A&);    // not const
        A(const B&);
    };
    B b;
    A a = b;

According to 9.4 [dcl.init] paragraph 14, the initialization of a is performed in two steps. First, a temporary of type A is created using A::A(const B&). Second, the resulting temporary is used to direct-initialize a using A::A(A&).

The second step requires binding a reference to non-const to the temporary resulting from the first step. However, 9.4.4 [dcl.init.ref] paragraph 5 requires that such a reference be bound only to lvalues.

It is not clear from 7.2.1 [basic.lval] whether the temporary created in the process of copy-initialization should be treated as an lvalue or an rvalue. If it is an lvalue, the example is well-formed, otherwise it is ill-formed.

Proposed resolution (04/01):

  1. In 9.4 [dcl.init] paragraph 14, insert the following after "the call initializes a temporary of the destination type":

    The temporary is an rvalue.
  2. In 14.2 [except.throw] paragraph 3, replace

    The temporary is used to initialize the variable...

    with

    The temporary is an lvalue and is used to initialize the variable...

(See also issue 84.)




277. Zero-initialization of pointers

Section: 9.4  [dcl.init]     Status: CD1     Submitter: Andrew Sawyer     Date: 5 Apr 2001

[Moved to DR at 10/01 meeting.]

The intent of 9.4 [dcl.init] paragraph 5 is that pointers that are zero-initialized will contain a null pointer value. Unfortunately, the wording used,

...set to the value of 0 (zero) converted to T

does not match the requirements for creating a null pointer value given in 7.3.12 [conv.ptr] paragraph 1:

A null pointer constant is an integral constant expression (7.7 [expr.const]) rvalue of integer type that evaluates to zero. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type...

The problem is that the "value of 0" in the description of zero-initialization is not specified to be an integral constant expression. Nonconstant expressions can also have the value 0, and converting a nonconst 0 to pointer type need not result in a null pointer value.

Proposed resolution (04/01):

In 9.4 [dcl.init] paragraph 5, change

...set to the value 0 (zero) converted to T;

to

...set to the value 0 (zero), taken as an integral constant expression, converted to T; [footnote: as specified in 7.3.12 [conv.ptr], converting an integral constant expression whose value is 0 to a pointer type results in a null pointer value.]



302. Value-initialization and generation of default constructor

Section: 9.4  [dcl.init]     Status: CD1     Submitter: Steve Adamczyk     Date: 23 Jul 2001

[Moved to DR at October 2002 meeting.]

We've been looking at implementing value-initialization. At one point, some years back, I remember Bjarne saying that something like X() in an expression should produce an X object with the same value one would get if one created a static X object, i.e., the uninitialized members would be zero-initialized because the whole object is initialized at program startup, before the constructor is called.

The formulation for default-initialization that made it into TC1 (in 9.4 [dcl.init]) is written a little differently (see issue 178), but I had always assumed that it would still be a valid implementation to zero the whole object and then call the default constructor for the troublesome "non-POD but no user-written constructor" cases.

That almost works correctly, but I found a problem case:

    struct A {
      A();
      ~A();
    };
    struct B {
      // B is a non-POD with no user-written constructor.
      // It has a nontrivial generated constructor.
      const int i;
      A a;
    };
    int main () {
      // Value-initializing a "B" doesn't call the default constructor for
      // "B"; it value-initializes the members of B.  Therefore it shouldn't
      // cause an error on generation of the default constructor for the
      // following:
      new B();
    }

If the definition of the B::B() constructor is generated, an error is issued because the const member "i" is not initialized. But the definition of value-initialization doesn't require calling the constructor, and therefore it doesn't require generating it, and therefore the error shouldn't be detected.

So this is a case where zero-initializing and then calling the constructor is not equivalent to value-initializing, because one case generates an error and the other doesn't.

This is sort of unfortunate, because one doesn't want to generate all the required initializations at the point where the "()" initialization occurs. One would like those initializations to be packaged in a function, and the default constructor is pretty much the function one wants.

I see several implementation choices:

  1. Zero the object, then call the default generated constructor. This is not valid unless the standard is changed to say that the default constructor might be generated for value-initialization cases like the above (that is, it's implementation-dependent whether the constructor definition is generated). The zeroing operation can of course be optimized, if necessary, to hit only the pieces of the object that would otherwise be left uninitialized. An alternative would be to require generation of the constructor for value-initialization cases, even if the implementation technique doesn't call the constructor at that point. It's pretty likely that the constructor is going to have to be generated at some point in the program anyway.
  2. Make a new value-initialization "constructor," whose body looks a lot like the usual generated constructor, but which also zeroes other members. No errors would be generated while generating this modified constructor, because it generates code that does full initialization. (Actually, it wouldn't guarantee initialization of reference members, and that might be an argument for generating the constructor, in order to get that error.) This is standard-conforming, but it destroys object-code compatibility.
  3. Variation on (1): Zero first, and generate the object code for the default constructor when it's needed for value-initialization cases, but don't issue any errors at that time. Issue the errors only if it turns out the constructor is "really" referenced. Aside from the essential shadiness of this approach, I fear that something in the generation of the constructor will cause a template instantiation which will be an abservable side effect.

Personally, I find option 1 the least objectionable.

Proposed resolution (10/01):

Add the indicated wording to the third-to-last sentence of 6.3 [basic.def.odr] pararaph 2:

A default constructor for a class is used by default initialization or value initialization as specified in 9.4 [dcl.init].

Add a footnote to the indicated bullet in 9.4 [dcl.init] paragraph 5:

Add the indicated wording to the first sentence of 11.4.5 [class.ctor] paragraph 7:

An implicitly-declared default constructor for a class is implicitly defined when it is used (6.3 [basic.def.odr]) to create an object of its class type (6.7.2 [intro.object]).



509. Dead code in the specification of default initialization

Section: 9.4  [dcl.init]     Status: CD1     Submitter: Mike Miller     Date: 18 Mar 2005

[Voted into the WP at the September, 2008 meeting (resolution in paper N2762).]

The definition of default initialization (9.4 [dcl.init] paragraph 5) is:

However, default initialization is invoked only for non-POD class types and arrays thereof (7.6.2.8 [expr.new] paragraph 15 for new-expressions, 9.4 [dcl.init] paragraph 10 for top-level objects, and 11.9.3 [class.base.init] paragraph 4 for member and base class subobjects — but see issue 510). Consequently, all cases that invoke default initialization are handled by the first two bullets; the third bullet can never be reached. Its presence is misleading, so it should be removed.

Notes from the September, 2008 meeting:

The approach adopted in the resolution in paper N2762 was different from the suggestion above: it changes the definition of default initialization to include POD types and changes the third bullet to specify that “no initialization is performed.”




543. Value initialization and default constructors

Section: 9.4  [dcl.init]     Status: CD1     Submitter: Mike Miller     Date: 27 October 2005

[Voted into the WP at the September, 2008 meeting (resolution in paper N2762).]

The wording resulting from the resolution of issue 302 does not quite implement the intent of the issue. The revised wording of 6.3 [basic.def.odr] paragraph 2 is:

A default constructor for a class is used by default initialization or value initialization as specified in 9.4 [dcl.init].

This sounds as if 9.4 [dcl.init] specifies how and under what circumstances value initialization uses a default constructor (which was, in fact, the case for default initialization in the original wording). However, the normative text there makes it plain that value initialization does not call the default constructor (the permission granted to implementations to call the default constructor for value initialization is in a non-normative footnote).

The example that occasioned this observation raises an additional question. Consider:

    struct POD {
      const int x;
    };

    POD data = POD();

According to the (revised) resolution of issue 302, this code is ill-formed because the implicitly-declared default constructor will be implicitly defined as a result of being used by value initialization (11.4.5 [class.ctor] paragraph 7), and the implicitly-defined constructor fails to initialize a const-qualified member (11.9.3 [class.base.init] paragraph 4). This seems unfortunate, because the (trivial) default constructor of a POD class is otherwise not used — default initialization applies only to non-PODs — and it is not actually needed in value initialization. Perhaps value initialization should be defined to “use” the default constructor only for non-POD classes? If so, both of these problems would be resolved by rewording the above-referenced sentence of 6.3 [basic.def.odr] paragraph 2 as:

A default constructor for a non-POD class is used by default initialization or value initialization as specified in (9.4 [dcl.init]).

Notes from the April, 2006 meeting:

The approach favored by the CWG was to leave 6.3 [basic.def.odr] unchanged and to add normative wording to 9.4 [dcl.init] indicating that it is unspecified whether the default constructor is called.

Notes from the October, 2006 meeting:

The CWG now prefers that it should not be left unspecified whether programs of this sort are well- or ill-formed; instead, the Standard should require that the default constructor be defined in such cases. Three possibilities of implementing this decision were discussed:

  1. Change 6.3 [basic.def.odr] to state flatly that the default constructor is used by value initialization (removing the implication that 9.4 [dcl.init] determines the conditions under which it is used).

  2. Change 9.4 [dcl.init] to specify that non-union class objects with no user-declared constructor are value-initialized by first zero-initializing the object and then calling the (implicitly-defined) default constructor, replacing the current specification of value-initializing each of its sub-objects.

  3. Add a normative statement to 9.4 [dcl.init] that value-initialization causes the implicitly-declared default constructor to be implicitly defined, even if it is not called.

Proposed resolution (June, 2008):

Change the second bullet of the value-initialization definition in 9.4 [dcl.init] paragraph 5 as follows:

Notes from the September, 2008 meeting:

The resolution supplied in paper N2762 differs from the June, 2008 proposed resolution in that the implicitly-declared default constructor is only called (and thus defined) if it is non-trivial, making the struct POD example above well-formed.




430. Ordering of expression evaluation in initializer list

Section: 9.4.2  [dcl.init.aggr]     Status: CD1     Submitter: Nathan Sidwell     Date: 23 July 2003

[Voted into the WP at the April, 2007 meeting as part of paper J16/07-0099 = WG21 N2239.]

A recent GCC bug report ( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11633) asks about the validity of

  int count = 23;
  int foo[] = { count++, count++, count++ };
is this undefined or unspecified or something else? I can find nothing in 9.4.2 [dcl.init.aggr] that indicates whether the components of an initializer-list are evaluated in order or not, or whether they have sequence points between them.

6.7.8/23 of the C99 std has this to say

The order in which any side effects occur among the initialization list expressions is unspecified.
I think similar wording is needed in 9.4.2 [dcl.init.aggr]

Steve Adamczyk: I believe the standard is clear that each initializer expression in the above is a full-expression (6.9.1 [intro.execution]/12-13; see also issue 392) and therefore there is a sequence point after each expression (6.9.1 [intro.execution]/16). I agree that the standard does not seem to dictate the order in which the expressions are evaluated, and perhaps it should. Does anyone know of a compiler that would not evaluate the expressions left to right?

Mike Simons: Actually there is one, that does not do left to right: gcc/C++. None of the post increment operations take effect until after the statement finishes. So in the sample code gcc stores 23 into all positions in the array. The commercial vendor C++ compilers for AIX, Solaris, Tru64, HPUX (parisc and ia64), and Windows, all do sequence points at each ',' in the initializer list.




491. Initializers for empty-class aggregrate members

Section: 9.4.2  [dcl.init.aggr]     Status: CD1     Submitter: Nathan Sidwell     Date: 15 Dec 2004

[Voted into WP at April, 2007 meeting.]

The current wording of 9.4.2 [dcl.init.aggr] paragraph 8 requires that

An initializer for an aggregate member that is an empty class shall have the form of an empty initializer-list {}.

This is overly constraining. There is no reason that the following should be ill-formed:

    struct S { };
    S s;
    S arr[1] = { s };

Mike Miller: The wording of 9.4.2 [dcl.init.aggr] paragraph 8 is unclear. “An aggregate member” would most naturally mean “a member of an aggregate.” In context, however, I think it must mean “a member [of an aggregate] that is an aggregate”, that is, a subaggregate. Members of aggregates need not themselves be aggregates (cf paragraph 13 and 11.9.2 [class.expl.init]); it cannot be the case that an object of an empty class with a user-declared constructor must be initialized with {} when it is a member of an aggregate. This wording should be clarified, regardless of the decision on Nathan's point.

Proposed resolution (October, 2005):

This issue is resolved by the resolution of issue 413.




632. Brace-enclosed initializer for scalar member of aggregate

Section: 9.4.2  [dcl.init.aggr]     Status: CD1     Submitter: Greg Comeau     Date: 3 May 2007

[Voted into the WP at the June, 2008 meeting as part of paper N2672.]

C (both C90 and C99) appear to allow a declaration of the form

    struct S { int i; } s = { { 5 } };

in which the initializer of a scalar member of an aggregate can itself be brace-enclosed. The relevant wording from the C99 Standard is found in 6.7.8 paragraph 11:

The initializer for a scalar shall be a single expression, optionally enclosed in braces.

and paragraph 16:

Otherwise, the initializer for an object that has aggregate or union type shall be a brace-enclosed list of initializers for the elements or named members.

The “list of initializers” in paragraph 16 must be a recursive reference to paragraph 11 (that's the only place that describes how an initialized item gets its value from the initializer expression), which would thus make the “brace-enclosed” part of paragraph 11 apply to each of the initializers in the list in paragraph 16 as well.

This appears to be an incompatibility between C and C++: 9.4.2 [dcl.init.aggr] paragraph 11 says,

If the initializer-list begins with a left brace, then the succeeding comma-separated list of initializer-clauses initializes the members of a subaggregate....

which clearly leaves the impression that only a subaggregate may be initialized by a brace-enclosed initializer-clause.

Either the specification in 9.4.2 [dcl.init.aggr] should be changed to allow a brace-enclosed initializer of a scalar member of an aggregate, as in C, or this incompatibility should be listed in Appendix Clause Annex C [diff].

Notes from the July, 2007 meeting:

It was noted that implementations differ in their handling of this construct; however, the issue is long-standing and fairly obscure.

Notes from the October, 2007 meeting:

The initializer-list proposal will resolve this issue when it is adopted.




291. Overload resolution needed when binding reference to class rvalue

Section: 9.4.4  [dcl.init.ref]     Status: CD1     Submitter: Andrei Iltchenko     Date: 15 Jun 2001

[Voted into WP at October 2005 meeting.]

There is a place in the Standard where overload resolution is implied but the way that a set of candidate functions is to be formed is omitted. See below.

According to the Standard, when initializing a reference to a non-volatile const class type (cv1 T1) with an rvalue expression (cv2 T2) where cv1 T1 is reference compatible with cv2 T2, the implementation shall proceed in one of the following ways (except when initializing the implicit object parameter of a copy constructor) 9.4.4 [dcl.init.ref] bullet 5.2 sub-bullet 1:

While the first case is quite obvious, the second one is a bit unclear as it says "a constructor is called to copy the entire rvalue object into the temporary" without specifying how the temporary is created -- by direct-initialization or by copy-initialization? As stated in DR 152, this can make a difference when the copy constructor is declared as explicit. How should the set of candidate functions be formed? The most appropriate guess is that it shall proceed as per 12.2.2.4 [over.match.ctor].

Another detail worth of note is that in the draft version of the Standard as of 2 December 1996 the second bullet read:

J. Stephen Adamczyk replied that the reason for changing "a copy constructor" to "a constructor" was to allow for member template converting constructors.

However, the new wording is somewhat in conflict with the footnote #93 that says that when initializing the implicit object parameter of a copy constructor an implementation must eventually choose the first alternative (binding without copying) to avoid infinite recursion. This seems to suggest that a copy constructor is always used for initializing the temporary of type "cv1 T2".

Furthermore, now that the set of candidate functions is not limited to only the copy constructors of T2, there might be some unpleasant consequences. Consider a rather contrived sample below:

    int   * pi = ::new(std::nothrow) int;
    const std::auto_ptr<int>   & ri = std::auto_ptr<int>(pi);

In this example the initialization of the temporary of type '<TT>const std::auto_ptr<int>' (to which 'ri' is meant to be subsequently bound) doesn't fail, as it would had the approach with copy constructors been retained, instead, a yet another temporary gets created as the well-known sequence:

    std::auto_ptr<int>::operator std::auto_ptr_ref<int>()
    std::auto_ptr<int>(std::auto_ptr_ref<int>)

is called (assuming, of course, that the set of candidate functions is formed as per 12.2.2.4 [over.match.ctor]). The second temporary is transient and gets destroyed at the end of the initialization. I doubt that this is the way that the committee wanted this kind of reference binding to go.

Besides, even if the approach restricting the set of candidates to copy constructors is restored, it is still not clear how the initialization of the temporary (to which the reference is intended to be bound) is to be performed -- using direct-initialization or copy-initialization.

Another place in the Standard that would benefit from a similar clarification is the creation of an exception object, which is delineated in 14.2 [except.throw].

David Abrahams (February 2004): It appears, looking at core 291, that there may be a need to tighten up 9.4.4 [dcl.init.ref]/5.

Please see the attached example file, which demonstrates "move semantics" in C++98. Many compilers fail to compile test 10 because of the way 8.5.3/5 is interpreted. My problem with that interpretation is that test 20:

    typedef X const XC;
    sink2(XC(X()));
does compile. In other words, it *is* possible to construct the const temporary from the rvalue. IMO, that is the proper test.

8.5.3/5 doesn't demand that a "copy constructor" is used to copy the temporary, only that a constructor is used "to copy the temporary". I hope that when the language is tightened up to specify direct (or copy initialization), that it also unambiguously allows the enclosed test to compile. Not only is it, I believe, within the scope of reasonable interpretation of the current standard, but it's an incredibly important piece of functionality for library writers and users alike.

#include <iostream>
#include <cassert>

template <class T, class X>
struct enable_if_same
{
};

template <class X>
struct enable_if_same<X, X>
{
    typedef char type;
};

struct X
{
    static int cnt;  // count the number of Xs

    X()
      : id(++cnt)
      , owner(true)
    {
        std::cout << "X() #" << id << std::endl;
    }

    // non-const lvalue - copy ctor
    X(X& rhs)
      : id(++cnt)
      , owner(true)
    {
        std::cout << "copy #" << id << " <- #" << rhs.id << std::endl;
    }

    // const lvalue - T will be deduced as X const
    template <class T>
    X(T& rhs, typename enable_if_same<X const,T>::type = 0)
      : id(++cnt)
      , owner(true)
    {
        std::cout << "copy #" << id << " <- #" << rhs.id << " (const)" << std::endl;
    }

    ~X()
    {
        std::cout << "destroy #" << id << (owner?"":" (EMPTY)") << std::endl;
    }

 private:    // Move stuff
    struct ref { ref(X*p) : p(p) {} X* p; };

 public:    // Move stuff
    operator ref() {
        return ref(this);
    }

    // non-const rvalue
    X(ref rhs)
      : id(++cnt)
      , owner(rhs.p->owner)
    {
        std::cout << "MOVE #" << id << " <== #" << rhs.p->id << std::endl;
        rhs.p->owner = false;
        assert(owner);
    }

 private:   // Data members
    int id;
    bool owner;
};

int X::cnt;


X source()
{
    return X();
}

X const csource()
{
    return X();
}

void sink(X)
{
    std::cout << "in rvalue sink" << std::endl;
}

void sink2(X&)
{
    std::cout << "in non-const lvalue sink2" << std::endl;
}

void sink2(X const&)
{
    std::cout << "in const lvalue sink2" << std::endl;
}

void sink3(X&)
{
    std::cout << "in non-const lvalue sink3" << std::endl;
}

template <class T>
void tsink(T)
{
    std::cout << "in templated rvalue sink" << std::endl;
}

int main()
{
    std::cout << " ------ test 1, direct init from rvalue ------- " << std::endl;
#ifdef __GNUC__ // GCC having trouble parsing the extra parens
    X z2((0, X() ));
#else
    X z2((X()));
#endif

    std::cout << " ------ test 2, copy init from rvalue ------- " << std::endl;
    X z4 = X();

    std::cout << " ------ test 3, copy init from lvalue ------- " << std::endl;
    X z5 = z4;

    std::cout << " ------ test 4, direct init from lvalue ------- " << std::endl;
    X z6(z4);

    std::cout << " ------ test 5, construct const ------- " << std::endl;
    X const z7;

    std::cout << " ------ test 6, copy init from lvalue ------- " << std::endl;
    X z8 = z7;

    std::cout << " ------ test 7, direct init from lvalue ------- " << std::endl;
    X z9(z7);

    std::cout << " ------ test 8, pass rvalue by-value ------- " << std::endl;
    sink(source());

    std::cout << " ------ test 9, pass const rvalue by-value ------- " << std::endl;
    sink(csource());

    std::cout << " ------ test 10, pass rvalue by overloaded reference ------- " << std::endl;
    // This one fails in Comeau's strict mode due to 8.5.3/5.  GCC 3.3.1 passes it.
    sink2(source());

    std::cout << " ------ test 11, pass const rvalue by overloaded reference ------- " << std::endl;
    sink2(csource());

#if 0    // These two correctly fail to compile, just as desired
    std::cout << " ------ test 12, pass rvalue by non-const reference ------- " << std::endl;
    sink3(source());

    std::cout << " ------ test 13, pass const rvalue by non-const reference ------- " << std::endl;
    sink3(csource());
#endif

    std::cout << " ------ test 14, pass lvalue by-value ------- " << std::endl;
    sink(z5);

    std::cout << " ------ test 15, pass const lvalue by-value ------- " << std::endl;
    sink(z7);

    std::cout << " ------ test 16, pass lvalue by-reference ------- " << std::endl;
    sink2(z4);

    std::cout << " ------ test 17, pass const lvalue by const reference ------- " << std::endl;
    sink2(z7);

    std::cout << " ------ test 18, pass const lvalue by-reference ------- " << std::endl;
#if 0   // correctly fails to compile, just as desired
    sink3(z7);
#endif

    std::cout << " ------ test 19, pass rvalue by value to template param ------- " << std::endl;
    tsink(source());

    std::cout << " ------ test 20, direct initialize a const A with an A ------- " << std::endl;
    typedef X const XC;
    sink2(XC(X()));
}

Proposed Resolution:

(As proposed by N1610 section 5, with editing.)

Change 8.5.3 [stmt.switch] paragraph 5, second bullet, first sub-bullet, second sub-sub-bullet as follows:

A temporary of type "cv1 T2" [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary via copy-initialization from the entire rvalue object. The reference is bound to the temporary or to a sub-object within the temporary.

The text immediately following that is changed as follows:

The constructor that would be used to make the copy shall be callable whether or not the copy is actually done. The constructor and any conversion function that would be used in the initialization shall be callable whether or not the temporary is actually created.

Note, however, that the way the core working group is leaning on issue 391 (i.e., requiring direct binding) would make this change unnecessary.

Proposed resolution (April, 2005):

This issue is resolved by the resolution of issue 391.




391. Require direct binding of short-lived references to rvalues

Section: 9.4.4  [dcl.init.ref]     Status: CD1     Submitter: Raoul Gough     Date: 14 Nov 2002

[Voted into WP at October 2005 meeting.]

After some email exchanges with Rani Sharoni, I've come up with the following proposal to allow reference binding to non-copyable rvalues in some cases. Rationale and some background appear afterwards.

---- proposal ----

Replace the section of 9.4.4 [dcl.init.ref] paragraph 5 that begins "If the initializer expression is an rvalue" with the following:

---- rationale ----

  1. The intention of the current wording is to provide the implementation freedom to construct an rvalue of class type at an arbitrary location and copy it zero or more times before binding any reference to it.
  2. The standard allows code to call a member function on an rvalue of class type (in 7.6.1.5 [expr.ref], I guess). This means that the implementation can be forced to bind the reference directly, with no freedom to create any temporary copies. e.g.
       class nc {
         nc (nc const &);  // private, nowhere defined
       public:
         nc ();
         nc const &by_ref () const { return *this; }
       };
    
       void f () {
         void g (nc const &);
    
         g (nc());          // Ill-formed
         g (nc().by_ref()); // Ok - binds directly to rvalue
       }
    
    Forcing a direct binding in this way is possible wherever the lifetime of the reference does not extend beyond the containing full expression, since the reference returned by the member function remains valid for this long.
  3. As demonstrated above, existing implementations must already be capable of constructing an rvalue of class type in the "right" place the first time. Some compilers already silently allow the direct binding of references to non-copyable rvalues.
  4. The change will not break any portable user code. It would break any platform-specific user code that relies on copies being performed by the particular implementation.

---- background ----

The proposal is based on a recent discussion in this group. I originally wanted to leave the implementation free to copy the rvalue if there was a callable copy constructor, and only have to bind directly if none was callable. Unfortunately, a traditional compiler can't always tell whether a function is callable or not, e.g. if the copy constructor is declared but not defined. Rani pointed this out in an example, and suggested that maybe trivial copy constructors should still be allowed (by extension, maybe wherever the compiler can determine callability). I've gone with this version because it's simpler, and I also figure the "as if" rule gives the compiler some freedom with POD types anyway.

Notes from April 2003 meeting:

We agreed generally with the proposal. We were unsure about the need for the restriction regarding long-lived references. We will check with the proposer about that.

Jason Merrill points out that the test case in issue 86 may be a case where we do not want to require direct binding.

Further information from Rani Sharoni (April 2003):

I wasn't aware about the latest suggestion of Raoul as it appears in core issue 391. In our discussions we tried to formulate a different proposal.

The rational, as we understood, behind the implementation freedom to make an extra copying (8.5.3/5/2/12) of the rvalue is to allow return values in registers which on some architectures are not addressable. The example that Raoul and I presented shows that this implementation freedom is not always possible since we can "force" the rvalue to be addressable using additional member function (by_ref). The example only works for short lived rvalues and this is probably why Raoul narrow the suggestion.

I had different rational which was related to the implementation of conditional operator in VC. It seems that when conditional operator is involved VC does use an extra copying when the lifetime of the temporary is extended:

  struct A { /* ctor with side effect */};

  void f(A& x) {
    A const& r = cond ? A(1) : x; // VC actually make an extra copy of
                                  // the rvalue A(1)
  }

I don't know what the consideration behind the VC implementation was (I saw open bug on this issue) but it convinced me to narrow the suggestion.

IMHO such limitation seems to be too strict because it might limit the optimizer since returning class rvalues in registers might be useful (although I'm not aware about any implementation that actually does it). My suggestion was to forbid the extra copying if the ctor is not viable (e.g. A::A(A&) ). In this case the implementation "freedom" doesn't exist (since the code might not compile) and only limits the programmer freedom (e.g. Move Constructors - http://www.cuj.com/experts/2102/alexandr.htm [Note: URL is now defunct; observed March,2019.]).

Core issue 291 is strongly related to the above issue and I personally prefer to see it resolved first. It seems that VC already supports the resolution I prefer.

Notes from October 2003 meeting:

We ended up feeling that this is just one of a number of cases of optimizations that are widely done by compilers and allowed but not required by the standard. We don't see any strong reason to require compilers to do this particular optimization.

Notes from the March 2004 meeting:

After discussing issue 450, we found ourselves reconsidering this, and we are now inclined to make a change to require the direct binding in all cases, with no restriction on long-lived references. Note that such a change would eliminate the need for a change for issue 291.

Proposed resolution (October, 2004):

Change 9.4.4 [dcl.init.ref] bullet 5.2 sub-bullet 1 as follows:

If the initializer expression is an rvalue, with T2 a class type, and "cv1 T1" is reference-compatible with "cv2 T2", the reference is bound to the object represented by the rvalue (see 7.2.1 [basic.lval]) or to a sub-object within that object. in one of the following ways (the choice is implementation-defined): The constructor that would be used to make the copy shall be callable whether or not the copy is actually done. [Example:
  struct A { };
  struct B : public A { } b;
  extern B f();
  const A& rca = f ();  // Bound Either bound to the A sub-object of the B rvalue,
                        // or the entire B object is copied and the reference
                        // is bound to the A sub-object of the copy
end example]

[This resolution also resolves issue 291.]




450. Binding a reference to const to a cv-qualified array rvalue

Section: 9.4.4  [dcl.init.ref]     Status: CD1     Submitter: Steve Adamczyk     Date: 16 Jan 2004

[Voted into WP at October 2005 meeting.]

It's unclear whether the following is valid:

const int N = 10;
const int M = 20;
typedef int T;
void f(T const (&x)[N][M]){}

struct X {
	int i[10][20];
};

X g();

int main()
{
	f(g().i);
}

When you run this through 9.4.4 [dcl.init.ref], you sort of end up falling off the end of the standard's description of reference binding. The standard says in the final bullet of paragraph 5 that an array temporary should be created and copy-initialized from the rvalue array, which seems implausible.

I'm not sure what the right answer is. I think I'd be happy with allowing the binding in this case. We would have to introduce a special case like the one for class rvalues.

Notes from the March 2004 meeting:

g++ and EDG give an error. Microsoft (8.0 beta) and Sun accept the example. Our preference is to allow the direct binding (no copy). See the similar issue with class rvalues in issue 391.

Proposed resolution (October, 2004):

  1. Insert a new bullet in 9.4.4 [dcl.init.ref] bullet 5.2 before sub-bullet 2 (which begins, “Otherwise, a temporary of type ‘cv1 T1’ is created...”):

    If the initializer expression is an rvalue, with T2 an array type, and “cv1 T1” is reference-compatible with “cv2 T2”, the reference is bound to the object represented by the rvalue (see 7.2.1 [basic.lval]).
  2. Change 7.2.1 [basic.lval] paragraph 2 as follows:

    An lvalue refers to an object or function. Some rvalue expressions — those of (possibly cv-qualified) class or array type or cv-qualified class type — also refer to objects.



172. Unsigned int as underlying type of enum

Section: 9.7.1  [dcl.enum]     Status: CD1     Submitter: Bjarne Stroustrup     Date: 26 Sep 1999

[Moved to DR at October 2002 meeting.]

According to 9.7.1 [dcl.enum] paragraph 5, the underlying type of an enum is an unspecified integral type, which could potentially be unsigned int. The promotion rules in 7.3.7 [conv.prom] paragraph 2 say that such an enumeration value used in an expression will be promoted to unsigned int. This means that a conforming implementation could give the value false for the following code:

    enum { zero };
    -1 < zero;       // might be false
This is counterintuitive. Perhaps the description of the underlying type of an enumeration should say that an unsigned underlying type can be used only if the values of the enumerators cannot be represented in the corresponding signed type. This approach would be consistent with the treatment of integral promotion of bitfields (7.3.7 [conv.prom] paragraph 3) .

On a related note, 9.7.1 [dcl.enum] paragraph 5 says,

the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int.

This specification does not allow for an enumeration like

    enum { a = -1, b = UINT_MAX };

Since each enumerator can fit in an int or unsigned int, the underlying type is required to be no larger than int, even though there is no such type that can represent all the enumerators.

Proposed resolution (04/01; obsolete, see below):

Change 9.7.1 [dcl.enum] paragraph 5 as follows:

It is implementation-defined which integral type is used as the underlying type for an enumeration except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int neither int nor unsigned int can represent all the enumerator values. Furthermore, the underlying type shall not be an unsigned type if the corresponding signed type can represent all the enumerator values.

See also issue 58.

Notes from 04/01 meeting:

It was noted that 7.3.7 [conv.prom] promotes unsigned types smaller than int to (signed) int. The signedness chosen by an implementation for small underlying types is therefore unobservable, so the last sentence of the proposed resolution above should apply only to int and larger types. This observation also prompted discussion of an alternative approach to resolving the issue, in which the bmin and bmax of the enumeration would determine the promoted type rather than the underlying type.

Proposed resolution (10/01):

Change 7.3.7 [conv.prom] paragraph 2 from

An rvalue of type wchar_t (6.8.2 [basic.fundamental]) or an enumeration type (9.7.1 [dcl.enum]) can be converted to an rvalue of the first of the following types that can represent all the values of its underlying type: int, unsigned int, long, or unsigned long.
to
An rvalue of type wchar_t (6.8.2 [basic.fundamental]) can be converted to an rvalue of the first of the following types that can represent all the values of its underlying type: int, unsigned int, long, or unsigned long. An rvalue of an enumeration type (9.7.1 [dcl.enum]) can be converted to an rvalue of the first of the following types that can represent all the values of the enumeration (i.e., the values in the range bmin to bmax as described in 9.7.1 [dcl.enum]): int, unsigned int, long, or unsigned long.




377. Enum whose enumerators will not fit in any integral type

Section: 9.7.1  [dcl.enum]     Status: CD1     Submitter: Mark Mitchell     Date: 30 August 2002

[Voted into WP at April 2003 meeting.]

9.7.1 [dcl.enum] defines the underlying type of an enumeration as an integral type "that can represent all the enumerator values defined in the enumeration".

What does the standard say about this code:

  enum E { a = LONG_MIN, b = ULONG_MAX };

?

I think this should be ill-formed.

Proposed resolution:

In 9.7.1 [dcl.enum] paragraph 5 after

The underlying type of an enumeration is an integral type that can represent all the enumerator values defined in the enumeration.
insert
If no integral type can represent all the enumerator values, the enumeration is ill-formed.




518. Trailing comma following enumerator-list

Section: 9.7.1  [dcl.enum]     Status: CD1     Submitter: Charles Bryant     Date: 10 May 2005

[Voted into WP at April, 2006 meeting.]

The C language (since C99), and some C++ compilers, accept:

    enum { FOO, };

as syntactically valid. It would be useful

This proposed change is to permit a trailing comma in enum by adding:

enum identifieropt { enumerator-list , }

as an alternative definition for the enum-specifier nonterminal in 9.7.1 [dcl.enum] paragraph 1.

Proposed resolution (October, 2005):

Change the grammar in 9.7.1 [dcl.enum] paragraph 1 as indicated:

enum-specifier:



660. Unnamed scoped enumerations

Section: 9.7.1  [dcl.enum]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 15 November 2007

[Voted into the WP at the September, 2008 meeting.]

The current specification of scoped enumerations does not appear to forbid an example like the following, even though the enumerator e cannot be used:

    enum class { e };

This might be covered by 9.1 [dcl.pre] paragraph 3,

In a simple-declaration, the optional init-declarator-list can be omitted only when declaring a class (Clause 11 [class]) or enumeration (9.7.1 [dcl.enum]), that is, when the decl-specifier-seq contains either a class-specifier, an elaborated-type-specifier with a class-key (11.3 [class.name]), or an enum-specifier. In these cases and whenever a class-specifier or enum-specifier is present in the decl-specifier-seq, the identifiers in these specifiers are among the names being declared by the declaration (as class-names, enum-names, or enumerators, depending on the syntax). In such cases, and except for the declaration of an unnamed bit-field (11.4.10 [class.bit]), the decl-specifier-seq shall introduce one or more names into the program, or shall redeclare a name introduced by a previous declaration.

which, when combined with paragraph 2,

A declaration occurs in a scope (6.4 [basic.scope]); the scope rules are summarized in 6.5 [basic.lookup]. A declaration that declares a function or defines a class, namespace, template, or function also has one or more scopes nested within it. These nested scopes, in turn, can have declarations nested within them. Unless otherwise stated, utterances in Clause 9 [dcl.dcl] about components in, of, or contained by a declaration or subcomponent thereof refer only to those components of the declaration that are not nested within scopes nested within the declaration.

appears to rule out the similar class definition,

    struct { int m; };

However, a scoped enumeration is not listed in paragraph 2 among the constructs containing a nested scope (although 6.4.8 [basic.scope.enum] does describe “enumeration scope”); furthermore, an enumerator-definition is not formally a “nested declaration.” If unusable scoped enumeration definitions are to be banned, these shortcomings in 9.1 [dcl.pre] paragraph 2 must be addressed. (A note in 9.7.1 [dcl.enum] mentioning that unnamed scoped enumerations are not allowed would also be helpful.)

Notes from the February, 2008 meeting:

The consensus was to require that the identifier be present in an enum-specifier unless the enum-key is enum.

Proposed resolution (June, 2008):

Change 9.7.1 [dcl.enum] paragraph 2 as follows:

...The enum-keys enum class and enum struct are semantically equivalent; an enumeration type declared with one of these is a scoped enumeration, and its enumerators are scoped enumerators. The optional identifier shall not be omitted in the declaration of a scoped enumeration. The type-specifier-seq of an enum-base...



540. Propagation of cv-qualifiers in reference-to-reference collapse

Section: 9.8.2  [namespace.def]     Status: CD1     Submitter: Russell Yanofsky     Date: 24 September 2005

[Voted into the WP at the October, 2006 meeting as part of paper J16/06-0188 = WG21 N2118.]

The resolution of issue 106 specifies that an attempt to create a type “reference to cv1 T,” where T is a typedef or template parameter of the type “reference to cv2 S,” actually creates the type “reference to cv12 S,” where cv12 is the union of the two sets of cv-qualifiers.

One objection that has been raised to this resolution is that it is inconsistent with the treatment of cv-qualification and references specified in 9.3.4.3 [dcl.ref] paragraph 1, which says that cv-qualifiers applied to a typedef or template argument that is a reference type are ignored. For example:

    typedef int& intref;
    const intref r1;       // reference to int
    const intref& r2;      // reference to const int

In fact, however, these two declarations are quite different. In the declaration of r1, const applies to a “top-level” reference, while in the declaration of t2, it occurs under a reference. In general, cv-qualifiers that appear under a reference are preserved, even if the type appears in a context in which top-level cv-qualification is removed, for example, in determining the type of a function from parameter types (9.3.4.6 [dcl.fct] paragraph 3) and in template argument deduction (13.10.3.2 [temp.deduct.call] paragraph 2).

Another objection to the resolution is that type composition gives different results in a single declaration than it does when separated into two declarations. For example:

    template <class T>
    struct X {
       typedef T const T_const;
       typedef T_const& type1;
       typedef T const& type2;
    };

    X<int&>::type1 t1;    // int&
    X<int&>::type2 t2;    // int const&

The initial motivation for the propagation of cv-qualification during reference-to-reference collapse was to prevent inadvertent loss of cv-qualifiers in contexts in which it could make a difference. For example, if the resolution were changed to discard, rather than propagate, embedded cv-qualification, overload resolution could surprisingly select a non-const version of a member function:

   struct X {
       void g();
       void g() const;
   };

   template <typename T> struct S {
       static void f(const T& t) {
           t.g();    // const or non-const???
       }
   };

   X x;

   void q() {
       S<X>::f(x);    // calls X::g() const
       S<X&>::f(x);   // calls X::g()
   }

Another potentially-surprising outcome of dropping embedded cv-qualifiers would be:

   template <typename T> struct A {
       void f(T&);          // mutating version
       void f(const T&);    // non-mutating version
   };

   A<int&> ai;    // Ill-formed: A<int&> declares f(int&) twice

On the other hand, those who would like to see the resolution changed to discard embedded cv-qualifiers observe that these examples are too simple to be representative of real-world code. In general, it is unrealistic to expect that a template written with non-reference type parameters in mind will automatically work correctly with reference type parameters as a result of applying the issue 106 resolution. Instead, template metaprogramming allows the template author to choose explicitly whether cv-qualifiers are propagated or dropped, according to the intended use of the template, and it is more important to respect the reasonable intuition that a declaration involving a template parameter will not change the type that the parameter represents.

As a sample of real-world code, tr1::tuple was examined. In both cases — the current resolution of issue 106 and one in which embedded cv-qualifiers were dropped — some metaprogramming was required to implement the intended interface, although the version reflecting the revised resolution was somewhat simpler.

Notes from the October, 2005 meeting:

The consensus of the CWG was that the resolution of issue 106 should be revised not to propagate embedded cv-qualification.

Note (February, 2006):

The wording included in the rvalue-reference paper, J16/06-0022 = WG21 N1952, incorporates changes intended to implement the October, 2005 consensus of the CWG.




11. How do the keywords typename/template interact with using-declarations?

Section: 9.9  [namespace.udecl]     Status: CD1     Submitter: Bill Gibbons     Date: unknown

[Voted into WP at March 2004 meeting.]

Issue 1:

The working paper is not clear about how the typename/template keywords interact with using-declarations:

     template<class T> struct A {
         typedef int X;
     };

     template<class T> void f() {
         typename A<T>::X a;      // OK
         using typename A<T>::X;  // OK
         typename X b;  // ill-formed; X must be qualified
         X c;  // is this OK?
     }
When the rules for typename and the similar use of template were decided, we chose to require that they be used at every reference. The way to avoid typename at every use is to declare a typedef; then the typedef name itself is known to be a type. For using-declarations, we decided that they do not introduce new declarations but rather are aliases for existing declarations, like symbolic links. This makes it unclear whether the declaration "X c;" above should be well-formed, because there is no new name declared so there is no declaration with a "this is a type" attribute. (The same problem would occur with the template keyword when a member template of a dependent class is used). I think these are the main options:
  1. Continue to allow typename in using-declarations, and template (for member templates) too. Attach the "is a type" or "is a template" attribute to the placeholder name which the using-declaration "declares"
  2. Disallow typename and template in using-declarations (just as class-keys are disallowed now). Allow typename and template before unqualified names which refer to dependent qualified names through using-declarations.
  3. Document that this is broken.
Suggested Resolution:

The core WG already resolved this issue according to (1), but the wording does not seem to have been added to the standard. New wording needs to be drafted.

Issue 2:

Either way, one more point needs clarification. If the first option is adopted:

     template<class T> struct A {
         struct X { };
     };

     template<class T> void g() {
         using typename A<T>::X;
         X c;    // if this is OK, then X by itself is a  type
         int X;  // is this OK?
     }
When "g" is instantiated, the two declarations of X are compatible (9.9 [namespace.udecl] paragraph 10) . But there is no way to know this when the definition of "g" is compiled. I think this case should be ill-formed under the first option. (It cannot happen under the second option.) If the second option is adopted:
     template<class T> struct A {
         struct X { };
     };

     template<class T> void g() {
         using A<T>::X;
         int X;  // is this OK?
     }
Again, the instantiation would work but there is no way to know that in the template definition. I think this case should be ill-formed under the second option. (It would already be ill-formed under the first option.)

From John Spicer:

The "not a new declaration" decision is more of a guiding principle than a hard and fast rule. For example, a name introduced in a using-declaration can have different access than the original declaration.

Like symbolic links, a using-declaration can be viewed as a declaration that declares an alias to another name, much like a typedef.

In my opinion, "X c;" is already well-formed. Why would we permit typename to be used in a using-declaration if not to permit this precise usage?

In my opinion, all that needs to be done is to clarify that the "typeness" or "templateness" attribute of the name referenced in the using-declaration is attached to the alias created by the using-declaration. This is solution #1.

Tentative Resolution:

The rules for multiple declarations with the same name in the same scope should treat a using-declaration which names a type as a typedef, just as a typedef of a class name is treated as a class declaration. This needs drafting work. Also see Core issue 36.

Rationale (04/99): Any semantics associated with the typename keyword in using-declarations should be considered an extension.

Notes from the April 2003 meeting:

This was reopened because we are now considering extensions again. We agreed that it is desirable for the typename to be "sticky" on a using-declaration, i.e., references to the name introduced by the using-declaration are known to be type names without the use of the typename keyword (which can't be specified on an unqualified name anyway, as of now). The related issue with the template keyword already has a separate issue 109.

Issue 2 deals with the "struct hack." There is an example in 9.9 [namespace.udecl] paragraph 10 that shows a use of using-declarations to import two names that coexist because of the "struct hack." After some deliberation, we decided that the template-dependent using-declaration case is different enough that we did not have to support the "struct hack" in that case. A name introduced in such a case is like a typedef, and no other hidden type can be accessed through an elaborated type specifier.

Proposed resolution (April 2003, revised October 2003):

Add a new paragraph to the bottom of 9.9 [namespace.udecl]:

If a using-declaration uses the keyword typename and specifies a dependent name (13.8.3 [temp.dep]), the name introduced by the using-declaration is treated as a typedef-name (9.2.4 [dcl.typedef]).



258. using-declarations and cv-qualifiers

Section: 9.9  [namespace.udecl]     Status: CD1     Submitter: Liam Fitzpatrick     Date: 2 Nov 2000

[Voted into WP at April 2003 meeting.]

According to 9.9 [namespace.udecl] paragraph 12,

When a using-declaration brings names from a base class into a derived class scope, member functions in the derived class override and/or hide member functions with the same name and parameter types in a base class (rather than conflicting).

Note that this description says nothing about the cv-qualification of the hiding and hidden member functions. This means, for instance, that a non-const member function in the derived class hides a const member function with the same name and parameter types instead of overloading it in the derived class scope. For example,

    struct A {
      virtual int f() const;
      virtual int f();
    };
    struct B: A {
      B();
      int f();
      using A::f;
    };

    const B cb;
    int i = cb.f(); // ill-formed: A::f() const hidden in B

The same terminology is used in 11.7.3 [class.virtual] paragraph 2:

If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name and same parameter list as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::vf.

Notes on the 04/01 meeting:

The hiding and overriding should be on the basis of the function signature, which includes any cv-qualification on the function.

Proposed resolution (04/02):

In 9.9 [namespace.udecl] paragraph 12 change:

When a using-declaration brings names from a base class into a derived class scope, member functions in the derived class override and/or hide member functions with the same name and parameter types in a base class (rather than conflicting).
to read:
When a using-declaration brings names from a base class into a derived class scope, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list (9.3.4.6 [dcl.fct]), and cv-qualification in a base class (rather than conflicting).

In 11.7.3 [class.virtual] paragraph 2 change:

If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name and same parameter list as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::vf.
to read:
If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list (9.3.4.6 [dcl.fct]), and cv-qualification as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::vf.

See issue 140 for the definition of parameter-type-list.




460. Can a using-declaration name a namespace?

Section: 9.9  [namespace.udecl]     Status: CD1     Submitter: John Spicer     Date: 12 Feb 2004

[Voted into WP at April 2005 meeting.]

Can a using-declaration be used to import a namespace?

namespace my_namespace{
  namespace my_namespace2 {
    int function_of_my_name_space(){ return 2;}
  }
}

int main (){
  using  ::my_namespace::my_namespace2;
  return my_namespace2::function_of_my_name_space();
}

Several popular compilers give an error on this, but there doesn't seem to be anything in 9.9 [namespace.udecl] that prohibits it. It should be noted that the user can get the same effect by using a namespace alias:

  namespace my_namespace2 = ::my_namespace::my_namespace2;

Notes from the March 2004 meeting:

We agree that it should be an error.

Proposed resolution (October, 2004):

Add the following as a new paragraph after 9.9 [namespace.udecl] paragraph 5:

A using-declaration shall not name a namespace;



4. Does extern "C" affect the linkage of function names with internal linkage?

Section: 9.11  [dcl.link]     Status: CD1     Submitter: Mike Anderson     Date: unknown

[Moved to DR at 4/01 meeting.]

9.11 [dcl.link] paragraph 6 says the following:

Does this apply to static functions as well? For example, is the following well-formed?
        extern "C" {
            static void f(int) {}
            static void f(float) {}
        };
Can a function with internal linkage "have C linkage" at all (assuming that phrase means "has extern "C" linkage"), for how can a function be extern "C" if it's not extern? The function type can have extern "C" linkage — but I think that's independent of the linkage of the function name. It should be perfectly reasonable to say, in the example above, that extern "C" applies only to the types of f(int) and f(float), not to the function names, and that the rule in 9.11 [dcl.link] paragraph 6 doesn't apply.

Suggested resolution: The extern "C" linkage specification applies only to the type of functions with internal linkage, and therefore some of the rules that have to do with name overloading don't apply.

Proposed Resolution:

The intent is to distingush implicit linkage from explicit linkage for both name linkage and language (function type) linkage. (It might be more clear to use the terms name linkage and type linkage to distinguish these concepts. A function can have a name with one kind of linkage and a type with a different kind of linkage. The function itself has no linkage: it has no name, only the declaration has a name. This becomes more obvious when you consider function pointers.)

The tentatively agreed proposal is to apply implicit linkage to names declared in brace-enclosed linkage specifications and to non-top-level names declared in simple linkage specifications; and to apply explicit linkage to top-level names declared in simple linkage specifications.

The language linkage of any function type formed through a function declarator is that of the nearest enclosing linkage-specification. For purposes of determining whether the declaration of a namespace-scope name matches a previous declaration, the language linkage portion of the type of a function declaration (that is, the language linkage of the function itself, not its parameters, return type or exception specification) is ignored.

For a linkage-specification using braces, i.e.

extern string-literal { declaration-seqopt }
the linkage of any declaration of a namespace-scope name (including local externs) which is not contained in a nested linkage-specification, is not declared to have no linkage (static), and does not match a previous declaration is given the linkage specified in the string-literal. The language linkage of the type of any function declaration of a namespace-scope name (including local externs) which is not contained in a nested linkage-specification and which is declared with function declarator syntax is the same as that of a matching previous declaration, if any, else is specified by string-literal.

For a linkage-specification without braces, i.e.

extern string-literal declaration

the linkage of the names declared in the top-level declarators of declaration is specified by string-literal; if this conflicts with the linkage of any matching previous declarations, the program is ill-formed. The language linkage of the type of any top-level function declarator is specified by string-literal; if this conflicts with the language linkage of the type of any matching previous function declarations, the program is ill-formed. The effect of the linkage-specification on other (non top-level) names declared in declaration is the same as that of the brace-enclosed form.

Bill Gibbons: In particular, these should be well-formed:

    extern "C" void f(void (*fp)());   // parameter type is pointer to
                                       // function with C language linkage
    extern "C++" void g(void (*fp)()); // parameter type is pointer to
                                       // function with C++ language linkage

    extern "C++" {                     // well-formed: the linkage of "f"
        void f(void(*fp)());           // and the function type used in the
    }                                  // parameter still "C"

    extern "C" {                       // well-formed: the linkage of "g"
        void g(void(*fp)());           // and the function type used in the
    }                                  // parameter still "C++"

but these should not:

    extern "C++" void f(void(*fp)());  // error - linkage of "f" does not
                                       // match previous declaration
                                       // (linkage of function type used in
                                       // parameter is still "C" and is not
                                       // by itself ill-formed)
    extern "C" void g(void(*fp)());    // error - linkage of "g" does not
                                       // match previous declaration
                                       // (linkage of function type used in
                                       // parameter is still "C++" and is not
                                       // by itself ill-formed)

That is, non-top-level declarators get their linkage from matching declarations, if any, else from the nearest enclosing linkage specification. (As already described, top-level declarators in a brace-enclosed linkage specification get the linkage from matching declarations, if any, else from the linkage specifcation; while top-level declarators in direct linkage specifications get their linkage from that specification.)

Mike Miller: This is a pretty significant change from the current specification, which treats the two forms of language linkage similarly for most purposes. I don't understand why it's desirable to expand the differences.

It seems very unintuitive to me that you could have a top-level declaration in an extern "C" block that would not receive "C" linkage.

In the current standard, the statement in 9.11 [dcl.link] paragraph 4 that

the specified language linkage applies to the function types of all function declarators, function names, and variable names introduced by the declaration(s)

applies to both forms. I would thus expect that in

    extern "C" void f(void(*)());
    extern "C++" {
        void f(void(*)());
    }
    extern "C++" f(void(*)());

both "C++" declarations would be well-formed, declaring an overloaded version of f that takes a pointer to a "C++" function as a parameter. I wouldn't expect that either declaration would be a redeclaration (valid or invalid) of the "C" version of f.

Bill Gibbons: The potential difficulty is the matching process and the handling of deliberate overloading based on language linkage. In the above examples, how are these two declarations matched:

    extern "C" void f(void (*fp1)());

    extern "C++" {
        void f(void(*fp2)());
    }

given that the linkage that is part of fp1 is "C" while the linkage (prior to the matching process) that is part of fp2 is "C++"?

The proposal is that the linkage which is part of the parameter type is not determined until after the match is attempted. This almost always correct because you can't overload "C" and "C++" functions; so if the function names match, it is likely that the declarations are supposed to be the same.

Mike Miller: This seems like more trouble than it's worth. This comparison of function types ignoring linkage specifications is, as far as I know, not found anywhere in the current standard. Why do we need to invent it?

Bill Gibbons: It is possible to construct pathological cases where this fails, e.g.

    extern "C" typedef void (*PFC)();  // pointer to "C" linkage function
    void f(PFC);         // parameter is pointer to "C" function
    void f(void (*)());  // matching declaration or overload based on
                         // difference in linkage type?

It is reasonable to require explicit typedefs in this case so that in the above example the second function declaration gets its parameter type function linkage from the first function declaration.

(In fact, I think you can't get into this situation without having already used typedefs to declare different language linkage for the top-level and parameter linkages.)

For example, if the intent is to overload based on linkage a typedef is needed:

    extern "C" typedef void (*PFC)();  // pointer to "C" linkage function
    void f(PFC);              // parameter is pointer to "C" function
    typedef void (*PFCPP)();  // pointer to "C++" linkage function
    void f(PFCPP);            // parameter is pointer to "C++" function

In this case the two function declarations refer to different functions.

Mike Miller: This seems pretty strange to me. I think it would be simpler to determine the type of the parameter based on the containing linkage specification (implicitly "C++") and require a typedef if the user wants to override the default behavior. For example:

    extern "C" {
        typedef void (*PFC)();    // pointer to "C" function
        void f(void(*)());        // takes pointer to "C" function
    }

    void f(void(*)());            // new overload of "f", taking
                                  // pointer to "C++" function

    void f(PFC);                  // redeclare extern "C" version

Notes from 04/00 meeting:

The following changes were tentatively approved, but because they do not completely implement the proposal above the issue is being kept for the moment in "drafting" status.

Notes from 10/00 meeting:

After further discussion, the core language working group determined that the more extensive proposal described above is not needed and that the following changes are sufficient.

Proposed resolution (04/01):

  1. Change the first sentence of 9.11 [dcl.link] paragraph 1 from

    All function types, function names, and variable names have a language linkage.

    to

    All function types, function names with external linkage, and variable names with external linkage have a language linkage.
  2. Change the following sentence of 9.11 [dcl.link] paragraph 4:
    In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names, and variable names introduced by the declaration(s).

    to

    In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names with external linkage, and variable names with external linkage declared within the linkage-specification.
  3. Add at the end of the final example on 9.11 [dcl.link] paragraph 4:

        extern "C" {
          static void f4();    // the name of the function f4 has
                               // internal linkage (not C language
                               // linkage) and the function's type
                               // has C language linkage
        }
        extern "C" void f5() {
          extern void f4();    // Okay -- name linkage (internal)
                               // and function type linkage (C
                               // language linkage) gotten from
                               // previous declaration.
        }
        extern void f4();      // Okay -- name linkage (internal)
                               // and function type linkage (C
                               // language linkage) gotten from
                               // previous declaration.
        void f6() {
          extern void f4();    // Okay -- name linkage (internal)
                               // and function type linkage (C
                               // language linkage) gotten from
                               // previous declaration.
        }
    
  4. Change 9.11 [dcl.link] paragraph 7 from

    Except for functions with internal linkage, a function first declared in a linkage-specification behaves as a function with external linkage. [Example:

        extern "C" double f();
        static double f();     // error
    

    is ill-formed (9.2.2 [dcl.stc]). ] The form of linkage-specification that contains a braced-enclosed declaration-seq does not affect whether the contained declarations are definitions or not (6.2 [basic.def]); the form of linkage-specification directly containing a single declaration is treated as an extern specifier (9.2.2 [dcl.stc]) for the purpose of determining whether the contained declaration is a definition. [Example:

        extern "C" int i;      // declaration
        extern "C" {
    	  int i;           // definition
        }
    

    end example] A linkage-specification directly containing a single declaration shall not specify a storage class. [Example:

        extern "C" static void f(); // error
    

    end example]

    to

    A declaration directly contained in a linkage-specification is treated as if it contains the extern specifier (9.2.2 [dcl.stc]) for the purpose of determining the linkage of the declared name and whether it is a definition. Such a declaration shall not specify a storage class. [Example:
        extern "C" double f();
        static double f();     // error
        extern "C" int i;      // declaration
        extern "C" {
    	    int i;         // definition
        }
        extern "C" static void g(); // error
    

    end example]




29. Linkage of locally declared functions

Section: 9.11  [dcl.link]     Status: CD1     Submitter: Mike Ball     Date: 19 Mar 1998

[Moved to DR at October 2002 meeting. This was incorrectly marked as having DR status between 4/01 and 4/02. It was overlooked when issue 4 was moved to DR at the 4/01 meeting; this one should have been moved as well, because it's resolved by the changes there.]

Consider the following:

    extern "C" void foo()
    {
        extern void bar();
        bar();
    }
Does "bar()" have "C" language linkage?

The ARM is explicit and says

A linkage-specification for a function also applies to functions and objects declared within it.
The DIS says
In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names, and variable names introduced by the declaration(s).
Is the body of a function definition part of the declaration?

From Mike Miller:

Yes: from 9.1 [dcl.pre] paragraph 1,

and 9.5 [dcl.fct.def] paragraph 1: At least that's how I'd read it.

From Dag Brück:

Consider the following where extern "C" has been moved to a separate declaration:

    extern "C" void foo();

    void foo() { extern void bar(); bar(); }
I think the ARM wording could possibly be interpreted such that bar() has "C" linkage in my example, but not the DIS wording.

As a side note, I have always wanted to think that placing extern "C" on a function definition or a separate declaration would produce identical programs.

Proposed Resolution (04/01):

See the proposed resolution for Core issue 4, which covers this case.

The ODR should also be checked to see whether it addresses name and type linkage.




175. Class name injection and base name access

Section: Clause 11  [class]     Status: CD1     Submitter: John Spicer     Date: 21 February 1999

[Moved to DR at 10/01 meeting.]

With class name injection, when a base class name is used in a derived class, the name found is the injected name in the base class, not the name of the class in the scope containing the base class. Consequently, if the base class name is not accessible (e.g., because is is in a private base class), the base class name cannot be used unless a qualified name is used to name the class in the class or namespace of which it is a member.

Without class name injection the following example is valid. With class name injection, A is inaccessible in class C.

    class A { };
    class B: private A { };
    class C: public B {
        A* p;    // error: A inaccessible
    };

At the least, the standard should be more explicit that this is, in fact, ill-formed.

(See paper J16/99-0010 = WG21 N1187.)

Proposed resolution (04/01):

Add to the end of 11.8.2 [class.access.spec] paragraph 3:

[Note: In a derived class, the lookup of a base class name will find the injected-class-name instead of the name of the base class in the scope in which it was declared. The injected-class-name might be less accessible than the name of the base class in the scope in which it was declared.] [Example:

    class A { };
    class B : private A { };
    class C : public B {
        A* p;    // error: injected-class-name A is inaccessible
        ::A* q;  // OK
    };

end example]




273. POD classes and operator&()

Section: Clause 11  [class]     Status: CD1     Submitter: Andrei Iltchenko     Date: 10 Mar 2001

[Moved to DR at October 2002 meeting.]

I think that the definition of a POD class in the current version of the Standard is overly permissive in that it allows for POD classes for which a user-defined operator function operator& may be defined. Given that the idea behind POD classes was to achieve compatibility with C structs and unions, this makes 'Plain old' structs and unions behave not quite as one would expect them to.

In the C language, if x and y are variables of struct or union type S that has a member m, the following expression are allowed: &x, x.m, x = y. While the C++ standard guarantees that if x and y are objects of a POD class type S, the expressions x.m, x = y will have the same effect as they would in C, it is still possible for the expression &x to be interpreted differently, subject to the programmer supplying an appropriate version of a user-defined operator function operator& either as a member function or as a non-member function.

This may result in surprising effects. Consider:

    // POD_bomb is a POD-struct. It has no non-static non-public data members,
    // no virtual functions, no base classes, no constructors, no user-defined
    // destructor, no user-defined copy assignment operator, no non-static data
    // members of type pointer to member, reference, non-POD-struct, or
    // non-POD-union.
    struct  POD_bomb  {
       int   m_value1;
       int   m_value2;
       int  operator&()
       {   return  m_value1++;   }
       int  operator&() const
       {   return  m_value1 + m_value2;   }
    };

6.8 [basic.types] paragraph 2 states:

For any complete POD object type T, whether or not the object holds a valid value of type T, the underlying bytes (6.7.1 [intro.memory]) making up the object can be copied into an array of char or unsigned char [footnote: By using, for example, the library functions (16.4.2.3 [headers]) memcpy or memmove]. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value. [Example:
    #define N sizeof(T)
    char buf[N];
    T obj;   // obj initialized to its original value
    memcpy(buf, &obj, N);
		// between these two calls to memcpy,
		// obj might be modified
    memcpy(&obj, buf, N);
		// at this point, each subobject of obj of scalar type
		// holds its original value
end example]

Now, supposing that the complete POD object type T in the example above is POD_bomb, and we cannot any more count on the assertions made in the comments to the example. Given a standard conforming implementation, the code will not even compile. And I see no legal way of copying the contents of an object of a complete object type POD_bomb into an array of char or unsigned char with memcpy or memmove without making use of the unary & operator. Except, of course, by means of an ugly construct like:

    struct  POD_without_ampersand  {
       POD_bomb   a_bomb;
    }  obj;
    #define N sizeof(POD_bomb)
    char buf[N];
    memcpy(buf, &obj, N);
    memcpy(&obj, buf, N);

The fact that the definition of a POD class allows for POD classes for which a user-defined operator& is defined, may also present major obstacles to implementers of the offsetof macro from <cstddef>

17.2 [support.types] paragraph 5 says:

The macro offsetof accepts a restricted set of type arguments in this International Standard. type shall be a POD structure or a POD union (Clause 11 [class]). The result of applying the offsetof macro to a field that is a static data member or a function is undefined."

Consider a well-formed C++ program below:

    #include <cstddef>
    #include <iostream>


    struct  POD_bomb  {
       int   m_value1;
       int   m_value2;
       int  operator&()
       {   return  m_value1++;   }
       int  operator&() const
       {   return  m_value1 + m_value2;   }
    };


    // POD_struct is a yet another example of a POD-struct.
    struct  POD_struct  {
       POD_bomb   m_nonstatic_bomb1;
       POD_bomb   m_nonstatic_bomb2;
    };


    int  main()
    {

       std::cout << "offset of m_nonstatic_bomb2: " << offsetof(POD_struct,
           m_nonstatic_bomb2) << '\n';
       return  0;

    }

See Jens Maurer's paper 01-0038=N1324 for an analysis of this issue.

Notes from 10/01 meeting:

A consensus was forming around the idea of disallowing operator& in POD classes when it was noticed that it is permitted to declare global-scope operator& functions, which cause the same problems. After more discussion, it was decided that such functions should not be prohibited in POD classes, and implementors should simply be required to "get the right answer" in constructs such as offsetof and va_start that are conventionally implemented using macros that use the "&" operator. It was noted that one can cast the original operand to char & to de-type it, after which one can use the built-in "&" safely.

Proposed resolution:




284. qualified-ids in class declarations

Section: Clause 11  [class]     Status: CD1     Submitter: Mike Miller     Date: 01 May 2001

[Moved to DR at October 2002 meeting.]

Although 9.3.4 [dcl.meaning] requires that a declaration of a qualified-id refer to a member of the specified namespace or class and that the member not have been introduced by a using-declaration, it applies only to names declared in a declarator. It is not clear whether there is existing wording enforcing the same restriction for qualified-ids in class-specifiers and elaborated-type-specifiers or whether additional wording is required. Once such wording is found/created, the proposed resolution of issue 275 must be modified accordingly.

Notes from 10/01 meeting:

The sentiment was that this should be required on class definitions, but not on elaborated type specifiers in general (which are references, not declarations). We should also make sure we consider explicit instantiations, explicit specializations, and friend declarations.

Proposed resolution (10/01):

Add after the end of 11.3 [class.name] paragraph 3:

When a nested-name-specifier is specified in a class-head or in an elaborated-type-specifier, the resulting qualified name shall refer to a previously declared member of the class or namespace to which the nested-name-specifier refers, and the member shall not have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier.



327. Use of "structure" without definition

Section: Clause 11  [class]     Status: CD1     Submitter: James Kanze     Date: 9 Dec 2001

[Voted into WP at April, 2007 meeting.]

In Clause 11 [class] paragraph 4, the first sentence says "A structure is a class definition defined with the class-key struct". As far as I know, there is no such thing as a structure in C++; it certainly isn't listed as one of the possible compound types in 6.8.4 [basic.compound]. And defining structures opens the question of whether a forward declaration is a structure or not. The parallel here with union (which follows immediately) suggests that structures and classes are really different things, since the same wording is used, and classes and unions do have some real differences, which manifest themselves outside of the definition. It also suggests that since one can't forward declare union with class and vice versa, the same should hold for struct and class -- I believe that the intent was that one could use struct and class interchangeably in forward declaration.

Suggested resolution:

I suggest something like the following:

If a class is defined with the class-key class, its members and base classes are private by default. If a class is defined with the class-key struct, its members and base classes are public by default. If a class is defined with the class-key union, its members are public by default, and it holds only one data member at a time. Such classes are called unions, and obey a number of additional restrictions, see 11.5 [class.union].

Proposed resolution (April, 2006):

This issue is resolved by the resolution of issue 538.




379. Change "class declaration" to "class definition"

Section: Clause 11  [class]     Status: CD1     Submitter: Jens Maurer     Date: 21 Oct 2002

[Voted into WP at March 2004 meeting.]

The ARM used the term "class declaration" to refer to what would usually be termed the definition of the class. The standard now often uses "class definition", but there are some surviving uses of "class declaration" with the old meaning. They should be found and changed.

Proposed resolution (April 2003):

Replace in 6.2 [basic.def] paragraph 2

A declaration is a definition unless it declares a function without specifying the function's body (9.5 [dcl.fct.def]), it contains the extern specifier (9.2.2 [dcl.stc]) or a linkage-specification [Footnote: Appearing inside the braced-enclosed declaration-seq in a linkage-specification does not affect whether a declaration is a definition. --- end footnote] (9.11 [dcl.link]) and neither an initializer nor a function-body, it declares a static data member in a class declaration definition (11.4.9 [class.static]), it is a class name declaration (11.3 [class.name]), or it is a typedef declaration (9.2.4 [dcl.typedef]), a using-declaration (9.9 [namespace.udecl]), or a using-directive (9.8.4 [namespace.udir]).

Replace in 9.2.3 [dcl.fct.spec] paragraphs 5 and 6

The virtual specifier shall only be used in declarations of nonstatic class member functions that appear within a member-specification of a class declaration definition; see 11.7.3 [class.virtual].

The explicit specifier shall be used only in declarations of constructors within a class declaration definition; see 11.4.8.2 [class.conv.ctor].

Replace in 9.2.4 [dcl.typedef] paragraph 4

A typedef-name that names a class is a class-name (11.3 [class.name]). If a typedef-name is used following the class-key in an elaborated-type-specifier (9.2.9.5 [dcl.type.elab]) or in the class-head of a class declaration definition (Clause 11 [class]), or is used as the identifier in the declarator for a constructor or destructor declaration (11.4.5 [class.ctor], 11.4.7 [class.dtor]), the program is ill-formed.

Replace in _N4868_.9.8.2.3 [namespace.memdef] paragraph 3

The name of the friend is not found by simple name lookup until a matching declaration is provided in that namespace scope (either before or after the class declaration definition granting friendship).

Replace in 9.3.4.3 [dcl.ref] paragraph 4

There shall be no references to references, no arrays of references, and no pointers to references. The declaration of a reference shall contain an initializer (9.4.4 [dcl.init.ref]) except when the declaration contains an explicit extern specifier (9.2.2 [dcl.stc]), is a class member (11.4 [class.mem]) declaration within a class declaration definition, or is the declaration of a parameter or a return type (9.3.4.6 [dcl.fct]); see 6.2 [basic.def].

Replace in 9.4.4 [dcl.init.ref] paragraph 3

The initializer can be omitted for a reference only in a parameter declaration (9.3.4.6 [dcl.fct]), in the declaration of a function return type, in the declaration of a class member within its class declaration definition (11.4 [class.mem]), and where the extern specifier is explicitly used.

Replace in 11.3 [class.name] paragraph 2

A class definition declaration introduces the class name into the scope where it is defined declared and hides any class, object, function, or other declaration of that name in an enclosing scope (6.4 [basic.scope]). If a class name is declared in a scope where an object, function, or enumerator of the same name is also declared, then when both declarations are in scope, the class can be referred to only using an elaborated-type-specifier (6.5.6 [basic.lookup.elab]).

Replace in 11.4.9 [class.static] paragraph 4

Static members obey the usual class member access rules ( 11.8 [class.access]). When used in the declaration of a class member, the static specifier shall only be used in the member declarations that appear within the member-specification of the class declaration definition.

Replace in 11.4.12 [class.nest] paragraph 1

A class can be defined declared within another class. A class defined declared within another is called a nested class. The name of a nested class is local to its enclosing class. The nested class is in the scope of its enclosing class. Except by using explicit pointers, references, and object names, declarations in a nested class can use only type names, static members, and enumerators from the enclosing class.

Replace in 11.6 [class.local] paragraph 1

A class can be defined declared within a function definition; such a class is called a local class. The name of a local class is local to its enclosing scope. The local class is in the scope of the enclosing scope, and has the same access to names outside the function as does the enclosing function. Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators from the enclosing scope.

Replace in 11.7 [class.derived] paragraph 1

... The class-name in a base-specifier shall not be an incompletely defined class (Clause 11 [class]); this class is called a direct base class for the class being declared defined. During the lookup for a base class name, non-type names are ignored (_N4868_.6.4.10 [basic.scope.hiding]). If the name found is not a class-name, the program is ill-formed. A class B is a base class of a class D if it is a direct base class of D or a direct base class of one of D's base classes. A class is an indirect base class of another if it is a base class but not a direct base class. A class is said to be (directly or indirectly) derived from its (direct or indirect) base classes. [Note: See 11.8 [class.access] for the meaning of access-specifier.] Unless redefined redeclared in the derived class, members of a base class are also considered to be members of the derived class. The base class members are said to be inherited by the derived class. Inherited members can be referred to in expressions in the same manner as other members of the derived class, unless their names are hidden or ambiguous (6.5.2 [class.member.lookup]). [Note: the scope resolution operator :: (_N4567_.5.1.1 [expr.prim.general]) can be used to refer to a direct or indirect base member explicitly. This allows access to a name that has been redefined redeclared in the derived class. A derived class can itself serve as a base class subject to access control; see 11.8.3 [class.access.base]. A pointer to a derived class can be implicitly converted to a pointer to an accessible unambiguous base class (7.3.12 [conv.ptr]). An lvalue of a derived class type can be bound to a reference to an accessible unambiguous base class (9.4.4 [dcl.init.ref]).]

Replace in 11.7.2 [class.mi] paragraph 5

For another example,
class V { /* ... */ };
class A : virtual public V { /* ... */ };
class B : virtual public V { /* ... */ };
class C : public A, public B { /* ... */ };
for an object c of class type C, a single subobject of type V is shared by every base subobject of c that is declared to have has a virtual base class of type V.

Replace in the example in 6.5.2 [class.member.lookup] paragraph 6 (the whole paragraph was turned into a note by the resolution of core issue 39)

The names defined declared in V and the left hand instance of W are hidden by those in B, but the names defined declared in the right hand instance of W are not hidden at all.

Replace in 11.7.4 [class.abstract] paragraph 2

... A virtual function is specified pure by using a pure-specifier (11.4 [class.mem]) in the function declaration in the class declaration definition. ...

Replace in the footnote at the end of 11.8.3 [class.access.base] paragraph 1

[Footnote: As specified previously in 11.8 [class.access], private members of a base class remain inaccessible even to derived classes unless friend declarations within the base class declaration definition are used to grant access explicitly.]

Replace in _N3225_.11.3 [class.access.dcl] paragraph 1

The access of a member of a base class can be changed in the derived class by mentioning its qualified-id in the derived class declaration definition. Such mention is called an access declaration. ...

Replace in the example in 12.3 [over.over] paragraph 5

The initialization of pfe is ill-formed because no f() with type int(...) has been defined declared, and not because of any ambiguity.

Replace in C.7.6 [diff.dcl] paragraph 1

Rationale: Storage class specifiers don't have any meaning when associated with a type. In C++, class members can be defined declared with the static storage class specifier. Allowing storage class specifiers on type declarations could render the code confusing for users.

Replace in C.7.7 [diff.class] paragraph 3

In C++, a typedef name may not be redefined redeclared in a class declaration definition after being used in the declaration that definition
Drafting notes:

The resolution of core issue 45 (DR) deletes 11.8.8 [class.access.nest] paragraph 2.

The following occurrences of "class declaration" are not changed:




383. Is a class with a declared but not defined destructor a POD?

Section: Clause 11  [class]     Status: CD1     Submitter: Gennaro Prota     Date: 18 Sep 2002

[Voted into WP at March 2004 meeting.]

The standard (Clause 11 [class] par. 4) says that "A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-defined copy assignment operator and no user-defined destructor."

Note that it says 'user-defined', not 'user-declared'. Is it the intent that if e.g. a copy assignment operator is declared but not defined, this does not (per se) prevent the class to be a POD-struct?

Proposed resolution (April 2003):

Replace in Clause 11 [class] paragraph 4

A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-defined declared copy assignment operator and no user-defined declared destructor. Similarly, a POD-union is an aggregate union that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-defined declared copy assignment operator and no user-defined declared destructor.

Drafting note: The changes are shown relative to TC1, incorporating the changes from the resolution of core issue 148.




413. Definition of "empty class"

Section: Clause 11  [class]     Status: CD1     Submitter: Pete Becker     Date: 30 Apr 2003

[Voted into WP at April, 2007 meeting.]

The proposal says that value is true if "T is an empty class (10)". Clause 10 doesn't define an empty class, although it has a note that says a base class may "be of zero size (clause 9)" 9/3 says "Complete objects and member subobjects of class type shall have nonzero size." This has a footnote, which says "Base class subobjects are not so constrained."

The standard uses the term "empty class" in two places (9.4.2 [dcl.init.aggr]), but neither of those places defines it. It's also listed in the index, which refers to the page that opens clause 9, i.e. the nonzero size stuff cited above.

So, what's the definition of "empty class" that determines whether the predicate is_empty is true?

The one place where it's used is 9.4.2 [dcl.init.aggr] paragraph 8, which says (roughly paraphrased) that an aggregate initializer for an empty class must be "{}", and when such an initializer is used for an aggregate that is not an empty class the members are default-initialized. In this context it's pretty clear what's meant. In the type traits proposal it's not as clear, and it was probably intended to have a different meaning. The boost implementation, after it eliminates non-class types, determines whether the trait is true by comparing the size of a class derived from T to the size of an otherwise-identical class that is not derived from T.

Howard Hinnant: is_empty was created to find out whether a type could be derived from and have the empty base class optimization successfully applied. It was created in part to support compressed_pair which attempts to optimize away the space for one of its members in an attempt to reduce spatial overhead. An example use is:

  template <class T, class Compare = std::less<T> >
  class SortedVec
  {
  public:
  ...
  private:
    T* data_;
    compressed_pair<Compare, size_type> comp_;

    Compare&       comp()       {return comp_.first();}
    const Compare& comp() const {return comp_.first();}
    size_type&     sz()         {return comp_.second();}
    size_type      sz() const   {return comp_.second();}
  };

Here the compare function is optimized away via the empty base optimization if Compare turns out to be an "empty" class. If Compare turns out to be a non-empty class, or a function pointer, the space is not optimized away. is_empty is key to making this work.

This work built on Nathan's article: http://www.cantrip.org/emptyopt.html.

Clark Nelson: I've been looking at issue 413, and I've reached the conclusion that there are two different kinds of empty class. A class containing only one or more anonymous bit-field members is empty for purposes of aggregate initialization, but not (necessarily) empty for purposes of empty base-class optimization.

Of course we need to add a definition of emptiness for purposes of aggregate initialization. Beyond that, there are a couple of questions:

  1. Should the definition of emptiness used by the is_empty predicate be defined in a language clause or a library clause?
  2. Do we need to open a new core issue pointing out the fact that the section on aggregate initialization does not currently say that unnamed bit-fields are skipped?

Notes from the October, 2005 meeting:

There are only two places in the Standard where the phrase “empty class” appears, both in 9.4.2 [dcl.init.aggr] paragraph 8. Because it is not clear whether the definition of “empty for initialization purposes” is suitable for use in defining the is_empty predicate, it would be better just to avoid using the phrase in the language clauses. The requirements of 9.4.2 [dcl.init.aggr] paragraph 8 appear to be redundant; paragraph 6 says that an initializer-list must have no more initializers than the number of elements to initialize, so an empty class already requires an empty initializer-list, and using an empty initializer-list with a non-empty class is covered adequately by paragraph 7's description of the handling of an initializer-list with fewer initializers than the number of members to initialize.

Proposed resolution (October, 2005):

  1. Change 9.4.2 [dcl.init.aggr] paragraph 5 by inserting the indicated text:

  2. Static data members and anonymous bit fields are not considered members of the class for purposes of aggregate initialization. [Example:

        struct A {
            int i;
            static int s;
            int j;
            int :17;
            int k;
        } a = { 1 , 2 , 3 };
    

    Here, the second initializer 2 initializes a.j and not the static data member A::s, and the third initializer 3 initializes a.k and not the padding before it.end example]

  3. Delete 9.4.2 [dcl.init.aggr] paragraph 8:

  4. An initializer for an aggregate member that is an empty class shall have the form of an empty initializer-list {}. [Example:

        struct S { };
        struct A {
            S s;
            int i;
        } a = { { } , 3 };
    

    end example] An empty initializer-list can be used to initialize any aggregate. If the aggregate is not an empty class, then each member of the aggregate shall be initialized with a value of the form T() (7.6.1.4 [expr.type.conv]), where T represents the type of the uninitialized member.

This resolution also resolves issue 491.

Additional note (October, 2005):

Deleting 9.4.2 [dcl.init.aggr] paragraph 8 altogether may not be a good idea. It would appear that, in its absence, the initializer elision rules of paragraph 11 would allow the initializer for a in the preceding example to be written { 3 } (because the empty-class member s would consume no initializers from the list).

Proposed resolution (October, 2006):

(Drafting note: this resolution also cleans up incorrect references to syntactic non-terminals in the nearby paragraphs.)

  1. Change 9.4.2 [dcl.init.aggr] paragraph 4 as indicated:

    An array of unknown size initialized with a brace-enclosed initializer-list containing n initializers initializer-clauses, where n shall be greater than zero... An empty initializer list {} shall not be used as the initializer initializer-clause for an array of unknown bound.
  2. Change 9.4.2 [dcl.init.aggr] paragraph 5 by inserting the indicated text:

    Static data members and anonymous bit fields are not considered members of the class for purposes of aggregate initialization. [Example:

        struct A {
            int i;
            static int s;
            int j;
            int :17;
            int k;
        } a = { 1 , 2 , 3 };
    

    Here, the second initializer 2 initializes a.j and not the static data member A::s, and the third initializer 3 initializes a.k and not the anonymous bit field before it.end example]

  3. Change 9.4.2 [dcl.init.aggr] paragraph 6 as indicated:

    An initializer-list is ill-formed if the number of initializers initializer-clauses exceeds the number of members...
  4. Change 9.4.2 [dcl.init.aggr] paragraph 7 as indicated:

    If there are fewer initializers initializer-clauses in the list than there are members...
  5. Replace 9.4.2 [dcl.init.aggr] paragraph 8:

    An initializer for an aggregate member that is an empty class shall have the form of an empty initializer-list {}. [Example:

        struct S { };
        struct A {
            S s;
            int i;
        } a = { { } , 3 };
    

    end example] An empty initializer-list can be used to initialize any aggregate. If the aggregate is not an empty class, then each member of the aggregate shall be initialized with a value of the form T() (7.6.1.4 [expr.type.conv]), where T represents the type of the uninitialized member.

    with:

    If an aggregate class C contains a subaggregate member m that has no members for purposes of aggregate initialization, the initializer-clause for m shall not be omitted from an initializer-list for an object of type C unless the initializer-clauses for all members of C following m are also omitted. [Example:

        struct S { } s;
        struct A {
            S s1;
            int i1;
            S s2;
            int i2;
            S s3;
            int i3;
        } a = {
            { },   // Required initialization
            0,
            s,     // Required initialization
            0
        };         // Initialization not required for A::s3 because A::i3 is also not initialized
    

    end example]

  6. Change 9.4.2 [dcl.init.aggr] paragraph 10 as indicated:

    When initializing a multi-dimensional array, the initializers initializer-clauses initialize the elements...
  7. Change 9.4.2 [dcl.init.aggr] paragraph 11 as indicated:

    Braces can be elided in an initializer-list as follows. If the initializer-list begins with a left brace, then the succeeding comma-separated list of initializers initializer-clauses initializes the members of a subaggregate; it is erroneous for there to be more initializers initializer-clauses than members. If, however, the initializer-list for a subaggregate does not begin with a left brace, then only enough initializers initializer-clauses from the list are taken to initialize the members of the subaggregate; any remaining initializers initializer-clauses are left to initialize the next member of the aggregate of which the current subaggregate is a member. [Example:...
  8. Change 9.4.2 [dcl.init.aggr] paragraph 12 as indicated:

    All implicit type conversions (7.3 [conv]) are considered when initializing the aggregate member with an initializer from an initializer-list assignment-expression. If the initializer assignment-expression can initialize a member, the member is initialized. Otherwise, if the member is itself a non-empty subaggregate, brace elision is assumed and the initializer assignment-expression is considered for the initialization of the first member of the subaggregate. [Note: As specified above, brace elision cannot apply to subaggregates with no members for purposes of aggregate initialization; an initializer-clause for the entire subobject is required. —end note] [Example:... Braces are elided around the initializer initializer-clause for b.a1.i...
  9. Change 9.4.2 [dcl.init.aggr] paragraph 15 as indicated:

    When a union is initialized with a brace-enclosed initializer, the braces shall only contain an initializer initializer-clause for the first member of the union...
  10. Change 9.4.2 [dcl.init.aggr] paragraph 16 as indicated:

    [Note: as As described above, the braces around the initializer initializer-clause for a union member can be omitted if the union is a member of another aggregate. —end note]

(Note: this resolution also resolves issue 491.)




538. Definition and usage of structure, POD-struct, POD-union, and POD class

Section: Clause 11  [class]     Status: CD1     Submitter: Alisdair Meredith     Date: 10 August 2005

[Voted into WP at April, 2007 meeting.]

There are several problems with the terms defined in Clause 11 [class] paragraph 4:

A structure is a class defined with the class-key struct; its members and base classes (11.7 [class.derived]) are public by default ( 11.8 [class.access]). A union is a class defined with the class-key union; its members are public by default and it holds only one data member at a time (11.5 [class.union]). [Note: aggregates of class type are described in 9.4.2 [dcl.init.aggr]. —end note] A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-declared copy assignment operator and no user-declared destructor. Similarly, a POD-union is an aggregate union that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-declared copy assignment operator and no user-declared destructor. A POD class is a class that is either a POD-struct or a POD-union.
  1. Although the term structure is defined here, it is used only infrequently throughout the Standard, often apparently inadvertently and consequently incorrectly:

    There does not appear to be a reason for defining the term structure. The one reference where it is arguably useful, in the note in 7.6.1.5 [expr.ref], could be rewritten as something like, “'class objects' may be defined using the class, struct, or union class-keys; see Clause 11 [class].”

  2. Based on its usage later in the paragraph and elsewhere, “POD-struct” appears to be intended to exclude unions. However, the definition of “aggregate class” in 9.4.2 [dcl.init.aggr] paragraph 1 includes unions. Furthermore, the name itself is confusing, leading to the question of whether it was intended that only classes defined using struct could be POD-structs or if class-classes are included. The definition should probably be rewritten as, “A POD-struct is an aggregate class defined with the class-key struct or the class-key class that has no...

  3. In most references outside Clause 11 [class], POD-struct and POD-union are mentioned together and treated identically. These references should be changed to refer to the unified term, “POD class.”

  4. Noted in passing: 17.2 [support.types] paragraph 4 refers to the undefined terms “POD structure” and (unhyphenated) “POD union;” the pair should be replaced by a single reference to “POD class.”

Proposed resolution (April, 2006):

  1. Change Clause 11 [class] paragraph 4 as indicated:

    A structure is a class defined with the class-key struct; its members and base classes (11.7 [class.derived]) are public by default ( 11.8 [class.access]). A union is a class defined with the class-key union; its members are public by default and it holds only one data member at a time (11.5 [class.union]). [Note: aggregates of class type are described in 9.4.2 [dcl.init.aggr]. —end note] A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-declared copy assignment operator and no user-declared destructor. Similarly, a POD-union is an aggregate union that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-declared copy assignment operator and no user-declared destructor. A POD class is a class that is either a POD-struct or a POD-union. A POD class is an aggregate class that has no non-static data members of non-POD type (or array of such a type) or reference, and has no user-declared copy assignment operator and no user-declared destructor. A POD-struct is a POD class defined with the class-key struct or the class-key class. A POD-union is a POD class defined with the class-key union.
  2. Change 11.8.3 [class.access.base] paragraph 2 as indicated:

    In the absence of an access-specifier for a base class, public is assumed when the derived class is declared defined with the class-key struct and private is assumed when the class is declared defined with the class-key class. [Example:...
  3. Delete the note in 7.6.1.5 [expr.ref] paragraph 4:

    [Note: “class objects” can be structures (11.4 [class.mem]) and unions (11.5 [class.union]). Classes are discussed in Clause 11 [class]. —end note]
  4. Change the commentary in the example in 11.4 [class.mem] paragraph 11 as indicated:

    ...an integer, and two pointers to similar structures objects of the same type. Once this definition...

    ...the count member of the structure object to which sp points; s.left refers to the left subtree pointer of the structure object s; and...

  5. Change _N4567_.17.3 [definitions] “iostream class templates” as indicated:

    ...the argument traits is a structure class which defines additional characteristics...
  6. Change 17.6 [support.dynamic] paragraph 4 as indicated:

    If type is not a POD structure or a POD union POD class (clause 9), the results are undefined.
  7. Change the third bullet of Clause Annex B [implimits] paragraph 2 as indicated:

  8. Change the nineteenth bullet of Clause Annex B [implimits] paragraph 2 as indicated:

  9. Change the twenty-first bullet of Clause Annex B [implimits] paragraph 2 as indicated:

  10. Change C.8 [diff.library] paragraph 6 as indicated:

    The C++ Standard library provides 2 standard structures structs from the C library, as shown in Table 126.
  11. Change the last sentence of 6.8 [basic.types] paragraph 10 as indicated:

    Scalar types, POD-struct types, POD-union types POD classes (Clause 11 [class]), arrays of such types and cv-qualified versions of these types (6.8.5 [basic.type.qualifier]) are collectively called POD types.

    Drafting note: Do not change 6.8 [basic.types] paragraph 11, because it's a note and the definition of “layout-compatible” is separate for POD-struct and POD-union in 11.4 [class.mem].

(This resolution also resolves issue 327.)




568. Definition of POD is too strict

Section: Clause 11  [class]     Status: CD1     Submitter: Matt Austern     Date: 20 March 2006

[Voted into the WP at the July, 2007 meeting as part of paper J16/07-0202 = WG21 N2342.]

A POD struct (Clause 11 [class] paragraph 4) is “an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types), or reference, and that has no user-defined copy assignment operator and no user-defined destructor.” Meanwhile, an aggregate class (9.4.2 [dcl.init.aggr] paragraph 1) must have “no user-declared constructors, no private or protecte non-static data members, no base classes, and no virtual functions.”

This is too strict. The whole reason we define the notion of POD is for the layout compatibility guarantees in 11.4 [class.mem] paragraphs 14-17 and the byte-for-byte copying guarantees of 6.8 [basic.types] paragraph 2. None of those guarantees should be affected by the presence of ordinary constructors, any more than they're affected by the presence of any other member function. It’s silly for the standard to make layout and memcpy guarantees for this class:

 struct A {
    int n;
 };

but not for this one:

  struct B {
    int n;
    B(n_) : n(n_) { }
  };

With either A or B, it ought to be possible to save an array of those objects to disk with a single call to Unix’s write(2) system call or the equivalent. At present the standard says that it’s legal for A but not B, and there isn’t any good reason for that distinction.

Suggested resolution:

The following doesn’t fix all problems (in particular it still doesn’t let us treat pair<int, int> as a POD), but at least it goes a long way toward fixing the problem: in 9.4.2 [dcl.init.aggr] paragraph 1, change “no user-declared constructors” to “no nontrivial default constructor and no user-declared copy constructor.”

(Yes, I’m aware that this proposed change would also allow brace initialization for some types that don't currently allow it. I consider this to be a feature, not a bug.)

Mike Miller: I agree that something needs to be done about “POD,” but I’m not sure that this is it. My own take is that “POD” is used for too many different things — things that are related but not identical — and the concept should be split. The current definition is useful, as is, for issues regarding initialization and lifetime. For example, I wouldn’t want to relax the prohibition of jumping over a constructor call in 8.8 [stmt.dcl] (which is currently phrased in terms of POD types). On the other hand, I agree that the presence of a user-declared constructor says nothing about layout and bitwise copying. This needs (IMHO) a non-trivial amount of further study to determine how many categories we need (instead of just POD versus non-POD), which guarantees and prohibitions go with which category, the interaction of “memcpy initialization” (for want of a better term) with object lifetime, etc.

(See paper J16/06-0172 = WG21 N2102.)

Proposed resolution (April, 2007):

Adoption of the POD proposal (currently J16/07-0090 = WG21 N2230) will resolve this issue.




417. Using derived-class qualified name in out-of-class nested class definition

Section: 11.3  [class.name]     Status: CD1     Submitter: Jon Caves     Date: 19 May 2003

[Voted into WP at October 2004 meeting.]

We had a user complain that our compiler was allowing the following code:

  struct B {
    struct S;
  };

  struct D : B { };

  struct D::S {
  };

We took one look at the code and made the reasonable (I would claim) assumption that this was indeed a bug in our compiler. Especially as we had just fixed a very similar issue with the definition of static data members.

Imagine our surprise when code like this showed up in Boost and that every other compiler we tested accepts this code. So is this indeed legal (it seems like it must be) and if so is there any justification for this beyond 6.5.5.2 [class.qual]?

John Spicer: The equivalent case for a member function is covered by the declarator rules in 9.3.4 [dcl.meaning] paragraph 1. The committee has previously run into cases where a restriction should apply to both classes and non-classes, but fails to do so because there is no equivalent of 9.3.4 [dcl.meaning] paragraph 1 for classes.

Given that, by the letter of the standard, I would say that this case is allowed.

Notes from October 2003 meeting:

We feel this case should get an error.

Proposed Resolution (October 2003):

Note that the change here interacts with issue 432.

Add the following as a new paragraph immediately following 6.4.2 [basic.scope.pdecl] paragraph 2:

The point of declaration for a class first declared by a class-specifier is immediately after the identifier or template-id (if any) in its class-head (Clause 11 [class]). The point of declaration for an enumeration is immediately after the identifier (if any) in its enum-specifier (9.7.1 [dcl.enum]).

Change point 1 of 6.4.7 [basic.scope.class] paragraph 1 to read:

The potential scope of a name declared in a class consists not only of the declarative region following the name's declarator point of declaration, but also of all function bodies, default arguments, and constructor ctor-initializers in that class (including such things in nested classes).

[Note that the preceding change duplicates one of the changes in the proposed resolution of issue 432.]

Change 13.9.3 [temp.explicit] paragraph 2 to read:

If the explicit instantiation is for a member function, a member class or a static data member of a class template specialization, the name of the class template specialization in the qualified-id for the member declarator name shall be a template-id.

Add the following as paragraph 5 of Clause 11 [class]:

If a class-head contains a nested-name-specifier, the class-specifier shall refer to a class that was previously declared directly in the class or namespace to which the nested-name-specifier refers (i.e., neither inherited nor introduced by a using-declaration), and the class-specifier shall appear in a namespace enclosing the previous declaration.

Delete 11.3 [class.name] paragraph 4 (this was added by issue 284):

When a nested-name-specifier is specified in a class-head or in an elaborated-type-specifier, the resulting qualified name shall refer to a previously declared member of the class or namespace to which the nested-name-specifier refers, and the member shall not have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier.



328. Missing requirement that class member types be complete

Section: 11.4  [class.mem]     Status: CD1     Submitter: Michiel Salters     Date: 10 Dec 2001

[Voted into WP at March 2004 meeting.]

Is it legal to use an incomplete type (6.8 [basic.types] paragraph 6) as a class member, if no object of such class is ever created ?

And as a class template member, even if the template is instantiated, but no object of the instantiated class is created?

The consensus seems to be NO, but no wording was found in the standard which explicitly disallows it.

The problem seems to be that most of the restrictions on incomplete types are on their use in objects, but class members are not objects.

A possible resolution, if this is considered a defect, is to add to 6.3 [basic.def.odr] paragraph 4, (situations when T must be complete), the use of T as a member of a class or instantiated class template.

The thread on comp.std.c++ which brought up the issue was "Compiler differences: which is correct?", started 2001 11 30. <3c07c8fb$0$8507$ed9e5944@reading.news.pipex.net>

Proposed Resolution (April 2002, revised April 2003):

Change the first bullet of the note in 6.3 [basic.def.odr] paragraph 4 and add two new bullets following it, as follows:

Replace 11.4 [class.mem] paragraph 8 by:

Non-static (11.4.9 [class.static]) data members shall not have incomplete types. In particular, a class C shall not contain a non-static member of class C, but it can contain a pointer or reference to an object of class C.

See also 6.8 [basic.types] paragraph 6, which is relevant but not changed by the Proposed Resolution.




437. Is type of class allowed in member function exception specification?

Section: 11.4  [class.mem]     Status: CD1     Submitter: Cary Coutant     Date: 10 Oct 2003

[Voted into WP at April 2005 meeting.]

I've encountered a C++ program in which a member function wants to declare that it may throw an object of its own class, e.g.:

  class Foo {
  private:
     int val;
  public:
     Foo( int &initval ) { val = initval; };
     void throwit() throw(Foo) { throw (*this); };
  };

The compiler is complaining that Foo is an incomplete type, and can't be used in the exception specification.

My reading of the standard [basic.types] is inconclusive. Although it does state that the class declaration is considered complete when the closing brace is read, I believe it also intends that the member function declarations should not be semantically validated until the class has been completely declared.

If this isn't allowed, I don't know how else a member function could be declared to throw an object of its own class.

John Spicer: The type is considered complete within function bodies, but not in their declaration (see 11.4 [class.mem] paragraph 2).

Proposed Resolution:

Change 11.4 [class.mem] paragraph 2 as follows:

Within the class member-specification, the class is regarded as complete within function bodies, default arguments, exception-specifications, and constructor ctor-initializers (including such things in nested classes).

Rationale: Taken with 9.3.4.6 [dcl.fct] paragraph 6, the exception-specification is the only part of a function declaration/definition in which the class name cannot be used because of its putative incompleteness. There is no justification for singling out exception specifications this way; both in the function body and in a catch clause, the class type will be complete, so there is no harm in allowing the class name to be used in the exception-specification.




613. Unevaluated uses of non-static class members

Section: 11.4  [class.mem]     Status: CD1     Submitter: Herb Sutter     Date: 28 October 2006

[Voted into WP at April, 2007 meeting.]

According to 11.4 [class.mem] paragraph 9, the name of a non-static data member can only be used with an object reference (explicit or implied by the this pointer of a non-static member function) or to form a pointer to member. This restriction applies even in the operand of sizeof, although the operand is not evaluated and thus no object is needed to perform the operation. Consequently, determining the size of a non-static class member often requires a circumlocution like

    sizeof ((C*) 0)->m

instead of the simpler and more obvious (but incorrect)

    sizeof (C::m)

The CWG considered this question as part of issue 198 and decided at that time to retain the restriction on consistency grounds: the rule was viewed as applying uniformly to expressions, and making an exception for sizeof would require introducing a special-purpose “wart.”

The issue has recently resurfaced, in part due to the fact that the restriction would also apply to the decltype operator. Like the unary & operator to form a pointer to member, sizeof and decltype need neither an lvalue nor an rvalue, requiring solely the declarative information of the named operand. One possible approach would be to define the concept of “unevaluated operand” or the like, exempt unevaluated operands from the requirement for an object reference in 11.4 [class.mem] paragraph 9, and then define the operands of these operators as “unevaluated.”

Proposed resolution (April, 2007):

The wording is given in paper J16/07-0113 = WG21 N2253.




620. Declaration order in layout-compatible POD structs

Section: 11.4  [class.mem]     Status: CD1     Submitter: Martin Sebor     Date: 1 March 2007

[Voted into the WP at the July, 2007 meeting as part of paper J16/07-0202 = WG21 N2342.]

It should be made clear in 11.4 [class.mem] paragraph 15,

Two POD-struct (Clause 11 [class]) types are layout-compatible if they have the same number of non-static data members, and corresponding non-static data members (in order) have layout-compatible types (6.8 [basic.types]).

that “corresponding... (in order)” refers to declaration order and not the order in which the members are laid out in memory.

However, this raises the point that, in cases where an access-specifier is involved, the declaration and layout order can be different (see paragraph 12). Thus, for two POD-struct classes A and B,

    struct A {
        char c;
        int i;
    }
    struct B {
        char c;
      public:
        int i;
    };

a compiler could move B::i before B::c, but A::c must precede A::i. It does not seem reasonable that these two POD-structs would be considered layout-compatible, even though they satisfy the requirement that corresponding members in declaration order are layout-compatible.

One possibility would be to require that neither POD-struct have an access-specifier in order to be considered layout-compatible. (It's not sufficient to require that they have the same access-specifiers, because the compiler is not required to lay out the storage the same way for different classes.)

9.4.2 [dcl.init.aggr] paragraph 2 should also be clarified to make explicit that “increasing... member order” refers to declaration order.

Proposed resolution (April, 2007):

This issue will be resolved by the adoption of the POD proposal (currently J16/07-0090 = WG21 N2230). That paper does not propose a change to the wording of 9.4.2 [dcl.init.aggr] paragraph 2, but the CWG feels that the intent of that paragraph (that the initializers are used in declaration order) is clear enough not to require revision.




263. Can a constructor be declared a friend?

Section: 11.4.5  [class.ctor]     Status: CD1     Submitter: Martin Sebor     Date: 13 Nov 2000

[Voted into WP at April 2003 meeting.]

According to 11.4.5 [class.ctor] paragraph 1, a declaration of a constructor has a special limited syntax, in which only function-specifiers are allowed. A friend specifier is not a function-specifier, so one interpretation is that a constructor cannot be declared in a friend declaration.

(It should also be noted, however, that neither friend nor function-specifier is part of the declarator syntax, so it's not clear that anything conclusive can be derived from the wording of 11.4.5 [class.ctor].)

Notes from 04/01 meeting:

The consensus of the core language working group was that it should be permitted to declare constructors as friends.

Proposed Resolution (revised October 2002):

Change paragraph 1a in 6.5.5.2 [class.qual] (added by the resolution of issue 147) as follows:

If the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C ( Clause 11 [class]), the name is instead considered to name the constructor of class C. Such a constructor name shall be used only in the declarator-id of a constructor definition declaration that appears outside of the class definition names a constructor....

Note: the above does not allow qualified names to be used for in-class declarations; see 9.3.4 [dcl.meaning] paragraph 1. Also note that issue 318 updates the same paragraph.

Change the example in 11.8.4 [class.friend], paragraph 4 as follows:

class Y {
  friend char* X::foo(int);
  friend X::X(char);   // constructors can be friends
  friend X::~X();      // destructors can be friends
  //...
};



326. Wording for definition of trivial constructor

Section: 11.4.5  [class.ctor]     Status: CD1     Submitter: James Kanze     Date: 9 Dec 2001

[Voted into WP at October 2003 meeting.]

In 11.4.5 [class.ctor] paragraph 5, the standard says "A constructor is trivial if [...]", and goes on to define a trivial default constructor. Taken literally, this would mean that a copy constructor can't be trivial (contrary to 11.4.5.3 [class.copy.ctor] paragraph 6). I suggest changing this to "A default constructor is trivial if [...]". (I think the change is purely editorial.)

Proposed Resolution (revised October 2002):

Change 11.4.5 [class.ctor] paragraph 5-6 as follows:

A default constructor for a class X is a constructor of class X that can be called without an argument. If there is no user-declared user-declared constructor for class X, a default constructor is implicitly declared. An implicitly-declared implicitly-declared default constructor is an inline public member of its class. A default constructor is trivial if it is an implicitly-declared default constructor and if:

Otherwise, the default constructor is non-trivial.

Change 11.4.7 [class.dtor] paragraphs 3-4 as follows (the main changes are removing italics):

If a class has no user-declared user-declared destructor, a destructor is declared implicitly. An implicitly-declared implicitly-declared destructor is an inline public member of its class. A destructor is trivial if it is an implicitly-declared destructor and if:

Otherwise, the destructor is non-trivial non-trivial.

In 11.5 [class.union] paragraph 1, change "trivial constructor" to "trivial default constructor".

In 6.7.7 [class.temporary] paragraph 3, add to the reference to 11.4.5 [class.ctor] a second reference, to 11.4.5.3 [class.copy.ctor].




331. Allowed copy constructor signatures

Section: 11.4.5  [class.ctor]     Status: CD1     Submitter: Richard Smith     Date: 8 Jan 2002

[Voted into WP at October 2003 meeting.]

11.4.5 [class.ctor] paragraph 10 states

A copy constructor for a class X is a constructor with a first parameter of type X & or of type const X &. [Note: see 11.4.5.3 [class.copy.ctor] for more information on copy constructors.]

No mention is made of constructors with first parameters of types volatile X & or const volatile X &. This statement seems to be in contradiction with 11.4.5.3 [class.copy.ctor] paragraph 2 which states

A non-template constructor for class X is a copy constructor if its first parameter is of type X &, const X &, volatile X & or const volatile X &, ...

11.4.5.3 [class.copy.ctor] paragraph 5 also mentions the volatile versions of the copy constructor, and the comparable paragraphs for copy assignment (11.4.5.3 [class.copy.ctor] paragraphs 9 and 10) all allow volatile versions, so it seems that 11.4.5 [class.ctor] is at fault.

Proposed resolution (October 2002):

Change 11.4.5 [class.ctor] paragraph 10 from

A copy constructor for a class X is a constructor with a first parameter of type X& or of type const X&. [Note: see 11.4.5.3 [class.copy.ctor] for more information on copy constructors. ]
to (note that the dropping of italics is intentional):
A copy constructor (11.4.5.3 [class.copy.ctor]) is used to copy objects of class type.




683. Requirements for trivial subobject special functions

Section: 11.4.5.3  [class.copy.ctor]     Status: CD1     Submitter: Jens Maurer     Date: 13 March, 2008

[Voted into the WP at the September, 2008 meeting (resolution in paper N2757).]

Part of the decision regarding whether a class has a trivial special function (copy constructor, copy assignment operator, default constructor) is whether its base and member subobjects have corresponding trivial member functions. However, with the advent of defaulted functions, it is now possible for a single class to have both trivial and nontrivial overloads for those functions. For example,

    struct B {
       B(B&) = default;    // trivial
       B(const B&);        // non-trivial, because user-provided
    };

    struct D : B { };

Although B has a trivial copy constructor and thus satisfies the requirements in 11.4.5.3 [class.copy.ctor] paragraph 6, the copy constructor in B that would be called by the implicitly-declared copy constructor in D is not trivial. This could be fixed either by requiring that all the subobject's copy constructors (or copy assignment operators, or default constructors) be trivial or that the one that would be selected by overload resolution be trivial.

Proposed resolution (July, 2008):

Change 9.5 [dcl.fct.def] paragraph 9 as follows:

... A special member function that would be implicitly defined as deleted shall not be explicitly defaulted. If a special member function for a class X is defaulted on its first declaration, no other special member function of the same kind (default constructor, copy constructor, or copy assignment operator) shall be declared in class X. A special member function is user-provided...

Notes from the September, 2008 meeting:

The resolution adopted as part of paper N2757 differs from the July, 2008 proposed resolution by allowing defaulted and user-provided special member functions to coexist. Instead, a trivial class is defined as having no non-trivial copy constructors or copy assignment operators, and a trivial copy constructor or assignment operator is defined as invoking only trivial copy operations for base and member subobjects.




244. Destructor lookup

Section: 11.4.7  [class.dtor]     Status: CD1     Submitter: John Spicer     Date: 6 Sep 2000

[Moved to DR at October 2002 meeting.]

11.4.7 [class.dtor] contains this example:

    struct B {
        virtual ~B() { }
    };
    struct D : B {
        ~D() { }
    };

    D D_object;
    typedef B B_alias;
    B* B_ptr = &D_object;

    void f() {
        D_object.B::~B();               // calls B's destructor
        B_ptr->~B();                    // calls D's destructor
        B_ptr->~B_alias();              // calls D's destructor
        B_ptr->B_alias::~B();           // calls B's destructor
        B_ptr->B_alias::~B_alias();     // error, no B_alias in class B
    }

On the other hand, 6.5.5 [basic.lookup.qual] contains this example:

    struct C {
        typedef int I;
    };
    typedef int I1, I2;
    extern int* p;
    extern int* q;
    p->C::I::~I();       // I is looked up in the scope of C
    q->I1::~I2();        // I2 is looked up in the scope of
                         // the postfix-expression
    struct A {
        ~A();
    };
    typedef A AB;
    int main()
    {
        AB *p;
        p->AB::~AB();    // explicitly calls the destructor for A
    }

Note that

     B_ptr->B_alias::~B_alias();

is claimed to be an error, while the equivalent

     p->AB::~AB();

is claimed to be well-formed.

I believe that clause 3 is correct and that clause 12 is in error. We worked hard to get the destructor lookup rules in clause 3 to be right, and I think we failed to notice that a change was also needed in clause 12.

Mike Miller:

Unfortunately, I don't believe 6.5.5 [basic.lookup.qual] covers the case of p->AB::~AB(). It's clearly intended to do so, as evidenced by 6.5.5.2 [class.qual] paragraph 1 ("a destructor name is looked up as specified in 6.5.5 [basic.lookup.qual]"), but I don't think the language there does so.

The relevant paragraph is 6.5.5 [basic.lookup.qual] paragraph 5. (None of the other paragraphs in that section deal with this topic at all.) It has two parts. The first is

If a pseudo-destructor-name (_N4778_.7.6.1.4 [expr.pseudo]) contains a nested-name-specifier, the type-names are looked up as types in the scope designated by the nested-name-specifier.

This sentence doesn't apply, because ~AB isn't a pseudo-destructor-name. _N4778_.7.6.1.4 [expr.pseudo] makes clear that this syntactic production (7.6.1 [expr.post] paragraph 1) only applies to cases where the type-name is not a class-name. p->AB::~AB is covered by the production using id-expression.

The second part of 6.5.5 [basic.lookup.qual] paragraph 5 says

In a qualified-id of the form:

    ::opt nested-name-specifier ~ class-name

where the nested-name-specifier designates a namespace name, and in a qualified-id of the form:

    ::opt nested-name-specifier class-name :: ~ class-name

the class-names are looked up as types in the scope designated by the nested-name-specifier.

This wording doesn't apply, either. The first one doesn't because the nested-name-specifier is a class-name, not a namespace name. The second doesn't because there's only one layer of qualification.

As far as I can tell, there's no normative text that specifies how the ~AB is looked up in p->AB::~AB(). 6.5.5.2 [class.qual], where all the other class member qualified lookups are handled, defers to 6.5.5 [basic.lookup.qual], and 6.5.5 [basic.lookup.qual] doesn't cover the case.

See also issue 305.

Jason Merrill: My thoughts on the subject were that the name we use in a destructor call is really meaningless; as soon as we see the ~ we know what the user means, all we're doing from that point is testing their ability to name the destructor in a conformant way. I think that everyone will agree that

  anything::B::~B()
should be well-formed, regardless of the origins of the name "B". I believe that the rule about looking up the second "B" in the same context as the first was intended to provide this behavior, but to me this seems much more heavyweight than necessary. We don't need a whole new type of lookup to be able to use the same name before and after the ~; we can just say that if the two names match, the call is well-formed. This is significantly simpler to express, both in the standard and in an implementation.

Anyone writing two different names here is either deliberately writing obfuscated code, trying to call the destructor of a nested class, or fighting an ornery compiler (i.e. one that still wants to see B_alias::~B()). I think we can ignore the first case. The third would be handled by reverting to the old rule (look up the name after ~ in the normal way) with the lexical matching exception described above -- or we could decide to break such code, do no lookup at all, and only accept a matching name. In a good implementation, the second should probably get an error message telling them to write Outer::Inner::~Inner instead.

We discussed this at the meetings, but I don't remember if we came to any sort of consensus on a direction. I see three options:

  1. Stick with the status quo, i.e. the special lookup rule such that if the name before ::~ is a class name, the name after ::~ is looked up in the same scope as the previous one. If we choose this option, we just need better wording that actually expresses this, as suggested in the issue list. This option breaks old B_alias::~B code where B_alias is declared in a different scope from B.
  2. Revert to the old rules, whereby the name after ::~ is looked up just like a name after ::, with the exception that if it matches the name before ::~ then it is considered to name the same class. This option supports old code and code that writes B_alias::~B_alias. It does not support the q->I1::~I2 usage of 6.5.5 [basic.lookup.qual], but that seems like deliberate obfuscation. This option is simpler to implement than #1.
  3. Do no lookup for a name after ::~; it must match the name before. This breaks old code as #1, but supports the most important case where the names match. This option may be slightly simpler to implement than #2. It is certainly easier to teach.

My order of preference is 2, 3, 1.

Incidentally, it seems to me oddly inconsistent to allow Namespace::~Class, but not Outer::~Inner. Prohibiting the latter makes sense from the standpoint of avoiding ambiguity, but what was the rationale for allowing the former?

John Spicer: I agree that allowing Namespace::~Class is odd. I'm not sure where this came from. If we eliminated that special case, then I believe the #1 rule would just be that in A::B1::~B2 you look up B1 and B2 in the same place in all cases.

I don't like #2. I don't think the "old" rules represent a deliberate design choice, just an error in the way the lookup was described. The usage that rule permits p->X::~Y (where Y is a typedef to X defined in X), but I doubt people really do that. In other words, I think that #1 a more useful special case than #2 does, not that I think either special case is very important.

One problem with the name matching rule is handling cases like:

  A<int> *aip;

  aip->A<int>::~A<int>();  // should work
  aip->A<int>::~A<char>(); // should not
I would favor #1, while eliminating the special case of Namespace::~Class.

Proposed resolution (10/01):

Replace the normative text of 6.5.5 [basic.lookup.qual] paragraph 5 after the first sentence with:

Similarly, in a qualified-id of the form:

    ::opt nested-name-specifieropt class-name :: ~ class-name
the second class-name is looked up in the same scope as the first.

In 11.4.7 [class.dtor] paragraph 12, change the example to

D D_object;
typedef B B_alias;
B* B_ptr = &D_object;

void f() {
  D_object.B::~B();                //  calls  B's destructor
  B_ptr->~B();                    //  calls  D's destructor
  B_ptr->~B_alias();              //  calls  D's destructor
  B_ptr->B_alias::~B();           //  calls  B's destructor
  B_ptr->B_alias::~B_alias();     //  calls  B's destructor
}

April 2003: See issue 399.




252. Looking up deallocation functions in virtual destructors

Section: 11.4.7  [class.dtor]     Status: CD1     Submitter: Steve Clamage     Date: 19 Oct 2000

[Moved to DR at 10/01 meeting.]

There is a mismatch between 11.4.7 [class.dtor] paragraph 11 and 11.4.11 [class.free] paragraph 4 regarding the lookup of deallocation functions in virtual destructors. 11.4.7 [class.dtor] says,

At the point of definition of a virtual destructor (including an implicit definition (11.4.5.3 [class.copy.ctor])), non-placement operator delete shall be looked up in the scope of the destructor's class (6.5.3 [basic.lookup.unqual]) and if found shall be accessible and unambiguous. [Note: this assures that an operator delete corresponding to the dynamic type of an object is available for the delete-expression (11.4.11 [class.free]). ]

The salient features to note from this description are:

  1. The lookup is "in the scope of the destructor's class," which implies that only members are found (cf 6.7.7 [class.temporary]). (The cross-reference would indicate otherwise, however, since it refers to the description of looking up unqualified names; this kind of lookup "spills over" into the surrounding scope.)
  2. Only non-placement operator delete is looked up. Presumably this means that a placement operator delete is ignored in the lookup.

On the other hand, 11.4.11 [class.free] says,

If a delete-expression begins with a unary :: operator, the deallocation function's name is looked up in global scope. Otherwise, if the delete-expression is used to deallocate a class object whose static type has a virtual destructor, the deallocation function is the one found by the lookup in the definition of the dynamic type's virtual destructor (11.4.7 [class.dtor]). Otherwise, if the delete-expression is used to deallocate an object of class T or array thereof, the static and dynamic types of the object shall be identical and the deallocation function's name is looked up in the scope of T. If this lookup fails to find the name, the name is looked up in the global scope. If the result of the lookup is ambiguous or inaccessible, or if the lookup selects a placement deallocation function, the program is ill-formed.

Points of interest in this description include:

  1. For a class type with a virtual destructor, the lookup is described as being "in the definition of the dynamic type's virtual destructor," rather than "in the scope of the dynamic type." That is, the lookup is assumed to be an unqualified lookup, presumably terminating in the global scope.
  2. The assumption is made that the lookup in the virtual destructor was successful ("...the one found...", not "...the one found..., if any"). This will not be the case if the deallocation function was not declared as a member somewhere in the inheritance hierarchy.
  3. The lookup in the non-virtual-destructor case does find placement deallocation functions and can fail as a result.

Suggested resolution: Change the description of the lookup in 11.4.7 [class.dtor] paragraph 11 to match the one in 11.4.11 [class.free] paragraph 4.

Proposed resolution (10/00):

  1. Replace 11.4.7 [class.dtor] paragraph 11 with the following:

    At the point of definition of a virtual destructor (including an implicit definition), the non-array deallocation function is looked up in the scope of the destructor's class (6.5.2 [class.member.lookup]), and, if no declaration is found, the function is looked up in the global scope. If the result of this lookup is ambiguous or inaccessible, or if the lookup selects a placement deallocation function, the program is ill-formed. [Note: this assures that a deallocation function corresponding to the dynamic type of an object is available for the delete-expression (11.4.11 [class.free]).]
  2. In 11.4.11 [class.free] paragraph 4, change

    ...the deallocation function is the one found by the lookup in the definition of the dynamic type's virtual destructor (11.4.7 [class.dtor]).

    to

    ...the deallocation function is the one selected at the point of definition of the dynamic type's virtual destructor (11.4.7 [class.dtor]).



272. Explicit destructor invocation and qualified-ids

Section: 11.4.7  [class.dtor]     Status: CD1     Submitter: Mike Miller     Date: 22 Feb 2001

[Moved to DR at 10/01 meeting.]

11.4.7 [class.dtor] paragraph 12 contains the following note:
an explicit destructor call must always be written using a member access operator (7.6.1.5 [expr.ref]); in particular, the unary-expression ~X() in a member function is not an explicit destructor call (7.6.2.2 [expr.unary.op]).

This note is incorrect, as an explicit destructor call can be written as a qualified-id, e.g., X::~X(), which does not use a member access operator.

Proposed resolution (04/01):

Change 11.4.7 [class.dtor] paragraph 12 as follows:

[Note: an explicit destructor call must always be written using a member access operator (7.6.1.5 [expr.ref]) or a qualified-id (_N4567_.5.1.1 [expr.prim.general]); in particular, the unary-expression ~X() in a member function is not an explicit destructor call (7.6.2.2 [expr.unary.op]).]



677. Deleted operator delete and virtual destructors

Section: 11.4.7  [class.dtor]     Status: CD1     Submitter: Mike Miller     Date: 15 February, 2008

[Voted into the WP at the September, 2008 meeting.]

Deallocation functions can't be virtual because they are static member functions; however, according to 11.4.11 [class.free] paragraph 7, they behave like virtual functions when the class's destructor is virtual:

Since member allocation and deallocation functions are static they cannot be virtual. [Note: however, when the cast-expression of a delete-expression refers to an object of class type, because the deallocation function actually called is looked up in the scope of the class that is the dynamic type of the object, if the destructor is virtual, the effect is the same.

Because the intent is to make any use of a deleted function diagnosable at compile time, a virtual deleted function can neither override nor be overridden by a non-deleted function, as described in 11.7.3 [class.virtual] paragraph 14:

A function with a deleted definition (9.5 [dcl.fct.def]) shall not override a function that does not have a deleted definition. Likewise, a function that does not have a deleted definition shall not override a function with a deleted definition.

One would assume that a similar kind of prohibition is needed for deallocation functions in a class hierarchy with virtual destructors, but it's not clear that the current specification says that. 9.5 [dcl.fct.def] paragraph 10 says,

A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.

Furthermore, the deallocation function is looked up at the point of definition of a virtual destructor (11.4.7 [class.dtor] paragraph 11) , and the function found by this lookup is considered to be “used” (6.3 [basic.def.odr] paragraph 2). However, it's not completely clear that this “use” constitutes a “reference” in the sense of 9.5 [dcl.fct.def] paragraph 10, especially in a program in which an object of a type that would call that deallocation function is never deleted.

Suggested resolution:

Augment the list of lookup results from a virtual destructor that render a program ill-formed in 11.4.7 [class.dtor] paragraph 10 to include a deleted function:

If the result of this lookup is ambiguous or inaccessible, or if the lookup selects a placement deallocation function or a function with a deleted definition (9.5 [dcl.fct.def]), the program is ill-formed.

Proposed resolution (June, 2008):

Change 11.4.7 [class.dtor] paragraph 10 as follows:

If the result of this lookup is ambiguous or inaccessible, or if the lookup selects a placement deallocation function or a function with a deleted definition (9.5 [dcl.fct.def]), the program is ill-formed.



296. Can conversion functions be static?

Section: 11.4.8.3  [class.conv.fct]     Status: CD1     Submitter: Scott Meyers     Date: 5 Jul 2001

[Moved to DR at October 2002 meeting.]

May user-defined conversion functions be static? That is, should this compile?

    class Widget {
    public:
      static operator bool() { return true; }
    };

All my compilers hate it. I hate it, too. However, I don't see anything in 11.4.8.3 [class.conv.fct] that makes it illegal. Is this a prohibition that arises from the grammar, i.e., the grammar doesn't allow "static" to be followed by a conversion-function-id in a member function declaration? Or am I just overlooking something obvious that forbids static conversion functions?

Proposed Resolution (4/02):

Add to 11.4.8.3 [class.conv.fct] as a new paragraph 7:

Conversion functions cannot be declared static.



406. Static data member in class with name for linkage purposes

Section: 11.4.9.3  [class.static.data]     Status: CD1     Submitter: Jorgen Bundgaard     Date: 12 Mar 2003

[Voted into WP at March 2004 meeting.]

The following test program is claimed to be a negative C++ test case for "Unnamed classes shall not contain static data members", c.f. ISO/IEC 14882 section 11.4.9.3 [class.static.data] paragraph 5.

  struct B {
         typedef struct {
                 static int i;          // Is this legal C++ ?
         } A;
  };

  int B::A::i = 47;      // Is this legal C++ ?

We are not quite sure about what an "unnamed class" is. There is no exact definition in ISO/IEC 14882; the closest we can come to a hint is the wording of section 9.2.4 [dcl.typedef] paragraph 5, where it seems to be understood that a class-specifier with no identifier between "class" and "{" is unnamed. The identifier provided after "}" ( "A" in the test case above) is there for "linkage purposes" only.

To us, class B::A in the test program above seems "named" enough, and there is certainly a mechanism to provide the definition for B::A::i (in contrast to the note in section 11.4.9.3 [class.static.data] paragraph 5) .

Our position is therefore that the above test program is indeed legal C++. Can you confirm or reject this claim?

Herb Sutter replied to the submitter as follows: Here are my notes based on a grep for "unnamed class" in the standard:

So yes, an unnamed class is one where there is no identifier (class name) between the class-key and the {. This is also in harmony with the production for class-name in Clause 11 [class] paragraph 1:

Notes from the October 2003 meeting:

We agree that the example is not valid; this is an unnamed class. We will add wording to define an unnamed class. The note in 11.4.9.3 [class.static.data] paragraph 5 should be corrected or deleted.

Proposed Resolution (October 2003):

At the end of Clause 11 [class], paragraph 1, add the following:

A class-specifier where the class-head omits the optional identifier defines an unnamed class.

Delete the following from 11.4.9.3 [class.static.data] paragraph 5:

[ Note: this is because there is no mechanism to provide the definitions for such static data members. ]



454. When is a definition of a static data member required?

Section: 11.4.9.3  [class.static.data]     Status: CD1     Submitter: Gennaro Prota     Date: 18 Jan 2004

[Voted into WP at the October, 2006 meeting.]

As a result of the resolution of core issue 48, the current C++ standard is not in sync with existing practice and with user expectations as far as definitions of static data members having const integral or const enumeration type are concerned. Basically what current implementations do is to require a definition only if the address of the constant is taken. Example:

void f() {

  std::string s;
  ...

  // current implementations don't require a definition
  if (s.find('a', 3) == std::string::npos) {
   ...
  }

To the letter of the standard, though, the above requires a definition of npos, since the expression std::string::npos is potentially evaluated. I think this problem would be easily solved with simple changes to 11.4.9.3 [class.static.data] paragraph 4, 11.4.9.3 [class.static.data] paragraph 5 and 6.3 [basic.def.odr] paragraph 3.

Suggested resolution:

Replace 11.4.9.3 [class.static.data] paragraph 4 with:

If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be [note1] an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. No definition of the member is required, unless an lvalue expression that designates it is potentially evaluated and either used as operand to the built-in unary & operator [note 2] or directly bound to a reference.

If a definition exists, it shall be at namespace scope and shall not contain an initializer.

In 11.4.9.3 [class.static.data] paragraph 5 change

There shall be exactly one definition of a static data member that is used in a program; no diagnostic is required; see 3.2.

to

Except as allowed by 9.4.2 par. 4, there shall be exactly one definition of a static data member that is potentially evaluated (3.2) in a program; no diagnostic is required.

In 6.3 [basic.def.odr] paragraph 3 add, at the beginning:

Except for the omission allowed by 9.4.2, par. 4, ...

[note 1] Actually it shall be a "= followed by a constant-expression". This could probably be an editorial fix, rather than a separate DR.

[note 2] Note that this is the case when reinterpret_cast-ing to a reference, like in

struct X { static const int value = 0; };
const char & c = reinterpret_cast<const char&>(X::value);
See 7.6.1.10 [expr.reinterpret.cast]/10

More information, in response to a question about why issue 48 does not resolve the problem:

The problem is that the issue was settled in a way that solves much less than it was supposed to solve; that's why I decided to file, so to speak, a DR on a DR.

I understand this may seem a little 'audacious' on my part, but please keep reading. Quoting from the text of DR 48 (emphasis mine):

Originally, all static data members still had to be defined outside the class whether they were used or not.

But that restriction was supposed to be lifted [...]

In particular, if an integral/enum const static data member is initialized within the class, and its address is never taken, we agreed that no namespace-scope definition was required.

The corresponding resolution doesn't reflect this intent, with the definition being still required in most situations anyway: it's enough that the constant appears outside a place where constants are required (ignoring the obvious cases of sizeof and typeid) and you have to provide a definition. For instance:

  struct X {
   static const int c = 1;
  };

  void f(int n)
  {
   if (n == X::c)   // <-- potentially evaluated
    ...
  }

<start digression>

Most usages of non-enum BOOST_STATIC_COSTANTs, for instance, are (or were, last time I checked) non-conforming. If you recall, Paul Mensonides pointed out that the following template

// map_integral

template<class T, T V> struct map_integral : identity<T> {
  static const T value = V;
};

template<class T, T V> const T map_integral<T, V>::value;

whose main goal is to map the same couples (type, value) to the same storage, also solves the definition problem. In this usage it is an excellent hack (if your compiler is good enough), but IMHO still a hack on a language defect.

<end digression>

What I propose is to solve the issue according to the original intent, which is also what users expect and all compilers that I know of already do. Or, in practice, we would have a rule that exists only as words in a standard document.

PS: I've sent a copy of this to Mr. Adamczyk to clarify an important doubt that occurred to me while writing this reply:

if no definition is provided for an integral static const data member is that member an object? Paragraph 1.8/1 seems to say no, and in fact it's difficult to think it is an object without assuming/pretending that a region of storage exists for it (an object *is* a region of storage according to the standard).

I would think that when no definition is required we have to assume that it could be a non-object. In that case there's nothing in 3.2 which says what 'used' means for such an entity and the current wording would thus be defective. Also, since the name of the member is an lvalue and 3.10/2 says an lvalue refers to an object we would have another problem.

OTOH the standard could pretend it is always an object (though the compiler can optimize it away) and in this case it should probably make a special case for it in 3.2/2.

Notes from the March 2004 meeting:

We sort of like this proposal, but we don't feel it has very high priority. We're not going to spend time discussing it, but if we get drafting for wording we'll review it.

Proposed resolution (October, 2005):

  1. Change the first two sentences of 6.3 [basic.def.odr] paragraph 2 from:

    An expression is potentially evaluated unless it appears where an integral constant expression is required (see 7.7 [expr.const]), is the operand of the sizeof operator (7.6.2.5 [expr.sizeof]), or is the operand of the typeid operator and the expression does not designate an lvalue of polymorphic class type (7.6.1.8 [expr.typeid]). An object or non-overloaded function is used if its name appears in a potentially-evaluated expression.

    to

    An expression that is the operand of the sizeof operator (7.6.2.5 [expr.sizeof]) is unevaluated, as is an expression that is the operand of the typeid operator if it is not an lvalue of a polymorphic class type (7.6.1.8 [expr.typeid]); all other expressions are potentially evaluated. An object or non-overloaded function whose name appears as a potentially-evaluated expression is used, unless it is an object that satisfies the requirements for appearing in an integral constant expression (7.7 [expr.const]) and the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is immediately applied.
  2. Change the first sentence of 11.4.9.3 [class.static.data] paragraph 2 as indicated:

  3. If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which whose constant-expression shall be an integral constant expression (7.7 [expr.const]).



58. Signedness of bit fields of enum type

Section: 11.4.10  [class.bit]     Status: CD1     Submitter: Steve Adamczyk     Date: 13 Oct 1998

[Voted into WP at the October, 2006 meeting.]

Section 11.4.10 [class.bit] paragraph 4 needs to be more specific about the signedness of bit fields of enum type. How much leeway does an implementation have in choosing the signedness of a bit field? In particular, does the phrase "large enough to hold all the values of the enumeration" mean "the implementation decides on the signedness, and then we see whether all the values will fit in the bit field", or does it require the implementation to make the bit field signed or unsigned if that's what it takes to make it "large enough"?

(See also issue 172.)

Note (March, 2005): Clark Nelson observed that there is variation among implementations on this point.

Notes from April, 2005 meeting:

Although implementations enjoy a great deal of latitude in handling bit-fields, it was deemed more user-friendly to ensure that the example in paragraph 4 will work by requiring implementations to use an unsigned underlying type if the enumeration type has no negative values. (If the implementation is allowed to choose a signed representation for such bit-fields, the comparison against TRUE will be false.)

In addition, it was observed that there is an apparent circularity between 9.7.1 [dcl.enum] paragraph 7 and 11.4.10 [class.bit] paragraph 4 that should be resolved.

Proposed resolution (April, 2006):

  1. Replace 9.7.1 [dcl.enum] paragraph 7, deleting the embedded footnote 85, with the following:

    For an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a two's complement representation and 0 for a one's complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin|-K,|emax|) and equal to 2M-1, where M is a non-negative integer. bmin is zero if emin is non-negative and -(bmax+K) otherwise. The size of the smallest bit-field large enough to hold all the values of the enumeration type is max(M,1) if bmin is zero and M+1 otherwise. It is possible to define an enumeration that has values not defined by any of its enumerators.
  2. Add the indicated text to the second sentence of 11.4.10 [class.bit] paragraph 4:

    If the value of an enumerator is stored into a bit-field of the same enumeration type and the number of bits in the bit-field is large enough to hold all the values of that enumeration type (9.7.1 [dcl.enum]), the original enumerator value and the value of the bit-field shall compare equal.



436. Problem in example in 9.6 paragraph 4

Section: 11.4.10  [class.bit]     Status: CD1     Submitter: Roberto Santos     Date: 10 October 2003

[Voted into WP at October 2004 meeting.]

It looks like the example on 11.4.10 [class.bit] paragraph 4 has both the enum and function contributing the identifier "f" for the same scope.

  enum BOOL { f=0, t=1 };
  struct A {
    BOOL b:1;
  };
  A a;
  void f() {
    a.b = t;
    if (a.b == t) // shall yield true
    { /* ... */ }
  }

Proposed resolution:

Change the example at the end of 11.4.10 [class.bit]/4 from:

  enum BOOL { f=0, t=1 };
  struct A {
    BOOL b:1;
  };
  A a;
  void f() {
    a.b = t;
    if (a.b == t) // shall yield true
    { /* ... */ }
  }

To:

  enum BOOL { FALSE=0, TRUE=1 };
  struct A {
    BOOL b:1;
  };
  A a;
  void f() {
    a.b = TRUE;
    if (a.b == TRUE) // shall yield true
    { /* ... */ }
  }




198. Definition of "use" in local and nested classes

Section: 11.6  [class.local]     Status: CD1     Submitter: Erwin Unruh     Date: 27 Jan 2000

[Voted into WP at April 2003 meeting.]

11.6 [class.local] paragraph 1 says,

Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators from the enclosing scope.
The definition of when an object or function is "used" is found in 6.3 [basic.def.odr] paragraph 2 and essentially says that the operands of sizeof and non-polymorphic typeid operators are not used. (The resolution for issue 48 will add contexts in which integral constant expressions are required to the list of non-uses.)

This definition of "use" would presumably allow code like

    void foo() {
        int i;
        struct S {
            int a[sizeof(i)];
        };
    };
which is required for C compatibility.

However, the restrictions on nested classes in 11.4.12 [class.nest] paragraph 1 are very similar to those for local classes, and the example there explicitly states that a reference in a sizeof expression is a forbidden use (abbreviated for exposition):

    class enclose {
    public:
        int x;
        class inner {
            void f(int i)
            {
                int a = sizeof(x);  // error: refers to enclose::x
            }
        };
    };

[As a personal note, I have seen real-world code that was exactly like this; it was hard to persuade the author that the required writearound, sizeof(((enclose*) 0)->x), was an improvement over sizeof(x). —wmm]

Similarly, 11.4 [class.mem] paragraph 9 would appear to prohibit examples like the following:

    struct B {
        char x[10];
    };
    struct D: B {
        char y[sizeof(x)];
    };

Suggested resolution: Add cross-references to 6.3 [basic.def.odr] following the word "use" in both 11.4.12 [class.nest] and 11.6 [class.local] , and change the example in 11.4.12 [class.nest] to indicate that a reference in a sizeof expression is permitted. In 11.4 [class.mem] paragraph 9, "referred to" should be changed to "used" with a cross_reference to 6.3 [basic.def.odr].

Notes from 10/01 meeting:

It was noted that the suggested resolution did not make the sizeof() example in 11.4.12 [class.nest] valid. Although the reference to the argument of sizeof() is not regarded as a use, the right syntax must be used nonetheless to reference a non-static member from the enclosing class. The use of the member name by itself is not valid. The consensus within the core working group was that nothing should be done about this case. It was later discovered that 11.4.9 [class.static] paragraph 3 states that

The definition of a static member shall not use directly the names of the nonstatic members of its class or of a base class of its class (including as operands of the sizeof operator). The definition of a static member may only refer to these members to form pointer to members (7.6.2.2 [expr.unary.op]) or with the class member access syntax (7.6.1.5 [expr.ref]).

This seems to reinforce the decision of the working group.

The use of "use" should still be cross-referenced. The statements in 11.4.12 [class.nest] and 11.6 [class.local] should also be rewritten to state the requirement positively rather than negatively as the list of "can't"s is already missing some cases such as template parameters.

Notes from the 4/02 meeting:

We backed away from "use" in the technical sense, because the requirements on the form of reference are the same whether or not the reference occurs inside a sizeof.

Proposed Resolution (revised October 2002):

In 11.4 [class.mem] paragraph 9, replace

Except when used to form a pointer to member (7.6.2.2 [expr.unary.op]), when used in the body of a nonstatic member function of its class or of a class derived from its class (11.4.3 [class.mfct.non.static]), or when used in a mem-initializer for a constructor for its class or for a class derived from its class (11.9.3 [class.base.init]), a nonstatic data or function member of a class shall only be referred to with the class member access syntax (7.6.1.5 [expr.ref]).

with the following paragraph

Each occurrence in an expression of the name of a nonstatic data member or nonstatic member function of a class shall be expressed as a class member access (7.6.1.5 [expr.ref]), except when it appears in the formation of a pointer to member (7.6.2.2 [expr.unary.op]), when it appears in the body of a nonstatic member function of its class or of a class derived from its class (11.4.3 [class.mfct.non.static]), or when it appears in a mem-initializer for a constructor for its class or for a class derived from its class (11.9.3 [class.base.init]).

In 11.4.12 [class.nest] paragraph 1, replace the last sentence,

Except by using explicit pointers, references, and object names, declarations in a nested class can use only type names, static members, and enumerators from the enclosing class.

with the following

[Note: In accordance with 11.4 [class.mem], except by using explicit pointers, references, and object names, declarations in a nested class shall not use nonstatic data members or nonstatic member functions from the enclosing class. This restriction applies in all constructs including the operands of the sizeof operator.]

In the example following 11.4.12 [class.nest] paragraph 1, change the comment on the first statement of function f to emphasize that sizeof(x) is an error. The example reads in full:

  int x;
  int y;
  class enclose {
  public:
    int x;
    static int s;
    class inner {
      void f(int i)
      {
        int a = sizeof(x);  // error: direct use of enclose::x even in sizeof
        x = i;              // error: assign to enclose::x
        s = i;              // OK: assign to enclose::s
        ::x = i;            // OK: assign to global x
        y = i;              // OK: assign to global y
      }
      void g(enclose* p, int i)
      {
        p->x = i;        // OK: assign to enclose::x
      }
    };
  };

  inner* p = 0;             // error: inner not in scope



484. Can a base-specifier name a cv-qualified class type?

Section: 11.7  [class.derived]     Status: CD1     Submitter: Richard Corden     Date: 21 Oct 2004

[Voted into WP at the October, 2006 meeting.]

Issue 298, recently approved, affirms that cv-qualified class types can be used as nested-name-specifiers. Should the same be true for base-specifiers?

Rationale (April, 2005):

The resolution of issue 298 added new text to 11.3 [class.name] paragraph 5 making it clear that a typedef that names a cv-qualified class type is a class-name. Because the definition of base-specifier simply refers to class-name, it is already the case that cv-qualified class types are permitted as base-specifiers.

Additional notes (June, 2005):

It's not completely clear what it means to have a cv-qualified type as a base-specifier. The original proposed resolution for issue 298 said that “the cv-qualifiers are ignored,” but that wording is not in the resolution that was ultimately approved.

If the cv-qualifiers are not ignored, does that mean that the base-class subobject should be treated as always similarly cv-qualified, regardless of the cv-qualification of the derived-class lvalue used to access the base-class subobject? For instance:

    typedef struct B {
        void f();
        void f() const;
        int i;
    } const CB;

    struct D: CB { };

    void g(D* dp) {
        dp->f();    // which B::f?
        dp->i = 3;  // permitted?
    }

Proposed resolution (October, 2005):

  1. Change 11.3 [class.name] paragraph 5 as indicated:

  2. A typedef-name (9.2.4 [dcl.typedef]) that names a class type, or a cv-qualified version thereof, is also a class-name, but class-name. If a typedef-name that names a cv-qualified class type is used where a class-name is required, the cv-qualifiers are ignored. A typedef-name shall not be used as the identifier in a class-head.
  3. Delete 9.2.4 [dcl.typedef] paragraph 8:

  4. [Note: if the typedef-name is used where a class-name (or enum-name) is required, the program is ill-formed. For example,

        typedef struct {
            S();     // error: requires a return type because S is
                      // an ordinary member function, not a constructor
        } S;
    

    end note]




390. Pure virtual must be defined when implicitly called

Section: 11.7.4  [class.abstract]     Status: CD1     Submitter: Daniel Frey     Date: 14 Nov 2002

[Voted into WP at March 2004 meeting.]

In 11.7.4 [class.abstract] paragraph 2, it reads:

A pure virtual function need be defined only if explicitly called with the qualified-id syntax (_N4567_.5.1.1 [expr.prim.general]).

This is IMHO incomplete. A dtor is a function (well, a "special member function", but this also makes it a function, right?) but it is called implicitly and thus without a qualified-id syntax. Another alternative is that the pure virtual function is called directly or indirectly from the ctor. Thus the above sentence which specifies when a pure virtual function need be defined ("...only if...") needs to be extended:

A pure virtual function need be defined only if explicitly called with the qualified-id syntax (_N4567_.5.1.1 [expr.prim.general]) or if implicitly called (11.4.7 [class.dtor] or 11.9.5 [class.cdtor]).

Proposed resolution:

Change 11.7.4 [class.abstract] paragraph 2 from

A pure virtual function need be defined only if explicitly called with the qualified-id syntax (_N4567_.5.1.1 [expr.prim.general]).

to

A pure virtual function need be defined only if explicitly called with, or as if with (11.4.7 [class.dtor]), the qualified-id syntax (_N4567_.5.1.1 [expr.prim.general]).

Note: 11.4.7 [class.dtor] paragraph 6 defines the "as if" cited.




8. Access to template arguments used in a function return type and in the nested name specifier

Section: 11.8  [class.access]     Status: CD1     Submitter: Mike Ball     Date: unknown

[Moved to DR at 4/01 meeting.]

Consider the following example:

    class A {
       class A1{};
       static void func(A1, int);
       static void func(float, int);
       static const int garbconst = 3;
     public:
       template < class T, int i, void (*f)(T, int) > class int_temp {};
       template<> class int_temp<A1, 5, func> { void func1() };
       friend int_temp<A1, 5, func>::func1();
       int_temp<A1, 5, func>* func2();
   };
   A::int_temp<A::A1, A::garbconst + 2, &A::func>* A::func2() {...}
ISSUE 1:

In 11.8 [class.access] paragraph 5 we have:

This means, if we take the loosest possible definition of "access from a particular scope", that we have to save and check later the following names

      A::int_temp
      A::A1
      A::garbconst (part of an expression)
      A::func (after overloading is done)
I suspect that member templates were not really considered when this was written, and that it might have been written rather differently if they had been. Note that access to the template arguments is only legal because the class has been declared a friend, which is probably not what most programmers would expect.

Rationale:

Not a defect. This behavior is as intended.

ISSUE 2:

Now consider void A::int_temp<A::A1, A::garbconst + 2, &A::func>::func1() {...} By my reading of 11.8.8 [class.access.nest] , the references to A::A1, A::garbconst and A::func are now illegal, and there is no way to define this function outside of the class. Is there any need to do anything about either of these Issues?

Proposed resolution (04/01):

The resolution for this issue is contained in the resolution for issue 45.




494. Problems with the resolution of issue 45

Section: 11.8  [class.access]     Status: CD1     Submitter: Lloyd J. Lewins     Date: 17 Dec 2004

[Voted into WP at the October, 2006 meeting.]

The proposed resolution for issue 45 inserts the following sentence after 11.8 [class.access] paragraph 1:

A member of a class can also access all names as the class of which it is a member.

I don't think that this is correctly constructed English. I see two possibilities:

  1. This is a typo, and the correct change is:

    A member of a class can also access all names of the class of which it is a member.

  2. The intent is something more like:

    A member of a nested class can also access all names accessible by any other member of the class of which it is a member.

[Note: this was editorially corrected at the time defect resolutions were being incorporated into the Working Paper to read, “...can also access all the names declared in the class of which it is a member,” which is essentially the same as the preceding option 1.]

I would prefer to use the language proposed for 11.8.8 [class.access.nest]:

A nested class is a member and as such has the same access rights as any other member.

A second problem is with the text in 11.8.4 [class.friend] paragraph 2:

[Note: this means that access to private and protected names is also granted to member functions of the friend class (as if the functions were each friends) and to the static data member definitions of the friend class. This also means that private and protected type names from the class granting friendship can be used in the base-clause of a nested class of the friend class. However, the declarations of members of classes nested within the friend class cannot access the names of private and protected members from the class granting friendship. Also, because the base-clause of the friend class is not part of its member declarations, the base-clause of the friend class cannot access the names of the private and protected members from the class granting friendship. For example,
    class A {
        class B { };
        friend class X;
    };
    class X : A::B {     // ill-formed: A::B cannot be accessed
                         // in the base-clause for X
        A::B mx;         // OK: A::B used to declare member of X
        class Y: A::B {  // OK: A::B used to declare member of X
            A::B my;     // ill-formed: A::B cannot be accessed
                         // to declare members of nested class of X
        };
    };
end note]

This seems to be an oversight. The proposed change to 11.8.8 [class.access.nest] paragraph 1 would appear to have eliminated the restrictions on nested class access. However, at least one compiler (gcc 3.4.3) doesn't appear to take my view, and continues with the restrictions on access by classes within a friend class, while implementing the rest of the resolution of issue 45.

Note (March, 2005):

Andreas Hommel: I think issue 45 requires an additional change in 11.4.12 [class.nest] paragraph 4:

Like a member function, a friend function (11.8.4 [class.friend]) defined within a nested class is in the lexical scope of that class; it obeys the same rules for name binding as a static member function of that class (11.4.9 [class.static]) and has no special access rights to members of an enclosing class.

I believe the “no special access rights” language should be removed.

Proposed resolution (October, 2005):

This issue is resolved by the resolution of issue 372.




9. Clarification of access to base class members

Section: 11.8.3  [class.access.base]     Status: CD1     Submitter: unknown     Date: unknown

[Moved to DR at 4/01 meeting.]

11.8.3 [class.access.base] paragraph 4 says:

A base class is said to be accessible if an invented public member of the base class is accessible. If a base class is accessible, one can implicitly convert a pointer to a derived class to a pointer to that base class.
Given the above, is the following well-formed?
    class D;

    class B
    {
     protected:
       int b1;

       friend void foo( D* pd );
    };

    class D : protected B { };

    void foo( D* pd )
    {
       if ( pd->b1 > 0 ); // Is 'b1' accessible?
    }
Can you access the protected member b1 of B in foo? Can you convert a D* to a B* in foo?

1st interpretation:

A public member of B is accessible within foo (since foo is a friend), therefore foo can refer to b1 and convert a D* to a B*.

2nd interpretation:

B is a protected base class of D, and a public member of B is a protected member of D and can only be accessed within members of D and friends of D. Therefore foo cannot refer to b1 and cannot convert a D* to a B*.

(See J16/99-0042 = WG21 N1218.)

Proposed Resolution (04/01):

  1. Add preceding 11.8.3 [class.access.base] paragraph 4:
    A base class B of N is accessible at R, if
    • an invented public member of B would be a public member of N, or
    • R occurs in a member or friend of class N, and an invented public member of B would be a private or protected member of N, or
    • R occurs in a member or friend of a class P derived from N, and an invented public member of B would be a private or protected member of P, or
    • there exists a class S such that B is a base class of S accessible at R and S is a base class of N accessible at R. [Example:
          class B {
          public:
              int m;
          };
      
          class S: private B {
              friend class N;
          };
      
          class N: private S {
              void f() {
      	    B* p = this;  // OK because class S satisfies the
      			// fourth condition above: B is a base
      			// class of N accessible in f() because
      			// B is an accessible base class of S
      			// and S is an accessible base class of N.
              }
          };
      
      end example]
  2. Delete the first sentence of 11.8.3 [class.access.base] paragraph 4:
    A base class is said to be accessible if an invented public member of the base class is accessible.
  3. Replace the last sentence ("A member m is accessible...") by the following:
    A member m is accessible at the point R when named in class N if
    • m as a member of N is public, or
    • m as a member of N is private, and R occurs in a member or friend of class N, or
    • m as a member of N is protected, and R occurs in a member or friend of class N, or in a member or friend of a class P derived from N, where m as a member of P is private or protected, or
    • there exists a base class B of N that is accessible at R, and m is accessible at R when named in class B. [Example:...

The resolution for issue 207 modifies this wording slightly.




16. Access to members of indirect private base classes

Section: 11.8.3  [class.access.base]     Status: CD1     Submitter: unknown     Date: unknown

[Moved to DR at 4/01 meeting.]

The text in 11.8.3 [class.access.base] paragraph 4 does not seem to handle the following cases:

    class D;

    class B {
    private:
        int i;
        friend class D;
    };

    class C : private B { };

    class D : private C {
        void f() {
            B::i; //1: well-formed?
            i;    //2: well-formed?
        }
    };
The member i is not a member of D and cannot be accessed in the scope of D. What is the naming class of the member i on line //1 and line //2?

Proposed Resolution (04/01): The resolution for this issue is contained in the resolution for issue 9..




207. using-declarations and protected access

Section: 11.8.3  [class.access.base]     Status: CD1     Submitter: Jason Merrill     Date: 28 Feb 2000

[Moved to DR at 10/01 meeting.]

Consider the following example:

  class A {
  protected:
    static void f() {};
  };

  class B : A {
  public:
    using A::f;
    void g() {
      A::f();
    }
  };

The standard says in 11.8.3 [class.access.base] paragraph 4 that the call to A::f is ill-formed:

A member m is accessible when named in class N if

Here, m is A::f and N is A.

It seems clear to me that the third bullet should say "public, private or protected".

Steve Adamczyk:The words were written before using-declarations existed, and therefore didn't anticipate this case.

Proposed resolution (04/01):

Modify the third bullet of the third change ("A member m is accessible...") in the resolution of issue 9 to read "public, private, or protected" instead of "private or protected."




77. The definition of friend does not allow nested classes to be friends

Section: 11.8.4  [class.friend]     Status: CD1     Submitter: Judy Ward     Date: 15 Dec 1998

[Moved to DR at 4/02 meeting.]

The definition of "friend" in 11.8.4 [class.friend] says:

A friend of a class is a function or class that is not a member of the class but is permitted to use the private and protected member names from the class. ...
A nested class, i.e. INNER in the example below, is a member of class OUTER. The sentence above states that it cannot be a friend. I think this is a mistake.
    class OUTER {
        class INNER;
        friend class INNER;
        class INNER {};
    };

Proposed resolution (04/01):

Change the first sentence of 11.8.4 [class.friend] as follows:

A friend of a class is a function or class that is not a member of the class but is allowed given permission to use the private and protected member names from the class. The name of a friend is not in the scope of the class, and the friend is not called with the member access operators (7.6.1.5 [expr.ref]) unless it is a member of another class. A class specifies its friends, if any, by way of friend declarations. Such declarations give special access rights to the friends, but they do not make the nominated friends members of the befriending class.



500. Access in base-specifiers of friend and nested classes

Section: 11.8.4  [class.friend]     Status: CD1     Submitter: Andreas Hommel     Date: 25 Jan 2005

[Voted into WP at the October, 2006 meeting.]

I don't know the reason for this distinction, but it seems to be surprising that Base::A is legal and D is illegal in this example:

    class D;
    class Base
    {
        class A;
        class B;
        friend class D;
    };
    class Base::B
    {
    };
    class Base::A : public Base::B  // OK because of issue 45
    {
    };
    class D : public Base::B        // illegal because of 11.4p4
    {
    };

Shouldn't this be consistent (either way)?

Notes from the April, 2005 meeting:

In discussing issue 372, the CWG decided that access in the base-specifiers of a class should be the same as for its members, and that resolution will apply to friend declarations, as well.

Proposed resolution (October, 2005):

This issue is resolved by the resolution of issue 372.




385. How does protected member check of 11.5 interact with using-declarations?

Section: 11.8.5  [class.protected]     Status: CD1     Submitter: Vincent Korstanje     Date: 24 Sep 2002

[Voted into WP at October 2004 meeting.]

We consider it not unreasonable to do the following

  class A {
    protected:
    void g();
  };
  class B : public A {
    public:
      using A::g; // B::g is a public synonym for A::g
  };

  class C: public A {
    void foo();
  };

  void C::foo() {
    B b;
    b.g();
  }

However the EDG front-end does not like and gives the error

  #410-D: protected function "A::g" is not accessible through a "B" pointer or  object
    b.g();
      ^

Steve Adamczyk: The error in this case is due to 11.8.5 [class.protected] of the standard, which is an additional check on top of the other access checking. When that section says "a protected nonstatic member function ... of a base class" it doesn't indicate whether the fact that there is a using-declaration is relevant. I'd say the current wording taken at face value would suggest that the error is correct -- the function is protected, even if the using-declaration for it makes it accessible as a public function. But I'm quite sure the wording in 11.8.5 [class.protected] was written before using-declarations were invented and has not been reviewed since for consistency with that addition.

Notes from April 2003 meeting:

We agreed that the example should be allowed.

Proposed resolution (April 2003, revised October 2003):

Change 11.8.5 [class.protected] paragraph 1 from

When a friend or a member function of a derived class references a protected nonstatic member function or protected nonstatic data member of a base class, an access check applies in addition to those described earlier in 11.8 [class.access]. [Footnote: This additional check does not apply to other members, e.g. static data members or enumerator member constants.] Except when forming a pointer to member (7.6.2.2 [expr.unary.op]), the access must be through a pointer to, reference to, or object of the derived class itself (or any class derived from that class (7.6.1.5 [expr.ref]). If the access is to form a pointer to member, the nested-name-specifier shall name the derived class (or any class derived from that class).

to

An additional access check beyond those described earlier in 11.8 [class.access] is applied when a nonstatic data member or nonstatic member function is a protected member of its naming class (11.8.3 [class.access.base]). [Footnote: This additional check does not apply to other members, e.g., static data members or enumerator member constants.] As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C. If the access is to form a pointer to member (7.6.2.2 [expr.unary.op]), the nested-name-specifier shall name C or a class derived from C. All other accesses involve a (possibly implicit) object expression (7.6.1.5 [expr.ref]). In this case, the class of the object expression shall be C or a class derived from C.

Additional discussion (September, 2004):

Steve Adamczyk: I wonder if this wording is incorrect. Consider:

    class A {
    public:
      int p;
    };
    class B : protected A {
      // p is a protected member of B
    };
    class C : public B {
      friend void fr();
    };
    void fr() {
      B *pb = new B;
      pb->p = 1;  // Access okay?  Naming class is B, p is a protected member of B,
                  // the "C" of the issue 385 wording is C, but access is not via
                  // an object of type C or a derived class thereof.
    }

I think the formulation that the member is a protected member of its naming class is not what we want. I think we intended that the member is protected in the declaration that is found, where the declaration found might be a using-declaration.

Mike Miller: I think the proposed wording makes the access pb->p ill-formed, and I think that's the right thing to do.

First, protected inheritance of A by B means that B intends the public and protected members of A to be part of B's implementation, available to B's descendants only. (That's why there's a restriction on converting from B* to A*, to enforce B's intention on the use of members of A.) Consequently, I see no difference in access policy between your example and

    class B {
    protected:
        int p;
    };

Second, the reason we have this rule is that C's use of inherited protected members might be different from their use in a sibling class, say D. Thus members and friends of C can only use B::p in a manner consistent with C's usage, i.e., in C or derived-from-C objects. If we rewrote your example slightly,

    class D: public B { };

    void fr(B* pb) {
        pb->p = 1;
    }

    void g() {
        fr(new D);
    }

it's clear that the intent of this rule is broken — fr would be accessing B::p assuming C's policies when the object in question actually required D's policies.

(See also issues 471 and 472.)




10. Can a nested class access its own class name as a qualified name if it is a private member of the enclosing class?

Section: 11.8.8  [class.access.nest]     Status: CD1     Submitter: Josee Lajoie     Date: unknown

[Moved to DR at 4/01 meeting.]

Paragraph 1 says: "The members of a nested class have no special access to members of an enclosing class..."

This prevents a member of a nested class from being defined outside of its class definition. i.e. Should the following be well-formed?

    class D {
        class E {
            static E* m;
        };
    };

    D::E* D::E::m = 1; // ill-formed
This is because the nested class does not have access to the member E in D. 11.8 [class.access] paragraph 5 says that access to D::E is checked with member access to class E, but unfortunately that doesn't give access to D::E. 11.8 [class.access] paragraph 6 covers the access for D::E::m, but it doesn't affect the D::E access. Are there any implementations that are standard compliant that support this?

Here is another example:

    class C {
        class B
        {
            C::B *t; //2 error, C::B is inaccessible
        };
    };
This causes trouble for member functions declared outside of the class member list. For example:
    class C {
        class B
        {
            B& operator= (const B&);
        };
    };

    C::B& C::B::operator= (const B&) { } //3
If the return type (i.e. C::B) is access checked in the scope of class B (as implied by 11.8 [class.access] paragraph 5) as a qualified name, then the return type is an error just like referring to C::B in the member list of class B above (i.e. //2) is ill-formed.

Proposed resolution (04/01):

The resolution for this issue is incorporated into the resolution for issue 45.




45. Access to nested classes

Section: 11.8.8  [class.access.nest]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 29 Sep 1998

[Moved to DR at 4/01 meeting.]

Example:

    #include <iostream.h>

    class C {  // entire body is private
        struct Parent {
            Parent() { cout << "C::Parent::Parent()\n"; }
        };

        struct Derived : Parent {
            Derived() { cout << "C::Derived::Derived()\n"; }
        };

        Derived d;
    };


    int main() {
        C c;      //  Prints message from both nested classes
        return 0;
    }
How legal/illegal is this? Paragraphs that seem to apply here are:

11.8 [class.access] paragraph 1:

A member of a class can be
and 11.8.8 [class.access.nest] paragraph 1:
The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (11.8 [class.access] ) shall be obeyed. [...]
This makes me think that the ': Parent' part is OK by itself, but that the implicit call of 'Parent::Parent()' by 'Derived::Derived()' is not.

From Mike Miller:

I think it is completely legal, by the reasoning given in the (non-normative) 11.8.8 [class.access.nest] paragraph 2. The use of a private nested class as a base of another nested class is explicitly declared to be acceptable there. I think the rationale in the comments in the example ("// OK because of injection of name A in A") presupposes that public members of the base class will be public members in a (publicly-derived) derived class, regardless of the access of the base class, so the constructor invocation should be okay as well.

I can't find anything normative that explicitly says that, though.

(See also papers J16/99-0009 = WG21 N1186, J16/00-0031 = WG21 N1254, and J16/00-0045 = WG21 N1268.)

Proposed Resolution (04/01):

  1. Insert the following as a new paragraph following 11.8 [class.access] paragraph 1:

    A member of a class can also access all names as the class of which it is a member. A local class of a member function may access the same names that the member function itself may access. [Footnote: Access permissions are thus transitive and cumulative to nested and local classes.]
  2. Delete 11.8 [class.access] paragraph 6.

  3. In 11.8.8 [class.access.nest] paragraph 1, change

    The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (11.8 [class.access]) shall be obeyed.

    to

    A nested class is a member and as such has the same access rights as any other member.

    Change

        B b;       // error: E::B is private
    

    to

        B b;       // Okay, E::I can access E::B
    

    Change

        p->x = i;      // error: E::x is private
    

    to

        p->x = i;      // Okay, E::I can access E::x
    
  4. Delete 11.8.8 [class.access.nest] paragraph 2.

(This resolution also resolves issues 8 and 10.




510. Default initialization of POD classes?

Section: 11.9  [class.init]     Status: CD1     Submitter: Mike Miller     Date: 18 Mar 2005

[Voted into WP at April, 2006 meeting.]

9.4 [dcl.init] paragraph 10 makes it clear that non-static POD class objects with no initializer are left uninitialized and have an indeterminate initial value:

If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for a non-static object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.

11.9 [class.init] paragraph 1, however, implies that all class objects without initializers, whether POD or not, are default-initialized:

When no initializer is specified for an object of (possibly cv-qualified) class type (or array thereof), or the initializer has the form (), the object is initialized as specified in 9.4 [dcl.init]. The object is default-initialized if there is no initializer, or value-initialized if the initializer is ().

Proposed resolution (October, 2005):

Remove the indicated words from 11.9 [class.init] paragraph 1:

When no initializer is specified for an object of (possibly cv-qualified) class type (or array thereof), or the initializer has the form (), the object is initialized as specified in 9.4 [dcl.init]. The object is default-initialized if there is no initializer, or value-initialized if the initializer is ().



162. (&C::f)() with nonstatic members

Section: 12.2.2.2  [over.match.call]     Status: CD1     Submitter: Steve Adamczyk     Date: 26 Aug 1999

[Moved to DR at October 2002 meeting.]

12.2.2.2 [over.match.call] paragraph 3 says that when a call of the form

   (&C::f)()
is written, the set of overloaded functions named by C::f must not contain any nonstatic member functions. A footnote gives the rationale: if a member of C::f is a nonstatic member function, &C::f is a pointer to member constant, and therefore the call is invalid.

This is clear, it's implementable, and it doesn't directly contradict anything else in the standard. However, I'm not sure it's consistent with some similar cases.

In 12.3 [over.over] paragraph 5, second example, it is made amply clear that when &C::f is used as the address of a function, e.g.,

   int (*pf)(int) = &C::f;
the overload set can contain both static and nonstatic member functions. The function with the matching signature is selected, and if it is nonstatic &C::f is a pointer to member function, and otherwise &C::f is a normal pointer to function.

Similarly, 12.2.2.2.2 [over.call.func] paragraph 3 makes it clear that

   C::f();
is a valid call even if the overload set contains both static and nonstatic member functions. Overload resolution is done, and if a nonstatic member function is selected, an implicit this-> is added, if that is possible.

Those paragraphs seem to suggest the general rule that you do overload resolution first and then you interpret the construct you have according to the function selected. The fact that there are static and nonstatic functions in the overload set is irrelevant; it's only necessary that the chosen function be static or nonstatic to match the context.

Given that, I think it would be more consistent if the (&C::f)() case would also do overload resolution first. If a nonstatic member is chosen, the program would be ill-formed.

Proposed resolution (04/01):

  1. Change the indicated text in 12.2.2.2 [over.match.call] paragraph 3:

    The fourth case arises from a postfix-expression of the form &F, where F names a set of overloaded functions. In the context of a function call, the set of functions named by F shall contain only non-member functions and static member functions. [Footnote: If F names a non-static member function, &F is a pointer-to-member, which cannot be used with the function call syntax.] And in this context using &F behaves the same as using &F is treated the same as the name F by itself. Thus, (&F)(expression-listopt) is simply (F)(expression-listopt), which is discussed in 12.2.2.2.2 [over.call.func]. If the function selected by overload resolution according to 12.2.2.2.2 [over.call.func] is a nonstatic member function, the program is ill-formed. [Footnote: When F is a nonstatic member function, a reference of the form &A::F is a pointer-to-member, which cannot be used with the function-call syntax, and a reference of the form &F is an invalid use of the "&" operator on a nonstatic member function.] (The resolution of &F in other contexts is described in 12.3 [over.over].)



239. Footnote 116 and Koenig lookup

Section: 12.2.2.2.2  [over.call.func]     Status: CD1     Submitter: Steve Clamage     Date: 2 Aug 2000

[Moved to DR at 4/01 meeting.]

In describing non-member functions in an overload set, footnote 116 (12.2.2.2.2 [over.call.func]) says,
Because of the usual name hiding rules, these will be introduced by declarations or by using-directives all found in the same block or all found at namespace scope.

At least in terms of the current state of the Standard, this is not correct: a block extern declaration does not prevent Koenig lookup from occurring. For example,

    enum E { zero };
    void f(E);
    void g() {
        void f(int);
        f(zero);
    }

In this example, the overload set will include declarations from both namespace and block scope.

(See also issue 12.)

Proposed resolution (04/01):

  1. In 6.5.4 [basic.lookup.argdep] paragraph 2, change

    If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated namespaces and classes are not considered.

    to

    If the ordinary unqualified lookup of the name finds the declaration of a class member function, or a block-scope function declaration that is not a using-declaration, the associated namespaces and classes are not considered.

    and change the example to:

        namespace NS {
            class T { };
            void f(T);
            void g(T, int);
        }
        NS::T parm;
        void g(NS::T, float);
        int main() {
            f(parm);            // OK: calls NS::f
            extern void g(NS::T, float);
            g(parm, 1);         // OK: calls g(NS::T, float)
        }
    
  2. In 12.2.2.2.2 [over.call.func] paragraph 3 from:

    If the name resolves to a non-member function declaration, that function and its overloaded declarations constitute the set of candidate functions.

    to

    If the name resolves to a set of non-member function declarations, that set of functions constitutes the set of candidate functions.

    Note that this text is also edited by issue 364. Also, remove the associated footnote 116.




364. Calling overloaded function with static in set, with no object

Section: 12.2.2.2.2  [over.call.func]     Status: CD1     Submitter: Steve Adamczyk     Date: 23 July 2002

[Voted into WP at October 2003 meeting.]

Consider this program:

   struct S {
     static void f (int);
     void f (char);
   };

   void g () {
     S::f ('a');
   }

G++ 3.1 rejects it, saying:

   test.C:7: cannot call member function `void S::f(char)' without object

Mark Mitchell: It looks to me like G++ is correct, given 12.2.2.2.2 [over.call.func]. This case is the "unqualified function call" case described in paragraph 3 of that section. ("Unqualified" here means that there is no "x->" or "x." in front of the call, not that the name is unqualified.)

That paragraph says that you first do name lookup. It then asks you to look at what declaration is returned. (That's a bit confusing; you presumably get a set of declarations. Or maybe not; the name lookup section says that if name lookup finds a non-static member in a context like this the program is in error. But surely this program is not erroneous. Hmm.)

Anyhow, you have -- at least -- "S::f(char)" as the result of the lookup.

The keyword "this" is not in scope, so "all overloaded declarations of the function name in T become candidate functions and a contrived object of type T becomes the implied object argument." That means we get both versions of "f" at this point. Then, "the call is ill-formed, however, if overload resolution selects one of the non-static members of T in this case." Since, in this case, "S::f(char)" is the winner, the program is ill-formed.

Steve Adamczyk: This result is surprising, because we've selected a function that we cannot call, when there is another function that can be called. This should either be ambiguous, or it should select the static member function. See also 12.2.2 [over.match.funcs] paragraph 2: "Similarly, when appropriate, the context can construct an argument list that contains an implied object argument..."

Notes from October 2002 meeting:

We agreed that g++ has it right, but the standard needs to be clearer.

Proposed resolution (October 2002, revised April 2003):

Change 12.2.2.2.2 [over.call.func] paragraphs 2 and 3 as follows:

In qualified function calls, the name to be resolved is an id-expression and is preceded by an -> or . operator. Since the construct A->B is generally equivalent to (*A).B, the rest of Clause 12 [over] assumes, without loss of generality, that all member function calls have been normalized to the form that uses an object and the . operator. Furthermore, Clause 12 [over] assumes that the postfix-expression that is the left operand of the . operator has type ``cv T'' where T denotes a class. [Footnote: Note that cv-qualifiers on the type of objects are significant in overload resolution for both lvalue and class rvalue objects. --- end footnote] Under this assumption, the id-expression in the call is looked up as a member function of T following the rules for looking up names in classes (6.5.2 [class.member.lookup]). If a member function is found, that function and its overloaded declarations The function declarations found by that lookup constitute the set of candidate functions. The argument list is the expression-list in the call augmented by the addition of the left operand of the . operator in the normalized member function call as the implied object argument (12.2.2 [over.match.funcs]).

In unqualified function calls, the name is not qualified by an -> or . operator and has the more general form of a primary-expression. The name is looked up in the context of the function call following the normal rules for name lookup in function calls (6.5.4 [basic.lookup.argdep] 6.5 [basic.lookup]). If the name resolves to a non-member function declaration, that function and its overloaded declarations The function declarations found by that lookup constitute the set of candidate functions. [Footnote: Because of the usual name hiding rules, these will be introduced by declarations or by using-directives all found in the same block or all found at namespace scope. --- end footnote] Because of the rules for name lookup, the set of candidate functions consists (1) entirely of non-member functions or (2) entirely of member functions of some class T. In case (1), tThe argument list is the same as the expression-list in the call. If the name resolves to a nonstatic member function, then the function call is actually a member function call. In case (2), the argument list is the expression-list in the call augmented by the addition of an implied object argument as in a qualified function call. If the keyword this (_N4868_.11.4.3.2 [class.this]) is in scope and refers to the class T of that member function, or a derived class thereof of T, then the function call is transformed into a normalized qualified function call using implied object argument is(*this) as the postfix-expression to the left of the . operator. The candidate functions and argument list are as described for qualified function calls above. If the keyword this is not in scope or refers to another class, then name resolution found a static member of some class T. In this case, all overloaded declarations of the function name in T become candidate functions and a contrived object of type T becomes the implied object argument. [Footnote: An implied object argument must be contrived to correspond to the implicit object parameter attributed to member functions during overload resolution. It is not used in the call to the selected function. Since the member functions all have the same implicit object parameter, the contrived object will not be the cause to select or reject a function. --- end footnote] If the argument list is augmented by a contrived object and The call is ill-formed, however, if overload resolution selects one of the non-static member functions of T, the call is ill-formed in this case.

Note that issue 239 also edits paragraph 3.




280. Access and surrogate call functions

Section: 12.2.2.2.3  [over.call.object]     Status: CD1     Submitter: Andrei Iltchenko     Date: 16 Apr 2001

[Voted into WP at October 2003 meeting.]

According to 12.2.2.2.3 [over.call.object] paragraph 2, when the primary-expression E in the function call syntax evaluates to a class object of type "cv T", a surrogate call function corresponding to an appropriate conversion function declared in a direct or indirect base class B of T is included or not included in the set of candidate functions based on class B being accessible.

For instance in the following code sample, as per the paragraph in question, the expression c(3) calls f2, instead of the construct being ill-formed due to the conversion function A::operator fp1 being inaccessible and its corresponding surrogate call function providing a better match than the surrogate call function corresponding to C::operator fp2:

    void  f1(int)  {   }
    void  f2(float)  {   }
    typedef void  (* fp1)(int);
    typedef void  (* fp2)(float);

    struct  A  {
       operator fp1()
       {   return  f1;   }
    };

    struct  B :  private A  {   };

    struct  C :  B  {
       operator  fp2()
       {   return  f2;   }
    };

    int  main()
    {
       C   c;
       c(3);  // f2 is called, instead of the construct being ill-formed.
       return  0;
    }

The fact that the accessibility of a base class influences the overload resolution process contradicts the fundamental language rule (6.5 [basic.lookup] paragraph 1, and 12.2 [over.match] paragraph 2) that access checks are applied only once name lookup and function overload resolution (if applicable) have succeeded.

Notes from 4/02 meeting:

There was some concern about whether 6.5.2 [class.member.lookup] (or anything else, for that matter) actually defines "ambiguous base class". See issue 39. See also issue 156.

Notes from October 2002 meeting:

It was suggested that the ambiguity check is done as part of the call of the conversion function.

Proposed resolution (revised October 2002):

In 12.2.2.2.3 [over.call.object] paragraph 2, replace the last sentence

Similarly, surrogate call functions are added to the set of candidate functions for each conversion function declared in an accessible base class provided the function is not hidden within T by another intervening declaration.

with

Similarly, surrogate call functions are added to the set of candidate functions for each conversion function declared in a base class of T provided the function is not hidden within T by another intervening declaration.

Replace 12.2.2.2.3 [over.call.object] paragraph 3

If such a surrogate call function is selected by overload resolution, its body, as defined above, will be executed to convert E to the appropriate function and then to invoke that function with the arguments of the call.
by
If such a surrogate call function is selected by overload resolution, the corresponding conversion function will be called to convert E to the appropriate function pointer or reference, and the function will then be invoked with the arguments of the call. If the conversion function cannot be called (e.g., because of an ambiguity), the program is ill-formed.




416. Class must be complete to allow operator lookup?

Section: 12.2.2.3  [over.match.oper]     Status: CD1     Submitter: Greg Comeau     Date: 22 May 2003

[Voted into WP at October 2004 meeting.]

Normally reference semantics allow incomplete types in certain contexts, but isn't this:

  class A;

  A& operator<<(A& a, const char* msg);
  void foo(A& a)
  {
    a << "Hello";
  }

required to be diagnosed because of the op<<? The reason being that the class may actually have an op<<(const char *) in it.

What is it? un- or ill-something? Diagnosable? No problem at all?

Steve Adamczyk: I don't know of any requirement in the standard that the class be complete. There is a rule that will instantiate a class template in order to be able to see whether it has any operators. But I wouldn't think one wants to outlaw the above example merely because the user might have an operator<< in the class; if he doesn't, he would not be pleased that the above is considered invalid.

Mike Miller: Hmm, interesting question. My initial reaction is that it just uses ::operator<<; any A::operator<< simply won't be considered in overload resolution. I can't find anything in the Standard that would say any different.

The closest analogy to this situation, I'd guess, would be deleting a pointer to an incomplete class; 7.6.2.9 [expr.delete] paragraph 5 says that that's undefined behavior if the complete type has a non-trivial destructor or an operator delete. However, I tend to think that that's because it deals with storage and resource management, not just because it might have called a different function. Generally, overload resolution that goes one way when it might have gone another with more declarations in scope is considered to be not an error, cf 9.9 [namespace.udecl] paragraph 9, _N4868_.13.8.4 [temp.nondep] paragraph 1, etc.

So my bottom line take on it would be that it's okay, it's up to the programmer to ensure that all necessary declarations are in scope for overload resolution. Worst case, it would be like the operator delete in an incomplete class -- undefined behavior, and thus not required to be diagnosed.

12.2.2.3 [over.match.oper] paragraph 3, bullet 1, says, "If T1 is a class type, the set of member candidates is the result of the qualified lookup of T1::operator@ (12.2.2.2.2 [over.call.func])." Obviously, that lookup is not possible if T1 is incomplete. Should 12.2.2.3 [over.match.oper] paragraph 3, bullet 1, say "complete class type"? Or does the inability to perform the lookup mean that the program is ill-formed? 6.3 [basic.def.odr] paragraph 4 doesn't apply, I don't think, because you don't know whether you'll be applying a class member access operator until you know whether the operator involved is a member or not.

Notes from October 2003 meeting:

We noticed that the title of this issue did not match the body. We checked the original source and then corrected the title (so it no longer mentions templates).

We decided that this is similar to other cases like deleting a pointer to an incomplete class, and it should not be necessary to have a complete class. There is no undefined behavior.

Proposed Resolution (October 2003):

Change the first bullet of 12.2.2.3 [over.match.oper] paragraph 3 to read:

If T1 is a complete class type, the set of member candidates is the result of the qualified lookup of T1::operator@ (12.2.2.2.2 [over.call.func]); otherwise, the set of member candidates is empty.



60. Reference binding and valid conversion sequences

Section: 12.2.4.2.5  [over.ics.ref]     Status: CD1     Submitter: Steve Adamczyk     Date: 13 Oct 1998

[Moved to DR at October 2002 meeting.]

Does dropping a cv-qualifier on a reference binding prevent the binding as far as overload resolution is concerned? Paragraph 4 says "Other restrictions on binding a reference to a particular argument do not affect the formation of a conversion sequence." This was intended to refer to things like access checking, but some readers have taken that to mean that any aspects of reference binding not mentioned in this section do not preclude the binding.

Proposed resolution (10/01):

In 12.2.4.2.5 [over.ics.ref] paragraph 4 add the indicated text:

Other restrictions on binding a reference to a particular argument that are not based on the types of the reference and the argument do not affect the formation of a standard conversion sequence, however.



115. Address of template-id

Section: 12.3  [over.over]     Status: CD1     Submitter: John Spicer     Date: 7 May 1999

[Voted into WP at October 2003 meeting.]

    template <class T> void f(T);
    template <class T> void g(T);
    template <class T> void g(T,T);

    int main()
    {
        (&f<int>);
        (&g<int>);
    }
The question is whether &f<int> identifies a unique function. &g<int> is clearly ambiguous.

12.3 [over.over] paragraph 1 says that a function template name is considered to name a set of overloaded functions. I believe it should be expanded to say that a function template name with an explicit template argument list is also considered to name a set of overloaded functions.

In the general case, you need to have a destination type in order to identify a unique function. While it is possible to permit this, I don't think it is a good idea because such code depends on there only being one template of that name that is visible.

The EDG front end issues an error on this use of "f". egcs 1.1.1 allows it, but the most current snapshot of egcs that I have also issues an error on it.

It has been pointed out that when dealing with nontemplates, the rules for taking the address of a single function differ from the rules for an overload set, but this asymmetry is needed for C compatibility. This need does not exist for the template case.

My feeling is that a general rule is better than a general rule plus an exception. The general rule is that you need a destination type to be sure that the operation will succeed. The exception is when there is only one template in the set and only then when you provide values for all of the template arguments.

It is true that in some cases you can provide a shorthand, but only if you encourage a fragile coding style (that will cause programs to break when additional templates are added).

I think the standard needs to specify one way or the other how this case should be handled. My recommendation would be that it is ill-formed.

Nico Josuttis: Consider the following example:

    template <int VAL>
    int add (int elem)
    {
	return elem + VAL;
    }

    std::transform(coll.begin(), coll.end(),
		   coll.begin(),
		   add<10>);

If John's recommendation is adopted, this code will become ill-formed. I bet there will be a lot of explanation for users necessary why this fails and that they have to change add<10> to something like (int (*)(int))add<10>.

This example code is probably common practice because this use of the STL is typical and is accepted in many current implementations. I strongly urge that this issue be resolved in favor of keeping this code valid.

Bill Gibbons: I find this rather surprising. Shouldn't a template-id which specifies all of the template arguments be treated like a declaration-only explicit instantiation, producing a set of ordinary function declarations? And when that set happens to contain only one function, shouldn't the example code work?

(See also issue 250.)

Notes from 04/01 meeting:

The consensus of the group was that the add example should not be an error.

Proposed resolution (October 2002):

In 13.4 add to the end of paragraph 2:

[Note: As described in 13.10.2 [temp.arg.explicit], if deduction fails and the function template name is followed by an explicit template argument list, the template-id is then examined to see whether it identifies a single function template specialization. If it does, the template-id is considered to be an lvalue for that function template specialization. The target type is not used in that determination.]

In 13.10.2 [temp.arg.explicit] paragraph 2 insert before the first example:

In contexts where deduction is done and fails, or in contexts where deduction is not done, if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.

Change the first example of 13.10.2 [temp.arg.explicit] paragraph 2:

  template<class X, class Y> X f(Y);
  void g()
  {
    int i = f<int>(5.6);    // Y is deduced to be double
    int j = f(5.6);         // ill-formed: X cannot be deduced
  }
to read:
  template<class X, class Y> X f(Y);
  void g()
  {
    int i = f<int>(5.6);    // Y is deduced to be double
    int j = f(5.6);         // ill-formed: X cannot be deduced
    f<void>(f<int, bool>);  // Y for outer f deduced to be
                            //   int (*)(bool)
    f<void>(f<int>);        // ill-formed: f<int> does not denote a
                            //   single template function specialization
  }

Note: This interacts with the resolution of issue 226 (default template arguments for function templates).




221. Must compound assignment operators be member functions?

Section: 12.4.3.2  [over.ass]     Status: CD1     Submitter: Jim Hyslop     Date: 3 Apr 2000

[Moved to DR at 4/01 meeting.]

Is the intent of 12.4.3.2 [over.ass] paragraph 1 that all assignment operators be non-static member functions (including operator+=, operator*=, etc.) or only simple assignment operators (operator=)?

Notes from 04/00 meeting:

Nearly all references to "assignment operator" in the IS mean operator= and not the compound assignment operators. The ARM was specific that this restriction applied only to operator=. If it did apply to compound assignment operators, it would be impossible to overload these operators for bool operands.

Proposed resolution (04/01):

  1. Change the title of 7.6.19 [expr.ass] from "Assignment operators" to "Assignment and compound assignment operators."

  2. Change the first sentence of 7.6.19 [expr.ass] paragraph 1 from

    There are several assignment operators, all of which group right-to-left. All require a modifiable lvalue as their left operand, and the type of an assignment expression is that of its left operand. The result of the assignment operation is the value stored in the left operand after the assignment has taken place; the result is an lvalue.

    to

    The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue with the type and value of the left operand after the assignment has taken place.

Additional note (10/00): Paragraphs 2-6 of 7.6.19 [expr.ass] should all be understood to apply to simple assignment only and not to compound assignment operators.




420. postfixexpression->scalar_type_dtor() inconsistent

Section: 12.4.6  [over.ref]     Status: CD1     Submitter: Markus Mauhart     Date: 8 June 2003

[Voted into WP at April, 2006 meeting.]

Lets start with the proposed solution. In 12.4.6 [over.ref], replace line ...

postfix-expression -> id-expression
.... with the lines ...
postfix-expression -> templateopt id-expression
postfix-expression -> pseudo-destructor-name
(This then is a copy of the two lines in 7.6.1 [expr.post] covering "->dtor")

Alternatively remove the sentence "It implements class member access using ->" and the syntax line following.

Reasons:

Currently stdc++ is inconsistent when handling expressions of the form "postfixexpression->scalar_type_dtor()": If "postfixexpression" is a pointer to the scalar type, it is OK, but if "postfixexpression" refers to any smart pointer class (e.g. iterator or allocator::pointer) with class specific CLASS::operator->() returning pointer to the scalar type, then it is ill-formed; so while c++98 does allow CLASS::operator->() returning pointer to scalar type, c++98 prohibits any '->'-expression involving this overloaded operator function.

Not only is this behaviour inconsistent, but also when comparing the corresponding chapters of c++pl2 and stdc++98 it looks like an oversight and unintended result. Mapping between stdc++98 and c++pl2:

c++pl2.r.5.2 -> 5.2 [expr.post]
c++pl2.r.5.2.4 -> 5.2.4 [expr.pseudo] + 5.2.5 [expr.ref]
c++pl2.r.13.4 -> 13.3.1.2 [over.match.oper]
c++pl2.r.13.4.6 -> 13.5.6 [over.ref]
For the single line of c++pl2.r.5.2 covering "->dtor", 5.2 [expr.post] has two lines. Analogously c++pl2.r.5.2.4 has been doubled to 5.2.4 [expr.pseudo] and 5.2.5 [expr.ref]. From 13.5.6 [over.ref], the sentence forbiding CLASS::operator->() returning pointer to scalar type has been removed. Only the single line of c++pl2.r.13.4.6 (<-> c++pl2.r.5.2's single line) has not gotten its 2nd line when converted into 13.5.6 [over.ref].

Additionally GCC32 does is right (but against 13.5.6 [over.ref]).

AFAICS this would not break old code except compilers like VC7x and Comeau4301.

It does not add new functionality, cause any expression class_type->scalar_type_dtor() even today can be substituted through (*class_type).scalar_type_dtor().

Without this fix, template functions like some_allocator<T>::destroy(p) must use "(*p).~T()" or "(*p).T::~T()" when calling the destructor, otherwise the simpler versions "p->~T()" or "p->T::~T()" could be used.

Sample code, compiled with GCC32, VC7[1] and Comeau4301:

struct A {};//any class

template <class T>
struct PTR
    {
    T& operator*  () const;
    T* operator-> () const;
    };

template <class T>
void f ()
    {
        {
        T*  p               ;
        p = new T           ;
        (*p).T::~T()        ;//OK
        p = new T           ;
        (*p).~T()           ;//OK
        p = new T           ;
        p->T::~T()          ;//OK
        p = new T           ;
        p->~T()             ;//OK
        }

        {
        PTR<T> p = PTR<T>() ;
        (*p).T::~T()        ;//OK
        (*p).~T()           ;//OK
        p.operator->()      ;//OK !!!
        p->T::~T()          ;//GCC32: OK; VC7x,Com4301: OK for A; ERROR w/ int
        p->~T()             ;//GCC32: OK; VC7x,Com4301: OK for A; ERROR w/ int
        }
    }

void test ()
    {
    f <A>  ();
    f <int>();
    }

Proposed resolution (April, 2005):

Change 12.4.6 [over.ref] paragraph 1 as indicated:

operator-> shall be a non-static member function taking no parameters. It implements the class member access using syntax that uses ->

An expression x->m is interpreted as (x.operator->())->m for a class object x of type T if T::operator->() exists and if the operator is selected as the best match function by the overload resolution mechanism (12.2 [over.match]).




425. Set of candidates for overloaded built-in operator with float operand

Section: 12.5  [over.built]     Status: CD1     Submitter: Daniel Frey     Date: 30 June 2003

[Voted into WP at March 2004 meeting.]

During a discussion over at the boost mailing list (www.boost.org), we came across the following "puzzle":

  struct A {
    template< typename T > operator T() const;
  } a;

  template<> A::operator float() const
  {
    return 1.0f;
  }

  int main()
  {
    float f = 1.0f * a;
  }

The code is compiled without errors or warnings from EDG-based compilers (Comeau, Intel), but rejected from others (GCC, MSVC [7.1]). The question: Who is correct? Where should I file the bug report?

To explain the problem: The EDG seems to see 1.0f*a as a call to the unambiguous operator*(float,float) and thus casts 'a' to 'float'. The other compilers have several operators (float*float, float*double, float*int, ...) available and thus can't decide which cast is appropriate. I think the latter is the correct behaviour, but I'd like to hear some comments from the language lawyers about the standard's point of view on this problem.

Andreas Hommel: Our compiler also rejects this code:

Error   : function call 'operator*(float, {lval} A)' is ambiguous
'operator*(float, unsigned long long)'
'operator*(float, int)'
'operator*(float, unsigned int)'
'operator*(float, long)'
'operator*(float, unsigned long)'
'operator*(float, float)'
'operator*(float, double)'
'operator*(float, long double)'
'operator*(float, long long)'
Test.cp line 12       float f = 1.0f * a;

Is this example really legal? It was my understanding that all candidates from 12.5 [over.built] participate in overload resolution.

Daveed Vandevoorde: I believe the EDG-based compiler is right. Note that the built-in operator* requires "usual arithmetic conversions" (see 7.6.5 [expr.mul] paragraph 2 and Clause 7 [expr] paragaph 9). This means that there is no candidate taking (float, double) arguments: Only (float, float) or (double, double).

Since your first argument is of type float, the (float, float) case is preferred over the (double, double) case (the latter would require a floating-point promotion).

Stave Adamczyk: Daveed's statement is wrong; as Andreas says, the prototypes in 12.5 [over.built] paragraph 12 have pairs of types, not the same type twice. However, the list of possibilities considered in Andreas' message is wrong also: 12.5 [over.built] paragraph 12 calls for pairs of promoted arithmetic types, and float is not a promoted type (it promotes to double -- see 7.3.8 [conv.fpprom]).

Nevertheless, the example is ambiguous. Let's look at the overload resolution costs. The right operand is always going to have a user-defined-conversion cost (the template conversion function will convert directly to the const version of the second parameter of the prototype). The left operand is always going to have a promotion (float --> double) or a standard conversion (anything else). So the cases with promotions are better than the others. However, there are several of those cases, with second parameters of type int, unsigned int, long, unsigned long, double, and long double, and all of those are equally good. Therefore the example is ambiguous.

Here's a reduced version that should be equivalent:

  struct A {
    template <typename T> operator T() const;
  } a;
  void f(double, int);
  void f(double, unsigned int);
  void f(double, long);
  void f(double, unsigned long);
  void f(double, double);
  void f(double, long double);
  int main() {
    f(1.0f, a);  // Ambiguous
  }

Personally, I think this is evidence that 12.5 [over.built] doesn't really do quite what it should. But the standard is clear, if possibly flawed.

Andreas Hommel: You are right, "float" is not a promoted arithmetic type, this is a bug in our compiler.

However, the usual arithmetic conversions (Clause 7 [expr] paragraph 9) do not promote the floating point types, so

  float operator+(float, float);
is a legal built-in operator function, so I wonder if it shouldn't be included in the candidate list.

Steve Adamczyk: Hmm, the definition of the term in 12.5 [over.built] paragraph 2 is highly ambiguous:

Similarly, the term promoted arithmetic type refers to promoted integral types plus floating types.
I can't tell if that's "promoted integral types plus (all) floating types" or "promoted integral types plus (promoted) floating types". I thought the latter was intended, but indeed the usual arithmetic conversions could give you "float + float", so it makes sense that float would be one of the possibilities. We should discuss this to make sure everyone has the same interpretation.

Proposed Resolution (October 2003):

Change the second sentence of 13.6 paragraph 2 as follows:

Similarly, the term promoted arithmetic type refers to promoted integral types plus floating types floating types plus promoted integral types.



204. Exported class templates

Section: Clause 13  [temp]     Status: CD1     Submitter: Robert Klarer     Date: 11 Feb 2000

[Voted into WP at April 2003 meeting.]

Clause 13 [temp] paragraph 7 allows class templates to be declared exported, including member classes and member class templates (implicitly by virtue of exporting the containing template class). However, paragraph 8 does not exclude exported class templates from the statement that

An exported template need only be declared (and not necessarily defined) in a translation unit in which it is instantiated.
This is an incorrect implication; however, it is also not dispelled in 13.9.2 [temp.inst] paragraph 6:
If an implicit instantiation of a class template specialization is required and the template is declared but not defined, the program is ill-formed.
This wording says nothing about the translation unit in which the definition must be provided. Contrast this with 13.9.3 [temp.explicit] paragraph 3:
A definition of a class template or a class member template shall be in scope at the point of the explicit instantiation of the class template or class member template.

Suggested resolution:


(See also issue 212.)

Notes from 04/00 meeting:

John Spicer opined that even though Clause 13 [temp] paragraph 7 speaks of "declaring a class template exported," that does not mean that the class template is "an exported template" in the sense of paragraph 8. He suggested clarifying paragraph 7 to that effect instead of the change to paragraph 8 suggested above, and questioned the need for a change to 13.9.2 [temp.inst].

Notes from the 4/02 meeting:

This is resolved by the proposed changes for issue 323.




323. Where must export appear?

Section: Clause 13  [temp]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 14 Nov 2001

[Voted into WP at April 2003 meeting.]

The standard doesn't seem to describe whether the keyword export should appear on exported template declarations that are not used or defined in that particular translation unit.

For example:

  // File 1:
  template<typename T> void f();  // export omitted

  // File 2:
  export template<typename T> void f() {}

  int main() { f<int>(); }

Another example is:

  // File 1:
  struct S {
     template<typename T> void m();
  };

  // File 2:
  struct S {
     template<typename T> void m();
  };

  export template<typename T> void S::m() {}

  int main() {
     S s;
     S.m<int>();
  }

I think both examples should be clarified to be invalid. If a template is exported in one translation unit, it should be declared export in all translation units in which it appears.

With the current wording, it seems that even the following is valid:

  // File 1:
  export template<typename T> void f();  // export effectively ignored

  // File 2:
  template<typename T> void f() {}  // Inclusion model
  void g() { f<int>(); }

  // File 3:
  void g();
  template<typename T> void f() {}  // Inclusion model

  int main() {
     g();
     f<int>();
  }

In fact, I think the declaration in "File 1" could be a definition and this would still satisfy the the requirements of the standard, which definitely seems wrong.

Proposed Resolution (revised October 2002):

Replace Clause 13 [temp] paragraphs 6, 7, and 8 by the following text:

A template-declaration may be preceded by the export keyword. Such a template is said to be exported. Declaring exported a class template is equivalent to declaring exported all of its non-inline member functions, static data members, member classes, member class templates, and non-inline member function templates.

If a template is exported in one translation unit, it shall be exported in all translation units in which it appears; no diagnostic is required. A declaration of an exported template shall appear with the export keyword before any point of instantiation (13.8.4.1 [temp.point]) of that template in that translation unit. In addition, the first declaration of an exported template containing the export keyword must not follow the definition of that template. The export keyword shall not be used in a friend declaration.

Templates defined in an unnamed namespace, inline functions, and inline function templates shall not be exported. An exported non-class template shall be defined only once in a program; no diagnostic is required. An exported non-class template need only be declared (and not necessarily defined) in a translation unit in which it is instantiated.

A non-exported non-class template must be defined in every translation unit in which it is implicitly instantiated (13.9.2 [temp.inst]), unless the corresponding specialization is explicitly instantiated (13.9.3 [temp.explicit]) in some translation unit; no diagnostic is required.

Note: This change also resolves issues 204 and 335.




335. Allowing export on template members of nontemplate classes

Section: Clause 13  [temp]     Status: CD1     Submitter: John Spicer     Date: 30 Jan 2002

[Voted into WP at April 2003 meeting.]

The syntax for "export" permits it only on template declarations. Clause 13 [temp] paragraph 6 further restricts "export" to appear only on namespace scope declarations. This means that you can't export a member template of a non-template class, as in:

  class A {
    template <class T> void f(T);
  };
You can, of course, put export on the definition:
  export template <class T> void A<T>::f(T){}
but in order for the template to be used from other translation units (the whole point of export) the declaration in the other translation unit must also be declared export.

There is also the issue of whether or not we should permit this usage:

  export struct A {
    template <class T> void f(T);
  };
My initial reaction is to retain this prohibition as all current uses of "export" are preceding the "template" keyword.

If we eliminate the requirement that "export" precede "template" there is a similar issue regarding this case, which is currently prohibited:

  template <class T> struct B {
    export void f();
  };
My preference is still to permit only "export template".

Notes from the 4/02 meeting:

This is resolved by the proposed changes for issue 323.




534. template-names and operator-function-ids

Section: Clause 13  [temp]     Status: CD1     Submitter: Jens Maurer     Date: 5 October 2005

[Voted into WP at the October, 2006 meeting.]

Taken literally, Clause 13 [temp] paragraph 2 does not permit operator functions to be templates:

In a function template declaration, the declarator-id shall be a template-name (i.e., not a template-id).

and, in 13.3 [temp.names] paragraph 1, a template-name is defined to be simply an identifier.

Issue 301 considered and rejected the idea of changing the definition of template-name to include operator-function-ids and conversion-function-ids. Either that decision should be reconsidered or the various references in the text to template-name should be examined to determine if they should also mention the non-identifier possibilities for function template names.

Proposed resolution (April, 2006):

This issue is resolved by the resolution of issue 301.




184. Default arguments in template template-parameters

Section: 13.2  [temp.param]     Status: CD1     Submitter: John Spicer     Date: 11 Nov 1999

[Voted into WP at April 2003 meeting.]

John Spicer: Where can default values for the template parameters of template template parameters be specified and where so they apply?

For normal template parameters, defaults can be specified only in class template declarations and definitions, and they accumulate across multiple declarations in the same way that function default arguments do.

I think that defaults for parameters of template template parameters should be handled differently, though. I see no reason why such a default should extend beyond the template declaration with which it is associated. In other words, such defaults are a property of a specific template declaration and are not part of the interface of the template.

    template <class T = float> struct B {};

    template <template <class _T = float> class T> struct A {
        inline void f();
        inline void g();
    };

    template <template <class _T> class T> void A<T>::f() {
        T<> t;  // Okay? (proposed answer - no)
    }

    template <template <class _T = char> class T> // Okay? (proposed answer - yes)
    void A<T>::g() {
        T<> t;  // T<char> or T<float>?  (proposed answer - T<char>)
    }

    int main() {
        A<B> ab;
        ab.f();
    }

I don't think this is clear in the standard.

Gabriel Dos Reis: On the other hand I fail to see the reasons why we should introduce yet another special rule to handle that situation differently. I think we should try to keep rules as uniform as possible. For default values, it has been the case that one should look for any declaration specifying default values. Breaking that rules doesn't buy us anything, at least as far as I can see. My feeling is that [allowing different defaults in different declarations] is very confusing.

Mike Miller: I'm with John on this one. Although we don't have the concept of "prototype scope" for template parameter lists, the analogy with function parameters would suggest that the two declarations of T (in the template class definition and the template member function definition) are separate declarations and completely unrelated. While it's true that you accumulate default arguments on top-level declarations in the same scope, it seems to me a far leap to say that we ought also to accumulate default arguments in nested declarations. I would expect those to be treated as being in different scopes and thus not to share default argument information.

When you look up the name T in the definition of A<T>::f(), the declaration you find has no default argument for the parameter of T, so T<> should not be allowed.

Proposed Resolution (revised October 2002):

In 13.2 [temp.param], add the following as a new paragraph at the end of this section:

A template-parameter of a template template-parameter is permitted to have a default template-argument. When such default arguments are specified, they apply to the template template-parameter in the scope of the template template-parameter. [Example:
    template <class T = float> struct B {};

    template <template <class TT = float> class T> struct A {
        inline void f();
        inline void g();
    };

    template <template <class TT> class T> void A<T>::f() {
        T<> t;  // error - TT has no default template argument
    }

    template <template <class TT = char> class T>void A<T>::g() {
        T<> t;  // OK - T<char>
    }
-- end example]



215. Template parameters are not allowed in nested-name-specifiers

Section: 13.2  [temp.param]     Status: CD1     Submitter: Martin von Loewis     Date: 13 Mar 2000

[Voted into WP at April, 2007 meeting.]

According to 13.2 [temp.param] paragraph 3, the following fragment is ill-formed:

    template <class T>
    class X{
      friend void T::foo();
    };

In the friend declaration, the T:: part is a nested-name-specifier (9.3 [dcl.decl] paragraph 4), and T must be a class-name or a namespace-name (_N4567_.5.1.1 [expr.prim.general] paragraph 7). However, according to 13.2 [temp.param] paragraph 3, it is only a type-name. The fragment should be well-formed, and instantiations of the template allowed as long as the actual template argument is a class which provides a function member foo. As a result of this defect, any usage of template parameters in nested names is ill-formed, e.g., in the example of 13.8 [temp.res] paragraph 2.

Notes from 04/00 meeting:

The discussion at the meeting revealed a self-contradiction in the current IS in the description of nested-name-specifiers. According to the grammar in _N4567_.5.1.1 [expr.prim.general] paragraph 7, the components of a nested-name-specifier must be either class-names or namespace-names, i.e., the constraint is syntactic rather than semantic. On the other hand, 6.5.5 [basic.lookup.qual] paragraph 1 describes a semantic constraint: only object, function, and enumerator names are ignored in the lookup for the component, and the program is ill-formed if the lookup finds anything other than a class-name or namespace-name. It was generally agreed that the syntactic constraint should be eliminated, i.e., that the grammar ought to be changed not to use class-or-namespace-name.

A related point is the explicit prohibition of use of template parameters in elaborated-type-specifiers in 9.2.9.5 [dcl.type.elab] paragraph 2. This rule was the result of an explicit Committee decision and should not be unintentionally voided by the resolution of this issue.

Proposed resolution (04/01):

Change _N4567_.5.1.1 [expr.prim.general] paragraph 7 and A.5 [gram.expr] from

to

This resolution depends on the resolutions for issues 245 (to change the name lookup rules in elaborated-type-specifiers to include all type-names) and 283 (to categorize template type-parameters as type-names).

Notes from 10/01 meeting:

There was some sentiment for going with simply identifier in front of the "::", and stronger sentiment for going with something with a more descriptive name if possible. See also issue 180.

Notes from April 2003 meeting:

This was partly resolved by the changes for issue 125. However, we also need to add a semantic check in 6.5.5 [basic.lookup.qual] to allow T::foo and we need to reword the first sentence of 6.5.5 [basic.lookup.qual].

Proposed resolution (October, 2004):

Change 6.5.5 [basic.lookup.qual] paragraph 1 as follows:

The name of a class or namespace member can be referred to after the :: scope resolution operator (_N4567_.5.1.1 [expr.prim.general]) applied to a nested-name-specifier that nominates its class or namespace. During the lookup for a name preceding the :: scope resolution operator, object, function, and enumerator names are ignored. If the name found is not a class-name (Clause 11 [class]) or namespace-name (9.8.2 [namespace.def]) does not designate a class or namespace, the program is ill-formed. [...]

Notes from the April, 2005 meeting:

The 10/2004 resolution does not take into account the fact that template type parameters do not designate class types in the context of the template definition. Further drafting is required.

Proposed resolution (April, 2006):

Change 6.5.5 [basic.lookup.qual] paragraph 1 as follows:

The name of a class or namespace member can be referred to after the :: scope resolution operator (_N4567_.5.1.1 [expr.prim.general]) applied to a nested-name-specifier that nominates its class or namespace. During the lookup for a name preceding the :: scope resolution operator, object, function, and enumerator names are ignored. If the name found is not a class-name (Clause 11 [class]) or namespace-name (9.8.2 [namespace.def]) does not designate a namespace or a class or dependent type, the program is ill-formed. [...]



226. Default template arguments for function templates

Section: 13.2  [temp.param]     Status: CD1     Submitter: Bjarne Stroustrup     Date: 19 Apr 2000

[Voted into WP at April 2003 meeting.]

The prohibition of default template arguments for function templates is a misbegotten remnant of the time where freestanding functions were treated as second class citizens and required all template arguments to be deduced from the function arguments rather than specified.

The restriction seriously cramps programming style by unnecessarily making freestanding functions different from member functions, thus making it harder to write STL-style code.

Suggested resolution:

Replace

A default template-argument shall not be specified in a function template declaration or a function template definition, nor in the template-parameter-list of the definition of a member of a class template.

by

A default template-argument shall not be specified in the template-parameter-list of the definition of a member of a class template.

The actual rules are as stated for arguments to class templates.

Notes from 10/00 meeting:

The core language working group was amenable to this change. Questions arose, however, over the interaction between default template arguments and template argument deduction: should it be allowed or forbidden to specify a default argument for a deduced parameter? If it is allowed, what is the meaning: should one or the other have priority, or is it an error if the default and deduced arguments are different?

Notes from the 10/01 meeting:

It was decided that default arguments should be allowed on friend declarations only when the declaration is a definition. It was also noted that it is not necessary to insist that if there is a default argument for a given parameter all following parameters have default arguments, because (unlike in the class case) arguments can be deduced if they are not specified.

Note that there is an interaction with issue 115.

Proposed resolution (revised October 2002):

  1. In 13.2 [temp.param] paragraph 9, replace

    A default template-argument may be specified in a class template declaration or a class template definition. A default template-argument shall not be specified in a function template declaration or a function template definition, nor in the template-parameter-list of the definition of a member of a class template.

    with

    A default template-argument may be specified in a template declaration. A default template-argument shall not be specified in the template-parameter-lists of the definition of a member of a class template that appears outside of the member's class.
  2. In 13.2 [temp.param] paragraph 9, replace

    A default template-argument shall not be specified in a friend template declaration.

    with

    A default template-argument shall not be specified in a friend class template declaration. If a friend function template declaration specifies a default template-argument, that declaration shall be a definition and shall be the only declaration of the function template in the translation unit.
  3. In 13.2 [temp.param] paragraph 11, replace

    If a template-parameter has a default template-argument, all subsequent template-parameters shall have a default template-argument supplied.

    with

    If a template-parameter of a class template has a default template-argument, all subsequent template-parameters shall have a default template-argument supplied. [Note: This is not a requirement for function templates because template arguments might be deduced (13.10.3 [temp.deduct]).]
  4. In 13.10 [temp.fct.spec] paragraph 1, replace

    Template arguments can either be explicitly specified when naming the function template specialization or be deduced (13.10.3 [temp.deduct]) from the context, e.g. from the function arguments in a call to the function template specialization.

    with

    Template arguments can be explicitly specified when naming the function template specialization, deduced from the context (13.10.3 [temp.deduct]), e.g., deduced from the function arguments in a call to the function template specialization), or obtained from default template arguments.
  5. In 13.10.2 [temp.arg.explicit] paragraph 2, replace

    Trailing template arguments that can be deduced (13.10.3 [temp.deduct]) may be omitted from the list of explicit template-arguments.

    with

    Trailing template arguments that can be deduced (13.10.3 [temp.deduct]) or obtained from default template-arguments may be omitted from the list of explicit template-arguments.
  6. In 13.10.3 [temp.deduct] paragraph 1, replace

    The values can be either explicitly specified or, in some cases, deduced from the use.

    with

    The values can be explicitly specified or, in some cases, be deduced from the use or obtained from default template-arguments.
  7. In 13.10.3 [temp.deduct] paragraph 4, replace

    The resulting substituted and adjusted function type is used as the type of the function template for template argument deduction. When all template arguments have been deduced, all uses of template parameters in nondeduced contexts are replaced with the corresponding deduced argument values. If the substitution results in an invalid type, as described above, type deduction fails.

    with

    The resulting substituted and adjusted function type is used as the type of the function template for template argument deduction. If a template argument has not been deduced, its default template argument, if any, is used. [Example:

        template <class T, class U = double>
        void f(T t = 0, U u = 0);
    
        void g()
        {
            f(1, 'c');         // f<int,char>(1,'c')
            f(1)               // f<int,double>(1,0)
            f();               // error: T cannot be deduced
            f<int>();          // f<int,double>(0,0)
            f<int,char>();     // f<int,char>(0,0)
        }
    

    ---end example]

    When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in nondeduced contexts are replaced with the corresponding deduced or default argument values. If the substitution results in an invalid type, as described above, type deduction fails.




401. When is access for template parameter default arguments checked?

Section: 13.2  [temp.param]     Status: CD1     Submitter: Steve Adamczyk     Date: 27 Jan 2003

[Voted into WP at October 2005 meeting.]

Is the following well-formed?

  class policy {};
  class policy_interface {};
  template <class POLICY_INTERFACE>
  class aph {
  protected:
    typedef POLICY_INTERFACE PI;
  };
  template <class POLICY, class BASE, class PI = typename BASE::PI>
  class ConcretePolicyHolder : public BASE, protected POLICY
  {};
  ConcretePolicyHolder < policy , aph < policy_interface > > foo;
  void xx() { }

The issue is whether the access to the default argument type BASE::PI is checked before or after it is known that BASE is a base class of the template. To some extent, one needs to develop the list of template arguments (and therefore evaluate the default argument) before one can instantiate the template, and one does not know what base classes the template has until it has been instantiated.

Notes from April 2003 meeting:

Shortened example:

  class B {
  protected:
    typedef int A;
  };
  template<class T, class U = typename T::A>
  class X : public T
  { };

The convincing argument here is that if we had only the declaration of the template (including the default argument), we would expect it to be usable in exactly the same way as the version with the definition. However, the special access needed is visible only when the definition is available. So the above should be an error, and information from the definition cannot affect the access of the default arguments.

Proposed Resolution (April 2003):

Add a new paragraph 16 to 13.2 [temp.param] after paragraph 15:

Since a default template-argument is encountered before any base-clause there is no special access to members used in a default template-argument. [Example:
  class B {};
  template <class T> class C {
  protected:
     typedef T TT;
  };

  template <class U, class V = typename U::TT>
  class D : public U {};

  D <C<B> > d;  // access error, C::TT is protected
--- end example]

Notes from October 2003 meeting:

We decided that template parameter default arguments should have their access checked in the context where they appear without special access for the entity declared (i.e., they are different than normal function default arguments). One reason: we don't know the instance of the template when we need the value. Second reason: compilers want to parse and throw away the form of the template parameter default argument, not save it and check it for each instantiation.

Class templates should be treated the same as function templates in this regard. The base class information is in the same category as friend declarations inside the class itself -- not available. If the body were used one would need to instantiate it in order to know whether one can name it.

Proposed resolution (October, 2004):

Add the following as a new paragraph following the last paragraph of 11.8 [class.access] (but before the new paragraph inserted by the resolution of issue 372, if adopted):

The names in a default template-argument (13.2 [temp.param]) have their access checked in the context in which they appear rather than at any points of use of the default template-argument. [Example:

    class B {};
    template <class T> class C {
    protected:
       typedef T TT;
    };

    template <class U, class V = typename U::TT>
    class D : public U {};

    D <C<B> >* d;  // access error, C::TT is protected

end example]




228. Use of template keyword with non-member templates

Section: 13.3  [temp.names]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 4 May 2000

[Voted into WP at April 2003 meeting.]

Consider the following example:

    template<class T>
    struct X {
       virtual void f();
    };

    template<class T>
    struct Y {
       void g(X<T> *p) {
	  p->template X<T>::f();
       }
    };

This is an error because X is not a member template; 13.3 [temp.names] paragraph 5 says:

If a name prefixed by the keyword template is not the name of a member template, the program is ill-formed.

In a way this makes perfect sense: X is found to be a template using ordinary lookup even though p has a dependent type. However, I think this makes the use of the template prefix even harder to teach.

Was this intentionally outlawed?

Proposed Resolution (4/02):

Elide the first use of the word "member" in 13.3 [temp.names] paragraph 5 so that its first sentence reads:

If a name prefixed by the keyword template is not the name of a member template, the program is ill-formed.



301. Syntax for template-name

Section: 13.3  [temp.names]     Status: CD1     Submitter: Mark Mitchell     Date: 24 Jul 2001

[Voted into WP at the October, 2006 meeting.]

The grammar for a template-name is:

That's not right; consider:

    template <class T> T operator+(const T&, const T&);
    template <> S operator+<S>(const S&, const S&);

This is ill-formed according to the standard, since operator+ is not a template-name.

Suggested resolution:

I think the right rule is

John Spicer adds that there's some question about whether conversion functions should be included, as they cannot have template argument lists.

Notes from 4/02 meeting:

If the change is made as a syntax change, we'll need a semantic restriction to avoid operator+<int> as a class. Clark Nelson will work on a compromise proposal -- not the minimal change to the syntax proposed, not the maximal change either.

Clark Nelson (April 2003):

The proposed solution (adding operator-function-id as an alternative for template-name) would have a large impact on the language described by the grammar. Specifically, for example, operator+<int> would become a syntactically valid class-name.

On the other hand, a change with (I believe) exactly the desired effect on the language accepted, would be to modify operator-function-id itself:

(Steve Adamczyk: this change was already made by issue 38 and is in TC1.)

Then there is the first sentence of 13.3 [temp.names] paragraph 3:

After name lookup (6.5 [basic.lookup]) finds that a name is a template-name, if this name is followed by a <, the < is always taken as the beginning of a template-argument-list and never as a name followed by the less-than operator.

This description seems to be adequate for names of class templates. As far as I can tell, the only ambiguity it resolves is from something that starts with new X <, in the scope of a class template X. But as far as I can tell is already inadequate for names of function templates, and is even worse for operator function templates.

Probably < should always be interpreted as introducing a template-argument-list if any member of the overload set is a function template. After all, function pointers are very rarely compared for ordering, and it's not clear what other rule might be workable.

I'm inclined to propose the simplest rule possible for operator-function-ids: if one is followed by <, then what follows is interpreted as a template-argument-list, unconditionally. Of course, if no template for that operator has been declared, then there's an error.

Also, note that if the operator in question is < or <<, it is possible to run into a problem similar to the famous >> nested template argument list closing delimiter problem. However, since in this case (at least) one of the < characters has a radically different interpretation than the other, and for other reasons as well, this is unlikely to be nearly as much of a practical problem as the >> problem.

Notes from April 2003 meeting:

We felt that the operator functions should not be special-cased. They should be treated like any other name.

September 2003:

Clark Nelson has provided the changes in N1490=03-0073.

Notes from October 2003 meeting:

We reviewed Clark Nelson's N1490. Clark will revise it and introduce a new syntax term for an identifier or the name of an operator function.

Notes from the April, 2005 meeting:

The CWG suggested a new approach to resolving this issue: the existing term template-id will be renamed to class-template-id, the term template-id will be defined to include operator functions with template arguments, and any current uses of template-id (such as in the definition of elaborated-type-specifier) where an operator function is not appropriate will be changed to refer to class-template-id.

Proposed resolution (April, 2006):

As specified in document J16/05-0156 = WG21 N1896, except that:

  1. In change 9 (_N4868_.6.5.6 [basic.lookup.classref]), omit the change from “entire postfix-expression” to “nested-name-specifier.”

  2. In change a (6.5.5.2 [class.qual] paragraph 1, third bullet), omit the change from “entire postfix-expression” to “qualified-id.”

  3. In change b (6.5.5.3 [namespace.qual] paragraph 1), omit the change from “entire postfix-expression” to “qualified-id.”

(Some of these omitted changes are addressed by issue 562.)

(This resolution also resolves issue 534.)




468. Allow ::template outside of templates

Section: 13.3  [temp.names]     Status: CD1     Submitter: John Spicer     Date: 9 Apr 2004

[Voted into WP at the October, 2006 meeting.]

For the same reasons that issue 382 proposes for relaxation of the requirements on typename, it would make sense to allow the ::template disambiguator outside of templates.

See also issues 11, 30, 96, and 109.

Proposed resolution (October, 2005):

Change 13.3 [temp.names] paragraph 5 as indicated:

If a name prefixed by the keyword template is not the name of a template, the program is ill-formed. [Note: the keyword template may not be applied to non-template members of class templates. —end note] Furthermore, names of member templates shall not be prefixed by the keyword template if the postfix-expression or qualified-id does not appear in the scope of a template. [Note: just as is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the nested-name-specifier or the expression on the left of the -> or ., or the nested-name-specifier is not dependent on a template-parameter, or the use does not appear in the scope of a template. —end note]



246. Jumps in function-try-block handlers

Section: 13.4  [temp.arg]     Status: CD1     Submitter: Mike Miller     Date: 15 Sep 2000

[Moved to DR at 4/01 meeting.]

Is it permitted to jump from a handler of a function-try-block into the body of the function?

Clause 14 [except] paragraph 2 would appear to disallow such a jump:

A goto, break, return, or continue statement can be used to transfer control out of a try block or handler, but not into one.

However, 14.4 [except.handle] paragraph 14 mentions only constructors and destructors for the prohibition:

If the handlers of a function-try-block contain a jump into the body of a constructor or destructor, the program is ill-formed.

Is this paragraph simply reemphasizing the more general restriction, or does it assume that such a jump would be permitted for functions other than constructors or destructors? If the former interpretation is correct, it is confusing and should be either eliminated or turned into a note. If the latter interpretation is accurate, Clause 14 [except] paragraph 2 must be revised.

(See also issue 98.)

Proposed resolution (04/01):

Delete 14.4 [except.handle] paragraph 14.




372. Is access granted by base class specifiers available in following base class specifiers?

Section: 13.4  [temp.arg]     Status: CD1     Submitter: Clark Nelson     Date: 13 August 2002

[Voted into WP at the October, 2006 meeting.]

I'm not really sure what the standard says about this. Personally, I'd like for it to be ill-formed, but I can't find any words that I can interpret to say so.

  template<class T>
  class X
  {
  protected:
    typedef T Type;
  };
  template<class T>
  class Y
  {
  };
  template<class T,
           template<class> class T1,
           template<class> class T2>
  class Z:
    public T2<typename T1<T>::Type>,
    public T1<T>
  {
  };
  Z<int, X, Y> z;

John Spicer: I don't think the standard really addresses this case. There is wording about access checking of things used as template arguments, but that doesn't address accessing members of the template argument type (or template) from within the template.

This example is similar, but does not use template template arguments.

  class X {
  private:
    struct Type {};
  };
  template <class T> struct A {
    typename T::Type t;
  };
  A<X> ax;

This gets an error from most compilers, though the standard is probably mute on this as well. An error makes sense -- if there is no error, there is a hole in the access checking. (The special rule about no access checks on template parameters is not a hole, because the access is checked on the type passed in as an argument. But when you look up something in the scope of a template parameter type, you need to check the access to the member found.)

The logic in the template template parameter case should be similar: anytime you look up something in a template-dependent class, the member's access must be checked, because it could be different for different template instances.

Proposed Resolution (October 2002):

Change the last sentence of 13.4 [temp.arg] paragraph 3 from:

For a template-argument of class type, the template definition has no special access rights to the inaccessible members of the template argument type.
to:
For a template-argument that is a class type or a class template, the template definition has no special access rights to the members of the template-argument. [Example:
  template <template <class TT> class T> class A {
    typename T<int>::S s;
  };

  template <class U> class B {
    private:
    struct S { /* ... */ };
  };

  A<B> b;   // ill-formed, A has no access to B::S
-- end example]

Daniel Frey posts on comp.std.c++ in July 2003: I just read DR 372 and I think that the problem presented is not really discussed/solved properly. Consider this example:

  class A {
  protected:
     typedef int N;
  };

  template< typename T >
  class B
  {};

  template< typename U >
  class C : public U, public B< typename U::N >
  {};

  C< A > x;

The question is: If C is derived from A as above, is it allowed to access A::N before the classes opening '{'?

The main problem is that you need to access U's protected parts in C's base-clause. This pattern is common when using policies, Andrei's Loki library was bitten by it as he tried to make some parts of the policies 'protected' but some compilers rejected the code. They were right to reject it, I think it's 11.8.4 [class.friend]/2 that applies here and prevents the code above to be legal, although it addresses a different and reasonable example. To me, it seems wrong to reject the code as it is perfectly reasonable to write such stuff. The questions are:

Steve Adamczyk: In other words, the point of the issue is over what range access derived from base class specifiers is granted, and whether any part of that range is the base specifier list itself, either the parts afterwards or the whole base specifier list. (Clark Nelson confirms this is what he was asking with the original question.) Personally, I find it somewhat disturbing that access might arrive incrementally; I'd prefer that the access happen all at once, at the opening brace of the class.

Notes from October 2003 meeting:

We decided it makes sense to delay the access checking for the base class specifiers until the opening brace of the class is seen. In other words, the base specifiers will be checked using the full access available for the class, and the order of the base classes is not significant in that determination. The implementors present all said they already had code to handle accumulation of delayed access checks, because it is already needed in other contexts.

Proposed resolution (October, 2004):

  1. Change the last sentence of 13.4 [temp.arg] paragraph 3 as indicated:

    For a template-argument of that is a class type or a class template, the template definition has no special access rights to the inaccessible members of the template argument type template-argument. [Example:
        template <template <class TT> class T> class A {
           typename T<int>::S s;
        };
    
        template <class U> class B {
           private:
           struct S { /* ... */ };
        };
    
        A<B> b;   // ill-formed, A has no access to B::S
    

    end example]

  2. Insert the following as a new paragraph after the final paragraph of 11.8 [class.access]:

    The access of names in a class base-specifier-list are checked at the end of the list after all base classes are known. [Example:

        class A {
          protected:
             typedef int N;
        };
    
        template<typename T> class B {};
    
        template<typename U> class C : public B<typename U::N>, public U {};
    
        C<A> x;  // OK: A is a base class so A::N in B<A::N> is accessible
    

    end example]

Notes from the April, 2005 meeting:

The 10/2004 resolution is not sufficient to implement the CWG's intent to allow these examples: 11.8 [class.access] paragraph 1 grants protected access only to “members and friends” of derived classes, not to their base-specifiers. The resolution needs to be extended to say either that access in base-specifiers is determined as if they were members of the class being defined or that access is granted to the class as an entity, including its base-specifiers. See also issue 500, which touches on the same issue and should be resolved in the same way.

Proposed resolution (October, 2005):

  1. Change the second bullet of 11.8 [class.access] paragraph 1 as indicated:

  2. Change 11.8 [class.access] paragraph 2 as indicated:

    A member of a class can also access all the names declared in the class of which it is a member to which the class has access.
  3. Change 11.8 [class.access] paragraph 6 as indicated:

    All access controls in 11.8 [class.access] affect the ability to access a class member name from a particular scope. The access control for names used in the definition of a class member that appears outside of the member's class definition is done as if the entire member definition appeared in the scope of the member's class. For purposes of access control, the base-specifiers of a class and the definitions of class members that appear outside of the class definition are considered to be within the scope of that class. In particular...
  4. Change the example and commentary in 11.8 [class.access] paragraphs 6-7 as indicated:

    [Example:

        class A {
          typedef int I; // private member
          I f();
          friend I g(I);
          static I x;
        protected:
          struct B { };
        };
    
        A::I A::f () { return 0; }
        A::I g(A::I p = A::x);
        A::I g(A::I p) { return 0; }
        A::I A::x = 0;
    
        struct D: A::B, A { };
    
    Here, all the uses of A::I are well-formed because A::f and A::x are members of class A and g is a friend of class A. This implies, for example, that access checking on the first use of A::I must be deferred until it is determined that this use of A::I is as the return type of a member of class A. Similarly, the use of A::B as a base-specifier is well-formed because D is derived from A, so access checking of base-specifiers must be deferred until the entire base-specifier-list has been seen.end example]
  5. In 11.8.4 [class.friend] paragraph 2, replace the following text:

    Declaring a class to be a friend implies that the names of private and protected members from the class granting friendship can be accessed in declarations of members of the befriended class. [Note: this means that access to private and protected names is also granted to member functions of the friend class (as if the functions were each friends) and to the static data member definitions of the friend class. This also means that private and protected type names from the class granting friendship can be used in the base-clause of a nested class of the friend class. However, the declarations of members of classes nested within the friend class cannot access the names of private and protected members from the class granting friendship. Also, because the base-clause of the friend class is not part of its member declarations, the base-clause of the friend class cannot access the names of the private and protected members from the class granting friendship. For example,

        class A {
          class B { };
          friend class X;
        };
        class X: A::B {     // ill-formed: A::B cannot be accessed
                            // in the base-clause for X
          A::B mx;          // OK: A::B used to declare member of X
          class Y: A::B {   // OK: A::B used to declare member of X
            A::B my;        // ill-formed: A::B cannot be accessed
                            // to declare members of nested class of X
          };
        };
    

    end note]

    with:

    Declaring a class to be a friend implies that the names of private and protected members from the class granting friendship can be accessed in the base-specifiers and member declarations of the befriended class. [Example:

        class A {
          class B { };
          friend class X;
        };
    
        struct X: A::B {   // OK: A::B accessible to friend
          A::B mx;         // OK: A::B accessible to member of friend
          class Y {
            A::B my;       // OK: A::B accessible to nested member of friend
          };
        };
    

    end example]

  6. Change the last sentence of 13.4 [temp.arg] paragraph 3 as indicated:

    For a template-argument of that is a class type or a class template, the template definition has no special access rights to the inaccessible members of the template argument type. template-argument. [Example:

        template <template <class TT> class T> class A {
          typename T<int>::S s;
        };
    
        template <class U> class B {
        private:
          struct S { /* ... */ };
        };
    
        A<B> b;    // ill-formed, A has no access to B::S
    
    

    end example]

  7. Change 11.4.12 [class.nest] paragraph 4 as indicated:

    Like a member function, a friend function (11.8.4 [class.friend]) defined within a nested class is in the lexical scope of that class; it obeys the same rules for name binding as a static member function of that class (11.4.9 [class.static]), but it and has no special access rights to members of an enclosing class.

(Note: this resolution also resolves issues 494 and 500.)




62. Unnamed members of classes used as type parameters

Section: 13.4.2  [temp.arg.type]     Status: CD1     Submitter: Steve Adamczyk     Date: 13 Oct 1998

[Moved to DR at 4/01 meeting.]

Section 13.4.2 [temp.arg.type] paragraph 2 says

A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template type-parameter.

It probably wasn't intended that classes with unnamed members should be included in this list, but they are arguably compounded from unnamed types.

Proposed resolution (04/01):

In 13.4.2 [temp.arg.type] paragraph 2, change

A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template type-parameter.

to

The following types shall not be used as a template-argument for a template type-parameter:



354. Null as nontype template argument

Section: 13.4.3  [temp.arg.nontype]     Status: CD1     Submitter: John Spicer     Date: 2 May 2002

[Voted into WP at October 2005 meeting.]

The standard does not permit a null value to be used as a nontype template argument for a nontype template parameter that is a pointer.

This code is accepted by EDG, Microsoft, Borland and Cfront, but rejected by g++ and Sun:

  template <int *p> struct A {};
  A<(int*)0> ai;

I'm not sure this was ever explicitly considered by the committee. Is there any reason to permit this kind of usage?

Jason Merrill: I suppose it might be useful for a program to be able to express a degenerate case using a null template argument. I think allowing it would be harmless.

Notes from October 2004 meeting:

CWG decided that it would be desirable to allow null pointers as nontype template arguments, even though they are not representable in some current ABIs. There was some discussion over whether to allow a bare 0 to be used with a pointer nontype template parameter. The following case was decisive:

    template<int i> void foo();
    template<int* i> void foo();
    ...
    foo<0>();

The current wording of 13.4 [temp.arg] paragraph 7 disambiguates the function call in favor of the int version. If the null pointer conversion were allowed for pointer nontype template parameters, this case would become ambiguous, so it was decided to require a cast.

Proposed resolution (April, 2005):

  1. In 13.4.3 [temp.arg.nontype] paragraph 1, insert the following after the third bullet:

  2. Add the indicated text to the note in the second bullet of 13.4.3 [temp.arg.nontype] paragraph 5:

    [Note: In particular, neither the null pointer conversion (7.3.12 [conv.ptr]) nor the derived-to-base conversion (7.3.12 [conv.ptr]) are applied. Although 0 is a valid template-argument for a non-type template-parameter of integral type, it is not a valid template-argument for a non-type template-parameter of pointer type. However, (int*)0 is a valid template-argument for a non-type template-parameter of type “pointer to int.”end note]
  3. Replace the normative wording of 13.6 [temp.type] paragraph 1 with the following:

    Two template-ids refer to the same class or function if

    • their template-names refer to the same template, and
    • their corresponding type template-arguments are the same type, and
    • their corresponding non-type template-arguments of integral or enumeration type have identical values, and
    • their corresponding non-type template-arguments of pointer type refer to the same external object or function or are both the null pointer value, and
    • their corresponding non-type template-arguments of pointer-to-member type refer to the same class member or are both the null member pointer value, and
    • their corresponding non-type template-argumentss for template parameters of reference type refer to the same external object or function, and
    • their corresponding template template-arguments refer to the same template.



603. Type equivalence and unsigned overflow

Section: 13.6  [temp.type]     Status: CD1     Submitter: James Widman     Date: 3 November 2006

[Voted into WP at April, 2007 meeting as part of paper N2258.]

One of the requirements for two template-ids to refer to the same class or function (13.6 [temp.type] paragraph 1) is that

If we have some template of the form

  template <unsigned char c> struct A;

does this imply that A<'\001'> and A<257> (for an eight-bit char) refer to different specializations?

Jens Maurer: Looks like it should say something like, “their corresponding converted non-type template arguments of integral or enumeration type have identical values.”

Proposed resolution (April, 2007):

The change to 13.6 [temp.type] paragraph 1 shown in document J16/07-0118 = WG21 N2258, in which the syntactic non-terminal template-argument is changed to the English term “template argument” is sufficient to remove the confusion about whether the value before or after conversion is used in matching template-ids.




679. Equivalence of template-ids and operator function templates

Section: 13.6  [temp.type]     Status: CD1     Submitter: Richard Corden     Date: 1 March, 2008

[Voted into the WP at the September, 2008 meeting.]

In order for two template-ids to refer to the same function, 13.6 [temp.type] paragraph 1, bullet 1 requires that

This makes it impossible for two template-ids referring to operator function templates to be equivalent, because only simple-template-ids have a template-name, and a template-id referring to an operator function template is not a simple-template-id (13.3 [temp.names] paragraph 1) .

Suggested resolution:

Change 13.6 [temp.type] paragraph 1, bullet 1 to read,

Proposed resolution (June, 2008):

Change 13.6 [temp.type] paragraph 1, first bullet, as follows:




582. Template conversion functions

Section: 13.7.3  [temp.mem]     Status: CD1     Submitter: PremAnand Rao     Date: 23 May 2006

[Voted into WP at April, 2007 meeting.]

The wholesale replacement of the phrase “template function” by the resolution of issue 105 seems to have overlooked the similar phrase “template conversion function.” This phrase appears a number of times in 12.2.4.2.3 [over.ics.user] paragraph 3, 13.7.3 [temp.mem] paragraphs 5-8, and 13.10.3 [temp.deduct] paragraph 1. It should be systematically replaced in similar fashion to the resolution of issue 105.

Proposed resolution (October, 2006):

  1. Change 12.2.4.2.3 [over.ics.user] paragraph 3 as follows:

  2. If the user-defined conversion is specified by a template conversion function specialization of a conversion function template, the second standard conversion sequence must have exact match rank.
  3. Change 13.7.3 [temp.mem] paragraph 5 as follows:

  4. A specialization of a template conversion function conversion function template is referenced in the same way as a non-template conversion function that converts to the same type.
  5. Change 13.7.3 [temp.mem] paragraph 6 as follows:

  6. A specialization of a template conversion function conversion function template is not found by name lookup. Instead, any template conversion functions conversion function templates visible in the context of the use are considered.
  7. Change 13.7.3 [temp.mem] paragraph 7 as follows:

  8. A using-declaration using-declaration in a derived class cannot refer to a specialization of a template conversion function conversion function template in a base class.
  9. Change 13.7.3 [temp.mem] paragraph 8 as follows:

  10. Overload resolution (12.2.4.3 [over.ics.rank]) and partial ordering (13.7.7.3 [temp.func.order]) are used to select the best conversion function among multiple template conversion functions specializations of conversion function templates and/or non-template conversion functions.
  11. Change 13.10.3.4 [temp.deduct.conv] paragraph 1 as follows:

  12. Template argument deduction is done by comparing the return type of the template conversion function conversion function template (call it P) with the type that is required as the result of the conversion (call it A) as described in 13.10.3.6 [temp.deduct.type].



329. Evaluation of friends of templates

Section: 13.7.5  [temp.friend]     Status: CD1     Submitter: John Spicer     Date: 19 Dec 2001

[Voted into WP at October 2003 meeting.]

13.7.5 [temp.friend] paragraph 5 says:

When a function is defined in a friend function declaration in a class template, the function is defined at each instantiation of the class template. The function is defined even if it is never used. The same restrictions on multiple declarations and definitions which apply to non-template function declarations and definitions also apply to these implicit definitions. [Note: if the function definition is ill-formed for a given specialization of the enclosing class template, the program is ill-formed even if the function is never used. ]

This means that the following program is invalid, even without the call of f(ai):

  template <class T> struct A {
    friend void f(A a) {
      g(a);
    }
  };
  int main()
  {
    A<int> ai;
  // f(ai);  // Error if f(ai) is actually called
  }

The EDG front end issues an error on this case even if f(ai) is never called. Of the compilers I tried (g++, Sun, Microsoft, Borland) we are the only ones to issue such an error.

This issue came up because there is a library that either deliberately or accidentally makes use of friend functions that are not valid for certain instantiations.

The wording in the standard is the result of a deliberate decision made long ago, but given the fact that most implementations do otherwise it raises the issue of whether we did the right thing.

Upon further investigation, the current rule was adopted as the resolution to issue 6.47 in my series of template issue papers. At the time the issue was discussed (7/96) most compilers did evaluate such friends. So it seems that a number of compilers have changed their behavior since then.

Based on current practice, I think the standard should be changed to evaluate such friends only when used.

Proposed resolution (October 2002):

Change section 13.7.5 [temp.friend] paragraph 5 from:

When a function is defined in a friend function declaration in a class template, the function is defined at each instantiation of the class template. The function is defined even if it is never used. The same restrictions on multiple declarations and definitions which apply to non-template function declarations and definitions also apply to these implicit definitions. [Note: if the function definition is ill-formed for a given specialization of the enclosing class template, the program is ill-formed even if the function is never used. ]
to:
When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is used. The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.
Note the change from "which" to "that" in the last sentence.




410. Paragraph missed in changes for issue 166

Section: 13.7.5  [temp.friend]     Status: CD1     Submitter: John Spicer     Date: 18 Apr 2003

[Voted into WP at October 2004 meeting.]

13.7.5 [temp.friend] paragraph 2 was overlooked when the changes for issue 166 were made.

The friend declaration of f<>(int) is now valid.

A friend function declaration that is not a template declaration and in which the name of the friend is an unqualified template-id shall refer to a specialization of a function template declared in the nearest enclosing namespace scope. [Example:
  namespace N {
          template <class T> void f(T);
          void g(int);
          namespace M {
                  template <class T> void h(T);
                  template <class T> void i(T);
                  struct A {
                          friend void f<>(int);   // ill-formed - N::f
                          friend void h<>(int);   // OK - M::h
                          friend void g(int);     // OK - new decl of M::g
                          template <class T> void i(T);
                          friend void i<>(int);   // ill-formed - A::i
                  };
          }
  }
--end example]

Proposed Resolution (October 2003):

Remove 13.7.5 [temp.friend] paragraph 2:

A friend function declaration that is not a template declaration and in which the name of the friend is an unqualified template-id shall refer to a specialization of a function template declared in the nearest enclosing namespace scope. [Example:
  namespace N {
          template <class T> void f(T);
          void g(int);
          namespace M {
                  template <class T> void h(T);
                  template <class T> void i(T);
                  struct A {
                          friend void f<>(int);   // ill-formed - N::f
                          friend void h<>(int);   // OK - M::h
                          friend void g(int);     // OK - new decl of M::g
                          template <class T> void i(T);
                          friend void i<>(int);   // ill-formed - A::i
                  };
          }
  }
--end example]



286. Incorrect example in partial specialization

Section: 13.7.6  [temp.spec.partial]     Status: CD1     Submitter: Martin Sebor     Date: 09 May 2001

[Moved to DR at 4/02 meeting.]

The example in 13.7.6.1 [temp.spec.partial.general] paragraph 6 is incorrect. It reads,

    template<class T> struct A {
        class C {
            template<class T2> struct B { };
        };
    };

    // partial specialization of A<T>::C::B<T2>
    template<class T> template<class T2>
        struct A<T>::C::B<T2*> { };

    A<short>::C::B<int*> absip; // uses partial specialization

Because C is a class rather than a struct, the use of the name B is inaccessible.

Proposed Resolution (10/01):

Change class C to struct C in the example in 13.7.6.1 [temp.spec.partial.general] paragraph 6. The example becomes

    template<class T> struct A {
        struct C {
            template<class T2> struct B { };
        };
    };

    // partial specialization of A<T>::C::B<T2>
    template<class T> template<class T2>
        struct A<T>::C::B<T2*> { };

    A<short>::C::B<int*> absip; // uses partial specialization



517. Partial specialization following explicit instantiation

Section: 13.7.6.1  [temp.spec.partial.general]     Status: CD1     Submitter: John Spicer     Date: 03 May 2005

[Voted into WP at the October, 2006 meeting.]

According to 13.7.6.1 [temp.spec.partial.general] paragraph 1,

If a template is partially specialized then that partial specialization shall be declared before the first use of that partial specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.

This leaves the impression that an explicit instantiation of the primary template may precede the declaration of an applicable partial specialization. Is the following example well-formed?

    template<typename T> class X{
        public:
        void foo(){};
    };

    template class X<void *>;

    template<typename T> class X<T*>{
        public:
        void baz();
    };

    void bar() {
        X<void *> x;
        x.foo();
    }

Proposed resolution (October, 2005):

Replace the last sentence of 13.7.6.1 [temp.spec.partial.general] paragraph 1:

If a template is partially specialized then that partial specialization shall be declared before the first use of that partial specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.

with:

A partial specialization shall be declared before the first use of a class template specialization that would make use of the partial specialization as the result of an implicit or explicit instantiation in every translation unit in which such a use occurs; no diagnostic is required.



214. Partial ordering of function templates is underspecified

Section: 13.7.7.3  [temp.func.order]     Status: CD1     Submitter: Martin von Loewis/Martin Sebor     Date: 13 Mar 2000

[Voted into WP at October 2003 meeting.]

In 13.7.7.3 [temp.func.order], partial ordering is explained in terms of template argument deduction. However, the exact procedure for doing so is not specified. A number of details are missing, they are explained as sub-issues below.

  1. 13.7.7.3 [temp.func.order] paragraph 2 refers to 13.10.3 [temp.deduct] for argument deduction. This is the wrong reference; it explains how explicit arguments are processed (paragraph 2) and how function parameter types are adjusted (paragraph 3). Neither of these steps is meaningful in the context of partial ordering. Next in deduction follows one of the steps in 13.10.3.2 [temp.deduct.call], 13.10.3.3 [temp.deduct.funcaddr], 13.10.3.4 [temp.deduct.conv], or 13.10.3.6 [temp.deduct.type]. The standard does not specify which of these contexts apply to partial ordering.
  2. Because 13.10.3.2 [temp.deduct.call] and 13.10.3.4 [temp.deduct.conv] both start with actual function parameters, it is meaningful to assume that partial ordering uses 13.10.3.6 [temp.deduct.type], which only requires types. With that assumption, the example in 13.7.7.3 [temp.func.order] paragraph 5 becomes incorrect, considering the two templates
        template<class T> void g(T);  // #1
        template<class T> void g(T&); // #2
    
    Here, #2 is at least as specialized as #1: With a synthetic type U, #2 becomes g(U&); argument deduction against #1 succeeds with T=U&. However, #1 is not at least as specialized as #2: Deducing g(U) against g(T&) fails. Therefore, the second template is more specialized than the first, and the call g(x) is not ambiguous.
  3. According to John Spicer, the intent of the partial ordering was that it uses deduction as in a function call (13.10.3.2 [temp.deduct.call]), which is indicated by the mentioning of "exact match" in 13.7.7.3 [temp.func.order] paragraph 4. If that is indeed the intent, it should be specified how values are obtained for the step in 13.10.3.2 [temp.deduct.call] paragraph 1, where the types of the arguments are determined. Also, since 13.10.3.2 [temp.deduct.call] paragraph 2 drops references from the parameter type, symmetrically, references should be dropped from the argument type (which is done in Clause 7 [expr] paragraph 2, for a true function call).
  4. 13.7.7.3 [temp.func.order] paragraph 4 requires an "exact match" for the "deduced parameter types". It is not clear whether this refers to the template parameters, or the parameters of the template function. Considering the example
        template<class S> void g(S);  // #1
        template<class T> void g(T const &); // #3
    
    Here, #3 is clearly at least as specialized as #1. To determine whether #1 is at least as specialized as #3, a unique type U is synthesized, and deduction of g<U>(U) is performed against #3. Following the rules in 13.10.3.2 [temp.deduct.call], deduction succeeds with T=U. Since the template argument is U, and the deduced template parameter is also U, we have an exact match between the template parameters. Even though the conversion from U to U const & is an exact match, it is not clear whether the added qualification should be taken into account, as it is in other places.

Issue 200 covers a related issue, illustrated by the following example:

    template <class T> T f(int);
    template <class T, class U> T f(U);
    void g() {
        f<int>(1);
    }

Even though one template is "obviously" more specialized than the other, deduction fails in both directions because neither function parameter list allows template parameter T to be deduced.

(See also issue 250.)

Nathan Sidwell:

13.7.7.3 [temp.func.order] describes the partial ordering of function templates. Paragraph 5 states,

A template is more specialized than another if, and only if, it is at least as specialized as the other template and that template is not at least as specialized as the first.
To paraphrase, given two templates A & B, if A's template parameters can be deduced by B, but B's cannot be deduced by A, then A is more specialized than B. Deduction is done as if for a function call. In particular, except for conversion operators, the return type is not involved in deduction. This leads to the following templates and use being unordered. (This example is culled from G++ bug report 4672 http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view&pr=4672)
  template <typename T, class U> T checked_cast(U from); //#1
  template <typename T, class U> T checked_cast(U * from); //#2
  class C {};

  void foo (int *arg)
  {
    checked_cast <C const *> (arg);
  }
In the call,

#1 can be deduced with T = 'C const *' and U = 'int *'

#2 can be deduced with T = 'C const *' and U = 'int'

It looks like #2 is more specialized that #1, but 13.7.7.3 [temp.func.order] does not make it so, as neither template can deduce 'T' from the other template's function parameters.

Possible Resolutions:

There are several possible solutions, however through experimentation I have discounted two of them.

Option 1:

When deducing function ordering, if the return type of one of the templates uses a template parameter, then return types should be used for deduction. This, unfortunately, makes existing well formed programs ill formed. For example

  template <class T> class X {};

  template <class T> X<T> Foo (T *);	// #1
  template <class T> int Foo (T const *); // #2

  void Baz (int *p1, int const *p2)
  {
    int j = Foo (p2); //#3
  }
Here, neither #1 nor #2 can deduce the other, as the return types fail to match. Considering only the function parameters gives #2 more specialized than #1, and hence makes the call #3 well formed.

Option 2:

As option 1, but only consider the return type when deducing the template whose return type involves template parameters. This has the same flaw as option 1, and that example is similarly ill formed, as #1's return type 'X<T,0>' fails to match 'int' so #1 cannot deduce #2. In the converse direction, return types are not considered, but the function parameters fail to deduce.

Option 3:

It is observed that the original example is only callable with a template-id-expr to supply a value for the first, undeducible, parameter. If that parameter were deducible it would also appear within at least one of the function parameters. We can alter paragraph 4 of [temp.func.order] to indicate that it is not necessary to deduce the parameters which are provided explicitly, when the call has the form of a template-id-expr. This is a safe extension as it only serves to make ill formed programs well formed. It is also in line with the concept that deduction for function specialization order should proceed in a similar manner to function calling, in that explicitly provided parameter values are taken into consideration.

Suggested resolution:

Insert after the first sentence of paragraph 4 in 13.7.7.3 [temp.func.order]

Should any template parameters remain undeduced, and the function call be of the form of a template-id-expr, those template parameters provided in the template-id-expr may be arbitrarily synthesized prior to determining whether the deduced arguments generate a valid function type.

See also issue 200.

(April 2002) John Spicer and John Wiegley have written a paper on this. See 02-0051/N1393.

Proposed resolution (October 2002):

Change 13.7.7.3 [temp.func.order] paragraph 2 to read:

Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function parameter types, or in the case of a conversion function the return type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.

Change 13.7.7.3 [temp.func.order] paragraph 3 to read:

To produce the transformed template, for each type, non-type, or template template parameter synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.

Change 13.7.7.3 [temp.func.order] paragraph 4 to read (note: the section reference should refer to the section added to 13.10.3 [temp.deduct] below):

Using the transformed function template's function parameter list, or in the case of a conversion function its transformed return type, perform type deduction against the function parameter list (or return type) of the other function. The mechanism for performing these deductions is given in 14.8.2.x.

Remove the text of 13.7.7.3 [temp.func.order] paragraph 5 but retain the example. The removed text is:

A template is more specialized than another if, and only if, it is at least as specialized as the other template and that template is not at least as specialized as the first.

Insert the following section before 14.8.2.5 (Note that this would either be a new 14.8.2.4, or would be given a number like 14.8.2.3a. If neither of these is possible from a troff point of view, this could be made 14.8.2.5. )

Deducing template arguments when determining the partial ordering of function templates (temp.deduct.partial)

Template argument deduction is done by comparing certain types associated with the two function templates being compared.

Two sets of types are used to determine the partial ordering. For each of the templates involved there is the original function type and the transformed function type. [Note: The creation of the transformed type is described in 13.7.7.3 [temp.func.order].] The deduction process uses the transformed type as the argument template and the original type of the other template as the parameter template. This process is done twice for each type involved in the partial ordering comparison: once using the transformed template-1 as the argument template and template-2 as the parameter template and again using the transformed template-2 as the argument template and template-1 as the parameter template.

The types used to determine the ordering depend on the context in which the partial ordering is

Each type from the parameter template and the corresponding type from the argument template are used as the types of P and A.

Before the partial ordering is done, certain transformations are performed on the types used for partial ordering:

If both P and A were reference types (before being replaced with the type referred to above), determine which of the two types (if any) is more cv-qualified than the other; otherwise the types are considered to be equally cv-qualified for partial ordering purposes. The result of this determination will be used below.

Remove any top-level cv-qualifiers:

Using the resulting types P and A the deduction is then done as described in (13.10.3.6 [temp.deduct.type]). If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template.

If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) if the type from the argument template is more cv-qualified than the type from the parameter template (as described above) that type is considered to be more specialized than the other. If neither type is more cv-qualified than the other then neither type is more specialized than the other.

If for each type being considered a given template is at least as specialized for all types and more specialized for some set of types and the other template is not more specialized for any types or is not at least as specialized for any types, then the given template is more specialized than the other template. Otherwise, neither template is more specialized than the other.

In most cases, all template parameters must have values in order for deduction to succeed, but for partial ordering purposes a template parameter may remain without a value provided it is not used in the types being used for partial ordering. [Note: A template parameter used in a non-deduced context is considered used.]

[Example:

template <class T> T f(int);        // #1
template <class T, class U> T f(U); // #2
void g() {
    f<int>(1);  // Calls #1
}
--end example]




180. typename and elaborated types

Section: 13.8  [temp.res]     Status: CD1     Submitter: Mike Miller     Date: 21 Dec 1999

[Moved to DR at 4/02 meeting.]

Mike Miller: A question about typename came up in the discussion of issue 68 that is somewhat relevant to the idea of omitting typename in contexts where it is clear that a type is required: consider something like

        template <class T>
        class X {
            friend class T::nested;
        };
Is typename required here? If so, where would it go? (The grammar doesn't seem to allow it anywhere in an elaborated-type-specifier that has a class-key.)

Bill Gibbons: The class applies to the last identifier in the qualified name, since all the previous names must be classes or namespaces. Since the name is specified to be a class it does not need typename. [However,] it looks like 13.8 [temp.res] paragraph 3 requires typename and the following paragraphs do not exempt this case. This is not what we agreed on.

Proposed resolution (04/01):

In 13.8 [temp.res] paragraph 5, change

The keyword typename is not permitted in a base-specifier or in a mem-initializer; in these contexts a qualified-name that depends on a template-parameter (13.8.3 [temp.dep]) is implicitly assumed to be a type name.

to

A qualified name used as the name in a mem-initializer-id, a base-specifier, or an elaborated-type-specifier (in the class-key and enum forms) 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.]

(The expected resolution for issue 254 will remove the typename forms from the grammar for elaborated-type-specifier. If that resolution is adopted, the parenthetical phrase "(in the class-key and enum forms)" in the preceding wording should be removed because those will be the only forms of elaborated-type-specifier.)

This has been consolidated with the edits for some other issues. See N1376=02-0034.




345. Misleading comment on example in templates chapter

Section: 13.8  [temp.res]     Status: CD1     Submitter: Jason Shirk     Date: 18 March 2002

[Voted into WP at April 2003 meeting.]

The following example from 13.8 [temp.res] paragraph 4:

struct A {
	struct X { };
	int X;
};
template<class T> void f(T t) {
	typename T::X x;        //  ill-formed: finds the data member  X
					//  not the member type  X
}

is not ill-formed. The intent of the example is obvious, but some mention should be made that it is only ill-formed when T=A. For other T's, it could be well formed.

Proposed resolution (October 2002):

In 13.8 [temp.res] paragraph 4, replace the example with:

struct A {
  struct X { };
  int X;
} ;
struct B {
  struct X { };
} ;
template<class T> void f(T t) {
  typename T::X x;
}
void foo() {
  A a;
  B b;
  f(b);  // OK -- T::X refers to B::X.
  f(a);  // error: T::X refers to the data member A::X not
         // the struct A::X.
}



382. Allow typename outside of templates

Section: 13.8  [temp.res]     Status: CD1     Submitter: Steve Adamczyk     Date: 8 Nov 2002

[Voted into WP at October 2005 meeting.]

P. J. Plauger, among others, has noted that typename is hard to use, because in a given context it's either required or forbidden, and it's often hard to tell which. It would make life easier for programmers if typename could be allowed in places where it is not required, e.g., outside of templates.

Notes from the April 2003 meeting:

There was unanimity on relaxing this requirement on typename. The question was how much to relax it. Everyone agreed on allowing it on all qualified names, which is an easy fix (no syntax change required). But should it be allowed other places? P.J. Plauger said he'd like to see it allowed anywhere a type name is allowed, and that it could actually be a decades-late assist for the infamous "the ice is thin here" typedef problem noted in K&R I.

Proposed resolution (April 2003):

Replace the text at the start of 13.8 [temp.res] paragraph 3:

A qualified-id that refers to a type and in which the nested-name-specifier depends on a template-parameter (13.8.3 [temp.dep]) shall be prefixed by the keyword typename to indicate that the qualified-id denotes a type, forming an elaborated-type-specifier (9.2.9.5 [dcl.type.elab]).

With:

The keyword typename can only be applied to a qualified-id. A qualified-id that refers to a type and in which the nested-name-specifier depends on a template-parameter (13.8.3 [temp.dep]) shall be prefixed by the keyword typename to indicate that the qualified-id denotes a type, forming an elaborated-type-specifier (9.2.9.5 [dcl.type.elab]). If a qualified-id which has been prefixed by the keyword typename does not denote a type the program is ill-formed. [ Note: The keyword is only required on a qualified-id within a template declaration or definition in which the nested-name-specifier depends on a template-parameter. ]

Remove 13.8 [temp.res] paragraph 5:

The keyword typename shall only be used in template declarations and definitions, including in the return type of a function template or member function template, in the return type for the definition of a member function of a class template or of a class nested within a class template, and in the type-specifier for the definition of a static member of a class template or of a class nested within a class template. The keyword typename shall be applied only to qualified names, but those names need not be dependent. The keyword typename shall be used only in contexts in which dependent names can be used. This includes template declarations and definitions but excludes explicit specialization declarations and explicit instantiation declarations. The keyword typename is not permitted in a base-specifier or in a mem-initializer; in these contexts a qualified-id that depends on a template-parameter (temp.dep) is implicitly assumed to be a type name.

Note: the claim here that a qualified name preceded by typename forms an elaborated type specifier conflicts with the changes made in issue 254 (see N1376=02-0034), which introduces typename-specifier.

Notes from October 2003 meeting:

We considered whether typename should be allowed in more places, and decided we only wanted to allow it in qualified names (for now at least).

Core issue 254 changed elaborated-type-specifier to typename-specifier. It also changed 13.8 [temp.res] paragraph 5, which this proposed resolution deletes.

See also issue 468.

Proposed resolution (October, 2004):

  1. Change 13.8 [temp.res] paragraph 3 as follows:

    A When a qualified-id that refers to a type and in which the nested-name-specifier depends on a template-parameter (13.8.3 [temp.dep]) is intended to refer to a type, it shall be prefixed by the keyword typename to indicate that the qualified-id denotes a type, forming a typename-specifier. If the qualified-id in a typename-specifier does not denote a type, the program is ill-formed.
  2. Change 13.8 [temp.res] paragraph 5 as follows:

    The keyword typename shall only be used in template declarations and definitions, including in the return type of a function template or member function template, in the return type for the definition of a member function of a class template or of a class nested within a class template, and in the type-specifier for the definition of a static member of a class template or of a class nested within a class template. The keyword typename shall be applied only to qualified names, but those names need not be dependent. The keyword typename shall be used only in contexts in which dependent names can be used. This includes template declarations and definitions but excludes explicit specialization declarations and explicit instantiation declarations. A qualified name used as the name in a mem-initializer-id, a base-specifier, or an elaborated-type-specifier 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. —end note]



409. Obsolete paragraph missed by changes for issue 224

Section: 13.8  [temp.res]     Status: CD1     Submitter: John Spicer     Date: 18 Apr 2003

[Voted into WP at October 2004 meeting.]

Paragraph 6 of 13.8 [temp.res] is obsolete as result of issue 224, and needs to be revised.

Within the definition of a class template or within the definition of a member of a class template, the keyword typename is not required when referring to the unqualified name of a previously declared member of the class template that declares a type. The keyword typename shall always be specified when the member is referred to using a qual- ified name, even if the qualifier is simply the class template name. [Example:
  template<class T> struct A {
      typedef int B;
      A::B b;                     // ill-formed: typename required before A::B
      void f(A<T>::B);            // ill-formed: typename required before A<T>::B
      typename A::B g();          // OK
  };
]

Proposed Resolution:

Change 13.8 [temp.res] paragraph 6 as follows

Within the definition of a class template or within the definition of a member of a class template, the keyword typename is not required when referring to the unqualified name of a previously declared member of the class template that declares a type. The keyword typename shall always be specified when the member is referred to using a qualified name, even if the qualifier is simply the class template name. [Example:
template<class T> struct A {
    typedef int B;
    B b;                      // ok, no typename required
    A::B b;                   //  ill-formed: typename required before  A::B
    void f(A<T>::B);          //  ill-formed: typename required before  A<T>::B
    typename A::B g();        //  OK
};
The keyword typename is required whether the qualified name is A or A<T> because A or A<T> are synonyms within a class template with the parameter list <T>. ]



559. Editing error in issue 382 resolution

Section: 13.8  [temp.res]     Status: CD1     Submitter: Mike Miller     Date: 11 February 2006

[Voted into WP at April, 2006 meeting.]

Part of the resolution for issue 224 was the addition of the phrase “but does not refer to a member of the current instantiation” to 13.8 [temp.res] paragraph 3. When the resolution of issue 382 was added to the current working draft, however, that phrase was inadvertently removed. Equivalent phrasing should be restored.

Proposed resolution (April, 2006):

Replace the first sentence of 13.8 [temp.res] paragraph 3 with the following text:

When a qualified-id is intended to refer to a type that is not a member of the current instantiation (13.8.3.2 [temp.dep.type]) and its nested-name-specifier depends on a template-parameter (13.8.3 [temp.dep]), it shall be prefixed by the keyword typename, forming a typename-specifier.



666. Dependent qualified-ids without the typename keyword

Section: 13.8  [temp.res]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 6 December 2007

[Voted into the WP at the June, 2008 meeting.]

13.8 [temp.res] paragraphs 2 and 4 read,

A name used in a template declaration or definition and that is dependent on a template-parameter is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.

If a specialization of a template is instantiated for a set of template-arguments such that the qualified-id prefixed by typename does not denote a type, the specialization is ill-formed.

It is not clear whether this is intended to, or is sufficient to, render a specialization ill-formed if a dependent qualified-id that is not prefixed by typename actually does denote a type. For example,

    int i;

    template <class T> void f() {
        T::x * i; // declaration or multiplication!?
    }

    struct Foo {
        typedef int x;
    };

    struct Bar {
        static int const x = 5;
    };

    int main() {
        f<Bar>(); // multiplication
        f<Foo>(); // declaration!
    }

I think that the specialization for Foo should be ill-formed.

Proposed resolution (February, 2008):

Add the following after 13.8 [temp.res] paragraph 5:

If, for a given set of template arguments, a specialization of a template is instantiated that refers to a qualified-id that denotes a type, and the nested-name-specifier of the qualified-id depends on a template parameter, the qualified-id shall either be prefixed by typename or shall be used in a context in which it implicitly names a type as described above. [Example:

    template <class T> void f(int i) {
      T::x * i;     // T::x must not be a type
    }

    struct Foo {
      typedef int x;
    };

    struct Bar {
      static int const x = 5;
    };

    int main() {
      f<Bar>(1);     // OK
      f<Foo>(1);     // error: Foo::x is a type
    }

end example]




515. Non-dependent references to base class members

Section: 13.8.3  [temp.dep]     Status: CD1     Submitter: Mike Miller     Date: 18 Apr 2005

[Voted into WP at the October, 2006 meeting.]

Implementations vary in their treatment of the following code:

    struct A {
      int foo_;
    };
    template <typename T> struct B: public A { };
    template <typename T> struct C: B<T> {
      int foo() {
        return A::foo_;  // #1
      }
    };
    int f(C<int>* p) {
      return p->foo();
    }

According to one analysis, because the expression A::foo_ on line #1 is non-dependent, it must be analyzed in the definition context. It that context, it violates the restrictions of 11.4 [class.mem] paragraph 10 on how the name of a nonstatic data member of a class can be used and thus should be treated as an error.

On the other hand, the description of the transformation of an id-expression into a class member access expression (11.4.3 [class.mfct.non.static] paragraph 3) does not have any special treatment of templates; when C<int>::foo() is instantiated, the reference to A::foo_ turns out to be to a base class member and is thus transformed into (*this).A::foo_ and is thus not an error.

Proposed resolution (October, 2005):

Change 11.4.3 [class.mfct.non.static] paragraph 3 as indicated:

When an id-expression (_N4567_.5.1.1 [expr.prim.general]) that is not part of a class member access syntax (7.6.1.5 [expr.ref]) and not used to form a pointer to member (7.6.2.2 [expr.unary.op]) is used in the body of a non-static member function of class X or used in the mem-initializer for a constructor of class X, if name lookup (6.5.3 [basic.lookup.unqual]) resolves the name in the id-expression to a non-static non-type member of class X or of a base class of X some class C, the id-expression is transformed into a class member access expression (7.6.1.5 [expr.ref]) using (*this) (_N4868_.11.4.3.2 [class.this]) as the postfix-expression to the left of the . operator. [Note: If C is not X or a base class of X, the class member access expression is ill-formed. —end note] The member name then refers to the member of the object for which the function is called. Similarly during name lookup...



524. Can function-notation calls to operator functions be dependent?

Section: 13.8.3  [temp.dep]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 19 July 2005

[Voted into WP at the October, 2006 meeting.]

The description of dependent function calls in 13.8.3 [temp.dep] paragraph 1 applies only to identifiers in postfix-notation function calls and to operator notation calls for operator functions:

In an expression of the form:

where the postfix-expression is an identifier, the identifier denotes a dependent name if and only if any of the expressions in the expression-list is a type-dependent expression (13.8.3.3 [temp.dep.expr]). If an operand of an operator is a type-dependent expression, the operator also denotes a dependent name.

It would appear from the related passage in 13.8.4.2 [temp.dep.candidate] paragraph 1 that the description of postfix-notation function calls should apply to all unqualified-ids that are not template-ids, including operator-function-ids, not just to identifiers:

For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found...

Proposed resolution (October, 2005):

  1. Change 13.8.3 [temp.dep] paragraph 1 as indicated:

  2. ...In an expression of the form:

    where the postfix-expression is an identifier unqualified-id but not a template-id, the identifier unqualified-id denotes a dependent name if and only if any of the expressions in the expression-list is a type-dependent expression (13.8.3.3 [temp.dep.expr])...

  3. Change 13.8.4.2 [temp.dep.candidate] paragraph 1 as indicated:

  4. For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, or if the function is called using operator notation, the candidate functions are found using the usual lookup rules (6.5.3 [basic.lookup.unqual], 6.5.4 [basic.lookup.argdep]) except that...

(See also issue 561.)




224. Definition of dependent names

Section: 13.8.3.2  [temp.dep.type]     Status: CD1     Submitter: Derek Inglis     Date: 30 Nov 1999

[Moved to DR at 10/01 meeting.]

The definition of when a type is dependent, given in 13.8.3.2 [temp.dep.type], is essentially syntactic: if the reference is a qualified-id and one of the class-names in the nested-name-specifier is dependent, the type is dependent. This approach leads to surprising results:

    template <class T> class X {
        typedef int I;
	I a;                 // non-dependent
        typename X<T>::I b;  // dependent
        typename X::I c;     // dependent (X is equivalent to X<T>)
    };

Suggested resolution:

The decision on whether a name is dependent or non-dependent should be based on lookup, not on the form of the name: if the name can be looked up in the definition context and cannot be anything else as the result of specialization, the name should be non-dependent.

See papers J16/00-0028 = WG21 N1251 and J16/00-0056 = WG21 N1279.

Proposed resolution (10/00):

  1. Replace section 13.8.3.2 [temp.dep.type] with the following:

    In the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, a name refers to the current instantiation if it is

    • the injected-class-name (Clause 11 [class]) of the class template or nested class,
    • in the definition of a primary class template, the name of the class template followed by the template argument list of the primary template (as described below) enclosed in <>,
    • in the definition of a nested class of a class template, the name of the nested class referenced as a member of the current instantiation, or
    • in the definition of a partial specialization, the name of the class template followed by the template argument list of the partial specialization enclosed in <>.

    The template argument list of a primary template is a template argument list in which the nth template argument has the value of the nth template parameter of the class template.

    A template argument that is equivalent to a template parameter (i.e., has the same constant value or the same type as the template parameter) can be used in place of that template parameter in a reference to the current instantiation. In the case of a nontype template argument, the argument must have been given the value of the template parameter and not an expression involving the template parameter.

    [Example:

    template <class T> class A {
        A* p1;      // A is the current instantiation
        A<T>* p2;   // A<T> is the current instantiation
        A<T*> p3;   // A<T*> is not the current instantiation
        ::A<T>* p4; // ::A<T> is the current instantiation
        class B {
    	B* p1;        // B is the current instantiation
    	A<T>::B* p2;  // A<T>::B is the current instantiation
    	typename A<T*>::B* p3; // A<T*>::B is not the
    			     // current instantiation
        };
    };
    
    template <class T> class A<T*> {
        A<T*>* p1;  // A<T*> is the current instantiation
        A<T>* p2;   // A<T> is not the current instantiation
    };
    
    template <class T1, class T2, int I> struct B {
        B<T1, T2, I>*	b1;        // refers to the current instantiation
        B<T2, T1, I>*	b2;        // not the current instantiation
        typedef T1 my_T1;
        static const int my_I = I;
        static const int my_I2 = I+0;
        static const int my_I3 = my_I;
        B<my_T1, T2, my_I>* b3;  // refers to the current instantiation
        B<my_T1, T2, my_I2>* b4; // not the current instantiation
        B<my_T1, T2, my_I3>* b5; // refers to the current instantiation
    };
    

    end example]

    A name is a member of the current instantiation if it is

    • An unqualified name that, when looked up, refers to a member of a class template. [Note: This can only occur when looking up a name in a scope enclosed by the definition of a class template.]
    • A qualified-id in which the nested-name-specifier refers to the current instantiation.

    [Example:

    template <class T> class A {
        static const int i = 5;
        int n1[i];        // i refers to a member of the current instantiation
        int n2[A::i];     // A::i refers to a member of the current instantiation
        int n3[A<T>::i];  // A<T>::i refers to a member of the current instantiation
        int f();
    };
    
    template <class T> int A<T>::f()
    {
        return i;  // i refers to a member of the current instantiation
    }
    

    end example]

    A name is a member of an unknown specialization if the name is a qualified-id in which the nested-name-specifier names a dependent type that is not the current instantiation.

    A type is dependent if it is

    • a template parameter,
    • a member of an unknown specialization,
    • a nested class that is a member of the current instantiation,
    • a cv-qualified type where the cv-unqualified type is dependent,
    • a compound type constructed from any dependent type,
    • an array type constructed from any dependent type or whose size is specified by a constant expression that is value-dependent, or
    • a template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent.

    [Note: Because typedefs to not introduce new types, but instead simply refer to other types, a name that refers to a typedef that is a member of the current instantiation is dependent only if the type referred to is dependent.]

  2. In 13.8.3.3 [temp.dep.expr] paragraph 3, replace

    • a nested-name-specifier that contains a class-name that names a dependent type.

    with

    • a nested-name-specifier or qualified-id that names a member of an unknown specialization.
  3. In 13.8.3.3 [temp.dep.expr], add the following paragraph:

    A class member access expression (7.6.1.5 [expr.ref]) is type-dependent if the type of the referenced member is dependent. [Note: In an expression of the form x.y or xp->y the type of the expression is usually the type of the member y of the class of x (or the class pointed to by xp). However, if x or xp refers to a dependent type that is not the current instantiation, the type of y is always dependent. If x or xp refers to a non-dependent type or refers to the current instantiation, the type of y is the type of the class member access expression.]
  4. In 13.8 [temp.res] paragraph 3, replace

    A qualified-name that refers to a type and that depends on a template-parameter (13.8.3 [temp.dep]) shall be prefixed by the keyword typename.

    with

    A qualified-id that refers to a type and that depends on a template-parameter (13.8.3 [temp.dep]) but does not refer to a member of the current instantiation shall be prefixed by the keyword typename.

    Note: the wording for this paragraph was changed in TC1. The words shown here are the pre-TC1 words.

  5. In 13.3 [temp.names] paragraph 4, replace

    When the name of a member template specialization appears after . or -> in a postfix-expression, or after a nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (13.8.3 [temp.dep]), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.

    with

    When the name of a member template specialization appears after . or -> in a postfix-expression, or after a nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (13.8.3 [temp.dep]) but does not refer to a member of the current instantiation (13.8.3.2 [temp.dep.type]), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.
  6. In 13.8.2 [temp.local] paragraph 2, remove the following text, which was added for issue 108. The updated definition of dependent name now addresses this case.

    Within the scope of a class template, when the unqualified name of a nested class of the class template is referred to, it is equivalent to the name of the nested class qualified by the name of the enclosing class template. [Example:

    template <class T> struct A {
    	class B {};
    	// B is equivalent to A::B, which is equivalent to A<T>::B,
    	// which is dependent.
    	class C : B { };
    };
    

    end example]




447. Is offsetof type-dependent?

Section: 13.8.3.4  [temp.dep.constexpr]     Status: CD1     Submitter: Mark Mitchell     Date: 7 Jan 2004

[Voted into WP at October 2005 meeting.]

As far as I can tell, the standard doesn't say whether "offsetof(...)" is type-dependent. In the abstract, it shouldn't be -- an "offsetof" expression is always of type "size_t". But the standard doesn't say to what the definition of the macro is, so I don't think one can deduce that it will always be considered non-dependent by a conforming compiler.

John Spicer: (1) I agree that you can't know if offsetof is dependent because you don't know what it expands to. (2) In principle, offsetof should be like sizeof -- it is value-dependent if its argument is type-dependent.

Mark Mitchell: I think we should say that: (a) offsetof is not type-dependent, and (b) offsetof is value dependent iff the first argument is type-dependent

Everyone is using slightly different builtins to implement this functionality, and I don't think that there's any guarantee that they're all behaving the same here.

Notes from the March 2004 meeting:

Note that any such requirement would be in the library section, not core.

Proposed resolution (October, 2004):

  1. At the end of 13.8.3.3 [temp.dep.expr] paragraph 4, add after the list that ends with throw assignment-expression:

    [Note: For the standard library macro offsetof, see 17.2 [support.types]. —end note]
  2. At the end of 13.8.3.4 [temp.dep.constexpr] paragraph 2, add after the list that ends with sizeof(type-id):

    [Note: For the standard library macro offsetof, see 17.2 [support.types]. —end note]
  3. In 17.2 [support.types] paragraph 4, replace

    The macro offsetof accepts a restricted set of type arguments in this International Standard. If type is not a POD structure or a POD union the results are undefined. The result of applying the offsetof macro to a field that is a static data member or a function member is undefined.

    with

    The macro offsetof(type, member-designator) accepts a restricted set of type arguments in this International Standard. If type is not a POD structure or a POD union ( Clause 11 [class]), the results are undefined. The expression offsetof(type, member-designator) is never type-dependent (13.8.3.3 [temp.dep.expr]) and it is value-dependent (13.8.3.4 [temp.dep.constexpr]) if and only if type is dependent. The result of applying the offsetof macro to a field that is a static data member or a function member is undefined.

    [Note: the original wording shown here reflects the resolutions of library issues 306 and 449.]




197. Issues with two-stage lookup of dependent names

Section: 13.8.4.2  [temp.dep.candidate]     Status: CD1     Submitter: Derek Inglis     Date: 26 Jan 2000

[Voted into WP at October 2005 meeting.]

The example in 13.8 [temp.res] paragraph 9 is incorrect, according to 13.8.4.2 [temp.dep.candidate] . The example reads,

    void f(char);

    template <class T> void g(T t)
    {
        f(1);        // f(char);
        f(T(1));     // dependent
        f(t);        // dependent
        dd++;        // not dependent
                     // error: declaration for dd not found
    }

    void f(int);

    double dd;
    void h()
    {
        g(2);        // will cause one call of f(char) followed
                     // by two calls of f(int)
        g('a');      // will cause three calls of f(char)
    }
Since 13.8.4.2 [temp.dep.candidate] says that only Koenig lookup is done from the instantiation context, and since 6.5.4 [basic.lookup.argdep] says that fundamental types have no associated namespaces, either the example is incorrect (and f(int) will never be called) or the specification in 13.8.4.2 [temp.dep.candidate] is incorrect.

Notes from 04/00 meeting:

The core working group agreed that the example as written is incorrect and should be reformulated to use a class type instead of a fundamental type. It was also decided to open a new issue dealing more generally with Koenig lookup and fundamental types.

(See also issues 213 and 225.)

Proposed resolution (April, 2005):

Change the example in 13.8 [temp.res] paragraph 9 as follows:

    void f(char);

    template <class T> void g(T t)
    {
        f(1);        // f(char);
        f(T(1));     // dependent
        f(t);        // dependent
        dd++;        // not dependent
                     // error: declaration for dd not found
    }

    enum E { e };
    void f(intE);

    double dd;
    void h()
    {
        g(2e);       // will cause one call of f(char) followed
                     // by two calls of f(intE)
        g('a');      // will cause three calls of f(char)
    }



259. Restrictions on explicit specialization and instantiation

Section: 13.9  [temp.spec]     Status: CD1     Submitter: Matt Austern     Date: 2 Nov 2000

[Moved to DR at 4/02 meeting.]

According to 13.9 [temp.spec] paragraph 5,

No program shall explicitly instantiate any template more than once, both explicitly instantiate and explicitly specialize a template, or specialize a template more than once for a given set of template-arguments.

This rule has an impact on library issue 120. Library authors would like to have the freedom to specialize (or not) various library functions without having to document their choices, while users need the flexibility to explicitly instantiate library functions in certain translation units.

If this rule could be slightly weakened, it would reduce the need for constraining either the library author or the programmer. For instance, the rule might be recast to say that if a specialization is followed by an explicit instantiation in the same translation unit, the explicit instantiation is ignored. A specialization and an explicit instantiation of the same template in two different translation units would still be an error, no diagnostic required.

Proposed resolution (04/01):

  1. Replace the first sentence of 13.9 [temp.spec] paragraph 5,

    No program shall explicitly instantiate any template more than once, both explicitly instantiate and explicitly specialize a template, or specialize a template more than once for a given set of template-arguments.

    by

    For a given template and a given set of template-arguments,
    • an explicit instantiation shall appear at most once in a program,
    • an explicit specialization shall be defined at most once according to 6.3 [basic.def.odr] in a program, and
    • both an explicit instantiation and a declaration of an explicit specialization shall not appear in a program unless the explicit instantiation follows a declaration of the explicit specialization.
  2. Replace 13.9.3 [temp.explicit] paragraph 4,

    The definition of a non-exported function template, a non-exported member function template, or a non-exported member function or static data member of a class template shall be present in every translation unit in which it is explicitly instantiated.

    by

    For a given set of template parameters, if an explicit instantiation of a template appears after a declaration of an explicit specialization for that template, the explicit instantiation has no effect. Otherwise, the definition of a non-exported function template, a non-exported member function template, or a non-exported member function or static data member of a class template shall be present in every translation unit in which it is explicitly instantiated.



63. Class instantiation from pointer conversion to void*, null and self

Section: 13.9.2  [temp.inst]     Status: CD1     Submitter: Steve Adamczyk     Date: 13 Oct 1998

[Moved to DR at October 2002 meeting.]

A template is implicitly instantiated because of a "pointer conversion" on an argument. This was intended to include related-class conversions, but it also inadvertently includes conversions to void*, null pointer conversions, cv-qualification conversions and the identity conversion.

It is not clear whether a reinterpret_cast of a pointer should cause implicit instantiation.

Proposed resolution (10/01): Replace 13.9.2 [temp.inst] paragraph 4, up to the example, with the following:

A class template specialization is implicitly instantiated if the class type is used in a context that requires a completely-defined object type or if the completeness of the class type might affect the semantics of the program. [Note: in particular, if the semantics of an expression depend on the member or base class lists of a class template specialization, the class template specialization is implicitly generated. For instance, deleting a pointer to class type depends on whether or not the class declares a destructor, and conversion between pointer to class types depends on the inheritance relationship between the two classes involved. ]

This version differs from the previous version is its use of the word "might" in the first sentence.

(See also issue 212.)




525. Missing * in example

Section: 13.9.2  [temp.inst]     Status: CD1     Submitter: Mike Miller     Date: 25 July 2005

[Voted into WP at April, 2006 meeting.]

The example in 13.9.2 [temp.inst] paragraph 4 has a typographical error: the third parameter of function g should be D<double>* ppp, but it is missing the *:

  template <class T> class B { /* ... */ };
  template <class T> class D : public B<T> { /* ... */ };
  void f(void*);
  void f(B<int >*);

  void g(D<int>* p, D<char>* pp, D<double> ppp)
  {
    f(p);             // instantiation of D<int> required: call f(B<int>*)

    B<char>* q = pp;  // instantiation of D<char> required:
                      // convert D<char>* to B<char>*
    delete ppp;       // instantiation of D<double> required
  }

Proposed resolution (October, 2005):

As suggested.




237. Explicit instantiation and base class members

Section: 13.9.3  [temp.explicit]     Status: CD1     Submitter: Christophe de Dinechin     Date: 28 Jul 2000

[Voted into WP at October 2005 meeting.]

In 13.9.3 [temp.explicit] paragraph 7 we read:

The explicit instantiation of a class template specialization implies the instantiation of all of its members not previously explicitly specialized in the translation unit containing the explicit instantiation.

Is "member" intended to mean "non-inherited member?" If yes, maybe it should be clarified since 11.7 [class.derived] paragraph 1 says,

Unless redefined in the derived class, members of a base class are also considered to be members of the derived class.

Proposed resolution (October, 2004):

Fixed by the resolution of issue 470.




470. Instantiation of members of an explicitly-instantiated class template

Section: 13.9.3  [temp.explicit]     Status: CD1     Submitter: Matt Austern     Date: 11 May 2004

[Voted into WP at October 2005 meeting.]

13.9.3 [temp.explicit] paragraph 7 says,

The explicit instantiation of a class template specialization implies the instantiation of all of its members not previously explicitly specialized in the translation unit containing the explicit instantiation.

It's not clear whether this “implied” instantiation is implicit or explicit instantiation. It makes a difference in cases like the following:

    template <typename T> struct foo {
        struct bar { };
    };

    template struct foo<int>;         // #1

    template struct foo<int>::bar;    // #2

If the instantiation of foo<int>::bar implied by #1 is implicit, the explicit instantiation in #2 is well-formed. Otherwise, #2 violates the requirement in 13.9 [temp.spec] that

No program shall explicitly instantiate any template more than once ... for a given set of template-arguments.

It's also unclear whether the implied instantiation applies only to direct members of the class template or to inherited members, as well.

John Spicer: I have always interpreted this as meaning only the members declared in the class, not those inherited from other classes. This is what EDG does, and appears to be what g++, Microsoft and Sun do, too. I also think this is the correct thing for the Standard to require. If I were to derive a class from a class in the standard library, an explicit instantiation of my class should not cause the explicit instantiation of things in the standard library (because the library might provide such explicit instantiations, thus causing my program to run afoul of the "can't instantiate more than once" rule).

Proposed resolution (October, 2004):

Change 13.9.3 [temp.explicit] paragraph 7 as follows:

The explicit instantiation of a class template specialization implies the instantiation of all also explicitly instantiates each of its members not (not including members inherited from base classes) whose definition is visible at the point of instantiation and that has not been previously explicitly specialized in the translation unit containing the explicit instantiation.



551. When is inline permitted in an explicit instantiation?

Section: 13.9.3  [temp.explicit]     Status: CD1     Submitter: Steve Clamage     Date: 07 December 2005

[Voted into the WP at the April, 2007 meeting as part of paper J16/07-0095 = WG21 N2235.]

The Standard does not definitively say when the inline specifier may be used in an explicit instantiation. For example, the following would seem to be innocuous, as the function being instantiated is already inline:

    template <typename T> struct S {
        void f() { }
    };
    template inline void S<int>::f();

However, presumably one would want to prohibit something like:

    template <typename T> void f(T) { }
    template inline void f(int);

9.2.3 [dcl.fct.spec] paragraph 4 (after application of the resolution of issue 317) comes close to covering the obvious problematic cases:

If the definition of a function appears in a translation unit before its first declaration as inline, the program is ill-formed. If a function with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required.

This would seem to prohibit the latter case, but apparently would not handle an exported template that was instantiated as inline (because the definition might not appear in the same translation unit as the inline instantiation). It would be better to make a clear statement regarding the use of inline in explicit instantiations.

Notes from the April, 2006 meeting:

The CWG favored completely disallowing the inline keyword in explicit instantiation directives.




44. Member specializations

Section: 13.9.4  [temp.expl.spec]     Status: CD1     Submitter: Nathan Myers     Date: 19 Sep 1998

[Moved to DR at 4/01 meeting.]

Some compilers reject the following:

    struct A {
        template <int I> void f();
        template <> void f<0>();
    };
on the basis of 13.9.4 [temp.expl.spec] paragraph 2:
An explicit specialization shall be declared in the namespace of which the template is a member, or, for member templates, in the namespace of which the enclosing class or enclosing class template is a member. An explicit specialization of a member function, member class or static data member of a class template shall be declared in the namespace of which the class template is a member. ...
claiming that the specialization above is not "in the namespace of which the enclosing class ... is a member". Elsewhere, declarations are sometimes required to be "at" or "in" "namespace scope", which is not what it says here. Paragraph 17 says:
A member or a member template may be nested within many enclosing class templates. If the declaration of an explicit specialization for such a member appears in namespace scope, the member declaration shall be preceded by a template<> for each enclosing class template that is explicitly specialized.
The qualification "if the declaration ... appears in namespace scope", implies that it might appear elsewhere. The only other place I can think of for a member specialization is in class scope.

Was it the intent of the committee to forbid the construction above? (Note that A itself is not a template.) If so, why?

Proposed resolution (04/01): In-class specializations of member templates are not allowed. In 13.9.4 [temp.expl.spec] paragraph 17, replace

If the declaration of an explicit specialization for such a member appears in namespace scope...
with
In an explicit specialization for such a member...

Notes from 04/00 meeting:

This issue was kept in "review" status for two major reasons:

  1. It's not clear that a change is actually needed. All uses of the phrase "in the namespace" in the IS mean "directly in the namespace," not in a scope nested within the namespace.
  2. There was substantial sentiment for actually adding support for in-class specializations at a future time, and it might be perceived as a reversal of direction to pass a change aimed at reinforcing the absence of the feature, only to turn around afterward and add it.

Notes from 10/00 meeting:

The core working group felt that the value of additional clarity here outweighs the potential disadvantages that were noted at the preceding meeting.




275. Explicit instantiation/specialization and using-directives

Section: 13.9.4  [temp.expl.spec]     Status: CD1     Submitter: John Spicer     Date: 15 Feb 2001

[Moved to DR at 4/02 meeting.]

Consider this example:

    namespace N {
	template <class T> void f(T){}
	template <class T> void g(T){}
	template <> void f(int);
	template <> void f(char);
	template <> void g(char);
    }

    using namespace N;

    namespace M {
	template <> void N::f(char){}  // prohibited by standard
	template <class T> void g(T){}
	template <> void g(char){}     // specialization of M::g or ambiguous?
	template void f(long);         // instantiation of N::f?
    }

    template <class T> void g(T){}

    template <> void N::f(char){}  // okay
    template <> void f(int){}      // is this a valid specialization of N::f?

    template void g(int);          // instantiation of ::g(int) or ambiguous?

The question here is whether unqualified names made visible by a using-directive can be used as the declarator in an explicit instantiation or explicit specialization.

Note that this question is already answered for qualified names in 9.3.4 [dcl.meaning] paragraph 1. In a qualified name such as N::f, f must be a member of class or namespace N, not a name made visible in N by a using-directive (or a using-declaration, for that matter).

The standard does not, as far as I can tell, specify the behavior of these cases one way or another.

My opinion is that names from using-directives should not be considered when looking up the name in an unqualified declarator in an explicit specialization or explicit instantiation. In such cases, it is reasonable to insist that the programmer know exactly which template is being specialized or instantiated, and that a qualified name must be used if the template is a member of a namespace.

As the example illustrates, allowing names from using-directives to be used would also have the affect of making ambiguous otherwise valid instantiation and specialization directives.

Furthermore, permitting names from using-directives would require an additional rule to prohibit the explicit instantiation of an entity in one namespace from being done in another (non-enclosing) namespace (as in the instantiation of f in namespace M in the example).

Mike Miller: I believe the explicit specialization case is already covered by _N4868_.9.8.2.3 [namespace.memdef] paragraph 2, which requires using a qualified name to define a namespace member outside its namespace.

John Spicer: _N4868_.9.8.2.3 [namespace.memdef] deals with namespace members. An explicit specialization directive deals with something that is a specialization of a namespace member. I don't think the rules in _N4868_.9.8.2.3 [namespace.memdef] could be taken to apply to specializations unless the standard said so explicitly.

Proposed resolution (suggested 04/01, proposed 10/01):

(The first change below will need to be revised in accordance with the resolution of issue 284 to add a cross-reference to the text dealing with class names.)

  1. Add in 13.9.3 [temp.explicit] paragraph 2 before the example:

    An explicit instantiation shall appear in an enclosing namespace of its template. If the name declared in the explicit instantiation is an unqualified name, the explicit instantiation shall appear in the namespace where its template is declared. [Note: Regarding qualified names in declarators, see 9.3.4 [dcl.meaning].]
  2. Change the first sentence of _N4868_.9.8.2.3 [namespace.memdef] paragraph 1 from

    Members of a namespace can be defined within that namespace.

    to

    Members (including explicit specializations of templates (13.9.4 [temp.expl.spec])) of a namespace can be defined within that namespace.
  3. Change the first sentence of _N4868_.9.8.2.3 [namespace.memdef] paragraph 2 from

    Members of a named namespace can also be defined...

    to

    Members (including explicit specializations of templates (13.9.4 [temp.expl.spec])) of a named namespace can also be defined...
  4. Change the last sentence of 13.9.4 [temp.expl.spec] paragraph 2 from

    If the declaration is not a definition, the specialization may be defined later in the namespace in which the explicit specialization was declared, or in a namespace that encloses the one in which the explicit specialization was declared.

    to

    If the declaration is not a definition, the specialization may be defined later (_N4868_.9.8.2.3 [namespace.memdef]).



336. Explicit specialization examples are still incorrect

Section: 13.9.4  [temp.expl.spec]     Status: CD1     Submitter: Jason Shirk     Date: 29 Jan 2002

[Voted into WP at April 2003 meeting.]

The examples corrected by issue 24 are still wrong in one case.

In item #4 (a correction to the example in paragraph 18), the proposed resolution is:

  template<class T1> class A {
    template<class T2> class B {
      template<class T3> void mf1(T3);
        void mf2();
      };
  };
  template<> template<class X>
    class A<int>::B { };
  template<> template<> template<class T>
    void A<int>::B<double>::mf1(T t) { }
  template<class Y> template<>
    void A<Y>::B<double>::mf2() { } // ill-formed; B<double> is specialized but
                                    // its enclosing class template A is not

The explicit specialization of member A<int>::B<double>::mf1 is ill-formed. The class template A<int>::B is explicitly specialized and contains no members, so any implicit specialization (such as A<int>::B<double>) would also contain no members.

Proposed Resolution (4/02):

Fix the example in 13.9.4 [temp.expl.spec] paragraph 18 to read:

  template<class T1> class A {
    template<class T2> class B {
      template<class T3> void mf1(T3);
      void mf2();
    };
  };
  template<> template<class X>
    class A<int>::B {
      template<class T> void mf1(T);
    };
  template<> template<> template<class T>
    void A<int>::B<double>::mf1(T t) { }
  template<class Y> template<>
    void A<Y>::B<double>::mf2() { } // ill-formed; B<double> is specialized but
                                    // its enclosing class template A is not



337. Attempt to create array of abtract type should cause deduction to fail

Section: 13.10.3  [temp.deduct]     Status: CD1     Submitter: John Spicer     Date: 30 Jan 2002

[Voted into WP at April 2003 meeting.]

In 13.10.3 [temp.deduct], attempting to create an array of abstract class type should be included in the list of things that cause type deduction to fail.

Proposed Resolution (4/02):

In 13.10.3 [temp.deduct] paragraph 2 amend the bullet item:

Attempting to create an array with an element type that is void, a function type, or a reference type, or attempting to create an array with a size that is zero or negative.

To the following:

Attempting to create an array with an element type that is void, a function type, or a reference type, or an abstract class type, or attempting to create an array with a size that is zero or negative.



368. Uses of non-type parameters that should cause deduction to fail

Section: 13.10.3  [temp.deduct]     Status: CD1     Submitter: Jason Shirk     Date: 29 July 2002

[Voted into WP at October 2003 meeting.]

I understand the rules in 13.10.3 [temp.deduct] paragraph 2 are meant to be an exhaustive list of what can cause type deduction to fail.

Consider:

  template<typename U,U u> struct wrap_t;

  template<typename U> static yes check( wrap_t<U,U(0)>* );

  struct X { X(int); };
  int main() {
    check<X>(0);
  }

I can see 2 reasons this might cause type deduction to fail:

Neither case is mentioned in 13.10.3 [temp.deduct] paragraph 2, nor do I see a DR mentioning these.

Proposed resolution (October 2002):

Add after the fourth-to-last bullet of 13.10.3 [temp.deduct] paragraph 2:




398. Ambiguous wording on naming a type in deduction

Section: 13.10.3  [temp.deduct]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 16 Jan 2003

[Voted into WP at March 2004 meeting.]

The following example (simplified from a posting to comp.lang.c++.moderated) is accepted by some compilers (e.g., EDG), but not by other (e.g., g++).

  struct S {
    static int const I = 42;
  };

  template<int N> struct X {};

  template<typename T> void f(X<T::I>*) {}

  template<typename T> void f(X<T::J>*) {}

  int main() {
    f<S>(0);
  }

The wording in the standard that normally would cover this (third sub-bullet in 13.10.3 [temp.deduct] paragraph 2) says:

Attempting to use a type in the qualifier portion of a qualified name that names a type when that type does not contain the specified member, or if the specified member is not a type where a type is required.
(emphasis mine). If the phrase "that names a type" applies to "a qualified name," then the example is invalid. If it applies to "the qualifier portion," then it is valid (because the second candidate is simply discarded).

I suspect we want this example to work. Either way, I believe the sub-bullet deserves clarification.

Notes from April 2003 meeting:

We agreed that the example should be valid. The phrase "that names a type" applies to "the qualifier portion."

Proposed resolution (October 2003):

In 13.10.3 [temp.deduct], paragraph 2, bullet 3, sub-bullet 3, replace

Attempting to use a type in the qualifier portion of a qualified name that names a type when that type does not contain the specified member, or if the specified member is not a type where a type is required.

With

Attempting to use a type in a nested-name-specifier of a qualified-id when that type does not contain the specified member, or

[Example:

Replace the example that follows the above text with

template <int I> struct X { };
template <template <class T> class> struct Z {};
template <class T> void f(typename T::Y*){}
template <class T> void g(X<T::N>*){}
template <class T> void h(Z<T::template TT>*){}
struct A {};
struct B { int Y; };
struct C {
	typedef int N;
};
struct D {
	typedef int TT;
};

int main()
{
	// Deduction fails in each of these cases:
	f<A>(0); // A does not contain a member Y
	f<B>(0); // The Y member of B is not a type
	g<C>(0); // The N member of C is not a nontype
	h<D>(0); // The TT member of D is not a template
}
]



486. Invalid return types and template argument deduction

Section: 13.10.3  [temp.deduct]     Status: CD1     Submitter: John Spicer     Date: 16 Nov 2004

[Voted into WP at April, 2006 meeting.]

According to 13.10.3 [temp.deduct] paragraph 2,

If a substitution in a template parameter or in the function type of the function template results in an invalid type, type deduction fails.

That would seem to apply to cases like the following:

    template <class T> T f(T&){}
    void f(const int*){}
    int main() {
      int a[5];
      f(a);
    }

Here, the return type of f is deduced as int[5], which is invalid according to 9.3.4.6 [dcl.fct] paragraph 6. The outcome of this example, then, should presumably be that type deduction fails and overload resolution selects the non-template function. However, the list of reasons in 13.10.3 [temp.deduct] for which type deduction can fail does not include function and array types as a function return type. Those cases should be added to the list.

Proposed resolution (October, 2005):

Change the last sub-bullet of 13.10.3 [temp.deduct] paragraph 2 as indicated:




488. Local types, overload resolution, and template argument deduction

Section: 13.10.3  [temp.deduct]     Status: CD1     Submitter: Mark Mitchell     Date: 24 Nov 2004

[Voted into the WP at the June, 2008 meeting as paper N2657.]

It is not clear how to handle the following example:

    struct S {
        template <typename T> S(const T&);
    };
    void f(const S&);
    void f(int);
    void g() {
        enum E { e };
        f(e);    // ill-formed?
    }

Three possibilities suggest themselves:

  1. Fail during overload resolution. In order to perform overload resolution for the call to f, the declaration of the required specialization of the S constructor must be instantiated. This instantiation uses a local type and is thus ill-formed (13.4.2 [temp.arg.type] paragraph 2) , rendering the example as a whole ill-formed, as well.

  2. Treat this as a type-deduction failure. Although it is not listed currently among the causes of type-deduction failure in 13.10.3 [temp.deduct] paragraph 2, it could plausibly be argued that instantiating a function declaration with a local type as a template type-parameter falls under the rubric of “If a substitution in a template parameter or in the function type of the function template results in an invalid type” and thus should be a type-deduction failure. The result would be that the example is well-formed because f(const S&) would be removed from the list of viable functions.

  3. Fail only if the function selected by overload resolution requires instantiation with a local type. This approach would require that the diagnostic resulting from the instantiation of the function type during overload resolution be suppressed and either regenerated or regurgitated once overload resolution is complete. (The example would be well-formed under this approach because f(int) would be selected as the best match.)

(See also issue 489.)

Notes from the April, 2005 meeting:

The question in the original example was whether there should be an error, even though the uninstantiable template was not needed for calling the best-matching function. The broader issue is whether a user would prefer to get an error or to call a “worse” non-template function in such cases. For example:

    template<typename T> void f(T);
    void f(int);
    void g() {
        enum E { e };
        f(e);    // call f(int) or get an error?
    }

It was observed that the type deduction rules are intended to model, albeit selectively, the other rules of the language. This would argue in favor of the second approach, a type-deduction failure, and the consensus of the group was that the incremental benefit of other approaches was not enough to outweigh the additional complexity of specification and implementation.

Proposed resolution (October, 2005):

Add a new sub-bullet following bullet 3, sub-bullet 7 ("Attempting to give an invalid type to a non-type template parameter") of 13.10.3 [temp.deduct] paragraph 2:

Additional note (December, 2005):

The Evolution Working Group is currently considering an extension that would effectively give linkage to some (but perhaps not all) types that currently have no linkage. If the proposed resolution above is adopted and then later a change along the lines that the EWG is considering were also adopted, the result would be a silent change in the result of overload resolution, because the newly-acceptable specializations would become part of the overload set. It is not clear whether that possibility is sufficient reason to delay adoption of this resolution or not.

Notes from the April, 2007 meeting:

The Evolution Working Group is now actively pursuing an extension that would allow local and/or nameless types to be used as template arguments, so this resolution will be held in abeyance until the outcome of that proposal is known.

Notes from the June, 2008 meeting:

Paper N2657, adopted at the Sophia Antipolis (June, 2008) meeting, removed the restriction against local and unnamed types as template parameters. The example is now well-formed.




352. Nondeduced contexts

Section: 13.10.3.2  [temp.deduct.call]     Status: CD1     Submitter: Andrei Iltchenko     Date: 24 April 2002

[Voted into WP at March 2004 meeting.]

The current definition of the C++ language speaks about nondeduced contexts only in terms of deducing template arguments from a type 13.10.3.6 [temp.deduct.type] paragraph 4. Those cases, however, don't seem to be the only ones when template argument deduction is not possible. The example below illustrates that:

namespace  A  {
   enum  ae  {   };
   template<class R, class A>
   int  foo(ae, R(*)(A))
   {   return  1;   }
}

template<typename T>
void  tfoo(T)
{   }

template<typename T>
int  tfoo(T)
{   return  1;   }

/*int  tfoo(int)
{   return  1;   }*/


int  main()
{
   A::ae   a;
   foo(a, &tfoo);
}

Here argument-dependent name lookup finds the function template 'A::foo' as a candidate function. None of the function template's function parameter types constitutes a nondeduced context as per 13.10.3.6 [temp.deduct.type] paragraph 4. And yet, quite clearly, argument deduction is not possible in this context. Furthermore it is not clear what a conforming implementation shall do when the definition of the non-template function '::tfoo' is uncommented.

Suggested resolution:

Add the following as a new paragraph immediately before paragraph 3 of 13.10.3.2 [temp.deduct.call]:

After the above transformations, in the event of P being a function type, a pointer to function type, or a pointer to member function type and the corresponding A designating a set of overloaded (member) functions with at least one of the (member) functions introduced by the use of a (member) function template name (12.3 [over.over]) or by the use of a conversion function template (13.10.3.4 [temp.deduct.conv]), the whole function call expression is considered to be a nondeduced context. [Example:

namespace  A  {
   enum  ae  {   };
   template<class R, class A>
   int  foo(ae, R(*)(A))
   {   return  1;   }
}

template<typename T>
void  tfoo(T)
{   }

template<typename T>
int  tfoo(T)
{   return  1;   }

int  tfoo(int)
{   return  1;   }

int  main()
{
   A::ae   a;
   foo(a, &tfoo);   //  ill-formed, the call is a nondeduced context
   using  A::foo;
   foo<void,int>(a, &tfoo);  // well-formed, the address of the spe-
                             // cialization 'void tfoo<int>(int)' is
                             // the second argument of the call
}

Notes from October 2002 meeting:

There was agreement that deduction should fail but it's still possible to get a result -- it's just not a "nondeduced context" in the sense of the standard.

The presence of a template in the overload set should not automatically disqualify the overload set.

Proposed Resolution (April 2003, revised October 2003):

In 13.10.3.6 [temp.deduct.type] paragraph 4 replace:

The nondeduced contexts are:
with:
The nondeduced contexts are:

In 13.10.3.2 [temp.deduct.call], add after paragraph 3:

When P is a function type, pointer to function type, or pointer to member function type:

[Example:

// Only one function of an overload set matches the call so the function
// parameter is a deduced context.
template <class T> int f(T (*p)(T));
int g(int);
int g(char);
int i = f(g);  // calls f(int (*)(int))
--end example]

[Example:

// Ambiguous deduction causes the second function parameter to be a
// nondeduced context.
template <class T> int f(T, T (*p)(T));
int g(int);
char g(char);
int i = f(1, g);  // calls f(int, int (*)(int))
--end example]

[Example:

// The overload set contains a template, causing the second function
// parameter to be a nondeduced context.
template <class T> int f(T, T (*p)(T));
char g(char);
template <class T> T g(T);
int i = f(1, g);  // calls f(int, int (*)(int))
--end example]

In 13.10.3.6 [temp.deduct.type] paragraph 14, replace:

If, in the declaration of a function template with a non-type template-parameter, the non-type template-parameter is used in an expression in the function parameter-list, the corresponding template-argument must always be explicitly specified or deduced elsewhere because type deduction would otherwise always fail for such a template-argument.
With:
If, in the declaration of a function template with a non-type template parameter, the non-type template parameter is used in an expression in the function parameter list, the expression is a nondeduced context.

Replace the example with:

[Example:
template<int i> class A { /* ... */ };
template<int i> void g(A<i+1>);
template<int i> void f(A<i>, A<i+1>);
void k() {
  A<1> a1;
  A<2> a2;
  g(a1);  //error: deduction fails for expression i+1
  g<0>(a1); //OK
  f(a1, a2);  // OK
}
--end example]

In 13.10.3.6 [temp.deduct.type] paragraph 16, replace:

A template-argument can be deduced from a pointer to function or pointer to member function argument if the set of overloaded functions does not contain function templates and at most one of a set of overloaded functions provides a unique match.

With:

A template-argument can be deduced from a function, pointer to function, or pointer to member function type.



522. Array-to-pointer decay in template argument deduction

Section: 13.10.3.2  [temp.deduct.call]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 3 June 2005

[Voted into WP at the October, 2006 meeting.]

Consider the following example:

    char* cmdline3_[1] = {};

    template<class charT>
    void func(const charT* const argv[]) {}

    int main()
    {
        func(cmdline3_);
    }

In terms of the process described in 13.10.3.2 [temp.deduct.call], P is const charT* const * and A is char*[1]. According to the first bullet in paragraph 2, the type used in deduction is not A but “the pointer type produced by the array-to-pointer standard conversion.”

According to paragraph 4,

In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:

In this example, the deduced A is not identical to the transformed A, because the deduced A has additional cv-qualification, so the three exceptions must be examined to see if they apply. The only one that might apply is the second bullet of paragraph 4:

However, A is not a pointer type but an array type; this provision does not apply and deduction fails.

It has been argued that the phrase “after the type A is transformed as described above” should be understood to apply to the A in the three bullets of paragraph 4. If that is the intent, the wording should be changed to make that explicit.

Proposed resolution (October, 2005):

Add the indicated words to 13.10.3.2 [temp.deduct.call] paragraph 4:

In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:




606. Template argument deduction for rvalue references

Section: 13.10.3.2  [temp.deduct.call]     Status: CD1     Submitter: Peter Dimov     Date: 1 December 2006

[Voted into the WP at the September, 2008 meeting as part of paper N2757.]

There are a couple of minor problems with the rvalue reference wording in the WP. The non-normative note in 13.10.3.2 [temp.deduct.call] paragraph 3 says,

[Note: The effect of this rule for lvalue arguments and rvalue reference parameters is that deduction in such cases will fail unless the function parameter is of the form cv T&& (13.10.3.6 [temp.deduct.type]). —end note]

It turns out that this isn't correct. For example:

    template <class T> void g(basic_string<T> && );
    ...
    basic_string<char> s;
    g(s);    // Note says that it should fail, we want it to call
             // g<char>(basic_string<char>&&)

Additionally, consider this case:

    template <class T> void f(const T&&);
    ...
    int i;
    f(i);

If we deduce T as int& in this case then f(i) calls f<int&>(int&), which seems counterintuitive. We prefer that f<int>(const int&&) be called. Therefore, we would like the wording clarified that the A& deduction rule in 13.10.3.2 [temp.deduct.call] paragraph 3 applies only to the form T&& and not to cv T&& as the note currently implies.

These are minor tweaks to the rvalue reference wording and a fallout from issue 540. In particular, the major applications of move semantics and perfect forwarding are not impacted with respect to the original intentions of the rvalue reference work by these suggestions.

Suggested resolution:

Change 13.10.3.2 [temp.deduct.call] paragraph 3 as follows:

If P is an rvalue reference type of the form T&&, where T is a template parameter, and the argument is an lvalue, the type A& is used in place of A for type deduction T is deduced as A&. [Example:

    template <typename T> int f(T&&);
    int i;
    int j = f(i); // calls f<int&>(i)
    template <typename T> int g(const T&&);
    int k;
    int n = g(k); // calls g<int>(k)

end example][Note: The effect of this rule for lvalue arguments and rvalue reference parameters is that deduction in such cases will fail unless the function parameter is of the form cv T&& (13.10.3.6 [temp.deduct.type]). —end note]

Proposed resolution (August, 2008):

Change 13.10.3.2 [temp.deduct.call] paragraph 3 as follows:

If P is an rvalue reference type of the form T&&, where T is a template parameter, and the argument is an lvalue, the type A& is used in place of A for type deduction. [Example:

    template <typename T> int f(T&&);
    int i;
    int j = f(i); // calls f<int&>(i)
    template <typename T> int g(const T&&);
    int k;
    int n = g(k); // calls g<int>(k)

end example][Note: The effect of this rule for lvalue arguments and rvalue reference parameters is that deduction in such cases will fail unless the function parameter is of the form cv T&& (13.10.3.6 [temp.deduct.type]). —end note]




322. Deduction of reference conversions

Section: 13.10.3.4  [temp.deduct.conv]     Status: CD1     Submitter: Jason Merrill     Date: 14 Nov 2001

[Voted into WP at April 2003 meeting.]

Consider:

  struct S {
    template <class T> operator T& ();
  };

  int main ()
  {
    S s;
    int i = static_cast<int&> (s);
  }
13.10.3.4 [temp.deduct.conv] says that we strip the reference from int&, but doesn't say anything about T&. As a result, P (T&) and A (int) have incompatible forms and deduction fails.

Proposed Resolution (4/02):

Change the last chunk of 13.10.3.4 [temp.deduct.conv] paragraph 2 from

If A is a cv-qualified type, the top level cv-qualifiers of A's type are ignored for type deduction. If A is a reference type, the type referred to by A is used for type deduction.
to
If A is a cv-qualified type, the top level cv-qualifiers of A's type are ignored for type deduction. If A is a reference type, the type referred to by A is used for type deduction. If P is a reference type, the type referred to by P is used for type deduction.




349. Template argument deduction for conversion functions and qualification conversions

Section: 13.10.3.4  [temp.deduct.conv]     Status: CD1     Submitter: John Spicer     Date: 16 April 2002

[Voted into WP at October 2003 meeting.]

We ran into an issue concerning qualification conversions when doing template argument deduction for conversion functions.

The question is: What is the type of T in the conversion functions called by this example? Is T "int" or "const int"?

If T is "int", the conversion function in class A works and the one in class B fails (because the return expression cannot be converted to the return type of the function). If T is "const int", A fails and B works.

Because the qualification conversion is performed on the result of the conversion function, I see no benefit in deducing T as const int.

In addition, I think the code in class A is more likely to occur than the code in class B. If the author of the class was planning on returning a pointer to a const entity, I would expect the function to have been written with a const in the return type.

Consequently, I believe the correct result should be that T is int.

struct A {
	template <class T> operator T***() {
		int*** p = 0;
		return p;
	}
};

struct B {
	template <class T> operator T***() {
		const int*** p = 0;
		return p;
	}
};

int main()
{
	A a;
	const int * const * const * p1 = a;
	B b;
	const int * const * const * p2 = b;
}

We have just implemented this feature, and pending clarification by the committee, we deduce T as int. It appears that g++ and the Sun compiler deduce T as const int.

One way or the other, I think the standard should be clarified to specify how cases like this should be handled.

Notes from October 2002 meeting:

There was consensus on having the deduced type be "int" in the above.

Proposed resolution (April 2003):

Add to the end of 13.10.3.4 [temp.deduct.conv] (as a new paragraph following paragraph 3):

When the deduction process requires a qualification conversion for a pointer or pointer to member type as described above, the following process is used to determine the deduced template argument values:

If A is a type cv1,0 pointer to ... cv 1,n-1 pointer to cv1,n T1

and P is a type cv2,0 pointer to ... cv2,n-1 pointer to cv2,n T2

The cv-unqualified T1 and T2 are used as the types of A and P respectively for type deduction.

[Example:

struct A {
	template <class T> operator T***();
};

A a;
const int * const * const * p1 = a;  // T is deduced as int, not const int
-- end example]




70. Is an array bound a nondeduced context?

Section: 13.10.3.6  [temp.deduct.type]     Status: CD1     Submitter: Jack Rouse     Date: 29 Sep 1998

[Moved to DR at 4/01 meeting.]

Paragraph 4 lists contexts in which template formals are not deduced. Were template formals in an expression in the array bound of an array type specification intentionally left out of this list? Or was the intent that such formals always be explicitly specified? Otherwise I believe the following should be valid:

    template <int I> class IntArr {};

    template <int I, int J>
    void concat( int (&d)[I+J], const IntArr<I>& a, const IntArr<J>& b ) {}

    int testing()
    {
        IntArr<2> a;
        IntArr<3> b;
        int d[5];

        concat( d, a, b );
    }
Can anybody shed some light on this?

From John Spicer:

Expressions involving nontype template parameters are nondeduced contexts, even though they are omitted from the list in 13.10.3.6 [temp.deduct.type] paragraph 4. See 13.10.3.6 [temp.deduct.type] paragraphs 12-14:

  1. A template type argument cannot be deduced from the type of a non-type template-argument.

     ...

  1. If, in the declaration of a function template with a non-type template-parameter, the non-type template-parameter is used in an expression in the function parameter-list, the corresponding template-argument must always be explicitly specified or deduced elsewhere because type deduction would otherwise always fail for such a template-argument.

Proposed resolution (04/01): In 13.10.3.6 [temp.deduct.type] paragraph 4, add a third bullet:




300. References to functions in template argument deduction

Section: 13.10.3.6  [temp.deduct.type]     Status: CD1     Submitter: Andrei Iltchenko     Date: 11 Jul 2001

[Moved to DR at October 2002 meeting.]

Paragraph 9 of 13.10.3.6 [temp.deduct.type] enumerates the forms that the types P and A need to have in order for template argument deduction to succeed.

For P denoting a pointer to function the paragraph lists the following forms as allowing for template argument deduction:

type(*)(T)
T(*)()
T(*)(T)

On the other hand, no provision has been made to accommodate similar cases for references to functions, which in light of the wording of 13.10.3.6 [temp.deduct.type] paragraph 11 means that the program below is ill-formed (some of the C++ compilers do not reject it however):

    template<typename Arg, typename Result, typename T>
    Result  foo_r(Result(& rf)(Arg), T x)
    {   return  rf(Arg(x));   }

    template<typename Arg, typename Result, typename T>
    Result  foo_p(Result(* pf)(Arg), T x)
    {   return  pf(Arg(x));   }

    #include <iostream>
    int  show_arg(char c)
    {
       std::cout << c << ' ';
       if(std::cout)  return  0;
       return  -1;
    }

    int  main()
    {
                                                   // The deduction
       int  (& rf1)(int(&)(char), double) = foo_r; // shall fail here
                                                   // While here
       int  (& rf2)(int(*)(char), double) = foo_p; // it shall succeed
       return  rf2(show_arg, 2);
    }

Proposed resolution (10/01, same as suggested resolution):

In the list of allowable forms for the types P and A in paragraph 9 of 13.10.3.6 [temp.deduct.type] replace

type(*)(T)
T(*)()
T(*)(T)

by

type(T)
T()
T(T)



526. Confusing aspects in the specification of non-deduced contexts

Section: 13.10.3.6  [temp.deduct.type]     Status: CD1     Submitter: Mike Miller     Date: 25 July 2005

[Voted into WP at the October, 2006 meeting.]

13.10.3.6 [temp.deduct.type] paragraph 5 reads:

The non-deduced contexts are:

There are two problems with this list:

  1. The last bullet is redundant with the second bullet. This appears to have been the result of applying the resolutions of issues 70 and 352 independently instead of in coordination.

  2. The second bullet appears to be contradicted by the statement in paragraph 8 saying that an argument can be deduced if P and A have the forms type[i] and template-name<i>.

    The intent of the wording in bullet 2 appears to have been that deduction cannot be done if the template parameter is a sub-expression of the template argument or array bound expression and that it can be done if it is the complete expression, but the current wording does not say that very clearly. (Similar wording also appears in 13.8.3.2 [temp.dep.type] paragraph 3 and 13.10.3.6 [temp.deduct.type] paragraph 14.)

Proposed resolution (October, 2005):

  1. Change 13.10.3.6 [temp.deduct.type] paragraph 5 as indicated:

  2. The non-deduced contexts are:

  3. Change 13.10.3.6 [temp.deduct.type] paragraph 14 as indicated:

  4. If, in the declaration of a function template with a non-type template parameter, the non-type template parameter is used in an expression a subexpression in the function parameter list, the expression is a non-deduced context as specified above...
  5. Change 13.8.3.2 [temp.dep.type] paragraph 3 as indicated:

  6. A template argument that is equivalent to a template parameter (i.e., has the same constant value or the same type as the template parameter) can be used in place of that template parameter in a reference to the current instantiation. In the case of a non-type template argument, the argument must have been given the value of the template parameter and not an expression involving that contains the template parameter as a subexpression...



415. Template deduction does not cause instantiation

Section: 13.10.4  [temp.over]     Status: CD1     Submitter: John Spicer     Date: 4 May 2003

[Voted into WP at the October, 2006 meeting.]

Mike Miller: In fact, now that I've looked more closely, that appears not to be the case. (At least, it's not the error I get when I compile his example.) Here's a minimal extract (without the inflammatory using-directive :-) that illustrates what I think is going on:

  template <typename _Iterator>
  struct iterator_traits {
    typedef typename _Iterator::difference_type difference_type;
  };

  template <typename _InputIterator>
  inline typename iterator_traits<_InputIterator>::difference_type
  distance(_InputIterator, _InputIterator);

  double distance(const int&, const int&);

  void f() {
    int i = 0;
    int j = 0;
    double d = distance(i, j);
  }

What happens is that iterator_traits<int> is instantiated as part of type deduction for the function template distance, and the instantiation fails. (Note that it can't be instantiation of distance<int>, as I had originally posited, because in this case only a declaration, not a definition, of that template is in scope.)

John Spicer: Yes, I believe that is what is going on.

Mike Miller: I seem to recall that there was some discussion of questions related to this during the core meetings in Oxford. I think Steve Adamczyk said something to the effect that it's infeasible to suppress all instantiation errors during template type deduction and simply call any such errors a deduction failure. (I could be misremembering, and I could be misapplying that comment to this situation.)

John Spicer: Regardless of other conditions in which this may apply, I don't think it would be reasonable for compilers to have to do "speculative instantiations" during template argument deduction. One class instantiation could kick off a series of other instantiations, etc.

Mike Miller: I don't see anything in the Standard that tells me whether it's legitimate or not to report an error in this case. I hope John or another template expert can enlighten me on that.

John Spicer: My opinion is that, because this case is not among those enumerated that cause deduction failure (rather than being ill-formed) that reporting an error is the right thing to do.

Mike Miller: I am still interested, though, in the question of why 13.10.4 [temp.over] says that viable function template specializations are instantiated, even if they are not selected by overload resolution.

John Spicer: I believe the wording in 13.10.4 [temp.over] is incorrect. I researched this and found that a change was made during the clause 14 restructuring that was incorporated in March of 1996. The prior wording was "the deduced template arguments are used to generate a single template function". This was changed to "deduced template arguments are used to instantiate a single function template specialization". I believe this resulted from what was basically a global replace of "generate" with "instantiate" and of "template function" with "function template specialization". In this case, the substitution changed the meaning. This paragraph needs reworking.

Proposed resolution (April, 2006):

Change 13.10.4 [temp.over] paragraph 1 as indicated:

...For each function template, if the argument deduction and checking succeeds, the template-arguments (deduced and/or explicit) are used to instantiate synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template. The complete set of candidate functions includes all the function templates instantiated in this way synthesized declarations and all of the non-template overloaded functions of the same name. The function template specializations synthesized declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in 12.2.4 [over.match.best].



208. Rethrowing exceptions in nested handlers

Section: 14.2  [except.throw]     Status: CD1     Submitter: Bill Wade     Date: 28 Feb 2000

[Moved to DR at 4/01 meeting.]

Paragraph 7 of 14.2 [except.throw] discusses which exception is thrown by a throw-expression with no operand.

May an expression which has been "finished" (paragraph 7) by an inner catch block be rethrown by an outer catch block?

    catch(...)    // Catch the original exception
    {
      try{ throw; }    // rethrow it at an inner level
                       // (in reality this is probably
                       // inside a function)
      catch (...)
      {
      }   // Here, an exception (the original object)
          // is "finished" according to 15.1p7 wording

      // 15.1p7 says that only an unfinished exception
      // may be rethrown.
      throw;    // Can we throw it again anyway?  It is
                // certainly still alive (15.1p4).
    }

I believe this is ok, since the paragraph says that the exception is finished when the "corresponding" catch clause exits. However since we have two clauses, and only one exception, it would seem that the one exception gets "finished" twice.

Proposed resolution (04/01):

  1. In 14.2 [except.throw] paragraph 4, change

    When the last handler being executed for the exception exits by any means other than throw; ...
    to
    When the last remaining active handler for the exception exits by any means other than throw; ...

  2. In 14.2 [except.throw] paragraph 6, change

    A throw-expression with no operand rethrows the exception being handled.
    to
    A throw-expression with no operand rethrows the currently handled exception (14.4 [except.handle]).

  3. Delete 14.2 [except.throw] paragraph 7.

  4. Add the following before 14.2 [except.throw] paragraph 6:

    An exception is considered caught when a handler for that exception becomes active (14.4 [except.handle]). [Note: an exception can have active handlers and still be considered uncaught if it is rethrown.]

  5. Change 14.4 [except.handle] paragraph 8 from

    An exception is considered handled upon entry to a handler. [Note: the stack will have been unwound at that point.]
    to

    A handler is considered active when initialization is complete for the formal parameter (if any) of the catch clause. [Note: the stack will have been unwound at that point.] Also, an implicit handler is considered active when std::terminate() or std::unexpected() is entered due to a throw. A handler is no longer considered active when the catch clause exits or when std::unexpected() exits after being entered due to a throw.

    The exception with the most recently activated handler that is still active is called the currently handled exception.

  6. In 14.4 [except.handle] paragraph 16, change "exception being handled" to "currently handled exception."




428. Mention of expression with reference type

Section: 14.2  [except.throw]     Status: CD1     Submitter: Steve Adamczyk     Date: 14 July 2003

[Voted into WP at March 2004 meeting.]

14.2 [except.throw] paragraph 3 says that the type of a throw expression shall not be a pointer or reference to an incomplete type. But an expression never has reference type.

Proposed Resolution (October 2003):

Change the penultimate sentence of 14.2 [except.throw] paragraph 3 as follows:

The type of the throw-expression shall not be an incomplete type, or a pointer or reference to an incomplete type other than (possibly cv-qualified) void, other than void*, const void*, volatile void*, or const volatile void*.



479. Copy elision in exception handling

Section: 14.2  [except.throw]     Status: CD1     Submitter: Mike Miller     Date: 07 Oct 2004

[Voted into WP at April, 2006 meeting.]

I have noticed a couple of confusing and overlapping passages dealing with copy elision. The first is 14.2 [except.throw] paragraph 5:

If the use of the temporary object can be eliminated without changing the meaning of the program except for the execution of constructors and destructors associated with the use of the temporary object (6.7.7 [class.temporary]), then the exception in the handler can be initialized directly with the argument of the throw expression.

The other is 14.4 [except.handle] paragraph 17:

If the use of a temporary object can be eliminated without changing the meaning of the program except for execution of constructors and destructors associated with the use of the temporary object, then the optional name can be bound directly to the temporary object specified in a throw-expression causing the handler to be executed.

I think these two passages are intended to describe the same optimization. However, as is often the case where something is described twice, there are significant differences. One is just different terminology — is “the exception in the handler” the same as “the object declared in the exception-declaration or, if the exception-declaration does not specify a name, a temporary object of that type” (14.4 [except.handle] paragraph 16)?

More significant, there is a difference in which kinds of throw-expressions are eligible for the optimization. In 14.2 [except.throw] paragraph 5, it appears that any object is a candidate, while in 14.4 [except.handle] paragraph 17 the thrown object must be a temporary (“the temporary object specified in a throw-expression”). For example, it's not clear looking at these two passages whether the copy of a local automatic can be elided. I.e., by analogy with the return value optimization described in 11.4.5.3 [class.copy.ctor] paragraph 15:

    X x;
    return x;    // copy may be elided

    X x;
    throw x;     // unclear whether copy may be elided

Which brings up another point: 11.4.5.3 [class.copy.ctor] paragraph 15 purports to be an exhaustive list in which copy elision is permitted even if the constructor and/or destructor have side effects; however, these two passages describe another case that is not mentioned in 11.4.5.3 [class.copy.ctor] paragraph 15.

A final point of confusion: in the unoptimized abstract machine, there are actually two copies in throwing and handling an exception: the copy from the object being thrown to the exception object, and the copy from the exception object to the object or temporary in the exception-declaration. 14.2 [except.throw] paragraph 5 speaks only of eliminating the exception object, copying the thrown object directly into the exception-declaration object, while 14.4 [except.handle] paragraph 17 refers to directly binding the exception-declaration object to the thrown object (if it's a temporary). Shouldn't these be separated, with a throw of an automatic object or temporary being like the return value optimization and the initialization of the object/temporary in the exception-declaration being a separate optimizable step (which could, presumably, be combined to effectively alias the exception-declaration onto the thrown object)?

(See paper J16/04-0165 = WG21 N1725.)

Proposed resolution (April, 2005):

  1. Add two items to the bulleted list in 11.4.5.3 [class.copy.ctor] paragraph 15 as follows:

    This elision of copy operations is permitted in the following circumstances (which may be combined to eliminate multiple copies):

    • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type, the copy operation can be omitted by constructing the automatic object directly into the function's return value

    • in a throw-expression, when the operand is the name of a non-volatile automatic object, the copy operation from the operand to the exception object (14.2 [except.throw]) can be omitted by constructing the automatic object directly into the exception object

    • when a temporary class object that has not been bound to a reference (6.7.7 [class.temporary]) would be copied to a class object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy

    • when the exception-declaration of an exception handler (Clause 14 [except]) declares an object of the same type (except for cv-qualification) as the exception object (14.2 [except.throw]), the copy operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration

  2. Change 14.2 [except.throw] paragraph 5 as follows:

    If the use of the temporary object can be eliminated without changing the meaning of the program except for the execution of constructors and destructors associated with the use of the temporary object (6.7.7 [class.temporary]), then the exception in the handler can be initialized directly with the argument of the throw expression. When the thrown object is a class object, and the copy constructor used to initialize the temporary copy is not and the destructor shall be accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated) even if the copy operation is elided (11.4.5.3 [class.copy.ctor]). Similarly, if the destructor for that object is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated).
  3. Change 14.4 [except.handle] paragraph 17 as follows:

    If the use of a temporary object can be eliminated without changing the meaning of the program except for execution of constructors and destructors associated with the use of the temporary object, then the optional name can be bound directly to the temporary object specified in a throw-expression causing the handler to be executed. The copy constructor and destructor associated with the object shall be accessible even when the temporary object is eliminated if the copy operation is elided (11.4.5.3 [class.copy.ctor]).



592. Exceptions during construction of local static objects

Section: 14.3  [except.ctor]     Status: CD1     Submitter: Alisdair Meredith     Date: 30 August 2006

[Voted into the WP at the September, 2008 meeting (resolution in paper N2757).]

According to 14.3 [except.ctor] paragraph 2,

An object that is partially constructed or partially destroyed will have destructors executed for all of its fully constructed subobjects, that is, for subobjects for which the principal constructor (11.9.3 [class.base.init]) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object's destructor will be invoked. Should a constructor for an element of an automatic array throw an exception, only the constructed elements of that array will be destroyed.

The requirement for destruction of array elements explicitly applies only to automatic arrays, and one might conclude from the context that only automatic class objects are in view as well, although that is not explicitly stated. What about local static arrays and class objects? Are they intended also to be subject to the requirement that fully-constructed subobjects are to be destroyed?

Proposed resolution (October, 2006):

Change 14.3 [except.ctor] paragraph 2 as follows:

An object that is partially constructed or partially destroyed will have destructors executed for all of its fully constructed subobjects, that is, for subobjects for which the principal constructor (11.9.3 [class.base.init]) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object's destructor will be invoked. Should a constructor for an element of an automatic array throw an exception, only the constructed elements of that array will be destroyed. If the object or array was allocated in a new-expression, the matching deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation], 7.6.2.8 [expr.new], 11.4.11 [class.free]), if any, is called to free the storage occupied by the object.



87. Exception specifications on function parameters

Section: 14.5  [except.spec]     Status: CD1     Submitter: Steve Adamczyk     Date: 25 Jan 1999

[Moved to DR at 4/01 meeting.]

In 14.5 [except.spec] paragraph 2:

An exception-specification shall appear only on a function declarator in a function, pointer, reference or pointer to member declaration or definition.
Does that mean in the top-level function declarator, or one at any level? Can one, for example, specify an exception specification on a pointer-to-function parameter of a function?
    void f(int (*pf)(float) throw(A))
Suggested answer: no. The exception specifications are valid only on the top-level function declarators.

However, if exception specifications are made part of a function's type as has been tentatively agreed, they would have to be allowed on any function declaration.

There is already an example of an exception specification for a parameter in the example in 14.5 [except.spec] paragraph 1.

Proposed resolution (04/01): Change text in 14.5 [except.spec] paragraph 1 from:

An exception-specification shall appear only on a function declarator in a function, pointer, reference or pointer to member declaration or definition.
to:
An exception-specification shall appear only on a function declarator for a function type, pointer to function type, reference to function type, or pointer to member function type that is the top-level type of a declaration or definition, or on such a type appearing as a parameter or return type in a function declarator.

(See also issues 25, 92, and 133.)




394. identifier-list is never defined

Section: Clause 15  [cpp]     Status: CD1     Submitter: Nicola Musatti     Date: 16 Dec 2002

[Voted into WP at October 2004 meeting.]

In Clause 15 [cpp], paragraph 1, the control-line non-terminal symbol is defined in terms of the identifier-list non-terminal, which is never defined within the standard document.

The same definition is repeated in A.13 [gram.cpp].

I suggest that the following definition is added to Clause 15 [cpp], paragraph 1, after the one for replacement-list:

This should be repeated again in A.13 [gram.cpp], again after the one for replacement-list. It might also be desirable to include a third repetition in 15.6 [cpp.replace], paragraph 9.

Proposed Resolution (Clark Nelson, Dec 2003):

In Clause 15 [cpp], paragraph 1, immediately before the definition of replacement-list, add:

If the correct TROFF macros are used, the definition will appear automatically in appendix A. It doesn't need to be repeated in 16.3p9.

With respect to the question of having the preprocessor description be synchronized with C99, this would fall into the category of a justified difference. (Other justified differences include those for Boolean expressions, alternative tokens, and terminology differences.)




370. Can #include <...> form be used other than for standard C++ headers?

Section: 15.3  [cpp.include]     Status: CD1     Submitter: Beman Dawes     Date: 01 August 2002

[Voted into WP at the October, 2006 meeting.]

The motivation for this issue is a desire to write portable programs which will work with any conforming implementation.

The C++ Standard (15.3 [cpp.include]) provides two forms of #include directives, with the <...> form being described (15.3 [cpp.include] paragraph 2) as "for a header", and the "..." form (15.3 [cpp.include] paragraph 3) as for "the source file" identified between the delimiters. When the standard uses the term "header", it often appears to be limiting the term to apply to the Standard Library headers only. Users of the standard almost always use the term "header" more broadly, to cover all #included source files, but particularly those containing interface declarations.

Headers, including source files, can be categorized according to their origin and usage:

  1. C++ Standard Library headers (which aren't necessarily files).
  2. Other standard libraries such as the POSIX headers.
  3. Operating system API's such as windows.h.
  4. Third party libraries, such as Boost, ACE, or commercial offerings.
  5. Organization-wide "standard" header files, such as a company's config.hpp.
  6. A project's "public" header files, often shared by all developers working on the same project.
  7. A project or user's "private", "local", or "detail" headers, in the same directory or sub-directory as the compilation unit.

Existing practice varies widely, but it is fairly easy to find users advocating:

Do any of the practices A, B, or C result in programs which can be rejected by a conforming implementation?

The first defect is that readers of the standard have not been able to reach consensus on the answers to the above question.

A second possible defect is that if A, B, or C can be rejected by a conforming implementation, then the standard should be changed because would mean there is a wide variance between the standard and existing practice.

Matt Austern: I really only see two positions:

  1. Implementations have some unspecified mechanism (copying files to a magic directory, adding a magic flag,...) such that the line #include <foo> results in textual inclusion of the file foo.
  2. Implementations are not required to have any mechanism for getting #include <foo> to perform textual inclusion of an arbitrary file foo. That form is reserved for standard library headers only.

I agree that the standard should clarify which of those two is the case (I imagine it'll hinge on finding one crucual sentence that either says "implementation defined" or "unspecified"), but from the standpoint of portability I don't see much difference between the two. I claim that, with either of those two interpretations, using #include <foo> is already nonportable.

(Of course, I claim that almost anything having to do with headers, including the #include "foo" form, is also nonportable. In practice there's wide variation in how compilers handle paths, especially relative paths.)

Beman Dawes: The whole issue can be resolved by replacing "header" with "header or source file" in 15.3 [cpp.include] paragraph 2. That will bring the standard into alignment with existing practice by both users and implementations. The "header and/or source file" wording is used at least three other places in the standard where otherwise there might be some confusion.

John Skaller: In light of Andrew Koenig's comments, this doesn't appear to be the case, since the mapping of include names to file names is implementation defined, and therefore source file inclusion cannot be made portable within the ISO C/C++ standards (since that provision obviously cannot be removed).

A possible idea is to create a binding standard, outside the C/C++ ISO Standards, which specifies not only the path lookup mechanism but also the translation from include names to file names. Clearly that is OS dependent, encoding dependent, etc, but there is no reason not to have a binding standard for Unix, Windows, etc, and specify these bindings in such a way that copying directories from one OS to the other can result in programs working on both OS's.

Andy Koenig: An easier solution might be to specify a (presumably unbounded, or bounded only by implementation capacity) collection of header-file names that every implementation must make it possible for programs to access somehow, without specifying exactly how.

Notes from October 2002 meeting:

This was discussed at some length. While there was widespread agreement that such inclusion is inherently implementation-dependent, we agreed to try to add wording that would make it clear that implementations are permitted (but not required) to allow inclusion of files using the <...> form of #include.

Proposed resolution (April, 2005):

Change 15.3 [cpp.include] paragraph 7 from:

[Example: The most common uses of #include preprocessing directives are as in the following:
    #include <stdio.h>
    #include "myprog.h"
end example]

to:

[Note: Although an implementation may provide a mechanism for making arbitrary source files available to the < > search, in general programmers should use the < > form for headers provided with the implementation, and the " " form for sources outside the control of the implementation. For instance:
    #include <stdio.h>
    #include <unistd.h>
    #include "usefullib.h"
    #include "myprog.h"
end note]

Notes from October, 2005 meeting:

Some doubt was expressed as to whether the benefit of this non-normative clarification outweighs the overall goal of synchronizing clause 16 with the corresponding text in the C99 Standard. As a result, this issue is being left in “review” status to allow further discussion.

Additional notes (October, 2006):

WG14 takes no position on this change.




106. Creating references to references during template deduction/instantiation

Section: Clause unknown  [unknown]     Status: CD1     Submitter: Bjarne Stroustrup     Date: unknown

[Moved to DR at 10/01 meeting.]

The main defect is in the library, where the binder template can easily lead to reference-to-reference situations.

Proposed resolution (04/01):

  1. Add the following as paragraph 6 of 9.2.4 [dcl.typedef]:

    If a typedef TD names a type "reference to cv1 S," an attempt to create the type "reference to cv2 TD" creates the type "reference to cv12" S," where cv12 is the union of the cv-qualifiers cv1 and cv2. Redundant qualifiers are ignored. [Example:

        int i;
        typedef int& RI;
        RI& r = i;          // r has the type int&
        const RI& r = i;    // r has the type const int&
    
    end example]
  2. Add the following as paragraph 4 of 13.4.2 [temp.arg.type]:

    If a template-argument for a template-parameter T names a type "reference to cv1 S," an attempt to create the type "reference to cv2 T" creates the type "reference to cv12 S," where cv12 is the union of the cv-qualifiers cv1 and cv2. Redundant cv-qualifiers are ignored. [Example:

        template <class T> class X {
            f(const T&);
            /* ... */
        };
        X<int&> x;    // X<int&>::f has the parameter type const int&
    
    end example]
  3. In 13.10.3 [temp.deduct] bullet 2.3 sub-bullet 5, remove the indicated text:
    Attempting to create a reference to a reference type or a reference to void.

(See also paper J16/00-0022 = WG21 N1245.)






Issues with "CD2" Status


816. Diagnosing violations of [[final]]

Section: _N3225_.7.6.4  [dcl.attr.final]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 108

[Voted into WP at July, 2009 meeting.]

According to _N3225_.7.6.4 [dcl.attr.final] paragraph 2, overriding a virtual function with the [[final]] attribute renders a program ill-formed, but no diagnostic is required. This is easily diagnosable and a diagnostic should be required in this case.

Notes from the March, 2009 meeting:

This specification was a deliberate decision on the part of the EWG; the general rule was that it should be possible to ignore attributes without changing the meaning of a program. However, the consensus of the CWG was that violation of the [[final]] attribute should require a diagnostic.

Proposed resolution (March, 2009):

Change _N3225_.7.6.4 [dcl.attr.final] paragraph 2 as follows:

If a virtual member function f in some class B is marked final and in a class D derived from B a function D::f overrides B::f, the program is ill-formed; no diagnostic required. [Footnote: If an implementation does not emit a diagnostic it should execute the program as if final were not present. —end footnote]



817. Meaning of [[final]] applied to a class definition

Section: _N3225_.7.6.4  [dcl.attr.final]     Status: CD2     Submitter: US     Date: 3 March, 2009

N2800 comment US 42

[Voted into WP at March, 2010 meeting.]

According to _N3225_.7.6.4 [dcl.attr.final] paragraph 1, the [[final]] attribute applied to a class is just a shorthand notation for marking each of the class's virtual functions as [[final]]. This is different from the similar usage in other languages, where it means that the class so marked cannot be used as a base class. This discrepancy is confusing, and the definition used by the other languages is more useful.

Notes from the March, 2009 meeting:

The intent of the [[final]] attribute is as an aid in optimization, to avoid virtual function calls when the final overrider is known. It is possible to use the [[final]] attribute to prevent derivation by marking the destructor as [[final]]; in fact, as most polymorphic classes will, as a matter of good programming practice, have a virtual destructor, marking the class as [[final]] will have the effect of preventing derivation.

Nonetheless, the general consensus of the CWG was to change the meaning of class [[final]] to parallel the usage in other languages.

Proposed resolution (October, 2009):

  1. Change _N3225_.7.6.4 [dcl.attr.final] paragraph 1 and add a new paragraph, as follows:

  2. The attribute-token final specifies derivation semantics for a class and overriding semantics for a virtual function. It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present. The attribute applies to class definitions and to virtual member functions being declared in a class definition. If the attribute is specified for a class definition, it is equivalent to being specified for each virtual member function of that class, including inherited member functions.

    If some class B is marked final and a class D is derived from B the program is ill-formed.

  3. Change the example in _N3225_.7.6.4 [dcl.attr.final] paragraph 3 as follows:

  4.   struct B1 {
        virtual void f [[ final ]] ();
      };
    
      struct D1 : B1 {
        void f();          // ill-formed
      };
    
      struct [[ final ]] B2 {
      };
    
      struct D2 : B2 {    // ill-formed
      };
    



789. Deprecating trigraphs

Section: _N4140_.2.4  [lex.trigraph]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 11

[Voted into WP at March, 2010 meeting as document N3077.]

Trigraphs are a complicated solution to an old problem, that cause more problems than they solve in the modern environment. Unexpected trigraphs in string literals and occasionally in comments can be very confusing for the non-expert. They should be deprecated.

Notes from the March, 2009 meeting:

IBM, at least, uses trigraphs in its header files in conditional compilation directives to select character-set dependent content in a character-set independent fashion and would thus be negatively affected by the removal of trigraphs. One possibility that was discussed was to avoid expanding trigraphs inside character string literals, which is the context that causes most surprise and confusion, but still to support them in the rest of the program text. Specifying that approach, however, would be challenging because trigraphs are replaced in phase 1, before character strings are recognized in phase 3. See also the similar discussion of universal-character-names in issue 787.

The consensus of the CWG was that trigraphs should be deprecated.

Proposed resolution (September, 2009):

See paper PL22.16/09-0168 = WG21 N2978.

Notes from the October, 2009 meeting:

The CWG is interested in exploring other alternatives that address the particular problem of trigraphs in raw strings but that do not require the grammar changes of the approach in N2978. One possibility might be to recognize raw strings in some way in translation phase 1.

Notes from the March, 2010 meeting:

The CWG decided not to deprecate trigraphs, acknowledging that there are communities in which they are viewed as necessary. Instead, it was decided to address what was considered to be the most pressing issue regarding trigraphs, that is, recognizing trigraph sequences inside raw string literals.




743. Use of decltype in a nested-name-specifier

Section: _N4567_.5.1.1  [expr.prim.general]     Status: CD2     Submitter: Jaakko Järvi     Date: 12 November, 2008

N2800 comment JP 8

[Voted into WP at March, 2010 meeting as document N3049.]

The grammar for nested-name-specifier in _N4567_.5.1.1 [expr.prim.general] paragraph 7 does not allow decltype to be used in a qualified-id. This could be useful for cases like:

   auto vec = get_vec();
   decltype(vec)::value_type v = vec.first();
(See also issue 950.)

Proposed resolution (September, 2009):

See paper PL22.16/09-0181 = WG21 N2991.

Proposed resolution (February, 2010):

See paper PL22.16/10-0021 = WG21 N3031.




760. this inside a nested class of a non-static member function

Section: _N4567_.5.1.1  [expr.prim.general]     Status: CD2     Submitter: Mike Miller     Date: 3 February, 2009

[Voted into WP at March, 2010 meeting.]

this is a keyword and thus not subject to ordinary name lookup. That makes the interpretation of examples like the following somewhat unclear:

    struct outer {
      void f() {
        struct inner {
          int a[sizeof(*this)];  // #1
        };
      }
    };

According to _N4567_.5.1.1 [expr.prim.general] paragraph 3,

The keyword this shall be used only inside a non-static class member function body (11.4.2 [class.mfct]) or in a brace-or-equal-initializer for a non-static data member.

Should the use of this at #1 be interepreted as a well-formed reference to outer::f()'s this or as an ill-formed attempt to refer to a this for outer::inner?

One possible interpretation is that the intent is as if this were an ordinary identifier appearing as a parameter in each non-static member function. (This view applies to the initializers of non-static data members as well if they are considered to be rewritten as mem-initializers in the constructor body.) Under this interpretation, the prohibition against using this in other contexts simply falls out of the fact that name lookup would fail to find this anywhere else, so the reference in the example is well-formed. (Implementations vary in their treatment of this example, so clearer wording is needed, whichever way the interpretation goes.)

Proposed resolution (February, 2010):

Change _N4567_.5.1.1 [expr.prim.general] paragraph 2 as follows:

...The keyword this shall be used only inside the body of a non-static class member function body (11.4.2 [class.mfct]) of the nearest enclosing class or in a brace-or-equal-initializer for a non-static data member (11.4 [class.mem]). The type of the expression is a pointer to the class of the function or non-static data member, possibly with cv-qualifiers on the class type. The expression is an rvalue. [Example:

  class Outer {
    int a[sizeof(*this)];            // error: not inside a member function
    unsigned int sz = sizeof(*this); // OK, in brace-or-equal-initializer

    void f() {
      int b[sizeof(*this)];          // OK

      struct Inner {
        int c[sizeof(*this)];        // error: not inside a member function of Inner
      };
    }
  };

end example]




850. Restrictions on use of non-static data members

Section: _N4567_.5.1.1  [expr.prim.general]     Status: CD2     Submitter: Jason Merrill     Date: 1 April, 2009

[Voted into WP at October, 2009 meeting.]

The resolution of issue 613, as reflected in the sixth bullet of _N4567_.5.1.1 [expr.prim.general] paragraph 10, allows an id-expression designating a non-static data member to be used

The requirement that the id-expression be the “sole constituent” of the unevaluated operand seems unnecessarily strict, forbidding such plausible use cases as

    struct S {
        int ar[42];
    };
    int i = sizeof(S::ar[0]);

or the use of the member as a function argument in template metaprogramming. The more general version of the restriction seems not to be very difficult to implement and may actually represent a simplification in some implementations.

Proposed resolution (July, 2009):

Change _N4567_.5.1.1 [expr.prim.general] paragraph 10 as follows:




735. Missing case in specification of safely-derived pointers

Section: _N4885_6.7.5.5.4  [basic.stc.dynamic.safety]     Status: CD2     Submitter: Jens Maurer     Date: 14 October, 2008

N2800 comment DE 3

[Voted into WP at October, 2009 meeting.]

The bullets in _N4885_6.7.5.5.4 [basic.stc.dynamic.safety] paragraph 2 do not appear to cover the following example:

   int& i = *new int(5);
   // do something with i
   delete &i;

Should &i be a safely-derived pointer value?

Proposed resolution (September, 2009):

Change _N4885_6.7.5.5.4 [basic.stc.dynamic.safety] paragraph 2, bullet 2, as follows:




853. Support for relaxed pointer safety

Section: _N4885_6.7.5.5.4  [basic.stc.dynamic.safety]     Status: CD2     Submitter: Jens Maurer     Date: 3 April, 2009

[Voted into WP at March, 2010 meeting.]

According to _N4885_.20.10.5 [util.dynamic.safety] paragraph 16, when std::get_pointer_safety() returns std::pointer_safety::relaxed,

pointers that are not safely derived will be treated the same as pointers that are safely derived for the duration of the program.

However, _N4885_6.7.5.5.4 [basic.stc.dynamic.safety] paragraph 4 says unconditionally that

If a pointer value that is not a safely-derived pointer value is dereferenced or deallocated, and the referenced complete object is of dynamic storage duration and has not previously been declared reachable (_N4885_.20.10.5 [util.dynamic.safety]), the behavior is undefined.

This is a contradiction: the library clause attempts to constrain undefined behavior, which by definition is unconstrained.

Proposed resolution (July, 2009):

Change _N4885_6.7.5.5.4 [basic.stc.dynamic.safety] paragraph 4 as follows to define the terms “strict pointer safety” and “relaxed pointer safety,” which could then be used by the library clauses to achieve the desired effect:

An implementation may have relaxed pointer safety, in which case the validity of a pointer value does not depend on whether it is a safely-derived pointer value or not. Alternatively, an implementation may have strict pointer safety, in which case if If a pointer value that is not a safely-derived pointer value is dereferenced or deallocated, and the referenced complete object is of dynamic storage duration and has not previously been declared reachable (_N4885_.20.10.5 [util.dynamic.safety]), the behavior is undefined. [Note: this is true even if the unsafely-derived pointer value might compare equal to some safely-derived pointer value. —end note] It is implementation-defined whether an implementation has relaxed or strict pointer safety.



690. The dynamic type of an rvalue reference

Section: Clause 3  [intro.defs]     Status: CD2     Submitter: Eelis van der Weegen     Date: 7 April, 2008

N2800 comment FR 5

[Voted into WP at March, 2010 meeting as document N3055.]

According to Clause 3 [intro.defs], “dynamic type,”

The dynamic type of an rvalue expression is its static type.

This is not true of an rvalue reference, which can be bound to an object of a class type derived from the reference's static type.

Proposed resolution (June, 2008):

Change Clause 3 [intro.defs], “dynamic type,” as follows:

the type of the most derived object (6.7.2 [intro.object]) to which the lvalue denoted by an lvalue or an rvalue-reference (Clause 7 [expr]) expression refers. [Example: if a pointer (9.3.4.2 [dcl.ptr]) p whose static type is “pointer to class B” is pointing to an object of class D, derived from B (11.7 [class.derived]), the dynamic type of the expression *p is “D.” References (9.3.4.3 [dcl.ref]) are treated similarly. —end example] The dynamic type of an rvalue expression that is not an rvalue reference is its static type.

Notes from the June, 2008 meeting:

Because expressions have an rvalue reference type only fleetingly, immediately becoming either lvalues or rvalues and no longer references, the CWG expressed a desire for a different approach that would somehow describe an rvalue that resulted from an rvalue reference instead of using the concept of an expression that is an rvalue reference, as above. This approach could also be used in the resolution of issue 664.

Additional note (August, 2008):

This issue, along with issue 664, indicates that rvalue references have more in common with lvalues than with other rvalues: they denote particular objects, thus allowing object identity and polymorphic behavior. That suggests that these issues may be just the tip of the iceberg: restrictions on out-of-lifetime access to objects, the aliasing rules, and many other specifications are written to apply only to lvalues, on the assumption that only lvalues refer to specific objects. That assumption is no longer valid with rvalue references.

This suggests that it might be better to classify all rvalue references, not just named rvalue references, as lvalues instead of rvalues, and then just change the reference binding, overload resolution, and template argument deduction rules to cater to the specific kind of lvalues that are associated with rvalue references.

Additional note, May, 2009:

Another place in the Standard where the assumption is made that only lvalues can have dynamic types that differ from their static types is 7.6.1.8 [expr.typeid] paragraph 2.

(See also issues 846 and 863.)

Additional note, September, 2009:

Yet another complication is the statement in 7.2.1 [basic.lval] paragraph 9 stating that “non-class rvalues always have cv-unqualified types.” If an rvalue reference is an rvalue, then the following example is well-formed:

    void f(int&&);    // reference to non-const
    void g() {
        const int i = 0;
        f(static_cast<const int&&>(i));
    }

The static_cast binds an rvalue reference to the const object i, but the fact that it's an rvalue means that the cv-qualification is lost, effectively allowing the parameter of f, a reference to non-const, to bind directly to the const object.

Proposed resolution (February, 2010):

See paper N3030.




787. Unnecessary lexical undefined behavior

Section: 5.2  [lex.phases]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 9
N2800 comment UK 12

[Voted into WP at March, 2010 meeting.]

There are several instances of undefined behavior in lexical processing:

These would be more appropriately handled as conditionally-supported behavior, requiring implementations either to document their handling of these constructs or to issue a diagnostic.

Additional note, March, 2009:

The undefined behavior referred to above regarding universal-character-names is the result of the considerations described in the C99 Rationale, section 5.2.1, in the part entitled “UCN models.” Three different models for support of UCNs are described, each involving different conversions between UCNs and wide characters and/or at different times during program translation. Implementations, as well as the specification in a language standard, can employ any of the three, but it must be impossible for a well-defined program to determine which model was actually employed by implementation. The implication of this “equivalence principle” is that any construct that would give different results under the different models must be classified as undefined behavior. For example, an apparent UCN resulting from a line-splice would be recognized as a UCN by an implementation in which all wide characters were translated immediately into UCNs, as described in C++ phase 1, but would not be recognized as a UCN by another implementation in which all UCNs were translated immediately into wide characters (a possibility mentioned parenthetically in C++ phase 1).

There are additional implications for this “equivalence principle” beyond the ones identified in the UK CD comments. See also issue 578; presumably a string like the one in that issue should also be described as having undefined behavior. Also, because C++'s model introduces backslash characters as part of UCNs for any character outside the basic source character set, any header-name that contains such a character (e.g., #include "@.h") will have undefined behavior in C++. This is also the reason that UCNs are translated into wide characters inside raw strings: two of the three models articulated in the C99 Rationale translate to or from UCNs in phase 1, before raw strings are recognized as tokens in phase 3, so raw strings cannot treat UCNs differently from the way they are treated in other contexts. See also issue 789 for similar points regarding trigraphs.

Notes from the October, 2009 meeting:

The CWG decided that the non-UCN aspects of this issue should be resolved, while the overall questions regarding trigraphs, UCNs, and raw strings will be investigated separately.

Proposed resolution (February, 2010):

  1. Change 5.2 [lex.phases] paragraph 1 phase 2 as follows:

  2. ...If a A source file that is not empty and that does not end in a new-line character, or that ends in a new-line character immediately preceded by a backslash character before any such splicing takes place, the behavior is undefined shall be processed as if an additional new-line character were appended to the file.
  3. Change 5.8 [lex.header] paragraph 2 as follows:

  4. If The appearance of either of the characters ' or \, or of either of the character sequences /* or // appears in a q-char-sequence or a an h-char-sequence is conditionally-supported with implementation-defined semantics, or as is the appearance of the character " appears in a an h-char-sequence, the behavior is undefined. [Footnote: Thus, a sequences of characters that resembles an escape sequences cause undefined behavior might result in an error, be interpreted as the character corresponding to the escape sequence, or have a completely different meaning, depending on the implementation. —end footnote]



630. Equality of narrow and wide character values in the basic character set

Section: 5.3  [lex.charset]     Status: CD2     Submitter: Tom Plum     Date: 21 April 2007

[Voted into WP at October, 2009 meeting.]

WG14 accepted DR 279 regarding the rule known colloquially as the L'x'=='x' rule. This change was made to C99 in TC2. The Austin Group subsequently opened DR 321 against TC2, observing that the change made in TC2 would invalidate existing conforming C code that relied on the L'x'=='x' rule.

DR 321 is now closed and will be included in the CD3 to C99. This change defines a new standard macro, which WG14 drafted as follows:

__STDC_MB_MIGHT_NEQ_WC__: The integer constant 1, intended to indicate that there might be some character x in the basic character set, such that 'x' need not be equal to L'x'.

WG14 requests that WG21 adopt this revision and this macro in C++0x.

Proposed resolution (July, 2009):

Add the following to 15.11 [cpp.predefined] paragraph 2:

__STDC_MB_MIGHT_NEQ_WC__
The integer constant 1, intended to indicate that, in the encoding for wchar_t, a member of the basic character set need not have a code value equal to its value when used as the lone character in an ordinary character literal.



788. Relationship between locale and values of the execution character set

Section: 5.3  [lex.charset]     Status: CD2     Submitter: FR     Date: 3 March, 2009

N2800 comment FR 10

[Voted into WP at March, 2010 meeting.]

According to 5.3 [lex.charset] paragraph 3,

The values of the members of the execution character sets are implementation-defined, and any additional members are locale-specific.

This makes it sound as if the locale determines only whether an extended character (one not in the basic execution character set) exists, not its value (which is just implementation-defined, not locale-specific). The description should be clarified to indicate that the value of a given character can vary between locales, as well.

Proposed resolution (February, 2010):

Change 5.3 [lex.charset] paragraph 3 as follows:

...The execution character set and the execution wide-character set are implementation-defined supersets of the basic execution character set and the basic execution wide-character set, respectively. The values of the members of the execution character sets and the sets of additional members are implementation-defined, and any additional members are locale-specific.



832. Value of preprocessing numbers

Section: 5.9  [lex.ppnumber]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 13

[Voted into WP at October, 2009 meeting.]

5.9 [lex.ppnumber] paragraph 2 says,

A preprocessing number does not have a type or a value; it acquires both after a successful conversion (as part of translation phase 7, 5.2 [lex.phases]) to an integral literal token or a floating literal token.

However, preprocessing directives are executed in phase 4, and the evaluation of constant-expressions in #if directives requires that preprocessing numbers have values.

Proposed resolution (July, 2009):

Change 5.9 [lex.ppnumber] paragraph 2 as follows:

A preprocessing number does not have a type or a value; it acquires both after a successful conversion (as part of translation phase 7 (5.2 [lex.phases])) to an integral literal token or a floating literal token.



933. 32-bit UCNs with 16-bit wchar_t

Section: 5.13.3  [lex.ccon]     Status: CD2     Submitter: Alisdair Meredith     Date: 7 July, 2009

[Voted into WP at October, 2009 meeting.]

According to 5.13.3 [lex.ccon] paragraph 2,

A character literal that begins with the letter L, such as L'x', is a wide-character literal. A wide-character literal has type wchar_t. The value of a wide-character literal containing a single c-char has value equal to the numerical value of the encoding of the c-char in the execution wide-character set.

A c-char that is a universal character name might, when translated to the execution character set, result in a multi-character sequence that is larger than can be represented in a wchar_t. There is wording that prevents this in char16_t literals, but not for wchar_t literals. This seems undesirable.

Proposed resolution (July, 2009):

  1. Change 5.13.3 [lex.ccon] paragraph 2 as follows:

  2. ...The value of a wide-character literal containing a single c-char has value equal to the numerical value of the encoding of the c-char in the execution wide-character set, unless the c-char has no representation in the execution wide-character set, in which case the value is implementation-defined. [Note: The type wchar_t is able to represent all members of the execution wide-character set, see 6.8.2 [basic.fundamental]. —end note]. The value of a wide-character literal containing multiple c-chars is implementation-defined.
  3. Change 5.13.3 [lex.ccon] paragraph 5 as follows:

  4. A universal-character-name is translated to the encoding, in the appropriate execution character set, of the character named...



790. Concatenation of raw and non-raw string literals

Section: 5.13.5  [lex.string]     Status: CD2     Submitter: JP     Date: 3 March, 2009

N2800 comment JP 5

[Voted into WP at October, 2009 meeting.]

The description of concatenation of string literals in 5.13.5 [lex.string] paragraph 11 does not mention raw strings explicitly, so it is not clear whether, and if so, how, they combine with non-raw strings.

Notes from the March, 2009 meeting:

A raw string should be considered equivalent to the corresponding non-raw string in string literal concatenation.

Proposed resolution (September, 2009):

  1. In 5.13.5 [lex.string], replace the definition of string-literal with:


  2. Change 5.13.5 [lex.string] paragraph 5 as follows:

  3. A After translation phase 6, a string literal that does not begin with u8, u, U, or L an encoding-prefix is an ordinary string literal, and is initialized with the given characters.
  4. Change 5.13.5 [lex.string] paragraph 12 as follows:

  5. In translation phase 6 (5.2 [lex.phases]), adjacent string literals are concatenated. If both string literals have the same prefix encoding-prefix, the resulting concatenated string literal has that prefix encoding-prefix. If one string literal has no prefix encoding-prefix, it is treated as a string literal of the same prefix encoding-prefix as the other operand. If a UTF-8 string literal token is adjacent to a wide string literal token, the program is ill-formed. Any other concatenations are conditionally supported with implementation-defined behavior. [Note: This concatenation is an interpretation, not a conversion. Because the interpretation happens in translation phase 6 (after each character from each literal has been translated into a value from the appropriate character set), a string literal's initial rawness has no effect on the interpretation or well-formedness of the concatenation.end note] [Example:...

(Note: this resolution also resolves issue 834.)




834. What is an “ordinary string literal”?

Section: 5.13.5  [lex.string]     Status: CD2     Submitter: Mike Miller     Date: 6 March, 2009

[Voted into WP at October, 2009 meeting.]

According to 5.13.5 [lex.string] paragraph 4,

A string literal that does not begin with u8, u, U, or L is an ordinary string literal, and is initialized with the given characters.

This is not as clear as it could be that a string like u8R"[xxx]" is not an ordinary string literal, because the string's prefix is not one of those listed (i.e., it's not obvious that possible substrings of the prefix are in view). This would be clearer if it simply said,

A string literal with no prefix or a prefix of R is an ordinary string literal.

Proposed resolution (September, 2009):

This issue is resolved by the resolution of issue 790.




872. Lexical issues with raw strings

Section: 5.13.5  [lex.string]     Status: CD2     Submitter: Joseph Myers     Date: 16 April, 2009

[Voted into WP at March, 2010 meeting as document N3077.]

The specification of raw string literals interacts poorly with the specification of preprocessing tokens. The grammar in 5.4 [lex.pptoken] has a production reading

This is echoed in the max-munch rule in paragraph 3:

If the input stream has been parsed into preprocessing tokens up to a given character, the next preprocessing token is the longest sequence of characters that could constitute a preprocessing token, even if that would cause further lexical analysis to fail.

This raises questions about the handling of raw string literals. Consider, for instance,

    #define R "x"
    const char* s = R"y";

The character sequence R"y" does not satisfy the syntactic requirements for a raw string. Should it be diagnosed as an ill-formed attempt at a raw string, or should it be well-formed, interpreting R as a preprocessor token that is a macro name and thus initializing s with a pointer to the string "xy"?

For another example, consider:

    #define R "]"
    const char* x = R"foo[";

Presumably this means that the entire rest of the file must be scanned for the characters ]foo" and, if they are not found, macro-expand R and initialize x with a pointer to the string "]foo[". Is this the intended result?

Finally, does the requirement in 5.13.5 [lex.string] that

A d-char-sequence shall consist of at most 16 characters.

mean that

    #define R "x"
    const char* y = R"12345678901234567[y]12345678901234567";

is ill-formed, or a valid initialization of y with a pointer to the string "x12345678901234567[y]12345678901234567"?

Additional note, June, 2009:

The translation of characters that are not in the basic source character set into universal-character-names in translation phase 1 raises an additional problem: each such character will occupy at least six of the 16 r-chars that are permitted. Thus, for example, R"@@@[]@@@" is ill-formed because @@@ becomes \u0040\u0040\u0040, which is 18 characters.

One possibility for addressing this might be to disallow the \ character completely as an d-char, which would have the effect of restricting r-chars to the basic source character set.

Proposed resolution (October, 2009):

  1. Change the grammar in 5.13.5 [lex.string] as follows:

  2. Change 5.13.5 [lex.string] paragraph 2 as follows:

  3. A string literal that has an R in the prefix is a raw string literal. The d-char-sequence serves as a delimiter. The terminating d-char-sequence of a raw-string is the same sequence of characters as the initial d-char-sequence. A d-char-sequence shall consist of at most 16 characters. If the input stream contains a sequence of characters that could be the prefix and initial double quote of a raw string literal, such as R", those characters are considered to begin a raw string literal even if that literal is not well-formed. [Example:

      #define R "x"
      const char* s = R"y"; // ill-formed raw string, not "x" "y"
    
    

    end example]




932. UCNs in closing delimiters of raw string literals

Section: 5.13.5  [lex.string]     Status: CD2     Submitter: Alisdair Meredith     Date: 7 July, 2009

[Voted into WP at March, 2010 meeting.]

Since members of the basic source character set can be written inside a string using a universal character name, it is not clear whether a UCN that represents ']' or one of the characters in the terminating d-char-sequence should be interpreted as that character or as an attempt to “escape” that character and prevent its interpretation as part of the terminating sequence of a raw character string.

Notes from the July, 2009 meeting:

The CWG supported a resolution in which the d-char-sequence of a raw string literal is considered to be outside the literal and thus, by 5.3 [lex.charset] paragraph 2, could not contain a UCN designating a member of the basic source character set.

Proposed resolution (October, 2009):

Change 5.3 [lex.charset] paragraph 2 as follows:

Additionally, if the hexadecimal value for a universal-character-name outside the c-char-sequence, s-char-sequence, or r-char-sequence of a character or string literal corresponds to a control character (in either of the ranges 0x00-0x1F or 0x7F-0x9F, both inclusive) or to a character in the basic source character set, the program is ill-formed.



931. Confusing reference to the length of a user-defined string literal

Section: 5.13.9  [lex.ext]     Status: CD2     Submitter: Alisdair Meredith     Date: 6 July, 2009

[Voted into WP at March, 2010 meeting.]

5.13.9 [lex.ext] paragraph 5 says,

If L is a user-defined-string-literal, let str be the literal without its ud-suffix and let len be the number of characters (or code points) in str (i.e., its length excluding the terminating null character).

The length of a null-terminated string is defined in 16.3.3.3.4.2 [byte.strings] as the number of bytes preceding the terminator, but a single code point in a UTF-8 string can require more than one byte, so this sentence is inconsistent and needs to be revised to make clear which definition is in view.

Proposed resolution (October, 2009):

Change 5.13.9 [lex.ext] paragraph 5 as follows:

If L is a user-defined-string-literal, let str be the literal without its ud-suffix and let len be the number of characters (or code points) code units in str (i.e., its length excluding the terminating null character)...



633. Specifications for variables that should also apply to references

Section: 6.1  [basic.pre]     Status: CD2     Submitter: Alisdair Meredith     Date: 17 May 2007

N2800 comment UK 22

[Voted into WP at March, 2010 meeting as document N2993.]

There are a number of specifications in the Standard that should also apply to references. For example:

A number of other examples could be cited. A thorough review is needed to make sure that references are completely specified.

Notes from the September, 2008 meeting:

The CWG expressed interest in an approach that would define “variable” to include both objects and references and to use that for both this issue and issue 570.

Proposed resolution (October, 2009):

See paper PL22.16/09-0183 = WG21 N2993. This resolution also resolves issue 570.




719. Specifications for operator-function-id that should also apply to literal-operator-id

Section: 6.1  [basic.pre]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 19 September, 2008

[Voted into WP at October, 2009 meeting.]

When user-defined literals were added, a new form of operator function was created. Presumably many of the existing specifications that deal with operator-function-ids (the definition of name, for instance, in paragraph 4 of 6.1 [basic.pre]) should also apply to literal-operator-ids.

Proposed resolution (June, 2009):

  1. Change 6.1 [basic.pre] paragraph 4 as follows:

  2. A name is a use of an identifier (5.10 [lex.name]), operator-function-id (12.4 [over.oper]), literal-operator-id (12.6 [over.literal]), conversion-function-id (11.4.8.3 [class.conv.fct]), or template-id (13.3 [temp.names]) that denotes an entity or label (8.7.6 [stmt.goto], 8.2 [stmt.label]).
  3. Change _N4567_.5.1.1 [expr.prim.general] paragraph 3 as follows:

  4. The operator :: followed by an identifier, a qualified-id, or an operator-function-id, or a literal-operator-id is a primary-expression. Its type is specified by the declaration of the identifier, qualified-id, or operator-function-id, or literal-operator-id. The result is the entity denoted by the identifier, qualified-id, or operator-function-id, or literal-operator-id. The result is an lvalue if the entity is a function or variable. The identifier, qualified-id, or operator-function-id, or literal-operator-id shall have global namespace scope or be visible in global scope because of a using-directive (9.8.4 [namespace.udir])...
  5. Add the following production to the grammar for qualified-id in _N4567_.5.1.1 [expr.prim.general] paragraph 7:

  6. Add the following production to the grammar for template-id in 13.3 [temp.names] paragraph 1:

  7. Change 13.3 [temp.names] paragraph 3 as follows:

  8. After name lookup (6.5 [basic.lookup]) finds that a name is a template-name, or that an operator-function-id or a literal-operator-id refers to a set of overloaded functions any member of which is a function template...
  9. Change 13.6 [temp.type] bullet 1.1 as follows:




942. Is this an entity?

Section: 6.1  [basic.pre]     Status: CD2     Submitter: Maurer     Date: 14 July, 2009

[Voted into WP at March, 2010 meeting.]

At least in the new wording for 7.5.5 [expr.prim.lambda] paragraph 10 as found in paper N2927, this is explicitly assumed to be an entity. It should be investigated whether this should be added to the list of entities found in 6.1 [basic.pre] paragraph 3.

Proposed resolution (October, 2009):

  1. Change 6.1 [basic.pre] paragraph 3 as follows:

  2. An entity is a value, object, variable, reference, function, enumerator, type, class member, template, template specialization, namespace, or parameter pack, or this.
  3. Change 6.3 [basic.def.odr] paragraph 2 as follows:

  4. ...is immediately applied. this is used if it appears as a potentially-evaluated expression (including as the result of the implicit transformation in the body of a non-static member function (11.4.3 [class.mfct.non.static])). A virtual member function...
  5. Delete 7.5.5 [expr.prim.lambda] paragraph 7:

  6. For the purpose of describing the behavior of lambda-expressions below, this is considered to be “used” if replacing this by an invented variable v with automatic storage duration and the same type as this would result in v being used (6.3 [basic.def.odr]).



570. Are references subject to the ODR?

Section: 6.3  [basic.def.odr]     Status: CD2     Submitter: Dave Abrahams     Date: 2 April 2006

N2800 comment UK 26

[Voted into WP at March, 2010 meeting as document N2993.]

6.3 [basic.def.odr] paragraph 1 says,

No translation unit shall contain more than one definition of any variable, function, class type, enumeration type or template.

This says nothing about references. Is it permitted to define a reference more than once in a single translation unit? (The list in paragraph 5 of things that can have definitions in multiple translation units does not include references.)

Notes from the September, 2008 meeting:

The CWG expressed interest in an approach that would define “variable” to include both objects and references and to use that for both this issue and issue 633.

Proposed resolution (October, 2009):

This issue is resolved by the resolution of issue 633.




481. Scope of template parameters

Section: 6.4  [basic.scope]     Status: CD2     Submitter: Gabriel Dos Reis     Date: 01 Nov 2004

N2800 comment FR 16

[Voted into WP at October, 2009 meeting.]

Sections 6.4.3 [basic.scope.block] to 6.4.7 [basic.scope.class] define and summarize different kinds of scopes in a C++ program. However it is missing a description for the scope of template parameters. I believe a section is needed there — even though some information may be found in clause 14.

Proposed resolution (September, 2009):

  1. Insert the following as a new paragraph following 6.4.2 [basic.scope.pdecl] paragraph 8:

  2. The point of declaration of a template parameter is immediately after its complete template-parameter. [Example:
      typedef unsigned char T;
      template<class T
           = T         // Lookup finds the typedef name of unsigned char.
    
           , T         //Lookup finds the template parameter.
               N = 0> struct A {};
    

    end example]

  3. Delete 13.2 [temp.param] paragraph 14:

  4. A template-parameter shall not be used in its own default argument.
    [Drafting note: This change conflicts with the resolution for issue 187 but is in accord with widespread implementation practice.]
  5. Insert the following as a new section following 6.4.8 [basic.scope.enum]:

  6. Template Parameter Scope [basic.scope.temp]

    The declarative region of the name of a template parameter of a template template-parameter is the smallest template-parameter-list in which the name was introduced.

    The declarative region of the name of a template parameter of a template is the smallest template-declaration in which the name was introduced. Only template parameter names belong to this declarative region; any other kind of name introduced by the declaration of a template-declaration is instead introduced into the same declarative region where it would be introduced as a result of a non-template declaration of the same name. [Example:

      namespace N {
        template<class T> struct A{};               // line 2
        template<class U> void f(U){}               // line 3
        struct B {
          template<class V>friend int g(struct C*); // line 5
        };
      }
    

    The declarative regions of T, U and V are the template-declarations on lines 2, 3 and 5, respectively. But the names A, f, g and C all belong to the same declarative region—namely, the namespace-body of N. (g is still considered to belong to this declarative region in spite of its being hidden during qualified and unqualified name lookup.) —end example]

    The potential scope of a template parameter name begins at its point of declaration (6.4.2 [basic.scope.pdecl]) and ends at the end of its declarative region. [Note: this implies that a template-parameter can be used in the declaration of subsequent template-parameters and their default arguments but cannot be used in preceding template-parameters or their default arguments. For example,

      template<class T, T* p, class U = T> class X { /* ... */ };
      template<class T> void f(T* p = new T);
    

    This also implies that a template-parameter can be used in the specification of base classes. For example,

      template<class T> class X : public Array<T> { /* ... */ };
      template<class T> class Y : public T { /* ... */ };
    

    The use of a template parameter as a base class implies that a class used as a template argument must be defined and not just declared when the class template is instantiated. —end note]

    The declarative region of the name of a template parameter is nested within the immediately-enclosing declarative region. [Note: as a result, a template-parameter hides any entity with the same name in an enclosing scope (_N4868_.6.4.10 [basic.scope.hiding]). [Example:

      typedef int N;
      template<N X, typename N, template<N Y> class T>
        struct A;
    

    Here, X is a non-type template parameter of type int and Y is a non-type template parameter of the same type as the second template parameter of A. —end example] —end note]

    [Note: because the name of a template parameter cannot be redeclared within its potential scope (13.8.2 [temp.local]), a template parameter's scope is often its potential scope. However, it is still possible for a template parameter name to be hidden; see 13.8.2 [temp.local]. —end note]

  7. Delete 13.2 [temp.param] paragraph 13, including the example:

  8. The scope of a template-parameter extends...
  9. Delete 13.8.2 [temp.local] paragraph 6, including the note and example:

  10. The scope of a template-parameter extends...



642. Definition and use of “block scope” and “local scope”

Section: 6.4.3  [basic.scope.block]     Status: CD2     Submitter: Alisdair Meredith     Date: 6 Aug 2007

[Voted into WP at March, 2010 meeting.]

The Standard uses the terms “block scope” and “local scope” interchangeably, but the former is never formally defined. Would it be better to use only one term consistently? “Block scope” seems to be more frequently used.

Notes from the October, 2007 meeting:

The CWG expressed a preference for the term “local scope.”

Notes from the September, 2008 meeting:

Reevaluating the relative prevalence of the two terms (including the fact that new uses of “block scope” are being introduced, e.g., in both the lambda and thread-local wording) led to CWG reversing its previous preference for “local scope.” The resolution will need to add a definition of “block scope” and should change the title of 6.4.3 [basic.scope.block].

Proposed resolution (October, 2009):

  1. Change 6.4.2 [basic.scope.pdecl] paragraph 2 as follows:

  2. [Note: a nonlocal name from an outer scope remains visible up to the point of declaration of the local name that hides it. [Example:

      const int i = 2;
      { int i[i]; }
    

    declares a local block-scope array of two integers. —end example] —end note]

  3. Change the section heading of 6.4.3 [basic.scope.block] from “Local scope” to “Block scope.”

  4. Change 6.4.3 [basic.scope.block] paragraph 1 as follows:

  5. A name declared in a block (8.4 [stmt.block]) is local to that block; it has block scope. Its potential scope begins at its point of declaration (6.4.2 [basic.scope.pdecl]) and ends at the end of its declarative region block. A variable declared at block scope is a local variable.
  6. Change 6.4.3 [basic.scope.block] paragraph 3 as follows:

  7. The name in a catch exception-declaration declared in an exception-declaration is local to the handler handler and shall not be redeclared in the outermost block of the handler handler.
  8. Change _N4868_.6.4.10 [basic.scope.hiding] paragraph 3 as follows:

  9. In a member function definition, the declaration of a local name at block scope hides the declaration of a member of the class with the same name...
  10. Change 6.6 [basic.link] paragraph 8 as follows:

  11. ...Moreover, except as noted, a name declared in a local at block scope (6.4.3 [basic.scope.block]) has no linkage...
  12. Change 6.9.3.3 [basic.start.dynamic] paragraph 1 as follows:

  13. ...For an object of array or class type, all subobjects of that object are destroyed before any local block-scope object with static storage duration initialized during the construction of the subobjects is destroyed.
  14. Change 6.9.3.3 [basic.start.dynamic] paragraph 2 as follows:

  15. If a function contains a local block-scope object of static or thread storage duration that has been destroyed and the function is called during the destruction of an object with static or thread storage duration, the program has undefined behavior if the flow of control passes through the definition of the previously destroyed local block-scope object. Likewise, the behavior is undefined if the function-local block-scope object is used indirectly (i.e., through a pointer) after its destruction.
  16. Change 6.9.3.3 [basic.start.dynamic] paragraph 3 as follows:

  17. If the completion of the initialization of a non-local non-block-scope object with static storage duration is sequenced before a call to std::atexit (see <cstdlib>, 17.5 [support.start.term]), the call to the function passed to std::atexit is sequenced before the call to the destructor for the object. If a call to std::atexit is sequenced before the completion of the initialization of a non-local non-block-scope object with static storage duration, the call to the destructor...

    [Editorial note: the occurrences of “non-local” in this change are removed by the proposed resolution for issue 946.]

  18. Change 8.4 [stmt.block] paragraph 1 as follows:

  19. ...A compound statement defines a local block scope (6.4 [basic.scope])...
  20. Change 8.5 [stmt.select] paragraph 1 as follows:

  21. ...The substatement in a selection-statement (each substatement, in the else form of the if statement) implicitly defines a local block scope (6.4 [basic.scope])...
  22. Change 8.5 [stmt.select] paragraph 5 as follows:

  23. If a condition can be syntactically resolved as either an expression or the declaration of a local block-scope name, it is interpreted as a declaration.
  24. Change 8.6 [stmt.iter] paragraph 2 as follows:

  25. The substatement in an iteration-statement implicitly defines a local block scope (6.4 [basic.scope]) which is entered and exited each time through the loop.
  26. Change 8.8 [stmt.dcl] paragraph 3 as follows:

  27. ...A program that jumps84 from a point where a local variable with automatic storage duration...
  28. Change 8.8 [stmt.dcl] paragraph 4 as follows:

  29. The zero-initialization (9.4 [dcl.init]) of all local block-scope objects with static storage duration (6.7.5.2 [basic.stc.static]) or thread storage duration (6.7.5.3 [basic.stc.thread]) is performed before any other initialization takes place. Constant initialization (6.9.3.2 [basic.start.static]) of a local block-scope entity with static storage duration, if applicable, is performed before its block is first entered. An implementation is permitted to perform early initialization of other local block-scope objects...
  30. Change 8.8 [stmt.dcl] paragraph 5 as follows:

  31. The destructor for a local block-scope object with static or thread storage duration will be executed if and only if the variable was constructed. [Note: 6.9.3.3 [basic.start.dynamic] describes the order in which local block-scope objects with static and thread storage duration are destroyed. —end note]
  32. Change 9.5 [dcl.fct.def] paragraph 7 as follows:

  33. In the function-body, a function-local predefined variable denotes a local block-scope object of static storage duration that is implicitly defined (see 6.4.3 [basic.scope.block]).
  34. Change the example in 11.3 [class.name] paragraph 2 as follows:

  35.   ...
      void g() {
        struct s;      // hide global struct s
                       // with a local block-scope declaration
      ...
    
  36. Change the example in 11.3 [class.name] paragraph 3 as follows:

  37.   ...
      void g(int s) {
        struct s* p = new struct s;   // global s
        p->a = s;                     // local parameter s
      }
    



490. Name lookup in friend declarations

Section: 6.5.3  [basic.lookup.unqual]     Status: CD2     Submitter: Ben Hutchings     Date: 7 Dec 2004

[Voted into WP at March, 2010 meeting.]

When 6.5.3 [basic.lookup.unqual] paragraph 10 says,

In a friend declaration naming a member function, a name used in the function declarator and not part of a template-argument in a template-id is first looked up in the scope of the member function's class. If it is not found, or if the name is part of a template-argument in a template-id, the look up is as described for unqualified names in the definition of the class granting friendship.

what does “in the scope of the member function's class” mean? Does it mean that only members of the class and its base classes are considered? Or does it mean that the same lookup is to be performed as if the name appeared in the member function's class? Implementations vary in this regard. For example:

     struct s1;

     namespace ns {
         struct s1;
     }

     struct s2 {
         void f(s1 &);
     };

     namespace ns {
         struct s3 {
             friend void s2::f(s1 &);
         };
     }

Microsoft Visual C++ and Comeau C++ resolve s1 in the friend declaration to ns::s1 and issue an error, while g++ resolves it to ::s1 and accepts the code.

Notes from the April, 2005 meeting:

The phrase “looked up in the scope of [a] class” occurs frequently throughout the Standard and always refers to the member name lookup described in 6.5.2 [class.member.lookup]. This is the first interpretation mentioned above (“only members of the class and its base classes”), resolving s1 to ns::s1. A cross-reference to 6.5.2 [class.member.lookup] will be added to 6.5.3 [basic.lookup.unqual] paragraph 10 to make this clearer.

In discussing this question, the CWG noticed another problem: the text quoted above applies to all template-arguments appearing in the function declarator. The intention of this rule, however, is that only template-arguments in the declarator-id should ignore the member function's class scope; template-arguments used elsewhere in the function declarator should be treated like other names. For example:

     template<typename T> struct S;
     struct A {
       typedef int T;
       void foo(S<T>);
     };
     template <typename T> struct B {
       friend void A::foo(S<T>);  // i.e., S<A::T>
     };

Proposed resolution (February, 2010):

Change 6.5.3 [basic.lookup.unqual] paragraph 10 as follows:

In a friend declaration naming a member function, a name used in the function declarator and not part of a template-argument in a template-id the declarator-id is first looked up in the scope of the member function's class (6.5.2 [class.member.lookup]). If it is not found, or if the name is part of a template-argument in a template-id the declarator-id, the look up is as described for unqualified names in the definition of the class granting friendship. [Example:

    struct A {
      typedef int AT;
      void f1(AT);
      void f2(float);
      template<typename T> void f3();
    };
    struct B {
      typedef char AT;
      typedef float BT;
      friend void A::f1(AT);    // parameter type is A::AT
      friend void A::f2(BT);    // parameter type is B::BT
      friend void A::f3<AT>();  // template argument is B::AT
    };

end example]




598. Associated namespaces of overloaded functions and function templates

Section: 6.5.4  [basic.lookup.argdep]     Status: CD2     Submitter: Mike Miller     Date: 27 September 2006

[Voted into the WP at the March, 2009 meeting.]

The resolution of issue 33 added the following wording in 6.5.4 [basic.lookup.argdep]:

In addition, if the argument is the name or address of a set of overloaded functions and/or function templates, its associated classes and namespaces are the union of those associated with each of the members of the set: the namespace in which the function or function template is defined and the classes and namespaces associated with its (non-dependent) parameter types and return type.

This wording is self-contradictory: although it claims that the treatment of overload sets is intended to be “the union of those associated with each of the members of the set,” it says that the namespace of which each function or function template is a member is to be considered an associated namespace. That is different from the case of a non-overloaded function argument; in that case, because only the type of the argument is considered, the namespace of which the function is a member is not an associated namespace. This should be rectified so that overloaded and unoverloaded functions really are treated the same.

Proposed resolution (June, 2008):

Change 6.5.4 [basic.lookup.argdep] paragraph 2 as follows:

...In addition, if the argument is the name or address of a set of overloaded functions and/or function templates, its associated classes and namespaces are the union of those associated with each of the members of the set: the namespace in which the function or function template is defined and, i.e., the classes and namespaces associated with its (non-dependent) parameter types and return type.



705. Suppressing argument-dependent lookup via parentheses

Section: 6.5.4  [basic.lookup.argdep]     Status: CD2     Submitter: Mike Miller     Date: 29 July, 2008

[Voted into WP at October, 2009 meeting.]

During the discussion of issue 704, some people expressed a desire to reconsider whether parentheses around the name of the function in a function call should suppress argument-dependent lookup, on the basis that this is overly subtle and not obvious. Others pointed out that this technique is used (both intentionally and inadvertently) in existing code and changing the behavior could cause problems.

It was also observed that the normative text that specifies this behavior is itself subtle, relying an a very precise interpretation of the preposition used in 6.5.4 [basic.lookup.argdep] paragraph 1:

When an unqualified name is used as the postfix-expression in a function call...

This is taken to mean that something like (f)(x) is not subject to argument-dependent lookup because the name f is used in but not as the postfix-expression. This could be confusing, especially in light of the use of the term postfix-expression to refer to the name inside the parentheses, not to the parenthesized expression, in 12.2.2.2 [over.match.call] paragraph 1. If the decision is to preserve this effect of a parenthesized name in a function call, the wording should probably be revised to specify it more explicitly.

Notes from the September, 2008 meeting:

The CWG agreed that the suppression of argument-dependent lookup by parentheses surrounding the postfix-expression is widely known and used in the C++ community and must be preserved. The wording should be changed to make this effect clearer.

Proposed resolution (September, 2008):

Change 6.5.4 [basic.lookup.argdep] paragraph 1 as follows:

When an unqualified name is used as the postfix-expression in a function call (7.6.1.3 [expr.call]) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (6.5.3 [basic.lookup.unqual]) may be searched...

Proposed resolution (September, 2009):

Change 6.5.4 [basic.lookup.argdep] paragraph 1 as follows:

When an unqualified name is used as the postfix-expression in a function call (7.6.1.3 [expr.call]) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (6.5.3 [basic.lookup.unqual]) may be searched, and in those namespaces, namespace- scope friend function declarations (11.8.4 [class.friend]) not otherwise visible may be found. These modifications to the search depend on the types of the arguments (and for template template arguments, the namespace of the template argument). [Example:

    namespace N {
      struct S { };
      void f(S);
    }

    void g() {
      N::S s;
      f(s);      // calls N::f
      (f)(s);    // error: N::f not considered; parentheses prevent argument-dependent lookup
    }

end example]




1000. Mistaking member typedefs for constructors

Section: 6.5.5.2  [class.qual]     Status: CD2     Submitter: Jason Merrill     Date: 20 November, 2009

[Voted into WP at March, 2010 meeting.]

The recent addition to support inherited constructors changed 6.5.5.2 [class.qual] paragraph 2 to say that

if the name specified after the nested-name-specifier is the same as the identifier or the simple-template-id's template-name in the last component of the nested-name-specifier,

the qualified-id is considered to name a constructor. This causes problems for a common naming scheme used in some class libraries:

  struct A {
    typedef int type;
  };
  struct B {
    typedef A type;
  };
  B::type::type t;

This change causes this to name the A constructor instead of the A::type typedef.

Proposed resolution (February, 2010):

Change 6.5.5.2 [class.qual] paragraph 2 as follows:

In a lookup in which the constructor is an acceptable lookup result and the nested-name-specifier nominates a class C:

the name is instead considered to name the constructor of class C...




861. Unintended ambiguity in inline namespace lookup

Section: 6.5.5.3  [namespace.qual]     Status: CD2     Submitter: Michael Wong     Date: 7 April, 2009

[Voted into WP at March, 2010 meeting as part of document N3079.]

The algorithm for namespace-qualified lookup is given in 6.5.5.3 [namespace.qual] paragraph 2:

Given X::m (where X is a user-declared namespace), or given ::m (where X is the global namespace), let S be the set of all declarations of m in X and in the transitive closure of all namespaces nominated by using-directives in X and its used namespaces, except that using-directives that nominate non-inline namespaces (9.8.2 [namespace.def]) are ignored in any namespace, including X, directly containing one or more declarations of m.

Consider the following example:

    namespace A {
        inline namespace B {
            namespace C {
                int i;
            }
            using namespace C;
        }
        int i;
    }

    int j = A::i;     // ambiguous

The transitive closure includes B because it is inline, and it includes C because there is no declaration of i in B. As a result, A::i finds both the i declared in A and the one declared in C, and the lookup is ambiguous.

This result is apparently unintended.

Proposed resolution (November, 2009):

  1. Change 9.8.2 [namespace.def] paragraph 9 as follows:

  2. These properties are transitive: if a namespace N contains an inline namespace M, which in turn contains an inline namespace O, then the members of O can be used as though they were members of M or N. The transitive closure of all inline namespaces in N is the inline namespace set of N. The set of namespaces consisting of the innermost non-inline namespace enclosing an inline namespace O, together with any intervening inline namespaces, is the enclosing namespace set of O.
  3. Insert a new paragraph before 6.5.5.3 [namespace.qual] paragraph 2 and change the existing paragraph 2 as follows:

  4. For a namespace X and name m, the namespace-qualified lookup set S(X,m) is defined as follows: Let S'(X,m) be the set of all declarations of m in X and the inline namespace set of X (9.8.2 [namespace.def]). If S'(X,m) is not empty, S(X,m) is S'(X,m); otherwise, S(X,m) is the union of S(Ni,m) for all non-inline namespaces Ni nominated by using-directives in X and its inline namespace set.

    Given X::m (where X is a user-declared namespace), or given ::m (where X is the global namespace), let S be the set of all declarations of m in X and in the transitive closure of all namespaces nominated by using-directives in X and its used namespaces, except that using-directives that nominate non-inline namespaces (9.8.2 [namespace.def]) are ignored in any namespace, including X, directly containing one or more declarations of m. No namespace is searched more than once in the lookup of a name. If if S(X,m) is the empty set, the program is ill-formed. Otherwise, if S(X,m) has exactly one member, or if the context of the reference is a using-declaration (9.9 [namespace.udecl]), S(X,m) is the required set of declarations of m. Otherwise if the use of m is not one that allows a unique declaration to be chosen from S(X,m), the program is ill-formed. [Example:...




527. Problems with linkage of types

Section: 6.6  [basic.link]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 28 July 2005

[Voted into WP at October, 2009 meeting.]

The resolution of issue 389 makes code like

    static struct {
        int i;
        int j;
    } X;

ill-formed. This breaks a lot of code for no apparent reason, since the name X is not known outside the translation unit in which it appears; there is therefore no danger of collision and no need to mangle its name.

There has also been recent discussion on the email reflectors as to whether the restrictions preventing use of types without linkage as template arguments is needed or not, with the suggestion that a mechanism like that used to give members of the unnamed namespace unique names could be used for unnamed and local types. See also issue 488, which would become moot if types without linkage could be used as template parameters.

Notes from the October, 2005 meeting:

The Evolution Working Group is discussing changes that would address this issue. CWG will defer consideration until the outcome of the EWG discussions is clear.

Notes from the April, 2006 meeting:

The CWG agreed that the restriction in 6.6 [basic.link] paragraph 8 on use of a type without linkage should apply only to variables and functions with external linkage, not to variables and functions with internal linkage (i.e., the example should be accepted). This is a separate issue from the question before the EWG and should be resolved independently.

Additional note (April, 2006):

Even the restriction of the rule to functions and objects with external linkage may not be exactly what we want. Consider an example like:

    namespace {
        struct { int i; } s;
    }

The variable s has external linkage but can't be named outside its translation unit, so there's again no reason to prohibit use of a type without linkage in its declaration.

Notes from the June, 2008 meeting:

Paper N2657, adopted at the June, 2008 meeting, allows local and unnamed types to be used as template parameters. That resolution is narrowly focused, however, and does not address this issue.

Proposed resolution (June, 2009):

Change 6.6 [basic.link] paragraph 8 as follows:

...A type without linkage shall not be used as the type of a variable or function with external linkage, unless

[Drafting note: the context shown for the preceding resolution assumes that the resolution for issue 757 has been applied.]




571. References declared const

Section: 6.6  [basic.link]     Status: CD2     Submitter: Dave Abrahams     Date: 31 March 2006

[Voted into the WP at the March, 2009 meeting.]

According to 6.6 [basic.link] paragraph 3,

A name having namespace scope (6.4.6 [basic.scope.namespace]) has internal linkage if it is the name of

It is not possible to declare a reference to be const.

Proposed resolution (March, 2008):

Change 6.6 [basic.link] paragraph 3 as indicated (note addition of punctuation in the first bullet):

A name having namespace scope (6.4.6 [basic.scope.namespace]) has internal linkage if it is the name of




757. Types without linkage in declarations

Section: 6.6  [basic.link]     Status: CD2     Submitter: John Spicer     Date: 23 December, 2008

[Voted into WP at July, 2009 meeting.]

Paper N2657, adopted at the June, 2008 meeting, removed the prohibition of local and unnamed types as template arguments. As part of the change, 6.6 [basic.link] paragraph 8 was modified to read,

A type without linkage shall not be used as the type of a variable or function with linkage, unless

Because a type without linkage can only be named as a dependent type, there are still some potentially useful things that cannot be done:

    template <class T> struct A {
      friend void g(A, T);  // this can't be defined later
      void h(T);  // this cannot be explicitly specialized
    };

    template <class T> void f(T) {
      A<T> at;
      g(at, (T)0);
    }

    enum { e };

    void g(A<decltype(e)>, decltype(e)){}  // not allowed

    int main() {
      f(e);
    }

These deficiencies could be addressed by allowing types without linkage to be used as the type of a variable or function, but with the requirement that any such entity that is used must also be defined in the same translation unit. This would allow issuing a compile-time, instead of a link-time, diagnostic if the definition were not provided, for example. It also seems to be easier to implement than the current rules.

Proposed resolution (March, 2009):

Change 6.6 [basic.link] paragraph 8 as follows:

...A type without linkage shall not be used as the type of a variable or function with linkage, unless

[Note: in other words, a type without linkage contains a class or enumeration that cannot be named outside its translation unit. An entity with external linkage declared using such a type could not correspond to any other entity in another translation unit of the program and thus is not permitted must be defined in the translation unit if it is used. Also note that classes with linkage may contain members whose types do not have linkage, and that typedef names are ignored in the determination of whether a type has linkage. —end note] [Example:

    void f() {
      struct A { int x; };    // no linkage
      extern A a;             // ill-formed
      typedef A B;
      extern B b;             // ill-formed
    }

end example]

[Example:

    template <class T> struct A {
      // in A<X>, the following is allowed because the type with no linkage
      // X is named using template parameter T.
      friend void f(A, T){}
    };

    template <class T> void g(T t) {
      A<T> at;
      f(at, t);
    }

    int main() {
      class X {} x;
      g(x);
    }


    template <typename T> struct B {
        void g(T){}
        void h(T);
        friend void i(B, T){}
    };

    void f() {
        struct A { int x; };  // no linkage
        A a = {1};
        B<A> ba;              // declares B<A>::g(A) and B<A>::h(A)
        ba.g(a);              // OK
        ba.h(a);              // error: B<A>::h(A) not defined in the translation unit
        i(ba, a);             // OK
    }

end example]

[Drafting note: issue 527 also changes part of the same text.]




966. Nested types without linkage

Section: 6.6  [basic.link]     Status: CD2     Submitter: Jason Merrill     Date: 15 September, 2009

[Voted into WP at March, 2010 meeting.]

The recent changes to allow use of unnamed types as template arguments require some rethinking of how unnamed types are treated in general. At least, a class-scope unnamed type should have the same linkage as its containing class. For example:

    // File "hdr.h"
    struct S {
      static enum { No, Yes } locked;
    };
    template<class T> void f(T);

    // File "impl1.c"
    #include "hdr.h"
    template void f(decltype(S::locked));

    // File "impl2.c"
    #include "hdr.h"
    template void f(decltype(S::locked));

The two explicit instantiation directives should refer to the same specialization.

Proposed resolution (February, 2010):

Change 6.6 [basic.link] paragraph 8 as follows:

Names not covered by these rules have no linkage. Moreover, except as noted, a name declared in a local scope (6.4.3 [basic.scope.block]) has no linkage. A type is said to have linkage if and only if:




793. Use of class members during destruction

Section: 6.7.3  [basic.life]     Status: CD2     Submitter: US     Date: 3 March, 2009

N2800 comment US 26

[Voted into WP at March, 2010 meeting.]

Read literally, 6.7.3 [basic.life] paragraphs 1 and 5 would make any access to non-static members of a class from the class's destructor undefined behavior. This is clearly not the intent.

Proposed resolution (October, 2009):

Change 6.7.3 [basic.life] paragraphs 5-6 as follows:

...any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. Such For an object under construction or destruction, see 11.9.5 [class.cdtor]. Otherwise, such a pointer refers to allocated storage...

...any lvalue which refers to the original object may be used but only in limited ways. Such For an object under construction or destruction, see 11.9.5 [class.cdtor]. Otherwise, such an lvalue refers to allocated storage...




650. Order of destruction for temporaries bound to the returned value of a function

Section: 6.7.7  [class.temporary]     Status: CD2     Submitter: Mike Miller     Date: 14 Aug 2007

[Voted into the WP at the March, 2009 meeting.]

In describing the order of destruction of temporaries, 6.7.7 [class.temporary] paragraphs 4-5 say,

There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression...

The second context is when a reference is bound to a temporary... A temporary bound to the returned value in a function return statement (8.7.4 [stmt.return]) persists until the function exits.

The following example illustrates the issues here:

    struct S {
        ~S();
    };

    S& f() {
        S s;            // #1
        return
            (S(),       // #2
             S());      // #3
    }

If the return type of f() were simply S instead of S&, the two temporaries would be destroyed at the end of the full-expression in the return statement in reverse order of their construction, followed by the destruction of the variable s at block-exit, i.e., the order of destruction of the S objects would be #3, #2, #1.

Because the temporary #3 is bound to the returned value, however, its lifetime is extended beyond the end of the full-expression, so that S object #2 is destroyed before #3.

There are two problems here. First, it is not clear what “until the function exits” means. Does it mean that the temporary is destroyed as part of the normal block-exit destructions, as described in 8.7 [stmt.jump] paragraph 2:

On exit from a scope (however accomplished), destructors (11.4.7 [class.dtor]) are called for all constructed objects with automatic storage duration (6.7.5.4 [basic.stc.auto]) (named objects or temporaries) that are declared in that scope, in the reverse order of their declaration.

Or is the point of destruction for #3 after the destruction of the “constructed objects... that are declared [emphasis mine] in that scope” (because temporary #3 was not “declared”)? I.e., should #3 be destroyed before or after #1?

The other problem is that, according to the recollection of one of the participants responsible for this wording, the intent was not to extend the lifetime of #3 but simply to emphasize that its lifetime ended before the function returned, i.e., that the result of f() could not be used without causing undefined behavior. This is also consistent with the treatment of this example by many implementations; MSVC++, g++, and EDG all destroy #3 before #2.

Suggested resolution:

Change 6.7.7 [class.temporary] paragraph 5 as indicated:

A The lifetime of a temporary bound to the returned value in a function return statement (8.7.4 [stmt.return]) persists until the function exits is not extended; it is destroyed at the end of the full-expression in the return statement.

Proposed resolution (June, 2008):

Change 6.7.7 [class.temporary] paragraph 5 as follows (converting the running text into a bulleted list and making the indicated edits to the wording):

... The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except: as specified below.

The destruction of a temporary whose lifetime is not extended...




883. std::memcpy vs std::memmove

Section: 6.8  [basic.types]     Status: CD2     Submitter: Lawrence Crowl     Date: 29 April, 2009

[Voted into WP at October, 2009 meeting.]

The std::memcpy library function is singled out for special treatment in 6.8 [basic.types] paragraph 3:

For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the value of obj1 is copied into obj2, using the std::memcpy library function, obj2 shall subsequently hold the same value as obj1.

This specification should not be restricted to std::memcpy but should apply to any bytewise copying, including std::memmove (as is done in the footnote in the preceding paragraph, for example).

Proposed resolution (July, 2009):

Change 6.8 [basic.types] paragraph 3 as follows:

For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the value of underlying bytes (6.7.1 [intro.memory]) making up obj1 is are copied into obj2, using the std::memcpy library function [Footnote: By using, for example, the library functions (16.4.2.3 [headers]) std::memcpy or std::memmove. —end footnote], obj2 shall subsequently hold the same value as obj1. [Example:...



612. Requirements on a conforming implementation

Section: 6.9.1  [intro.execution]     Status: CD2     Submitter: Clark Nelson     Date: 23 January 2007

[Voted into WP at October, 2009 meeting.]

The execution requirements on a conforming implementation are described twice in the Standard, once in 6.9.1 [intro.execution] paragraphs 5-6 and again in paragraph 11. These descriptions differ in at least a couple of important ways:

The most significant discrepancy has to do with the way output is described. In paragraph 11, the least requirements are described in terms of data written at program termination, clearly allowing arbitrary buffering, whereas in paragraph 6, the observable behavior is described in terms of calls to I/O functions. For example, there are compilers which transform a call to printf with a single argument into a call to fputs. That's valid under paragraph 11, but not under paragraph 6.

Also, in paragraph 6, volatile accesses and I/O operations are included in a single sequence, suggesting that they are equally constrained by sequencing requirements, whereas in paragraph 11, they are clearly not.

There are also editorial discrepancies that should be cleaned up.

Proposed resolution (September, 2009):

The resolution of issue 785 also resolves this issue.




785. “Execution sequence” is inappropriate phraseology

Section: 6.9.1  [intro.execution]     Status: CD2     Submitter: US/UK     Date: 3 March, 2009

N2800 comment US 16
N2800 comment UK 8
N2800 comment UK 7

[Voted into WP at October, 2009 meeting.]

In the presence of threads, it is no longer appropriate to characterize the abstract machine as having an “execution sequence.”

Proposed resolution (September, 2009):

  1. Change 6.9.1 [intro.execution] paragraph 3 as follows:

  2. ...An instance of the abstract machine can thus have more than one possible execution sequence for a given program and a given input.
  3. Change 6.9.1 [intro.execution] paragraph 5 as follows:

  4. A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible execution sequences executions of the corresponding instance of the abstract machine with the same program and the same input. However, if any such execution sequence contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).
  5. Delete 6.9.1 [intro.execution] paragraph 6, including the footnote:

  6. The observable behavior of the abstract machine is its sequence of reads and writes to volatile data and calls to library I/O functions. [Footnote: An implementation can offer additional library I/O functions as an extension. Implementations that do so should treat calls to those functions as “observable behavior” as well. —end footnote]
  7. Change 6.9.1 [intro.execution] paragraph 9 as follows:

  8. The least requirements on a conforming implementation are:

    These collectively are referred to as the observable behavior of the program. [Note: more stringent correspondences between abstract and actual semantics may be defined by each implementation. —end note]

(Note; this resolution also resolves issue 612.)




726. Atomic and non-atomic objects in the memory model

Section: 6.9.2  [intro.multithread]     Status: CD2     Submitter: Clark Nelson     Date: 30 September, 2008

[Voted into WP at October, 2009 meeting.]

In general, the description of the memory model is very careful to specify when the objects under discussion are atomic or non-atomic. However, there are a few cases where it could be clearer.

Proposed resolution (March, 2009):

  1. Modify 6.9.2 [intro.multithread] paragraph 5 as follows:

  2. All modifications to a particular atomic object M occur in some particular total order, called the modification order of M. If A and B are modifications of an atomic object M and A happens before (as defined below) B, then A shall precede B in the modification order of M, which is defined below. [Note: This states that the modification orders must respect happens before. —end note] [Note: There is a separate order for each scalar atomic object. There is no requirement that these can be combined into a single total order for all objects. In general this will be impossible since different threads may observe modifications to different variables in inconsistent orders. —end note]
  3. Modify 6.9.2 [intro.multithread] paragraph 7 as follows:

  4. Certain library calls synchronize with other library calls performed by another thread. In particular, an atomic operation A that performs a release operation on an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M and reads a value written by any side effect in the release sequence headed by A...
  5. Modify 6.9.2 [intro.multithread] paragraph 12 as follows:

  6. A visible side effect A on an a scalar object or bit-field M with respect to a value computation B of M satisfies the conditions:

    The value of a non-atomic scalar object or bit-field M, as determined by evaluation B, shall be the value stored by the visible side effect A. [Note: If there is ambiguity about which side effect to a non-atomic object or bit-field is visible, then there is a data race, and the behavior is undefined. —end note] ...




740. Incorrect note on data races

Section: 6.9.2  [intro.multithread]     Status: CD2     Submitter: Wolf Lammen     Date: 3 November, 2008

[Voted into WP at March, 2010 meeting.]

6.9.2 [intro.multithread] paragraph 12 says,

A visible side effect A on an object M with respect to a value computation B of M satisfies the conditions:

The value of a non-atomic scalar object M, as determined by evaluation B, shall be the value stored by the visible side effect A. [Note: If there is ambiguity about which side effect to a non-atomic object is visible, then there is a data race, and the behavior is undefined. —end note]

The note here suggests that, except in the case of a data race, visible side effects to value computation can always be determined. But unsequenced and indeterminately sequenced side effects on the same object create ambiguities with respect to a later value computation as well. So the wording needs to be revisited, see the following examples.

    int main(){
      int i = 0;
      i = // unsequenced side effect A
      i++; // unsequenced side effect B
      return i; // value computation C
    }

According to the definition in the draft, both A and B are visible side effects to C. However, there is no data race, because (paragraph 14) a race involves at least two threads. So the note in paragraph 12 is logically false.

The model introduces the special case of indeterminately sequenced side effects, that leave open what execution order is taken in a concrete situation. If the execution paths access the same data, unpredictable results are possible, just as it is the case with data races. Whereas data races constitute undefined behavior, indeterminatedly sequenced side effects on the same object do not. As a consequence of this disparity, indeterminately sequenced execution occasionally needs exceptional treatment.

    int i = 0;
    int f(){
      return
      i = 1; // side effect A
    }
    int g(){
      return
      i = 2; // side effect B
    }
    int h(int, int){
      return i; // value computation C
    }
    int main(){
      return h(f(),g()); // function call D returns 1 or 2?
    }

Here, either A or B is the visible side effect on the value computation C, but you cannot tell which (cf. 6.9.1 [intro.execution] paragraph 16) . Although an ambiguity is present, it is neither because of a data race, nor is the behavior undefined, in total contradiction to the note.

Proposed resolution (October, 2009):

Change 6.9.2 [intro.multithread] paragraph 12 as follows:

...The value of a non-atomic scalar object or bit-field M, as determined by evaluation B, shall be the value stored by the visible side effect A. [Note: If there is ambiguity about which side effect to a non-atomic object or bit-field is visible, then there is a data race, and the behavior is either unspecified or undefined. —end note]...



786. Definition of “thread”

Section: 6.9.2  [intro.multithread]     Status: CD2     Submitter: US     Date: 3 March, 2009

N2800 comment US 17

[Voted into WP at October, 2009 meeting.]

The term “thread” is introduced but not defined in 6.9.2 [intro.multithread] paragraph 1. A definition is needed.

Proposed resolution (September, 2009):

Chamge 6.9.2 [intro.multithread] paragraph 1 as follows:

A thread of execution (a.k.a. thread) is a single flow of control within a program, including the initial invocation of a specific top-level function, and recursively including every function invocation subsequently executed by the thread. [Note: When one thread creates another, the initial call to the top-level function of the new thread is executed by the new thread, not by the creating thread. —end note] Every thread in a program can potentially access every object and function in the program. [Footnote: An object with automatic or thread storage duration (6.7.5 [basic.stc]) is associated with one specific thread, and can be accessed by a different thread only indirectly through a pointer or reference (6.8.4 [basic.compound]). —end footnote] Under a hosted implementation, a C++ program can have more than one thread of execution (a.k.a. thread) thread running concurrently...



792. Effects of std::quick_exit

Section: 6.9.3.1  [basic.start.main]     Status: CD2     Submitter: US     Date: 3 March, 2009

N2800 comment US 24

[Voted into WP at October, 2009 meeting.]

6.9.3.1 [basic.start.main] paragraph 4 discusses the effects of calling std::exit but says nothing about std::quick_exit.

Proposed resolution (July, 2009):

Change 6.9.3.1 [basic.start.main] paragraph 4 as follows:

Calling the function std::exit(int) declared in <cstdlib> (17.5 [support.start.term]) terminates Terminating the program without leaving the current block (e.g., by calling the function std::exit(int) (17.5 [support.start.term])) and hence without destroying does not destroy any objects with automatic storage duration (11.4.7 [class.dtor])...


882. Defining main as deleted

Section: 6.9.3.1  [basic.start.main]     Status: CD2     Submitter: Steve Adamczyk     Date: 27 April, 2009

It should be stated in 6.9.3.1 [basic.start.main] that it a program that defines main as deleted is ill-formed.

Proposed resolution (July, 2009):

Change 6.9.3.1 [basic.start.main] paragraph 3 as follows:

...A program that declares main to be inline, static, or constexpr, or that defines main as deleted, is ill-formed...



776. Delegating constructors, destructors, and std::exit

Section: 6.9.3.3  [basic.start.dynamic]     Status: CD2     Submitter: Michael Wong     Date: 12 February, 2009

[Voted into WP at October, 2009 meeting.]

According to 6.9.3.3 [basic.start.dynamic] paragraph 1,

Destructors (11.4.7 [class.dtor]) for initialized objects with static storage duration are called as a result of returning from main and as a result of calling std::exit (17.5 [support.start.term]).

It is unclear, in the presence of delegating constructors, exactly what an “initialized object” is. 6.7.3 [basic.life] paragraph 1 says that the lifetime of an object does not begin until it is completely initialized, i.e., when its principal constructor finishes execution. 14.3 [except.ctor] paragraph 2 says that an exception during the construction of class object only invokes destructors for fully-constructed base and member sub-objects (those for which the principal constructor has completed). On the other hand, the destructor for a complete class object is called if its non-delegating constructor has completed, even if the principal constructor has not yet finished. Which of these models is appropriate for the behavior of std::exit?

Notes from the March, 2009 meeting:

The CWG agreed that the destructor for a complete object should be called by std::exit if its non-delegating constructor has finished, just as for an exception.

Notes from the July, 2009 meeting:

The CWG decided that the direction adopted at the March, 2009 meeting was incorrect. Instead, the model should be the way completely-constructed base and member subobjects are handled: their destructors are called when an exception is thrown but not when std::exit is called.

Proposed resolution (July, 2009):

Change 6.9.3.3 [basic.start.dynamic] paragraph 1 as follows:

Destructors (11.4.7 [class.dtor]) for initialized objects (that is, objects whose lifetime (6.7.3 [basic.life]) has begun) with static storage duration are called as a result of returning from main and as a result of calling std::exit (17.5 [support.start.term]). Destructors for initialized objects with thread storage duration...



946. Order of destruction of local static objects and calls to std::atexit

Section: 6.9.3.3  [basic.start.dynamic]     Status: CD2     Submitter: Fraser Ross     Date: 28 July, 2009

[Voted into WP at March, 2010 meeting.]

17.5 [support.start.term] paragraph 7 says that the order of destruction of objects with static storage duration and calls to functions registered by calling std::atexit is given in 6.9.3.3 [basic.start.dynamic]. Paragraph 1 of 6.9.3.3 [basic.start.dynamic] says,

If the completion of the constructor or dynamic initialization of an object with static storage duration is sequenced before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first.

This wording covers both local and namespace-scope objects, so it fixes the relative ordering of local object destructors with respect to those of namespace scope. Paragraph 3 says,

If the completion of the initialization of a non-local object with static storage duration is sequenced before a call to std::atexit (see <cstdlib>, 17.5 [support.start.term]), the call to the function passed to std::atexit is sequenced before the call to the destructor for the object. If a call to std::atexit is sequenced before the completion of the initialization of a non-local object with static storage duration, the call to the destructor for the object is sequenced before the call to the function passed to std::atexit.

This fixes the relative ordering of destructors for namespace scope objects with respect to calls of atexit functions. However, the relative ordering of local destructors and atexit functions is left unspecified.

In the 2003 Standard, this was clear: 18.3 paragraph 8 said,

A local static object obj3 is destroyed at the same time it would be if a function calling the obj3 destructor were registered with atexit at the completion of the obj3 constructor.

Proposed resolution (October, 2009):

Change 6.9.3.3 [basic.start.dynamic] paragraph 3 as follows:

If the completion of the initialization of a non-local an object with static storage duration is sequenced before a call to std::atexit (see <cstdlib>, 17.5 [support.start.term]), the call to the function passed to std::atexit is sequenced before the call to the destructor for the object. If a call to std::atexit is sequenced before the completion of the initialization of a non-local an object with static storage duration, the call to the destructor for the object is sequenced before the call to the function passed to std::atexit...



438. Possible flaw in wording for multiple accesses to object between sequence points

Section: Clause 7  [expr]     Status: CD2     Submitter: Jason Merrill     Date: 29 Oct 2003

Lisa Lippincott mentioned this case to me:

  A[0] = 0;
  A[A[0]] = 1;

This seems to use the old value of A[0] other than to calculate the new value, which is said to be undefined, but it also seems reasonable, since the old value is used in order to select the object to modify, so there's no ordering ambiguity.

Steve Adamczyk: the ordering rule referred to is in Clause 7 [expr] paragraph 4.

Notes from the March 2004 meeting:

Clark Nelson mentions that the C committee may have done something on this.

Note (July, 2009):

This issue was resolved by the adoption of the “sequenced before” wording.




695. Compile-time calculation errors in constexpr functions

Section: Clause 7  [expr]     Status: CD2     Submitter: Mike Miller     Date: 9 June, 2008

[Voted into WP at October, 2009 meeting.]

Evaluating an expression like 1/0 is intended to produce undefined behavior during the execution of a program but be ill-formed at compile time. The wording for this is in Clause 7 [expr] paragraph 4:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression appears where an integral constant expression is required (7.7 [expr.const]), in which case the program is ill-formed.

The formulation “appears where an integral constant expression is required” is intended as an acceptable Standardese circumlocution for “evaluated at compile time,” a concept that is not directly defined by the Standard. It is not clear that this formulation adequately covers constexpr functions.

Notes from the September, 2008 meeting:

The CWG felt that the concept of “compile-time evaluation” needs to be defined for use in discussing constexpr functions. There is a tension between wanting to diagnose errors at compile time versus not diagnosing errors that will not actually occur at runtime. In this context, a constexpr function might never be invoked, either in a constant expression context or at runtime, although the requirement that the expression in a constexpr function be a potential constant expression could be interpreted as triggering the provisions of Clause 7 [expr] paragraph 4.

There are also contexts in which it is not known in advance whether an expression must be constant or not, notably in the initializer of a const integer variable, where the nature of the initializer determines whether the variable can be used in constant expressions or not. In such a case, it is not clear whether an erroneous expression should be considered ill-formed or simply non-constant (and thus subject to runtime undefined behavior, if it is ever evaluated). The consensus of the CWG was that an expression like 1/0 should simply be considered non-constant; any diagnostic would result from the use of the expression in a context requiring a constant expression.

Proposed resolution (July, 2009):

This issue is resolved by the resolution of issue 699.




835. Scoped enumerations and the “usual arithmetic conversions”

Section: Clause 7  [expr]     Status: CD2     Submitter: Beman Dawes     Date: 5 March, 2009

[Voted into WP at October, 2009 meeting.]

A number of the operators described in Clause 7 [expr] take operands of enumeration type, relying on the “usual arithmetic conversions” (Clause 7 [expr] paragraph 10) to convert them to an appropriate integral type. The assumption behind this pattern is invalid when one or more of the operands has a scoped enumeration type.

Each operator that accepts operands of enumeration type should be evaluated as to whether the operation makes sense for scoped enumerations (for example, it is probably a good idea to allow comparison of operands having the same scoped enumeration type and conditional expressions in which the second and third operands have the same scoped enumeration type) and, if so, create a special case. The usual arithmetic conversions should not be invoked for scoped enumeration types.

(See also issue 880.)

Proposed resolution (July, 2009):

  1. Change Clause 7 [expr] paragraph 10 as follows:

  2. ...This pattern is called the usual arithmetic conversions, which are defined as follows:

  3. Change 7.6.1.2 [expr.sub] paragraph 1 as follows:

  4. ...One of the expressions shall have the type “pointer to T” and the other shall have unscoped enumeration or integral type...
  5. Change 7.6.2 [expr.unary] paragraphs 7-8 and 10 as follows:

  6. The operand of the unary + operator shall have arithmetic, unscoped enumeration, or pointer type...

    The operand of the unary - operator shall have arithmetic or unscoped enumeration type...

    The operand of ~ shall have integral or unscoped enumeration type...

  7. Change 7.6.2.8 [expr.new] paragraph 6 as follows:

  8. ...The expression in a noptr-new-declarator shall be of integral type, unscoped enumeration type, or a class type for which a single non-explicit conversion function to integral or unscoped enumeration type exists (11.4.8 [class.conv]). If the expression...
  9. Change 7.6.5 [expr.mul] paragraph 2 as follows:

  10. The operands of * and / shall have arithmetic or unscoped enumeration type; the operands of % shall have integral or unscoped enumeration type....
  11. Change 7.6.6 [expr.add] paragraph 1-2 as follows:

  12. ...For addition, either both operands shall have arithmetic or unscoped enumeration type, or one operand shall be a pointer to a completely-defined effective object type and the other shall have integral or unscoped enumeration type.

    For subtraction, one of the following shall hold:

  13. Change 7.6.7 [expr.shift] paragraph 1 as follows:

  14. ...The operands shall be of integral or unscoped enumeration type...
  15. Change 7.6.9 [expr.rel] paragraph 4 as follows:

  16. If both operands (after conversions) are of arithmetic or enumeration type, each of the operators shall yield true if the specified relationship is true and false if it is false.
  17. Change 7.6.11 [expr.bit.and] paragraph 1 as follows:

  18. ...The operator applies only to integral or unscoped enumeration operands.
  19. Change 7.6.12 [expr.xor] paragraph 1 as follows:

  20. ...The operator applies only to integral or unscoped enumeration operands.
  21. Change 7.6.13 [expr.or] paragraph 1 as follows:

  22. ...The operator applies only to integral or unscoped enumeration operands.



858. Example binding an rvalue reference to an lvalue

Section: Clause 7  [expr]     Status: CD2     Submitter: Daniel Krügler     Date: 6 April, 2009

[Voted into WP at March, 2010 meeting as part of document N3055.]

The adoption of paper N2844 made it ill-formed to attempt to bind an rvalue reference to an lvalue, but the example in Clause 7 [expr] paragraph 6 was overlooked in making this change:

    struct A { };
    A&& operator+(A, A);
    A&& f();

    A a;
    A&& ar = a;

The last line should be changed to use something like static_cast<A&&>(a).

(See also issue 847.)

Proposed resolution (July, 2009):

Change the example in Clause 7 [expr] paragraph 6 as follows:

[Example:

  struct A { };
  A&& operator+(A, A);
  A&& f();

  A a;
  A&& ar = static_cast<A&&>(a);

The expressions f() and a + a are rvalues of type A. The expression ar is an lvalue of type A. —end example]




846. Rvalue references to functions

Section: 7.2.1  [basic.lval]     Status: CD2     Submitter: Daniel Krügler     Date: 23 March, 2009

[Voted into WP at March, 2010 meeting as document N3055.]

The status of rvalue references to functions is not clear in the current wording. For example, 7.2.1 [basic.lval] paragraph 2 says,

An lvalue refers to an object or function. Some rvalue expressions—those of (possibly cv-qualified) class or array type—also refer to objects. [Footnote: Expressions such as invocations of constructors and of functions that return a class type refer to objects, and the implementation can invoke a member function upon such objects, but the expressions are not lvalues. —end footnote]

This would tend to indicate that there are no rvalues of function type. However, Clause 7 [expr] paragraph 6 says,

If an expression initially has the type “rvalue reference to T” (9.3.4.3 [dcl.ref], 9.4.4 [dcl.init.ref]), the type is adjusted to “T” prior to any further analysis, and the expression designates the object or function denoted by the rvalue reference. If the expression is the result of calling a function, whether implicitly or explicitly, it is an rvalue; otherwise, it is an lvalue.

This explicitly indicates that rvalue references to functions are possible and that, in some cases, they yield function-typed rvalues. Furthermore, _N2914_.20.2.4 [concept.operator] paragraph 20 describes the concept Callable as:

    auto concept Callable<typename F, typename... Args> {
      typename result_type;
      result_type operator()(F&, Args...);
      result_type operator()(F&&, Args...);
    }

It would be strange if Callable were satisfied for a function object type but not for a function type.

However, assuming that rvalue references to functions are intended to be supported, it is not clear how an rvalue of function type is supposed to behave. For instance, 7.6.1.3 [expr.call] paragraph 1 says,

For an ordinary function call, the postfix expression shall be either an lvalue that refers to a function (in which case the function-to-pointer standard conversion (7.3.4 [conv.func]) is suppressed on the postfix expression), or it shall have pointer to function type.

From this, it appears that an rvalue of function type cannot be used in a function call. It can't be converted to a pointer to function, either, as 7.3.4 [conv.func] paragraph 1 says,

An lvalue of function type T can be converted to an rvalue of type “pointer to T.” The result is a pointer to the function.

(See also issues 664 and especially 690. The approach described in the latter issue, viewing rvalue references as essentially lvalues rather than as essentially rvalues, could resolve the specification problems described above by eliminating the concept of an rvalue of function type.)

Proposed resolution (February, 2010):

See paper N3030.




693. New string types and deprecated conversion

Section: 7.3.3  [conv.array]     Status: CD2     Submitter: Alisdair Meredith     Date: 21 April, 2008

N2800 comment DE 4

[Voted into WP at October, 2009 meeting.]

The deprecated conversion from string literal to pointer to (non-const) character in 7.3.3 [conv.array] paragraph 2 has been extended to apply to char16_t and char32_t types, but not to UTF8 and raw string literals. Is this disparity intentional? Should it be extended to all new string types, reverted to just the original character types, or revoked altogether?

Additional places in the Standard that may need to change include 14.2 [except.throw] paragraph 3 and 12.2.4.3 [over.ics.rank] paragraph 3.

Additional discussion (August, 2008):

The removal of this conversion for current string literals would affect overload resolution for existing programs. For example,

    struct S {
        S(const char*);
    };
    int f(char *);
    int f(X);
    int i = f("hello");

If the conversion were removed, the result would be a quiet change in behavior. Another alternative to consider would be a required diagnostic (without making the program ill-formed).

Notes from the September, 2008 meeting:

The CWG agreed that the deprecated conversion should continue to apply to the literals to which it applied in C++ 2003. Consensus was not reached regarding whether it should apply only to those literals or to all the new literals as well, although it was agreed that the current situation in which it applies to some, but not all, of the new literals is unacceptable.

Notes from the July, 2009 meeting:

The CWG reached consensus that the deprecated conversion should be removed altogether.

Proposed resolution (September, 2009):

  1. Remove 7.3.3 [conv.array] paragraph 2:

  2. A string literal (5.13.5 [lex.string]) with no prefix, with a u prefix, with a U prefix, or with an L prefix can be converted to an rvalue of type “pointer to char”, “pointer to char16_t”, “pointer to char32_t”, or “pointer to wchar_t”, respectively. In any case, the result is a pointer to the first element of the array. This conversion is considered only when there is an explicit appropriate pointer target type, and not when there is a general need to convert from an lvalue to an rvalue. [Note: this conversion is deprecated. See Annex Clause Annex D [depr]. —end note] For the purpose of ranking in overload resolution (12.2.4.2.2 [over.ics.scs]), this conversion is considered an array-to-pointer conversion followed by a qualification conversion (7.3.6 [conv.qual]). [Example: "abc" is converted to “pointer to const char” as an array-to-pointer conversion, and then to “pointer to char” as a qualification conversion. —end example]
  3. Delete the indicated text from the third sub-bullet of the first bullet of paragraph 3 of 12.2.4.3 [over.ics.rank]:

  4. Delete the note from 14.2 [except.throw] paragraph 3 as follows:

  5. A throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T”, respectively. [Note: the temporary object created for a throw-expression that is a string literal is never of type char*, char16_t*, char32_t*, or wchar_t*; that is, the special conversions for string literals from the types “array of const char”, “array of const char16_t”, “array of const char32_t”, and “array of const wchar_t” to the types “pointer to char”, “pointer to char16_t”, “pointer to char32_t”, and “pointer to wchar_t”, respectively (7.3.3 [conv.array]), are never applied to a throw-expression. —end note] The temporary is an lvalue...
  6. Change the discussion of 5.13.5 [lex.string] in C.7.2 [diff.lex] as follows:

  7. Change: String literals made const
    The type of a string literal is changed... “array of const wchar_t.”

        char* p = "abc";   // valid in C, invalid in C++
    
    

    ...

    Difficulty of converting: Simple syntactic transformation, because string literals can be converted to char*; (7.3.3 [conv.array]). The most common cases are handled by a new but deprecated standard conversion Syntactic transformation. The fix is to add a cast:

      char* p = "abc";                // valid in C, deprecated in C++
      char* q = expr ? "abc" : "de";  // valid in C, invalid in C++
      void f(char*) {
          char* p = (char*)"abc";  // cast added
          f(p);
          f((char*)"def");         // cast added
       }
    
  8. Delete _N3000_.D.4 [depr.string]:

  9. D.4 Implicit conversion from const strings [depr.string]

    The implicit conversion from const to non-const qualification for string literals (7.3.3 [conv.array]) is deprecated.




685. Integral promotion of enumeration ignores fixed underlying type

Section: 7.3.7  [conv.prom]     Status: CD2     Submitter: Alberto Ganesh Barbati     Date: 6 January, 2008

[Voted into WP at July, 2009 meeting.]

According to 7.3.7 [conv.prom] paragraph 2,

An rvalue of an unscoped enumeration type (9.7.1 [dcl.enum]) can be converted to an rvalue of the first of the following types that can represent all the values of the enumeration (i.e. the values in the range bmin to bmax as described in 9.7.1 [dcl.enum]): int, unsigned int, long int, unsigned long int, long long int, or unsigned long long int.

This wording may have surprising behavior in this case:

    enum E: long { e };

    void f(int);
    void f(long);

    void g() {
        f(e);    // Which f is called?
    }

Intuitively, as the programmer has explicitly expressed preference for long as the underlying type, he/she might expect f(long) to be called. However, if long and int happen to have the same size, then e is promoted to int (as it is the first type in the list that can represent all values of E) and f(int) is called instead.

According to 9.7.1 [dcl.enum] the underlying type of an enumeration is always well-defined for both the fixed and the non-fixed cases, so it makes sense simply to promote to the underlying type unless such a type would itself require promotion.

Suggested resolution:

In 7.3.7 [conv.prom] paragraph 2, replace all the text from “An rvalue of an unscoped enumeration type” through the end of the paragraph with the following:

An rvalue of an unscoped enumeration type (9.7.1 [dcl.enum]) is converted to an rvalue of its underlying type if it is different from char16_t, char32_t, wchar_t, or has integer conversion rank greater than or equal to int. Otherwise, it is converted to an rvalue of the first of the following types that can represent all the values of its underlying type: int, unsigned int, long int, unsigned long int, long long int, or unsigned long long int.

(Note that this wording no longer needs to mention extended integer types as special cases.)

Proposed resolution (August, 2008):

Move the following text from 7.3.7 [conv.prom] paragraph 2 into a separate paragraph, making the indicated changes, and add the following new paragraph after it:

An rvalue of an unscoped enumeration type whose underlying type is not fixed (9.7.1 [dcl.enum]) can be converted to an rvalue of the first of the following types that can represent all the values of the enumeration (i.e. the values in the range bmin to bmax as described in 9.7.1 [dcl.enum]): int, unsigned int, long int, unsigned long int, long long int, or unsigned long long int. If none of the types in that list can represent all the values of the enumeration, an rvalue of an unscoped enumeration type can be converted to an rvalue of the extended integer type with lowest integer conversion rank (7.3.15 [conv.bool]) greater than the rank of long long in which all the values of the enumeration can be represented. If there are two such extended types, the signed one is chosen.

An rvalue of an unscoped enumeration type whose underlying type is fixed (9.7.1 [dcl.enum]) can be converted to an rvalue of its underlying type. Moreover, if integral promotion can be applied to its underlying type, an rvalue of an unscoped enumeration type whose underlying type is fixed can also be converted to an rvalue of the promoted underlying type.




707. Undefined behavior in integral-to-floating conversions

Section: 7.3.11  [conv.fpint]     Status: CD2     Submitter: Alberto Ganesh Barbati     Date: 2 Aug, 2008

[Voted into WP at July, 2009 meeting.]

The current wording of 7.3.11 [conv.fpint] paragraph 2 does not specify what should happen when converting an integer value that is outside the representable range of the target floating point type. The C99 Standard covers this case explicitly in 6.3.1.4 paragraph 2:

When a value of integer type is converted to a real floating type, if the value being converted can be represented exactly in the new type, it is unchanged. If the value being converted is in the range of values that can be represented but cannot be represented exactly, the result is either the nearest higher or nearest lower representable value, chosen in an implementation-defined manner. If the value being converted is outside the range of values that can be represented, the behavior is undefined.

While the current C++ specification requires defined behavior in all cases, the C specification allows for use of NaNs and traps, if those are needed for efficiency.

Notes from the September, 2008 meeting:

The CWG agreed that the C approach should be adopted.

Proposed resolution (March, 2009):

Change 7.3.11 [conv.fpint] paragraph 2 as indicated:

An rvalue of an integer type or of an unscoped enumeration type can be converted to an rvalue of a floating point type. The result is exact if possible. Otherwise If the value being converted is in the range of values that can be represented but cannot be represented exactly, it is an implementation-defined choice of either the next lower or higher representable value. [Note: loss of precision occurs if the integral value cannot be represented exactly as a value of the floating type. —end note] If the value being converted is outside the range of values that can be represented, the behavior is undefined. If the source type is bool, the value false is converted to zero and the value true is converted to one.



720. Need examples of lambda-expressions

Section: 7.5.5  [expr.prim.lambda]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 20 September, 2008

N2800 comment DE 9

[Voted into the WP at the July, 2009 meeting as part of N2927.]

There is not a single example of a lambda-expression in their specification. The Standard would be clearer if a few judiciously-chosen examples were added.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




752. Name lookup in nested lambda-expressions

Section: 7.5.5  [expr.prim.lambda]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 10 December, 2008

N2800 comment US 31

[Voted into the WP at the July, 2009 meeting as part of N2927.]

How does name binding work in nested lambda-expressions? For example,

    void f1() {
      float v;
      []() { return [v]() { return v; } }
    }

    void f2() {
      float v;
      [v]() { return [v]() { return v; } }
    }

According to 7.5.5 [expr.prim.lambda] paragraph 3,

A name in the lambda-capture shall be in scope in the context of the lambda expression, and shall be this or shall refer to a local variable or reference with automatic storage duration.

One possible interpretation is that the lambda expression in f1 is ill-formed because v is used in the compound-statement of the outer lambda expression but does not appear in its effective capture set. However, the appearance of v in the inner lambda-capture is not a “use” in the sense of 6.3 [basic.def.odr] paragraph 2, because a lambda-capture is not an expression, and it's not clear whether the reference in the inner lambda expression's return expression should be considered a use of the automatic variable or of the member of the inner lambda expression's closure object.

Similarly, the lambda expression in f2 could be deemed to be ill-formed because the reference to v in the inner lambda expression's lambda-capture would refer to the field of the outer lambda-expression's closure object, not to a local automatic variable; however, it's not clear whether the inner lambda expression should be evaluated in situ or as part of the generated operator() member of the outer lambda expression's closure object.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




754. Lambda expressions in default arguments of block-scope function declarations

Section: 7.5.5  [expr.prim.lambda]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 10 December, 2008

[Voted into the WP at the July, 2009 meeting as part of N2927.]

Is a lambda expression permitted in a default argument expression for a block-scope function declaration? For example,

    void g() {
      void f(std::reference_closure<void()> rc = []() {});
      f();
    }

This was not discussed in either the Evolution Working Group nor in the Core Working Group, and it is possible that some of the same implementation difficulties that led to prohibiting use of automatic variables in such default argument expressions (9.3.4.7 [dcl.fct.default] paragraph 7) might also apply to closure objects, even though they are not automatic variables.

(See also issue 772.)

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




762. Name lookup in the compound-statement of a lambda expression

Section: 7.5.5  [expr.prim.lambda]     Status: CD2     Submitter: John Spicer     Date: 5 February, 2009

N2800 comment US 29
N2800 comment US 30

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The current wording of 7.5.5 [expr.prim.lambda] is not clear as to how name lookup is to be performed for names appearing in the compound-statement of a lambda expression. Consider, for example:

    int fac(int n) {
      return [=]{ return n <= 1 ? 1 : n*operator()(n-1); }();
    }

There is no operator() in scope in the context of the lambda expression. Consequently, according to bullet 5 of paragraph 10, the reference to operator() is not transformed to the class member access syntax but appears untransformed in the closure object's function call operator, where presumably it is interpreted as a recursive call to itself.

A similar question (although it does not involve name lookup per se) arises with respect to use of this in the compound-statement of a lambda expression that does not appear in the body of a non-static member function; for example,

    void f() {
      float v;
      [v]() { return v+this->v; }
    }

this cannot refer to anything except the closure object, so are the two references to v equivalent?

The crux of this question is whether the lookups for names in the compound-statement are done in the context of the lambda expression or from the call operator of the closure object. The note at the end of bullet 10.5 would tend to support the latter interpretation:

[Note: Reference to captured variables or references within the compound-statement refer to the data members of F. —end note]

Another possible interpretation of the current wording is that there are two distinct compound-statements in view: the compound-statement that is part of the lambda-expression and the body of the closure object's function call operator that is “obtained from” the former. If this is the intended interpretation, one way of addressing the issues regarding the operator() example above would be to state that it is an error if the lookup of a name in the compound-statement fails, making the example ill-formed.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




766. Where may lambda expressions appear?

Section: 7.5.5  [expr.prim.lambda]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 6 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

According to 7.5.5 [expr.prim.lambda] paragraph 7, the appearance of a lambda expression results in the definition of a class “considered to be defined at the point where the lambda expression occurs.” It is not clear whether that means that a lambda expression cannot appear at any point where it is not permitted to define a class type. For example, 9.3.4.6 [dcl.fct] paragraph 10 says, “Types shall not be defined in return or parameter types.” Does that mean that a function declaration like

    void f(int a[sizeof ([]{ return 0; })]);

is ill-formed, because the parameter type defines the closure class for the lambda expression? (Issue 686 lists many contexts in which type definitions are prohibited. Each of these should be examined to see whether a lambda expression should be allowed or prohibited there.)

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




767. void and other unnamed lambda-parameters

Section: 7.5.5  [expr.prim.lambda]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 5 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The grammar in 7.5.5 [expr.prim.lambda] for lambda-parameter specifies that a declarator must be present, i.e., that all lambda-parameters must be named. This also has the effect of prohibiting a lambda like [](void){}. It is not clear that there is a good reason for these restrictions; programmers could reasonably expect that lambda-parameters were like ordinary function parameters in these regards.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




768. Ellipsis in a lambda parameter list

Section: 7.5.5  [expr.prim.lambda]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 5 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The grammar in 7.5.5 [expr.prim.lambda] for lambda-parameter-declaration does not allow for an ellipsis. Is this a desirable restriction?

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




769. Initialization of closure objects

Section: 7.5.5  [expr.prim.lambda]     Status: CD2     Submitter: Mike Miller     Date: 9 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

7.5.5 [expr.prim.lambda] paragraph 13 says simply,

The closure object is initialized by direct-initializing each member N of F with the local variable or reference named N; the member t is initialized with this.

The mechanism for this initialization is not specified. In particular, does the closure class have a default constructor that performs this initialization?

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




782. Lambda expressions and argument-dependent lookup

Section: 7.5.5  [expr.prim.lambda]     Status: CD2     Submitter: Mike Miller     Date: 1 March, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

Functions and function objects behave differently with respect to argument-dependent lookup. In particular, the associated namespaces of a function's parameters and return types, but not the namespace in which the function is declared, are associated namespaces of the function; the exact opposite is true of a function object. The Committee should consider rectifying that disparity; however, in the absence of such action, an explicit decision should be made as to whether lambdas are more function-like or object-like with respect to argument-dependent lookup. For example:

    namespace M {
      struct S { };
    }
    namespace N {
      void func(M::S);
      struct {
        void operator()(M::S);
      } fn_obj;
      const auto& lambda = [](M::S){};
    }
    void g() {
      f(N::func);    // assoc NS == M, not N
      f(N::fn_obj);  // assoc NS == N, not M
      f(N::lambda);  // assoc NS == ??
    }

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




796. Lifetime of a closure object with members captured by reference

Section: 7.5.5  [expr.prim.lambda]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 40

7.5.5 [expr.prim.lambda] paragraph 13 ties the effective lifetime of a closure object with members captured by reference to the innermost block scope in which the lambda appears, rather than to the lifetime of the objects to which the references are bound. This seems too restrictive.

Notes from the March, 2009 meeting:

Making the suggested change would be problematic for an implementation in which the “reference members” were actually implemented using offsets from a captured stack pointer and in which nested blocks were pushed onto the stack (to optimize space for large local objects, for example).

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




750. Implementation constraints on reference-only closure objects

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 10 December, 2008

N2800 comment US 73

[Voted into the WP at the July, 2009 meeting as part of N2927.]

Consider an example like:

    void f(vector<double> vec) {
      double x, y, z;
      fancy_algorithm(vec, [&]() { /* use x, y, and z in various ways */ });
    }

7.5.5 [expr.prim.lambda] paragraph 8 requires that the closure class for this lambda will have three reference members, and paragraph 12 requires that it be derived from std::reference_closure, implying two additional pointer members. Although 9.3.4.3 [dcl.ref] paragraph 4 allows a reference to be implemented without allocation of storage, current ABIs require that references be implemented as pointers. The practical effect of these requirements is that the closure object for this lambda expression will contain five pointers. If not for these requirements, however, it would be possible to implement the closure object as a single pointer to the stack frame, generating data accesses in the function-call operator as offsets relative to the frame pointer. The current specification is too tightly constrained.

Lawrence Crowl:

The original intent was that the reference members could be omitted from the closure object by an implementation. The problem we had was that we want the call to f in

    extern f(std::reference_closure<void()>);
    extern f(std::function<void()>);
    f([&](){});

to unambiguously bind to the reference_closure; using reference_closure can be an order of magnitude faster than using function.

(See also issue 751.)

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927. (See also document PL22.16/09-0035 = WG21 N2845, which partially addressed this issue by the removal of std::reference_clossure.)




751. Deriving from closure classes

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 11 December, 2008

[Voted into the WP at the July, 2009 meeting as part of N2927.]

During the discussion of issue 750, it was suggested that an implementation might be permitted to omit fields in the closure object of a lambda expression if the implementation does not need them to address the corresponding automatic variables. If permitted, this implementation choice might be visible to the program via inheritance. Consider:

    void f() {
      int const N = 10;
      typedef decltype([&N](){}) F;
      struct X: F {
        void n() { float z[N]; } // Error?
      };
    }

If it is implementation-defined or unspecified whether the reference member F::N will exist, then it is unknown whether the the reference to N in X::n() will be an error (because lookup finds F::N, which is private) or well-formed (because there is no F::N, so the reference is to the local automatic variable).

If implementations can omit fields, the implementation dependency might be addressed by either treating the lookup “as if” the fields existed, even if they are not present in the object layout, or by defining the names of the fields in the closure class to be unique identifiers, similar to the names of unnamed namespaces (9.8.2.2 [namespace.unnamed]).

Another suggestion was made that derivation from a closure class should be prohibited, at least for now. However, it was pointed out that inheritance is frequently used to give stateless function objects some state, suggesting a use case along the lines of:

    template<class T> struct SomeState: T {
      // ...
    };
    template<class F, typename T< void algo(T functor, ...) {
      SomeState<T< state(functor);
      ...
    }

    ... algo([](int a){ return 2*a; }) ...

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




756. Dropping cv-qualification on members of closure objects

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 15 December, 2008

[Voted into the WP at the July, 2009 meeting as part of N2927.]

Consider the following example:

    void f() {
      int const N = 10;
      [=]() mutable { N = 30; }  // Okay: this->N has type int, not int const.
      N = 20;  // Error.
    }

That is, the N that is a member of the closure object is not const, even though the captured variable is const. This seems strange, as capturing is basically a means of capturing the local environment in a way that avoids lifetime issues. More seriously, the change of type means that the results of decltype, overload resolution, and template argument deduction applied to a captured variable inside a lambda expression can be different from those in the scope containing the lambda expression, which could be a subtle source of bugs.

On the other hand, the copying involved in capturing has uses beyond avoiding lifetime issues (taking snapshots of values, avoiding data races, etc.), and the value of a cv-qualified object is not cv-qualified.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




759. Destruction of closure objects

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 22 January, 2009

N2800 comment UK 39

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The specification of closure objects is missing a couple of important points regarding their destruction. First, although 7.5.5 [expr.prim.lambda] paragraph 11 mentions other implicitly-declared special member functions, it is silent on the destructor, leading to questions about whether the closure class has one or not.

Second, nothing is said about the timing of the destruction of a closure object: is it normally destroyed at the end of the full-expression to which the lambda expression belongs, and is its lifetime extended if the closure object is bound to a reference? These questions would be addressed if paragraph 2 defined the closure object as a temporary instead of just as an rvalue. (It should be noted that 7.6.1.4 [expr.type.conv] also does not define the conceptually-similar T() as a temporary.)

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927. (The question regarding the failure of 7.6.1.4 [expr.type.conv] failing to categorize T() as a temporary was split off into a separate issue; see issue 943.)




761. Inferred return type of closure object call operator

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 5 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

According to 7.5.5 [expr.prim.lambda] paragraph 10, the following lambda expressions are ill-formed because the return types of the generated operator() functions are an array type and a function type, respectively:

    void f() {
      []{ return ""; };
      []{ return f; };
    }

It would seem reasonable to expect the array-to-pointer and function-to-pointer decay to apply to these return values and hence to the inferred return type of operator().

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




763. Is a closure object's operator() inline?

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 6 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

A lambda expression appearing in local scope presumably creates a local class (in the sense of 11.6 [class.local]) as the type of the closure object, because that class is “considered to be defined at the point where the lambda expression occurs” (7.5.5 [expr.prim.lambda] paragraph 7), and in the absence of any indication to the contrary that class must satisfy the restrictions of 11.6 [class.local] on local classes. One such restriction is that all its member functions must be defined within the class definition, making them inline. However, nothing is said about whether the function call operator for a non-local closure class is inline, and even for the local case it would be better if the specification were explicit.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




771. Move-construction of reference members of closure objects

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD2     Submitter: Jonathan Caves     Date: 9 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

According to 7.5.5 [expr.prim.lambda] paragraph 11, the closure class “has a public move constructor that performs a member-wise move.” Although the terms “move constructor” and “member-wise move” are not currently defined (see issue 680), this presumably means that a lambda like [&i]{} results in a closure class similar to:

    class F {
        int& i;
    public:
        F(&& other):
            i(std::move(other.i)) { }
        // etc.
    };

This constructor is ill-formed because it attempts to initialize an lvalue reference to non-const int with the rvalue returned by std::move.

It is not clear whether this should be handled by:

  1. Not generating the move constructor.

  2. Generating the declaration of the move constructor but only defining it (and giving the corresponding error) if the move constructor would be used, similar to the handling of other implicitly-defined special member functions.

  3. Generating the move constructor but copy-constructing any reference members.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




774. Can a closure class be a POD?

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD2     Submitter: John Spicer     Date: 11 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The current wording does not state under what conditions, if ever, a closure class is a POD. It should either be explicitly unspecified or definitively stated that a closure class is never a POD, to allow implementations freedom to determine the contents of closure classes.

Notes from the March, 2009 meeting:

A closure class is neither standard-layout nor trivial.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




779. Rvalue reference members of closure objects?

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD2     Submitter: Mike Miller     Date: 26 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

7.5.5 [expr.prim.lambda] paragraph 8, bullet 2, says of members of a closure class,

if the element is of the form & N, the data member has the name N and type “reference to object type of N

Is an implementation free to use an rvalue reference as the type of this member, as only a “reference” is specified? (See issue 771; the move constructor would be well-formed if the reference member were an rvalue reference.)

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




797. Converting a no-capture lambda to a function type

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 42

[Voted into WP at March, 2010 meeting as document N3052.]

A lambda with an empty capture list has identical semantics to a regular function type. By requiring this mapping we get an efficient lambda type with a known API that is also compatible with existing operating system and C library functions.

Notes from the July, 2009 meeting:

This functionality is part of the “unified function syntax” proposal and will be considered in that context.




955. Can a closure type's operator() be virtual?

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD2     Submitter: Daniel Krügler     Date: 19 August, 2009

[Voted into WP at March, 2010 meeting.]

The specification of the members of a closure type does not rule out the possibility that its operator() might be virtual. It would be better to make it clear that it cannot.

Proposed resolution (October, 2009):

Change 7.5.5 [expr.prim.lambda] paragraph 5 as follows:

... followed by mutable. It is not neither virtual nor declared volatile. Default arguments...



753. Array names in lambda capture sets

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 10 December, 2008

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The current specification does not adequately describe what happens when an array name is part of the effective capture set of a lambda expression. 7.5.5 [expr.prim.lambda] paragraph 13 says that the array member of the closure object is direct-initialized by the local array; however, 9.4 [dcl.init] paragraph 16 says that such an initialization is ill-formed. There are several possibilities for handling this problem:

  1. This results in an array member of the closure object, which is initialized by copying each element, along the lines of 11.4.5.3 [class.copy.ctor] paragraph 8.

  2. This results in a pointer member of the closure object, initialized to point to the first element of the array (i.e., the array lvalue decays to a pointer rvalue).

  3. This is ill-formed.

  4. This results in a reference-to-array member of the closure object, initialized to refer to the array, regardless of whether & was used or not.

  5. This is ill-formed unless the capture is “by reference.”

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




764. Capturing unused variables in a lambda expression

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: CD2     Submitter: Steve Adamczyk     Date: 6 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

7.5.5 [expr.prim.lambda] paragraph 5 says,

The compound-statement of a lambda expression shall use (6.3 [basic.def.odr]) an automatic variable or reference from the context where the lambda expression appears only if the name of the variable or reference is a member of the effective capture set...

The reference to 6.3 [basic.def.odr] makes clear that the technical meaning of “use” is in view here, and that the names of variables can appear without being captured if they are constants used as values or if they are unevaluated operands.

There appears to be a disconnect with the preceding paragraph, however, in the description of which variables are implicitly captured by a capture-default:

for each name v that appears in the lambda expression and denotes a local variable or reference with automatic storage duration in the context where the lambda expression appears and that does not appear in the capture-list or as a parameter name in the lambda-parameter-declaration-list...

It would be more consistent if only variables that were required by paragraph 5 to be captured were implicitly captured, i.e., if “that appears in the lambda expression” were replaced by “that is used (6.3 [basic.def.odr]) in the compound-statement of the lambda expression.” For example,

    struct A {
      A();
      A(const A&);
      ~A();
    };
    void f() {
      A a;
      int i = [=]() { return sizeof a; }();
    }

Here, a will be captured (and copied), even though it is not “used” in the lambda expression.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




772. capture-default in lambdas in local default arguments

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: CD2     Submitter: Steve Adamczyk     Date: 10 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

Assuming that it is permitted to use a lambda as a default argument in a block-scope function declaration (see issue 754), it is presumably ill-formed for such a lambda expression to refer to a local automatic variable (9.3.4.7 [dcl.fct.default] paragraph 7). What does this mean for capture-defaults? For example,

    void f() {
      int i = 1;
      void f(int = ([i]() { return i; })());  // Definitely an error
      void g(int = ([i]() { return 0; })());  // Probably an error
      void h(int = ([=]() { return i; })());  // Definitely an error
      void o(int = ([=]() { return 0; })());  // Okay?
      void p(int = ([]() { return sizeof i; })());  // Presumably okay
    }

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




775. Capturing references to functions

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: CD2     Submitter: Steve Adamczyk     Date: 12 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

According to 7.5.5 [expr.prim.lambda] paragraph 8, the “object type” of a captured function is the type to which the reference refers. That's clearly wrong when the captured reference is a reference to a function, because the resulting data member of the closure class will have a function type:

    void f() { }
    void g() {
      void (&fr)() = f;
      [fr]{};   // Oops...
    }

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




904. Parameter packs in lambda-captures

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: CD2     Submitter: Faisal Vali     Date: 23 May, 2009

[Voted into WP at March, 2010 meeting.]

The following is not allowed by the current syntax of lambda-capture but would be useful:

    template <typename ...Args> void f(Args... args) {
      auto l = [&, args...] { return g(args...); };
    }

Proposed resolution (October, 2009):

  1. Change the grammar in 7.5.5 [expr.prim.lambda] paragraph 1 as follows:

  2. Add a new paragraph at the end of 7.5.5 [expr.prim.lambda]:

  3. A capture followed by an ellipsis is a pack expansion (13.7.4 [temp.variadic]). [Example:

        template<typename ...Args>
        void f(Args... args) {
          auto l = [&, args...] { return g(args...); };
          l();
        }
    
    

    end example]

  4. Add a new bullet to the list in 13.7.4 [temp.variadic] paragraph 4:

  5. [Editorial note: the editor may wish to consider sorting the bullets in this list in order of section reference.]




863. Rvalue reference cast to incomplete type

Section: 7.6.1  [expr.post]     Status: CD2     Submitter: Steve Adamczyk     Date: 7 April, 2009

[Voted into WP at March, 2010 meeting as document N3055.]

A cast to an rvalue reference type produces an rvalue, and rvalues must have complete types (7.2.1 [basic.lval] paragraph 9). However, none of the sections dealing with cast operators in 7.6.1 [expr.post] require that the referred-to type must be complete in an rvalue reference cast.

(Note that the approach described for issue 690, in which an rvalue reference type would be essentially an lvalue instead of an rvalue, would address this issue as well, since lvalues can have incomplete types.)

Proposed resolution (February, 2010):

See paper N3030.




722. Can nullptr be passed to an ellipsis?

Section: 7.6.1.3  [expr.call]     Status: CD2     Submitter: Alisdair Meredith     Date: 25 September, 2008

[Voted into WP at March, 2010 meeting.]

The current wording of 7.6.1.3 [expr.call] paragraph 7 is:

After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or effective class type, the program is ill-formed.

It's not clear whether this is intended to exclude anything other than void, but the effect is to disallow passing nullptr to ellipsis. That seems unnecessary.

Notes from the October, 2009 meeting:

The CWG agreed that this should be supported and the effect should be like passing (void*)nullptr.

Proposed resolution (February, 2010):

Change 7.6.1.3 [expr.call] paragraph 7 as follows:

When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg (17.13 [support.runtime]). [Note: This paragraph does not apply to arguments passed to a function parameter pack. Function parameter packs are expanded during template instantiation (13.7.4 [temp.variadic]), thus each such argument has a corresponding parameter when a function template specialization is actually called. —end note] The lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are performed on the argument expression. An argument that has (possibly cv-qualified) type std::nullptr_t is converted to type void* (7.3.12 [conv.ptr]). After these conversions...



731. Omitted reference qualification of member function type

Section: 7.6.1.5  [expr.ref]     Status: CD2     Submitter: Daniel Krügler     Date: 6 October, 2008

N2800 comment DE 5
N2800 comment DE 10
N2800 comment DE 12

[Voted into WP at October, 2009 meeting.]

There are several places in the Standard that were overlooked when reference qualification of member functions was added. For example, 7.6.1.5 [expr.ref] paragraph 4, bullet 3, sub-bullet 2 says,

...if E1.E2 refers to a non-static member function, and the type of E2 is “function of parameter-type-list cv returning T”, then...

This wording incorrectly excludes member functions declared with a ref-qualifier.

Another place that should consider reference qualification is 7.6.4 [expr.mptr.oper]; it should not be possible to invoke an &-qualified member function with an rvalue object expression.

A third place is 9.9 [namespace.udecl] paragraph 15, which does not mention reference qualification in connection with the hiding/overriding of member functions brought in from a base class via a using-declaration.

Proposed resolution (September, 2009):

  1. Change 7.6.1.5 [expr.ref] paragraph 4, bullet 3, sub-bullet 2 as follows:

  2. Change 7.6.4 [expr.mptr.oper] paragraph 6 as follows:

  3. ...—end example] In a .* expression whose object expression is an rvalue, if the second operand is a pointer to member function with ref-qualifier &, the program is ill-formed. In a ->* expression, or in a .* expression whose object expression is an lvalue, if the second operand is a pointer to member function with ref-qualifier &&, the program is ill-formed. The result of a .* expression is an lvalue only if its first operand is an lvalue and...
  4. Change 9.9 [namespace.udecl] paragraph 15 as follows:

  5. When a using-declaration brings names from a base class into a derived class scope, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list (9.3.4.6 [dcl.fct]), and cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting). [Note:...



665. Problems in the specification of dynamic_cast

Section: 7.6.1.7  [expr.dynamic.cast]     Status: CD2     Submitter: Daniel Krügler     Date: 1 December 2007

[Voted into the WP at the March, 2009 meeting.]

At least one implementation accepts the following example as well-formed (returning a null pointer at runtime), although others reject it at compile time:

    struct A { virtual ~A(); };
    struct B: private A { } b;
    A* pa = dynamic_cast<A*>(&b);

Presumably the intent of 7.6.1.7 [expr.dynamic.cast] paragraph 5 is that all up-casts (converting from derived to base) are to be handled at compile time, regardless of whether the class involved is polymorphic or not:

If T is “pointer to cv1 B” and v has type “pointer to cv2 D” such that B is a base class of D, the result is a pointer to the unique B subobject of the D object pointed to by v. Similarly, if T is “reference to cv1 B” and v has type cv2 D such that B is a base class of D, the result is the unique B subobject of the D object referred to by v... In both the pointer and reference cases, cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2, and B shall be an accessible unambiguous base class of D.

One explanation for the implementation that accepts the example at compile time is that the final sentence is interpreted as part of the condition for the applicability of this paragraph, so that this case falls through into the description of runtime checking that follows. This (mis-)interpretation is buttressed by the example in paragraph 9, which reads in significant part:

    class A { virtual void f(); };
    class B { virtual void g(); };
    class D : public virtual A, private B {};
    void g() {
        D d;
        B* bp;
        bp = dynamic_cast<B*>(&d); // fails
    }

The “fails” comment is identical to the commentary on the lines in the example where the run-time check fails. If the interpretation that paragraph 5 is supposed to apply to all up-casts, presumably this comment should change to “ill-formed,” or the line should be removed from the example altogether.

It should be noted that this interpretation (that the example is ill-formed and the runtime check applies only to down-casts and cross-casts) rejects some programs that could plausibly be accepted and actually work at runtime. For example,

    struct B { virtual ~B(); };
    struct D: private virtual B { };

    void test(D* pd) {
        B* pb = dynamic_cast<B*>(pd); // #1
    }

    struct D2: virtual B, virtual D {};

    void demo() {
        D2 d2;
        B* pb = dynamic_cast<B*>(&d2); // #2
        test(&d2); // #3
    }

According to the interpretation that paragraph 5 applies, line #1 is ill-formed. However, converting from D2 to B (line #2) is well-formed; if the alternate interpretation were applied, the conversion in line #1 could succeed when applied to d2 (line #3).

One final note: the wording in 7.6.1.7 [expr.dynamic.cast] paragraph 8 is incorrect:

The run-time check logically executes as follows:

All uses of T in this paragraph treat it as if it were a class type; in fact, T is the type to which the expression is being cast and thus is either a pointer type or a reference type, not a class type.

Proposed resolution (June, 2008):

  1. Change 7.6.1.7 [expr.dynamic.cast] paragraph 5 as follows:

  2. ...In both the pointer and reference cases, cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2, and B shall be an accessible unambiguous base class of D the program is ill-formed if cv2 is greater cv-qualification than cv1 or if B is an inaccessible or ambiguous base class of D.
  3. Change the comment in the example in 7.6.1.7 [expr.dynamic.cast] paragraph 9 as follows:

  4.     bp = dynamic_cast<B*>(&d);     // fails ill-formed (not a run-time check)
    
  5. Change 7.6.1.7 [expr.dynamic.cast] paragraph 8 as follows:

  6. The If C is the class type to which T points or refers, the run-time check logically executes as follows:




833. Explicit conversion of a scoped enumeration value to a floating type

Section: 7.6.1.9  [expr.static.cast]     Status: CD2     Submitter: John Spicer     Date: 6 March, 2009

[Voted into WP at October, 2009 meeting.]

The current wording of 7.6.1.9 [expr.static.cast] paragraph 9 does not permit conversion of a value of a scoped enumeration type to a floating point type. This was presumably an oversight during the specification of scoped enumerations and should be rectified.

Proposed resolution (July, 2009):

Change 7.6.1.9 [expr.static.cast] paragraph 9 as follows:

A value of a scoped enumeration type (9.7.1 [dcl.enum]) can be explicitly converted to an integral type. The value is unchanged if the original value can be represented by the specified type. Otherwise, the resulting value is unspecified. A value of a scoped enumeration type can also be explicitly converted to a floating point type; the result is the same as that of converting from the original value to the floating point type.



658. Defining reinterpret_cast for pointer types

Section: 7.6.1.10  [expr.reinterpret.cast]     Status: CD2     Submitter: Dave Abrahams     Date: 4 November 2007

[Voted into the WP at the March, 2009 meeting.]

For years I've noticed that people will write code like this to get the address of an object's bytes:

  void foo(long* p) {
      char* q = reinterpret_cast<char*>(p);  // #1
      // do something with the bytes of *p by using q
  }

When in fact the only portable way to do it according to the standard is:

  void foo(long* p) {
      char* q = static_cast<char*>(static_cast<void*>(p));  // #2
      // do something with the bytes of *p by using q
  }

I thought reinterpret_cast existed so that vendors could provide some weird platform-specific things. However, recently Peter Dimov pointed out to me that if we substitute a class type for long above, reinterpret_cast is required to work as expected by 11.4 [class.mem] paragraph 18:

A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa.

So there isn't a whole lot of flexibility to do something different and useful on non-class types. Are there any implementations for which #1 actually fails? If not, I think it would be a good idea to nail reinterpret_cast down so that the standard says it does what people (correctly) think it does in practice.

Proposed resolution (March, 2008):

Change 7.6.1.10 [expr.reinterpret.cast] paragraph 7 as indicated:

A pointer to an object can be explicitly converted to a pointer to an object of different type. When an rvalue v of type “pointer to T1” is converted to the type “pointer to cv T2,” the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (6.8 [basic.types]) and the alignment requirements of T2 are no stricter than those of T1. Except that cConverting an rvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, t. The result of any other such a pointer conversion is unspecified.



734. Are unique addresses required for namespace-scope variables?

Section: 7.6.1.10  [expr.reinterpret.cast]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 15 October, 2008

[Voted into WP at March, 2010 meeting.]

Consider the following example:

    static const char test1 = 'x';
    static const char test2 = 'x';
    bool f() {
        return &test1 != &test2;
    }

Is f() allowed to return false? Can a smart optimizer alias these two variables, taking advantage of the fact that they are const, initialized to the same value, and thus can never be different in a well-defined program?

The C++ Standard doesn't explicitly specify address allocation of objects except as members of arrays and classes, so the answer would appear to be that such an implementation would be conforming.

This situation appears to have been the inadvertent result of the resolution of issue 73. Prior to that change, 7.6.10 [expr.eq] said,

Two pointers of the same type compare equal if and only if they... both point to the same object...

That resolution introduced the current wording,

Two pointers of the same type compare equal if and only if... both represent the same address.

Notes from the March, 2009 meeting:

The CWG agreed that this aliasing should not be permitted in a conforming implementation.

Proposed resolution (November, 2009):

  1. Add the following as a new paragraph after 6.7.2 [intro.object] paragraph 5:

  2. Unless an object is a bit-field or a base class subobject of zero size, the address of that object is the address of the first byte it occupies. Two distinct objects that are neither bit-fields nor base class subobjects of zero size shall have distinct addresses [Footnote: Under the “as-if” rule an implementation is allowed to store two objects at the same machine address or not store an object at all if the program cannot observe the difference (6.9.1 [intro.execution]). —end footnote]. [Example:

      static const char test1 = 'x';
      static const char test2 = 'x';
      const bool b = &test1 != &test2;   // always true
    

    end example]

  3. Change 7.6.2.2 [expr.unary.op] paragraph 3 as follows:

  4. The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. In the first case, if the type of the expression is “T,” the type of the result is “pointer to T.” In particular, the address of an object of type “cv T” is “pointer to cv T,” with the same cv-qualifiers. For a qualified-id, if the member is a static member of type “T”, the type of the result is plain “pointer to T.” If the member is a non-static member of class C of type T, the type of the result is “pointer to member of class C of type T.” If the operand is a qualified-id naming a non-static member m of some class C with type T, the result has type “pointer to member of class C of type T” and is an rvalue designating C::m. Otherwise, if the type of the expression is T, the result has type “pointer to T” and is an rvalue that is the address of the designated object (6.7.1 [intro.memory]) or a pointer to the designated function. [Note: In particular, the address of an object of type “cv T” is “pointer to cv T,” with the same cv-qualification. —end note] [Example:...



799. Can reinterpret_cast be used to cast an operand to its own type?

Section: 7.6.1.10  [expr.reinterpret.cast]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 55

[Voted into WP at March, 2010 meeting.]

The note in 7.6.1.10 [expr.reinterpret.cast] paragraph 2 says,

Subject to the restrictions in this section, an expression may be cast to its own type using a reinterpret_cast operator.

However, there is nothing in the normative text that permits this conversion, and paragraph 1 forbids any conversion not explicitly permitted.

(See also issue 944.)

Proposed resolution (October, 2009):

  1. Change 7.6.1.10 [expr.reinterpret.cast] paragraph 2 as follows:

  2. The reinterpret_cast operator shall not cast away constness (7.6.1.11 [expr.const.cast]). [Note: Subject to the restrictions in this section, an expression may be cast to its own type using a reinterpret_cast operator. —end note] An expression of integral, enumeration, pointer, or pointer-to-member type can be explictly converted to its own type; such a cast yields the value of its operand.
  3. Change 7.6.1.10 [expr.reinterpret.cast] paragraph 10 as follows:

  4. An rvalue of type “pointer to member of X of type T1” can be explicitly converted to an rvalue of a different type “pointer to member of Y of type T2” if T1 and T2 are both function types or both object types...



842. Casting to rvalue reference type

Section: 7.6.1.10  [expr.reinterpret.cast]     Status: CD2     Submitter: Steve Adamczyk     Date: 20 March, 2009

[Voted into WP at October, 2009 meeting.]

Both const_cast (7.6.1.11 [expr.const.cast] paragraph 1) and reinterpret_cast (7.6.1.10 [expr.reinterpret.cast] paragraph 1) say,

If T is an lvalue reference type, the result is an lvalue; otherwise, the result is an rvalue and the lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are performed on the expression v.

This introduces a contradiction in the text. According to 7.6.1.11 [expr.const.cast] paragraph 4,

The result of a reference const_cast refers to the original object.

However, the lvalue-to-rvalue conversion applied to the operand when the target is an rvalue reference type creates a temporary if the operand has class type (7.3.2 [conv.lval] paragraph 2), meaning that the result will not refer to the original object but to the temporary.

A similar problem exists for reinterpret_cast: according to 7.6.1.10 [expr.reinterpret.cast] paragraph 11,

a reference cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with the built-in & and * operators (and similarly for reinterpret_cast<T&&>(x)). The result refers to the same object as the source lvalue, but with a different type.

Here the issue is that the unary & operator used in the description requires an lvalue, but the lvalue-to-rvalue conversion is applied to the operand when the target is an rvalue reference type.

It would seem that the lvalue-to-rvalue conversion should not be applied when the target of the cast is an rvalue reference type.

Proposed resolution (July, 2009):

  1. Change 7.6.1.10 [expr.reinterpret.cast] paragraph 1 as follows:

  2. The result of the expression reinterpret_cast<T>(v) is the result of converting the expression v to type T. If T is an lvalue reference type, the result is an lvalue; if T is an rvalue reference type, the result is an rvalue; otherwise, the result is an rvalue and the lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are performed on the the expression v. Conversions that can be performed explicitly using reinterpret_cast are listed below. No other conversion can be performed explicitly using reinterpret_cast.
  3. Change 7.6.1.11 [expr.const.cast] paragraph 1 as follows:

  4. The result of the expression const_cast<T>(v) is of type T. If T is an lvalue reference type, the result is an lvalue; if T is an rvalue reference type, the result is an rvalue; otherwise, the result is an rvalue and the lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are performed on the expression v. Conversions that can be performed explicitly using const_cast are listed below. No other conversion shall be performed explicitly using const_cast.



801. Casting away constness in a cast to rvalue reference type

Section: 7.6.1.11  [expr.const.cast]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 58

[Voted into WP at October, 2009 meeting.]

The rules in 7.6.1.11 [expr.const.cast] paragraphs 8 and following, defining “casting away constness,” do not cover a cast to an rvalue reference type.

Proposed resolution (September, 2009):

Change 7.6.1.11 [expr.const.cast] paragraph 9 as follows:

Casting from an lvalue of type T1 to an lvalue of type T2 using a an lvalue reference cast, or casting from an expression of type T1 to an rvalue of type T2 using an rvalue reference cast, casts away constness if a cast from an rvalue of type “pointer to T1” to the type “pointer to T2” casts away constness.



891. const_cast to rvalue reference from objectless rvalue

Section: 7.6.1.11  [expr.const.cast]     Status: CD2     Submitter: Steve Adamczyk     Date: 8 May, 2009

[Voted into WP at March, 2010 meeting.]

7.6.1.11 [expr.const.cast] paragraph 4 says,

...Similarly, for two effective object types T1 and T2, an expression of type T1 can be explicitly converted to an rvalue of type T2 using the cast const_cast<T2&&> if a pointer to T1 can be explicitly converted to the type “pointer to T2” using a const_cast. The result of a reference const_cast refers to the original object.

However, in some rvalue-reference const_casts there is no “original object,” e.g.,

    const_cast<int&&>(2)

Notes from the July, 2009 meeting:

The coresponding cast to an lvalue reference to const is ill-formed: in such cases, the operand must be an lvalue. The consensus of the CWG was that a cast to an rvalue reference should only accept an rvalue that is an rvalue reference (i.e., an object).

Proposed resolution (February, 2010):

Change 7.6.1.11 [expr.const.cast] paragraph 4 as follows:

An lvalue of type T1 can be explicitly converted to an lvalue of type T2 using the cast const_cast<T2&> (where T1 and T2 are object types) if a pointer to T1 can be explicitly converted to the type “pointer to T2” using a const_cast. Similarly, for two object types T1 and T2, an expression lvalue of type T1 or, if T1 is a class type, an expression of type T1, can be explicitly converted to an rvalue of type T2 using the cast const_cast<T2&&> if a pointer to T1 can be explicitly converted to the type “pointer to T2” using a const_cast. The result of a reference const_cast refers to the original object.



983. Ambiguous pointer-to-member constant

Section: 7.6.2.2  [expr.unary.op]     Status: CD2     Submitter: Daniel Krügler     Date: 19 October, 2009

[Voted into WP at March, 2010 meeting.]

The resolution of issue 39 changed the diagnosis of ambiguity because of multiple subobjects from being a lookup error to being diagnosed where the result of the lookup is used. The formation of a pointer to member is one such context but was overlooked in the changes. 7.6.2.2 [expr.unary.op] paragraph 3 should have language similar to 7.6.1.5 [expr.ref] paragraph 5 and should be mentioned in the note in 6.5.2 [class.member.lookup] paragraph 13.

Proposed resolution (October, 2009):

  1. Change 7.6.2.2 [expr.unary.op] paragraph 3 as follows:

  2. ...For a qualified-id, if the member is a static member of type “T”, the type of the result is plain “pointer to T.” If the member is a non-static member of class C of type T, the type of the result is “pointer to member of class C of type T.,and the program is ill-formed if C is an ambiguous base (6.5.2 [class.member.lookup]) of the class designated by the nested-name-specifier of the qualified-id....
  3. Change 6.5.2 [class.member.lookup] paragraph 13 as follows:

  4. [Note: Even if the result of name lookup is unambiguous, use of a name found in multiple subobjects might still be ambiguous (7.3.13 [conv.mem], 7.6.1.5 [expr.ref], 7.6.2.2 [expr.unary.op], 11.8.3 [class.access.base]). —end note] [Example:...



803. sizeof an enumeration type with a fixed underlying type

Section: 7.6.2.5  [expr.sizeof]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 70

[Voted into WP at October, 2009 meeting.]

There is no reason for the prohibition of using sizeof on “an enumeration type before all its enumerators have been declared” (7.6.2.5 [expr.sizeof] paragraph 1) if the underlying type of the enumeration is fixed.

Proposed resolution (July, 2009):

Change 7.6.2.5 [expr.sizeof] paragraph 1 as follows:

...The sizeof operator shall not be applied to an expression that has function or incomplete type, or to an enumeration type whose underlying type is not fixed before all its enumerators have been declared, or to the parenthesized name of such types, or to an lvalue that designates a bit-field...



930. alignof with incomplete array type

Section: 7.6.2.6  [expr.alignof]     Status: CD2     Submitter: Alisdair Meredith     Date: 6 July, 2009

[Voted into WP at October, 2009 meeting.]

7.6.2.6 [expr.alignof] paragraph 1 currently says regarding alignof,

The operand shall be a type-id representing a complete effective object type or a reference to a complete effective object type.

This prohibits taking the alignment of an array type with an unknown bound. There doesn't appear to be any reason for this restriction.

Proposed resolution (July, 2009):

Change 7.6.2.6 [expr.alignof] paragraph 1 as follows:

The operand shall be a type-id representing a complete effective object type or an array thereof or a reference to a complete effective object type.



672. Sequencing of initialization in new-expressions

Section: 7.6.2.8  [expr.new]     Status: CD2     Submitter: Clark Nelson     Date: 11 January, 2008

[Voted into WP at October, 2009 meeting.]

Consider the following code, which uses double-checked locking (DCL):

    Widget* Widget::Instance() {
      if (pInstance == 0) {           // 1: first check
        lock<mutex> hold(mutW);       // 2: acquire lock
        if (pInstance == 0) {         // 3: second check
          pInstance = new Widget();   // 4: create and assign
        }
      }                               // 5: release lock
    }

We want this to be fully correct when pInstance is an atomic pointer to Widget. To get that result, we have to disallow any assignment to pInstance until after the new object is fully constructed. In other words, we want this to be an invalid transformation of line 4:

    pInstance = operator new(sizeof(Widget));
    new (pInstance) Widget;

I don't think it would be surprising if this were disallowed. For example, if the constructor were to throw an exception, I think many people would expect the variable not to be modified. I think the question is whether it's sufficiently clearly disallowed.

This could be clarified by stating (somewhere appropriate — probably either in 7.6.2.8 [expr.new] paragraph 16 or paragraph 22) that the initialization of the allocated object is sequenced before the value computation of the new-expression. Then by 7.6.19 [expr.ass] paragraph 1 (“In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.”), the initialization would have to be sequenced before the assignment.

This is probably not a problem for atomic<Widget*> because its operator= is a function, and function calls provide the necessary guarantees. But for the plain pointer assignment case, there's still a question about whether the sequencing of side effects is constrained as tightly as it should be. In fact, you don't even have to throw an exception from the constructor for there to be a question.

    struct X {
        static X* p;
        X();
    };

    X* X::p = new X;

When the constructor for X is invoked by this new-expression, would it be valid for X::p to be non-null? If the answer is supposed to be “no,” then I think the Standard should express that intent more clearly.

Proposed resolution (March, 2008):

Change 7.6.2.8 [expr.new] paragraph 22 as indicated:

Whether Initialization of the allocated object is sequenced before the value computation of the new-expression. It is unspecified whether the allocation function is called before evaluating the constructor arguments or after evaluating the constructor arguments but before entering the constructor is unspecified. It is also unspecified whether the arguments to a constructor are evaluated if the allocation function returns the null pointer or exits using an exception.

[Drafting note: The editor may wish to move paragraph 22 up to immediately follow paragraph 16 or 17. The paragraphs numbered 18-21 deal with the case where deallocation is done because initialization terminates with an exception, whereas paragraph 22 applies more to the initialization itself, described in paragraph 16.]

Notes from the September, 2008 meeting:

The proposed wording does not (but should) allow the call to the allocation function to occur in the middle of evaluating arguments for the constructor call.

Proposed resolution (July, 2009):

Change 7.6.2.8 [expr.new] paragraph 21 as follows:

Whether the allocation function is called before evaluating the constructor arguments or after evaluating the constructor arguments but before entering the constructor is unspecified. The invocation of the allocation function is indeterminately sequenced with respect to the evaluations of expressions in the new-initializer. Initialization of the allocated object is sequenced before the value computation of the new-expression. It is also unspecified whether the arguments to a constructor expressions in the new-initializer are evaluated if the allocation function returns the null pointer or exits using an exception.

[Drafting note: the editor may wish to consider moving this paragraph to follow paragraph 15 or 16. Paragraphs 17-19 deal with the case where deallocation is done because initialization terminates with an exception, whereas this paragraph applies more to the initialization itself (described in paragraph 15).]




804. Deducing the type in new auto(x)

Section: 7.6.2.8  [expr.new]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 71

[Voted into WP at October, 2009 meeting.]

The type of an allocated object wih the type specifier auto is determined by the rules of copy initialization, but the initialization applied will be direct initialization. This would affect classes which declare their copy constructor explicit, for instance. For consistency, use the same form of initiailization for the deduction as the new expression.

Proposed resolution (July, 2009):

Change 7.6.2.8 [expr.new] paragraph 2 as follows:

If the auto type-specifier appears in the type-specifier-seq of a new-type-id or type-id of a new-expression, the new-expression shall contain a new-initializer of the form

The allocated type is deduced from the new-initializer as follows: Let (e) be e be the assignment-expression in the new-initializer and T be the new-type-id or type-id of the new-expression, then the allocated type is the type deduced for the variable x in the invented declaration (9.2.9.7 [dcl.spec.auto]):

[Example:...




805. Which exception to throw for overflow in array size calculation

Section: 7.6.2.8  [expr.new]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 72
N2800 comment UK 192

[Voted into WP at July, 2009 meeting as part of N2932.]

Throwing std::length_error (7.6.2.8 [expr.new] paragraph 7) for an attempt to allocate a too-large array brings in too much of the Standard library. A simpler exception, like std::bad_alloc, should be thrown instead.

Notes from the March, 2009 meeting:

The CWG was in favor of throwing an exception derived from std::bad_alloc. This would be upwardly compatible; it would be harmless for programs that currently catch std::bad_alloc, but would allow programs to treat the calculation overflow case separately if they wish.




599. Deleting a null function pointer

Section: 7.6.2.9  [expr.delete]     Status: CD2     Submitter: Martin Sebor     Date: 3 October 2006

[Voted into WP at July, 2009 meeting.]

The requirements for the operand of the delete operators are given in 7.6.2.9 [expr.delete] paragraph 2:

In either alternative, the value of the operand of delete may be a null pointer value. If it is not a null pointer value, in the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a subobject (6.7.2 [intro.object]) representing a base class of such an object (11.7 [class.derived]). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete shall be the pointer value which resulted from a previous array new-expression. If not, the behavior is undefined.

There are no restrictions on the type of a null pointer, only on a pointer that is not null. That seems wrong.

Proposed resolution (June, 2008):

Change 7.6.2.9 [expr.delete] paragraph 1 as follows:

...The operand shall have a pointer to object type, or a class type having a single non-explicit conversion function (11.4.8.3 [class.conv.fct]) to a pointer to object type...

Proposed resolution (September, 2008):

  1. Change 7.6.2.9 [expr.delete] paragraph 1 as follows:

  2. ...The operand shall have a pointer to object type, or a class type having a single non-explicit conversion function (12.3.2) to a pointer to object type. [Footnote: This implies that an object cannot be deleted using a pointer of type void* because void is not an object type. —end footnote] ...
  3. Delete the footnote at the end of 7.6.2.9 [expr.delete] paragraph 3:

  4. ...if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined. [Footnote: This implies that an object cannot be deleted using a pointer of type void* because there are no objects of type void. —end footnote]



854. Left shift and unsigned extended types

Section: 7.6.7  [expr.shift]     Status: CD2     Submitter: Daniel Krügler     Date: 5 April, 2009

[Voted into WP at October, 2009 meeting.]

According to 7.6.7 [expr.shift] paragraph 2,

The value of E1 << E2 is E1 (interpreted as a bit pattern) left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned type, the value of the result is E1 multiplied by the quantity 2 raised to the power E2, reduced modulo ULLONG_MAX+1 if E1 has type unsigned long long int, ULONG_MAX+1 if E1 has type unsigned long int, UINT_MAX+1 otherwise.

This specification does not allow for extended types with rank greater than long long; in particular, it says that the value of a shifted unsigned extended type is truncated as if it were the same width as an unsigned int.

It's unclear that the second sentence has any normative value; it might be better to relegate it to a note or omit it than to correct it.

Proposed resolution (July, 2009):

Change 7.6.7 [expr.shift] paragraphs 2-3 as follows:

The value of E1 << E2 is E1 (interpreted as a bit pattern) left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned type, the value of the result is E1 multiplied by the quantity 2 raised to the power E2 × 2E2, reduced modulo ULLONG_MAX+1 if E1 has type unsigned long long int, ULONG_MAX+1 if E1 has type unsigned long int, UINT_MAX+1 otherwise. [Note: the constants ULLONG_MAX, ULONG_MAX, and UINT_MAX are defined in the header <climits>. —end note] one more than the maximum value representable in the result type. Otherwise, if E1 has a signed type and nonnegative value, and E1 × 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 divided by the quantity 2 raised to the power E2 / 2E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.




963. Comparing nullptr with 0

Section: 7.6.9  [expr.rel]     Status: CD2     Submitter: Mike Miller     Date: 8 September, 2009

[Voted into WP at March, 2010 meeting.]

The current wording of the draft does not indicate what is supposed to happen when an rvalue of type std::nullptr_t is compared with an integral null pointer constant. (This could occur, for example, in template code like

    template<typename T> void f(T t) {
        if (t == 0) // ...
    }

in a call like f(nullptr) -- presumably the body of the template was written before nullptr became available and thus used an integral null pointer constant.) Because an integral null pointer constant can be converted to std::nullptr_t (7.3.12 [conv.ptr] paragraph 1), one might expect that 0 would be converted to std::nullptr_t and the two operands would compare equal, but 7.6.9 [expr.rel] paragraph 2 does not handle this case at all, leaving it as undefined behavior.

The current situation is more well-defined (but perhaps not better) with respect to the conditional operator. 7.6.16 [expr.cond] paragraphs 3-6 make it ill-formed to have std::nullptr_t and 0 as the second and third operands. Again, it's not too hard to imagine a legacy function template like

    template<typename T> void f(T t, bool b) {
        T t = b ? t : 0;
    }

which would be ill-formed under the current wording of 7.6.16 [expr.cond].

Either 7.6.9 [expr.rel] and 7.6.10 [expr.eq] should be changed to make this combination of operands ill-formed, or those two sections should be changed to give the comparison defined semantics and 7.6.16 [expr.cond] should be changed to make those operands well-formed.

Proposed resolution (October, 2009):

  1. Change 7.6.9 [expr.rel] paragraph 2 as follows:

  2. The usual arithmetic conversions are performed on operands of arithmetic or enumeration type. Pointer conversions (7.3.12 [conv.ptr]) and qualification conversions (7.3.6 [conv.qual]) are performed on pointer operands (or on a pointer operand and a null pointer constant, or on two null pointer constants, at least one of which is non-integral) to bring them to their composite pointer type. If one operand is a null pointer constant, the composite pointer type is std::nullptr_t if the other operand is also a null pointer constant or, if the other operand is a pointer, the type of the other operand. Otherwise...
  3. Change 7.6.16 [expr.cond] bullet 6.3 as follows:




587. Lvalue operands of a conditional expression differing only in cv-qualification

Section: 7.6.16  [expr.cond]     Status: CD2     Submitter: Howard Hinnant     Date: 20 June 2006

[Voted into WP at October, 2009 meeting.]

Consider the following example:

    template <typename T>
    const T* f(bool b) {
        static T t1 = T();
        static const T t2 = T();
        return &(b ? t1 : t2);  // error?
    }

According to 7.6.16 [expr.cond], this function is well-formed if T is a class type and ill-formed otherwise. If the second and third operands of a conditional expression are lvalues of the same class type except for cv-qualification, the result of the conditional expression is an lvalue; if they are lvalues of the same non-class type except for cv-qualification, the result is an rvalue.

This difference seems gratuitous and should be removed.

Proposed resolution (June, 2009):

Change 7.6.16 [expr.cond] paragraph 3 as follows:

Otherwise, if the second and third operand have different types, and either has (possibly cv-qualified) class type, or if both are lvalues of the same type except for cv-qualification, an attempt is made to convert each of those operands to the type of the other. The process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows:




556. Conflicting requirements for acceptable aliasing

Section: 7.6.19  [expr.ass]     Status: CD2     Submitter: Mike Miller     Date: 30 January 2006

[Voted into the WP at the March, 2009 meeting.]

There appear to be two different specifications for when aliasing is permitted. One is in 7.2.1 [basic.lval] paragraph 15:

If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined

There is also a much more restrictive specification in 7.6.19 [expr.ass] paragraph 8:

If the value being stored in an object is accessed from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is undefined.

This affects, for example, the definedness of operations on union members: when may a value be stored into one union member and accessed via another.

It should be noted that this conflict existed in C90 and is unchanged in C99 (see, for example, section 6.5 paragraph 7 and section 6.5.16.1 paragraph 3 of ISO/IEC 9899:1999, which directly parallel the sections cited above).

Notes from the October, 2006 meeting:

This issue is based on a misunderstanding of the intent of the wording in 7.6.19 [expr.ass] paragraph 8. Instead of being a general statement about aliasing, it's describing the situation in which the source of the value being assigned is storage that overlaps the storage of the target object. The proposed resolution should make that clearer rather than changing the specification.

Proposed resolution (June, 2008):

Add the following note at the end of 7.6.19 [expr.ass] paragraph 8:

If the value being stored in an object is accessed from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is undefined. [Note: This restriction applies to the relationship between the left and right sides of the assignment operation; it is not a statement about how the target of the assignment may be aliased in general. See 7.2.1 [basic.lval]. —end note]



855. Incorrect comments in braced-init-list assignment example

Section: 7.6.19  [expr.ass]     Status: CD2     Submitter: Daniel Krügler     Date: 5 April, 2009

[Voted into WP at October, 2009 meeting.]

7.6.19 [expr.ass] paragraph 9 has the following example:

    complex<double> z;
    z = { 1,2 };      // meaning z.operator=(1,2)
    z += { 1, 2 };    // meaning z.operator+=(1,2)

These comments make it look as if the assignment operator takes two arguments, which is obviously not the case. It would be better if the comments read something like

     // meaning z.operator=(complex<double>(1,2))

or even

    // meaning z.operator=({1,2}), resolves to
    // z.operator=(complex<double>(1,2)

Proposed resolution (July, 2009):

Change the example in 7.6.19 [expr.ass] paragraph 9 as follows:

[Example:

  complex<double> z;
  z = { 1,2 };        // meaning z.operator=({1,2})
  z += { 1, 2 };      // meaning z.operator+=({1,2})
  int a, b;
  a = b = { 1 };      // meaning a=b=1;
  a = { 1 } = b;      // syntax error

end example]




652. Compile-time evaluation of floating-point expressions

Section: 7.7  [expr.const]     Status: CD2     Submitter: Jens Maurer     Date: 3 October 2007

[Voted into the WP at the March, 2009 meeting.]

It was the intention of the constexpr proposal that implementations be required to evaluate floating-point expressions at compile time. This intention is not reflected in the actual wording of 7.7 [expr.const] paragraph 2, bullet 5:

This restriction has the effect of forbidding the use of floating-point expressions in integral constant expressions.

Proposed resolution (June, 2008):

Delete bullet 6 of 7.7 [expr.const] paragraph 2:

Notes from the June, 2008 meeting:

The CWG agreed with the intent of this issue, that floating-point calculations should be permitted in constant expressions, but acknowledged that this opens the possibility of differing results between compile time and run time. Such issues should be addressed non-normatively, e.g., via a “recommended practice” note like that of C99's 6.4.4.2 or in a technical report.

Proposed resolution (August, 2008):

  1. Delete bullet 6 of 7.7 [expr.const] paragraph 2:

  2. Add a new paragraph after 7.7 [expr.const] paragraph 3:

  3. [Note: Although in some contexts constant expressions must be evaluated during program translation, others may be evaluated during program execution. Since this International Standard imposes no restrictions on the accuracy of floating-point operations, it is unspecified whether the evaluation of a floating-point expression during translation yields the same result as the evaluation of the same expression (or the same operations on the same values) during program execution. [Footnote: Nonetheless, implementations are encouraged to provide consistent results, irrespective of whether the evaluation was actually performed during translation or during program execution. —end footnote] [Example:

      bool f() {
        char array[1 + int(1 + 0.2 - 0.1 - 0.1)];  // Must be evaluated during translation
        int size = 1 + int(1 + 0.2 - 0.1 - 0.1);   // May be evaluated at runtime
        return sizeof(array) == size;
      }
    

    It is unspecified whether the value of f() will be true or false. —end example] —end note]




715. Class member access constant expressions

Section: 7.7  [expr.const]     Status: CD2     Submitter: Steve Adamczyk     Date: 17 September, 2008

[Voted into WP at October, 2009 meeting.]

Bullet 12 of paragraph 2 of 7.7 [expr.const] says,

This wording needs to be clearer that the “effective literal type” provision applies only to the . form of member access and the “pointer to effective literal type” applies only to the -> form.

Proposed resolution (March, 2009):

Delete 7.7 [expr.const] bullet 2.11:




721. Where must a variable be initialized to be used in a constant expression?

Section: 7.7  [expr.const]     Status: CD2     Submitter: James Kanze     Date: 22 September, 2008

[Voted into WP at October, 2009 meeting.]

7.7 [expr.const] paragraph 2 allows an lvalue-to-rvalue conversion in a constant expression if it is applied to “an lvalue of effective integral type that refers to a non-volatile const variable or static data member initialized with constant expressions.” However, this does not require, as it presumably should, that the initialization occur in the same translation unit and precede the constant expression, nor that the static data member be initialized within the member-specification of its class.

Proposed resolution (March, 2009):

Change 7.7 [expr.const] paragraph 2, bullet 4, sub-bullet 1 as follows:

Additional note, June, 2009:

It has been suggested that the requirement that a static data member be initialized in the class definition is not actually needed but that static data members should be treated like other variable declarations -- a preceding definition with initialization should be sufficient. That is, given

    extern const int i;
    const int i = 5;
    struct S {
      static const int j;
    };
    const int S::j = 5;
    int a1[i];
    int a2[S::j];

there doesn't appear to be a good rationale for making a1 well-formed and a2 ill-formed. Some major implementations accept the declaration of a2 without error.

Proposed resolution (July, 2009):

Change 7.7 [expr.const] paragraph 2, bullet 4, sub-bullet 1 as follows:




806. Enumeration types in integral constant expressions

Section: 7.7  [expr.const]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 50

[Voted into WP at October, 2009 meeting.]

According to 7.7 [expr.const] paragraph 2, bullet 4, sub-bullet 1, a non-volatile const variable or static data member initialized with constant expressions can be used in an integral constant expression only if it is “of effective integral type.” Unscoped enumeration types should also be accepted in such contexts.

Proposed resolution (September, 2009):

Change 7.7 [expr.const] paragraph 2, bullet 4, sub-bullet 1 as indicated:




1010. Address of object with dynamic storage duration in constant expression

Section: 7.7  [expr.const]     Status: CD2     Submitter: Adamczyk     Date: 2009-12-02

[Voted into WP at March, 2010 meeting as part of document N3078.]

7.7 [expr.const] paragraph 2 prohibits the unary & operator and an array-to-pointer conversion on operands with automatic and thread storage duration, but operands with dynamic storage duration are apparently allowed. Both these operations should be allowed only on operands with static storage duration.




569. Spurious semicolons at namespace scope should be allowed

Section: 9.1  [dcl.pre]     Status: CD2     Submitter: Matt Austern     Date: 20 March 2006

[Voted into the WP at the March, 2009 meeting.]

The grammar in 9.1 [dcl.pre] paragraph 1 says that a declaration-seq is either declaration or declaration-seq declaration. Some declarations end with semicolons and others (e.g. function definitions and namespace declarations) don't. This means that users who put a semicolon after every declaration are technically writing ill-formed code. The trouble is that in this respect the standard is out of sync with reality. It's convenient to allow semicolons after every declaration, and there's no implementation difficulty in doing so. All existing compilers accept this, except in extra-pedantic mode. When all implementations disagree with the standard, it's time for the standard to change.

Suggested resolution:

In the grammar in 9.1 [dcl.pre] paragraph 11, change the second line in the definition of declaration-seq to

Proposed resolution (October, 2006):

  1. Add the indicated lines to the grammar definitions in 9.1 [dcl.pre] paragraph 1:

  2. declaration:

    ...

    static_assert-declaration:


    empty-declaration:
      ;
  3. Add the following as a new paragraph after 9.1 [dcl.pre] paragraph 4:

  4. An empty-declaration has no effect.



808. Non-type decl-specifiers versus max-munch

Section: 9.2  [dcl.spec]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 83

[Voted into WP at March, 2010 meeting.]

According to 9.2 [dcl.spec] paragraph 2,

The longest sequence of decl-specifiers that could possibly be a type name is taken as the decl-specifier-seq of a declaration.

However, there are many decl-specifiers that cannot appear in a type name that are, nonetheless, part of a declaration's decl-specifier-seq, such as typedef, friend, static, etc.

Proposed resolution (November, 2009):

Change 9.2 [dcl.spec] paragraph 2 as follows:

The longest sequence of decl-specifiers that could possibly be a type name is taken as the decl-specifier-seq of a declaration If a type-name is encountered while parsing a decl-specifier-seq, it is interpreted as part of the decl-specifier-seq if and only if there is no previous type-specifier other than a cv-qualifier in the decl-specifier-seq.. The sequence shall be self-consistent as described below. [Example:...



717. Unintentional restrictions on the use of thread_local

Section: 9.2.2  [dcl.stc]     Status: CD2     Submitter: Clark Nelson     Date: 17 September, 2008

N2800 comment US 36

[Voted into WP at October, 2009 meeting.]

The current wording unintentionally restricts the use of the thread_local specifier in two contexts: block-scope extern variable declarations and static data members. These restrictions are in conflict with 9.2.2 [dcl.stc] paragraph 1.

Proposed resolution (July, 2009):

Change 9.2.2 [dcl.stc] paragraph 4 as follows:

The thread_local specifier shall be applied only to the names of objects or references of namespace scope and, to the names of objects or references of block scope that also specify extern or static, and to the names of static data members. It specifies that the named object or reference has thread storage duration (6.7.5.3 [basic.stc.thread]).



809. Deprecation of the register keyword

Section: 9.2.2  [dcl.stc]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 86

[Voted into WP at October, 2009 meeting.]

The register keyword serves very little function, offering no more than a hint that a note says is typically ignored. It should be deprecated in this version of the standard, freeing the reserved name up for use in a future standard, much like auto has been re-used this time around for being similarly useless.

Notes from the March, 2009 meeting:

The consensus of the CWG was in favor of deprecating register.

Proposed resolution (September, 2009):

  1. Change 9.2.2 [dcl.stc] paragraph 3 as follows:

  2. A register specifier is a hint to the implementation that the object so declared will be heavily used. [Note: the hint can be ignored and in most implementations it will be ignored if the address of the object is taken. This use is deprecated (see [depr.register]).end note]
  3. Add a new section following _N3000_.D.4 [depr.string]:

  4. register keyword [depr.register]

    The use of the register keyword as a storage-class-specifier is deprecated (see 9.2.2 [dcl.stc]).




810. Block-scope thread_local variables should be implicitly static

Section: 9.2.2  [dcl.stc]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 87

[Voted into WP at March, 2010 meeting.]

According to 9.2.2 [dcl.stc] paragraph 4,

The thread_local specifier shall be applied only to the names of objects or references of namespace scope and to the names of objects or references of block scope that also specify static.

Why require two keywords, where one on its own becomes ill-formed? thread_local should imply static in this case, and the combination of keywords should be banned rather than required. This would also eliminate the one of two exceptions documented in paragraph 1.

Notes from the July, 2009 meeting:

The consensus of the CWG was that thread_local should imply static, as suggested, but that the combination should still be allowed (it is needed, for example, for thread-local static data members).

Proposed resolution (October, 2009):

Change 9.2.2 [dcl.stc] paragraph 4 as follows:

The thread_local specifier indicates that the named entity has thread storage duration (6.7.5.3 [basic.stc.thread]). It shall be applied only to the names of objects or references of namespace scope, to the names of objects or references of or block scope that also specify extern or static, and to the names of static data members. It specifies that the named object or reference has thread storage duration (6.7.5.3 [basic.stc.thread]). When thread_local is applied to a variable of block scope the storage-class-specifier static is implied if it does not appear explicitly.



940. Global anonymous unions

Section: 9.2.2  [dcl.stc]     Status: CD2     Submitter: UK     Date: 14 July, 2009

N2800 comment UK 85

[Voted into WP at October, 2009 meeting.]

9.2.2 [dcl.stc] paragraph 1 refers to “global anonymous unions.” This reference should include anonymous unions declared in a named namespace, not just in global scope (cf 11.5 [class.union] paragraph 3).

Proposed resolution (September, 2009):

Change 9.2.2 [dcl.stc] paragraph 1 as follows:

If a storage-class-specifier appears in a decl-specifier-seq, there can be no typedef specifier in the same decl-specifier-seq and the init-declarator-list of the declaration shall not be empty (except for global an anonymous unions declared in a named namespace or in the global namespace, which shall be declared static (9.5))...



765. Local types in inline functions with external linkage

Section: 9.2.3  [dcl.fct.spec]     Status: CD2     Submitter: Mike Miller     Date: 6 February, 2009

[Voted into WP at March, 2010 meeting.]

9.2.3 [dcl.fct.spec] paragraph 4 specifies that local static variables and string literals appearing in the body of an inline function with external linkage must be the same entities in every translation unit in the program. Nothing is said, however, about whether local types are likewise required to be the same.

Although a conforming program could always have determined this by use of typeid, recent changes to C++ (allowing local types as template type arguments, lambda expression closure classes) make this question more pressing.

Notes from the July, 2009 meeting:

The types are intended to be the same.

Proposed resolution (November, 2009):

Change 9.2.3 [dcl.fct.spec] paragraph 4 as follows:

...A static local variable in an extern inline function always refers to the same object. A string literal in the body of an extern inline function is the same object in different translation units. [Note: A string literal appearing in a default argument expression is not in the body of an inline function merely because the expression is used in a function call from that inline function. —end note] A type defined within the body of an extern inline function is the same type in every translation unit.



576. Typedefs in function definitions

Section: 9.2.4  [dcl.typedef]     Status: CD2     Submitter: Jon Caves     Date: 21 April 2006

[Voted into the WP at the March, 2009 meeting.]

9.2.4 [dcl.typedef] paragraph 1 says,

The typedef specifier shall not be used in a function-definition (9.5 [dcl.fct.def])...

Does this mean that the following is ill-formed?

    void f() {
        typedef int INT;
    }

Proposed resolution (March, 2008):

Change 9.2.4 [dcl.typedef] paragraph 1 as follows:

...The typedef specifier shall not be used in a function-definition (9.5 [dcl.fct.def]), and it shall not be combined in a decl-specifier-seq with any other kind of specifier except a type-specifier, and it shall not be used in the declaration of a function parameter nor in the decl-specifier-seq of a function-definition (9.5 [dcl.fct.def])...

Proposed resolution (September, 2008):

Change 9.2.4 [dcl.typedef] paragraph 1 as follows:

...The typedef specifier shall not be used in a function-definition (9.5 [dcl.fct.def]), and it shall not be combined in a decl-specifier-seq with any other kind of specifier except a type-specifier, and it shall be used neither in the decl-specifier-seq of a parameter-declaration (9.3.4.6 [dcl.fct]) nor in the decl-specifier-seq of a function-definition (9.5 [dcl.fct.def]).



699. Must constexpr member functions be defined in the class member-specification?

Section: 9.2.6  [dcl.constexpr]     Status: CD2     Submitter: Mike Miller     Date: 26 June, 2008

N2800 comment UK 49
N2800 comment JP 12
N2800 comment DE 23

[Voted into WP at October, 2009 meeting.]

According to 9.2.6 [dcl.constexpr] paragraph 1,

The constexpr specifier shall be applied only to the definition of an object, function, or function template, or to the declaration of a static data member of a literal type (6.8 [basic.types]).

As a result, a constexpr member function cannot be simply declared in the class member-specification and defined later; it must be defined in its initial declaration.

This restriction was not part of the initial proposal but was added during the CWG review. However, the original intent is still visible in some of the wording in 9.2.6 [dcl.constexpr]. For example, paragraph 2 refers to applying the constexpr specifier to the “declaration” and not the “definition” of a function or constructor. Although that is formally correct, as definitions are also declarations, it could be confusing. Also, the example in paragraph 6 reads,

    class debug_flag {
    public:
      explicit debug_flag(bool);
      constexpr bool is_on();    // error: debug_flag not literal type
      ...

when the proximate error is that is_on is only declared, not defined. There are also many occurrences of the constexpr specifier in the library clauses where the member function is only declared, not defined.

It's not clear how much simplification is gained by this restriction. There are reasons for defining ordinary inline functions outside the class member-specification (reducing the size and complexity of the class definition, separating interface from implementation, making the editing task easier if program evolution results in an inline function being made non-inline, etc.) that would presumably apply to constexpr member functions as well. It seems feasible to allow separate declaration and definition of a constexpr function; it would simply not be permitted to use it in a constant expression before the definition is seen (although it could presumably still be used in non-constant expressions in that region, like an ordinary inline function).

If the prohibition were relaxed to allow separate declaration and definition of constexpr member functions, some questions would need to be answered, such as whether the constexpr specifier must appear on both declaration and definition (the inline specifier need not). If it can be omitted in one or the other, there's a usability issue regarding the fact that constexpr implies const; the const qualifier would need to be specified explicitly in the declaration in which constexpr was omitted.

If the current restriction is kept, the library clauses should state in an introduction that a non-defining declaration of a constexpr member function should be considered “for exposition only” and not literal code.

Notes from the September, 2008 meeting:

In addition to the original issues described above, the question has arisen whether recursive constexpr functions are or should be permitted. Although they were originally desired by the proposers of the feature, they were prohibited out of an abundance of caution. However, the wording that specified the prohibition was changed during the review process, inadvertently permitting them.

The CWG felt that there are sufficient use cases for recursion that it should not be forbidden (although a new minimum for recursion depth should be added to Annex Clause Annex B [implimits]). If mutual recursion is to be allowed, forward declaration of constexpr functions must also be permitted (answering the original question in this issue). A call to an undefined constexpr function in the body of a constexpr function should be diagnosed when the outer constexpr function is invoked in a context requiring a constant expression; in all other contexts, a call to an undefined constexpr function should be treated as a normal runtime function call, just as if it had been invoked with non-constant arguments.

Proposed resolution (July, 2009):

  1. Change Clause 7 [expr] paragraph 4 as follows:

  2. If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression appears where an integral constant expression is required (7.7 [expr.const]), in which case the program is ill-formed. [Note: most existing implementations of C++ ignore integer overflows. Treatment of division by zero, forming a remainder using a zero divisor, and all floating point exceptions vary among machines, and is usually adjustable by a library function. —end note]
  3. Add the indicated text to 7.7 [expr.const] paragraph 2:

  4. Change 9.2.6 [dcl.constexpr] paragraph 1 as follows:

  5. The constexpr specifier shall be applied only to the definition of an object, the declaration of a function, or function template, or to the declaration of a static data member of an effective literal type (6.8 [basic.types]). If any declaration of a function or function template has the constexpr specifier, then all its declarations shall contain the constexpr specifier. [Note: An explicit specialization can differ from the template declaration with respect to the constexpr specifier. —end note] [Note: function parameters cannot be declared constexpr. —end note] [Example:

      constexpr int square(int x);       //OK, declaration
      constexpr int square(int x) {      // OK
        return x * x;
      }
      constexpr int bufsz = 1024;        // OK, definition
      constexpr struct pixel {           // error: pixel is a type
        int x;
        int y;
        constexpr pixel(int);            // OK, declaration
      };
      constexpr pixel::pixel(int a)
        : x(square(a)), y(square(a)) { } //OK, definition
      constexpr pixel small(2);          // error: square not defined, so small(2)
                                         // not constant (7.7 [expr.const]), so constexpr not satisfied
      constexpr int square(int x) {      // OK, definition
        return x * x;
      }
      constexpr pixel large(4);          // OK, square defined
      int next(constexpr int x) {        // error, not for parameters
        return x + 1;
      }
      extern constexpr int memsz;        // error: not a definition
    

    end example]

  6. Add a new section following 16.4.6.5 [member.functions]:

  7. Implementations shall provide definitions for any non-defining declarations of constexpr functions and constructors within the associated header files.
  8. Add the following bullet to the list in Clause Annex B [implimits] paragraph 2:

(This resolution also resolves issue 695.)




991. Reference parameters of constexpr functions and constructors

Section: 9.2.6  [dcl.constexpr]     Status: CD2     Submitter: Gabriel Dos Reis     Date: 20 October, 2009

[Voted into WP at March, 2010 meeting as document N3078.]

It would be useful if constexpr functions and constructors could take arguments via reference-to-const parameters. See message 15357.




811. Unclear implications of const-qualification

Section: 9.2.9.2  [dcl.type.cv]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 89

[Voted into WP at March, 2010 meeting.]

The normative text in 9.2.9.2 [dcl.type.cv] paragraph 2 reads,

An object declared in namespace scope with a const-qualified type has internal linkage unless it is explicitly declared extern or unless it was previously declared to have external linkage. A variable of non-volatile const-qualified integral or enumeration type initialized by an integral constant expression can be used in integral constant expressions (7.7 [expr.const]).

These two sentences parallel the specifications of 9.2.2 [dcl.stc] paragraph 7 and 7.7 [expr.const]. However, the passages are not identical, leading to questions about whether the meanings are the same.

Proposed resolution (October, 2009):

Change 9.2.9.2 [dcl.type.cv] paragraph 2 as follows:

An object declared in namespace scope with a const-qualified type has internal linkage unless it is explicitly declared extern or unless it was previously declared to have external linkage. A variable of non-volatile const-qualified integral or enumeration type initialized by an integral constant expression can be used in integral constant expressions (7.7 [expr.const]). [Note: Declaring a variable const can affect its linkage (9.2.2 [dcl.stc]) and its usability in constant expressions (7.7 [expr.const]). As as described in 9.4 [dcl.init], the definition of an object or subobject of const-qualified type must specify an initializer or be subject to default-initialization. —end note]



950. Use of decltype as a class-name

Section: 9.2.9.3  [dcl.type.simple]     Status: CD2     Submitter: Alisdair Meredith     Date: 3 August, 2009

[Voted into WP at March, 2010 meeting as document N3049.]

In the current specification, a decltype resulting in a class type is not a class-name, meaning that it cannot be used as a base-specifier. There doesn't seem to be any reason not to allow that, and it would be consistent with the proposed outcome of issue 743.

Proposed resolution (February, 2010):

See paper PL22.16/10-0021 = WG21 N3031.




988. Reference-to-reference collapsing with decltype

Section: 9.2.9.3  [dcl.type.simple]     Status: CD2     Submitter: Michael Wong     Date: 19 October, 2009

[Voted into WP at March, 2010 meeting.]

References to references are ill-formed, but special provision is made in cases where this occurs via typedefs or template type parameters. A similar provision is probably needed for types resulting from decltype:

    int x, *p = &x;
    decltype(*p) &y = *p;  // reference to reference is ill-formed

Proposed resolution (October, 2009):

  1. Delete 9.2.4 [dcl.typedef] paragraph 9:

  2. If a typedef TD names a type that is a reference to a type T, an attempt to create the type “lvalue reference to cv TD” creates the type “lvalue reference to T,” while an attempt to create the type “rvalue reference to cv TD” creates the type TD. [Example: ... —end example]
  3. Delete 13.4.2 [temp.arg.type] paragraph 4:

  4. If a template-argument for a template-parameter T names a type that is a reference to a type A, an attempt to create the type “lvalue reference to cv T” creates the type “lvalue reference to A,” while an attempt to create the type “rvalue reference to cv T” creates the type T [Example: ... —end example]
  5. Add the following as a new paragraph at the end of 9.3.4.3 [dcl.ref]:

  6. If a typedef (9.2.4 [dcl.typedef]), a type template-parameter (13.4.2 [temp.arg.type]), or a decltype-specifier (9.2.9.3 [dcl.type.simple]) denotes a type TR that is a reference to a type T, an attempt to create the type “lvalue reference to cv TR” creates the type “lvalue reference to T,” while an attempt to create the type “rvalue reference to cv TR” creates the type TR. [Example:

       int i;
       typedef int& LRI;
       typedef int&& RRI;
       LRI& r1 = i;                      // r1 has the type int&
       const LRI& r2 = i;                // r2 has the type int&
       const LRI&& r3 = i;               // r3 has the type int&
       RRI& r4 = i;                      // r4 has the type int&
       RRI&& r5 = i;                     // r5 has the type int&&
    
       decltype(r2)& r6 = i;             // r6 has the type int&
       decltype(r2)&& r7 = i;            // r7 has the type int&
    

    end example]




962. Attributes appertaining to class and enum types

Section: 9.2.9.5  [dcl.type.elab]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 2 September, 2009

[Voted into WP at March, 2010 meeting.]

There is a lack of symmetry in the specification of attributes that apply to class and enum types. For example:

    class X [[attr]];               // #1
    typedef class Y [[attr]] YT;    // #2

According to 9.2.9.5 [dcl.type.elab] paragraph 1, #1 associates the attr attribute with class X for all subsequent references. On the other hand, 9.3.4 [dcl.meaning] paragraph 5 says that #2 associates the attr attribute with the type but not with class Y.

Existing implementations (Microsoft, GNU, Sun) with attributes place an attribute that is intended to be associated with a class type between the class-key and the class name, and it would be preferable to adopt such an approach instead of the contextual approach in the current formulation.

Proposed resolution (October, 2009):

  1. Change 6.4.2 [basic.scope.pdecl] bullet 6.1 as follows:

  2. Change 6.5.6 [basic.lookup.elab] paragraph 2 as follows:

  3. ...unless the elaborated-type-specifier appears in a declaration with the following form:

    the identifier is looked up... if the elaborated-type-specifier appears in a declaration with the form:

    the elaborated-type-specifier is a declaration...

  4. In 9.2.9.5 [dcl.type.elab], change the grammar and paragraph 1 as follows:

  5. An attribute-specifier shall not appear in an elaborated-type-specifier unless the latter is the sole constituent of a declaration. If an elaborated-type-specifier is the sole constituent of a declaration, the declaration is ill-formed unless it is an explicit specialization (13.9.4 [temp.expl.spec]), an explicit instantiation (13.9.3 [temp.explicit]) or it has one of the following forms:

  6. Change the grammar in 9.7.1 [dcl.enum] paragraph 1 as follows:

  7. Change the grammar in Clause 11 [class] paragraph 1 as follows:




625. Use of auto as a template-argument

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD2     Submitter: John Spicer     Date: 9 March 2007

[Voted into WP at March, 2010 meeting.]

The auto specifier can be used only in certain contexts, as specified in 9.2.9.7 [dcl.spec.auto] paragraphs 2-3:

Otherwise (auto appearing with no type specifiers other than cv-qualifiers), the auto type-specifier signifies that the type of an object being declared shall be deduced from its initializer. The name of the object being declared shall not appear in the initializer expression.

This use of auto is allowed when declaring objects in a block (8.4 [stmt.block]), in namespace scope (6.4.6 [basic.scope.namespace]), and in a for-init-statement (8.6.4 [stmt.for]). The decl-specifier-seq shall be followed by one or more init-declarators, each of which shall have a non-empty initializer of either of the following forms:

It was intended that auto could be used only at the top level of a declaration, but it is not clear whether this wording is sufficient to forbid usage like the following:

    template <class T> struct A {};
    template <class T> void f(A<T> x) {}

    void g()
    {
        f(A<short>());

        A<auto> x = A<short>();
    }

Notes from the February, 2008 meeting:

It was agreed that the example should be ill-formed.

Proposed resolution (October, 2009):

Change 9.2.9.7 [dcl.spec.auto] paragraph 3 as follows:

...The auto shall appear as one of the decl-specifiers in the decl-specifier-seq and the decl-specifier-seq shall be followed by one or more init-declarators, each of which shall have a non-empty initializer.



711. auto with braced-init-list

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD2     Submitter: Jason Merrill     Date: 27 August, 2008

[Voted into WP at July, 2009 meeting.]

One effect of the initializer-list proposal is that now we allow

    auto x = { 1, 2, 3 };  // decltype(x) is std::initializer_list<int>

but not

    auto ar[3] = { 1, 2, 3 };  // ill-formed

This seems unfortunate, as the code for the first could also support the second. Incidentally, I also failed to update the text in 9.2.9.7 [dcl.spec.auto] paragraph 3 which forbids the use of auto with braced-init-lists, so technically the first form above is currently ill-formed but has defined semantics.

Bjarne Stroustrup:

Is this the thin edge of a wedge? How about

    vector<auto> v = { 1, 2, 3 };

and

    template<class T> void f(vector<T>& v);
    f({1, 2, 3 });

(See also issue 625.)

Proposed resolution (March, 2009):

Change 9.2.9.7 [dcl.spec.auto] paragraph 3 as follows:

...The decl-specifier-seq shall be followed by one or more init-declarators, each of which shall have a non-empty initializer. of either of the following forms:

[Drafting note: This change does not address the original issue of the inability to use auto with an array initializer, only the secondary issue of permitted the braced-init-list. The CWG explicitly decided not to support the array case.]




746. Use of auto in new-expressions

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD2     Submitter: Jason Merrill     Date: 18 November, 2008

N2800 comment UK 95

[Voted into WP at July, 2009 meeting.]

In listing the acceptable contexts in which the auto specifier may appear, 9.2.9.7 [dcl.spec.auto]) paragraph 4 mentions “the type-specifier-seq in a new-type-id” but not the type-id in the parenthesized form; that is, new auto (42) is well-formed but new (auto) (42) is not. This seems an unnecessary restriction, as well as contradicting 7.6.2.8 [expr.new] paragraph 2:

If the auto type-specifier appears in the type-specifier-seq of a new-type-id or type-id of a new-expression...

(See also issue 496.)

Proposed resolution (March, 2009):

Change 9.2.9.7 [dcl.spec.auto] paragraph 4 as follows:

The auto type-specifier can also be used in declaring an object in the condition of a selection statement (8.5 [stmt.select]) or an iteration statement (8.6 [stmt.iter]), in the type-specifier-seq in a the new-type-id or type-id of a new-expression (7.6.2.8 [expr.new]), in a for-range-declaration...



984. “Deduced type” is unclear in auto type deduction

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD2     Submitter: Michael Wong     Date: 19 October, 2009

[Voted into WP at March, 2010 meeting.]

9.2.9.7 [dcl.spec.auto] paragraph 6 says,

Once the type of a declarator-id has been determined according to 9.3.4 [dcl.meaning], the type of the declared variable using the declarator-id is determined from the type of its initializer using the rules for template argument deduction. Let T be the type that has been determined for a variable identifier d. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list (9.4.5 [dcl.init.list]), with std::initializer_list<U>. The type deduced for the variable d is then the deduced type determined using the rules of template argument deduction from a function call (13.10.3.2 [temp.deduct.call]), where P is a function template parameter type and the initializer for d is the corresponding argument.

The reference to “the deduced type” is unclear; it could be taken as referring either to the template parameter or to the function parameter type. 13.10.3.2 [temp.deduct.call] uses the term “deduced A,” and that usage should be repeated here.

Proposed resolution (October, 2009):

Change 9.2.9.7 [dcl.spec.auto] paragraph 6 as follows:

...The type deduced for the variable d is then the deduced type A determined using the rules of template argument deduction...



770. Ambiguity in late-specified return type

Section: 9.3  [dcl.decl]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 9 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

It is currently unspecified whether a declaration like

    f() -> struct S { };

should be parsed as a declaration of f whose return type is a class definition (which will be ill-formed according to 9.2.9 [dcl.type] paragraph 3) or as a definition of f whose return type is an elaborated-type-specifier.

Proposed resolution (June, 2009):

See document PL22.16/09-0117 = WG21 N2927.




979. Position of attribute-specifier in declarator syntax

Section: 9.3  [dcl.decl]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 8 October, 2009

In function, pointer, and pointer-to-member declarators, the attribute-specifier appertains to the type being declared, but the syntax has the attribute-specifieropt appearing before the full type is seen — i.e., before the cv-qualifier-seqopt and, for the function case, before the ref-qualifieropt. GNU attributes appear after these elements (and, for the function case, after the exception-specificationopt as well). It would be better, both logically and for consistency with existing practice, to move the attribute-specifieropt accordingly.




374. Can explicit specialization outside namespace use qualified name?

Section: 9.3.4  [dcl.meaning]     Status: CD2     Submitter: Steve Adamczyk     Date: 23 August 2002

[Voted into WP at March, 2010 meeting as document N3064.]

This case is nonstandard by 9.3.4 [dcl.meaning] paragraph 1 (there is a requirement that the specialization first be declared within the namespace before being defined outside of the namespace), but probably should be allowed:

  namespace NS1 {
    template<class T>
    class CDoor {
    public:
      int mtd() { return 1; }
    };
  }
  template<> int NS1::CDoor<char>::mtd()
  {
    return 0;
  }

Notes from October 2002 meeting:

There was agreement that we wanted to allow this.

Proposed resolution (February, 2010):

  1. Change 9.3.4 [dcl.meaning] as follows:

  2. ...A declarator-id shall not be qualified except for the definition of a member function (11.4.2 [class.mfct]) or static data member (11.4.9 [class.static]) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or the definition of a previously declared an explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (11.8.4 [class.friend]). When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers (or of an inline namespace within that scope (9.8.2 [namespace.def])) or to a specialization thereof, and the member shall not have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id. [Note:...
  3. Change 13.9.4 [temp.expl.spec] paragraphs 2-4 as follows:

  4. An explicit specialization shall appear in namespace scope. An explicit specialization whose declarator-id is not qualified shall be declared in the nearest enclosing namespace of the template, or, if the namespace is inline (9.8.2 [namespace.def]), any namespace from its enclosing namespace set. Such a declaration may also be a definition. If the declaration is not a definition, the specialization may be defined later (_N4868_.9.8.2.3 [namespace.memdef]).

    A declaration of a function template or class template being explicitly specialized shall be in scope at the point of precede the declaration of an the explicit specialization. [Note: a declaration, but not a definition of the template is required. —end note] The definition of a class or class template shall be in scope at the point of precede the declaration of an explicit specialization for a member template of the class or class template. [Example: ... —end example]

    A member function, a member class or a static data member of a class template may be explicitly specialized for a class specialization that is implicitly instantiated; in this case, the definition of the class template shall be in scope at the point of declaration of preced the explicit specialization for the member of the class template. If such an explicit specialization for the member of a class template names an implicitly-declared special member function ( 11.4.4 [special]), the program is ill-formed.




920. Interaction of inline namespaces and using-declarations

Section: 9.3.4  [dcl.meaning]     Status: CD2     Submitter: Michael Wong     Date: 19 June, 2009

[Voted into WP at March, 2010 meeting as part of document N3079.]

According to 9.3.4 [dcl.meaning] paragraph 1,

When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers (or of an inline namespace within that scope (9.8.2 [namespace.def])), and the member shall not have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id.

This would appear to make the following example ill-formed, even though it would be well-formed if the using-declaration were omitted:

    namespace A {
      inline namespace B {
        template <class T> void foo() { }
     }
     using B::foo;
    }
    template void A::foo<int>();

This seems strange.

Proposed resolution (July, 2009):

Change 9.3.4 [dcl.meaning] paragraph 1 as follows:

...When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers (or, in the case of a namespace, of an element of the inline namespace within that scope set of that namespace (9.8.2 [namespace.def])), and; the member shall not merely have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id. [Note:...

(Note: this resolution depends on the resolution of issue 861.)




701. When is the array-to-pointer conversion applied?

Section: 9.3.4.5  [dcl.array]     Status: CD2     Submitter: Eelis van der Weegen     Date: 13 July, 2008

[Voted into WP at March, 2010 meeting.]

Paragraph 7 of 9.3.4.5 [dcl.array] says,

If E is an n-dimensional array of rank i × j × ... × k, then E appearing in an expression is converted to a pointer to an (n - 1)-dimensional array with rank j × ... × k.

This formulation does not allow for the existence of expressions in which the array-to-pointer conversion does not occur (as specified in Clause 7 [expr] paragraph 9). This paragraph should be no more than a note, if it appears at all, and the wording should be corrected.

Proposed resolution (November, 2009):

Change paragraphs 6-8 of 9.3.4.5 [dcl.array] into a note and make the indicated changes:

[Note: Except where it has been declared for a class (12.4.5 [over.sub]), the subscript operator [] is interpreted in such a way that E1[E2] is identical to *((E1)+(E2)). Because of the conversion rules that apply to +, if E1 is an array and E2 an integer, then E1[E2] refers to the E2-th member of E1. Therefore, despite its asymmetric appearance, subscripting is a commutative operation.

A consistent rule is followed for multidimensional arrays. If E is an n-dimensional array of rank i × j × . . . × k, then E appearing in an expression that is subject to the array-to-pointer conversion (7.3.3 [conv.array]) is converted to a pointer to an (n-1)-dimensional array with rank j × . . . × k. If the * operator, either explicitly or implicitly as a result of subscripting, is applied to this pointer, the result is the pointed-to (n-1)-dimensional array, which itself is immediately converted into a pointer.

[Example: consider

Here x is a 3 × 5 array of integers. When x appears in an expression, it is converted to a pointer to (the first of three) five-membered arrays of integers. In the expression x[i] which is equivalent to *(x+i), x is first converted to a pointer as described; then x+i is converted to the type of x, which involves multiplying i by the length of the object to which the pointer points, namely five integer objects. The results are added and indirection applied to yield an array (of five integers), which in turn is converted to a pointer to the first of the integers. If there is another subscript the same argument applies again; this time the result is an integer. —end example]end note]




713. Unclear note about cv-qualified function types

Section: 9.3.4.6  [dcl.fct]     Status: CD2     Submitter: Doug Gregor     Date: 11 September, 2008

[Voted into WP at October, 2009 meeting.]

7.3.6 [conv.qual] paragraph 3 consists of a note reading,

[Note: Function types (including those used in pointer to member function types) are never cv-qualified (9.3.4.6 [dcl.fct]). —end note]

However, 9.3.4.6 [dcl.fct] paragraph 7 says,

A cv-qualifier-seq shall only be part of the function type...

This sounds like a contradiction, although formally it is not: a “function type with a cv-qualifier-seq” is not a “cv-qualified function type.” It would be helpful to make this distinction clearer.

Proposed resolution (March, 2009):

  1. Change 9.3.4.6 [dcl.fct] paragraph 7 as follows:

  2. A cv-qualifier-seq shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration. [Note: A function type that has a cv-qualifier-seq is not a cv-qualified type; there are no cv-qualified function types. —end note] The effect of a cv-qualifier-seq in a function declarator...
  3. Change 6.8.5 [basic.type.qualifier] paragraph 3 as follows:

  4. ...See 9.3.4.6 [dcl.fct] and _N4868_.11.4.3.2 [class.this] regarding cv-qualified function types that have cv-qualifiers.



818. Function parameter packs in non-final positions

Section: 9.3.4.6  [dcl.fct]     Status: CD2     Submitter: US     Date: 3 March, 2009

N2800 comment US 45

[Voted into WP at March, 2010 meeting as part of document N3079.]

9.3.4.6 [dcl.fct] paragraph 13 requires that a parameter pack, if present, must appear at the end of the parameter list. This restriction is not necessary when template argument deduction is not needed and is inconsistent with the way pack expansions are handled. It should be removed.

(See also issue 692.)




956. Function prototype scope with late-specified return types

Section: 9.3.4.6  [dcl.fct]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 21 August, 2009

[Voted into WP at March, 2010 meeting.]

According to 6.4.4 [basic.scope.param] paragraph 1,

In a function declaration, or in any function declarator except the declarator of a function definition (9.5 [dcl.fct.def]), names of parameters (if supplied) have function prototype scope, which terminates at the end of the nearest enclosing function declarator.

Happily, this permits the use of parameter names with decltype in a late-specified return type, because the return type is part of the function's declarator. However, the note in 9.3.4.6 [dcl.fct] paragraph 11 is now inaccurate and should be updated:

[Note: ...If a parameter name is present in a function declaration that is not a definition, it cannot be used outside of the parameter-declaration-clause since it goes out of scope at the end of the function declarator (6.4 [basic.scope]). —end note]

Proposed resolution (February, 2010):

Change the note in 9.3.4.6 [dcl.fct] paragraph 10 as follows:

...[Note: in particular, parameter names are also optional in function definitions and names used for a parameter in different declarations and the definition of a function need not be the same. If a parameter name is present in a function declaration that is not a definition, it cannot be used outside of the parameter-declaration-clause since it goes out of scope at the end of the function declarator (6.4 [basic.scope]) its function declarator because that is the extent of its potential scope (6.4.4 [basic.scope.param]). —end note]



777. Default arguments and parameter packs

Section: 9.3.4.7  [dcl.fct.default]     Status: CD2     Submitter: Michael Wong     Date: 13 February, 2009

[Voted into WP at March, 2010 meeting.]

9.3.4.7 [dcl.fct.default] paragraph 4 says,

In a given function declaration, all parameters subsequent to a parameter with a default argument shall have default arguments supplied in this or previous declarations.

It is not clear whether this applies to parameter packs or not. For example, is the following well-formed?

    template <typename... T> void f(int i = 0, T ...args) { }

Note for comparison the corresponding wording in 13.2 [temp.param] paragraph 11 regarding template parameter packs:

If a template-parameter of a class template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack.

Proposed resolution (October, 2009):

Change 9.3.4.7 [dcl.fct.default] paragraph 4:

...In a given function declaration, all each parameters subsequent to a parameter with a default argument shall have a default arguments supplied in this or a previous declarations or shall be a function parameter pack. A default argument...



611. Zero-initializing references

Section: 9.4  [dcl.init]     Status: CD2     Submitter: Alisdair Meredith     Date: 29 December 2006

According to 9.4 [dcl.init] paragraph 5,

To zero-initialize an object of type T means:

However, a reference is not an object, so this makes no sense.

Proposed resolution (March, 2010):

This issue is resolved by the resolution of issue 633 in document N2993.




869. Uninitialized thread_local objects

Section: 9.4  [dcl.init]     Status: CD2     Submitter: Daniel Krügler     Date: 14 April, 2009

[Voted into WP at March, 2010 meeting.]

9.4 [dcl.init] paragraph 11 says,

If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, a non-static object has indeterminate value.

This is inaccurate, because objects with thread storage duration are zero-initialized (6.9.3.2 [basic.start.static] paragraph 2).

Proposed resolution (November, 2009):

Change 9.4 [dcl.init] paragraph 11 as follows:

If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, a non-static an object with automatic or dynamic storage duration has indeterminate value. [Note: objects with static or thread storage duration are zero-initialized, see 6.9.3.2 [basic.start.static].end note].



886. Member initializers and aggregates

Section: 9.4.2  [dcl.init.aggr]     Status: CD2     Submitter: Daniel Krügler     Date: 5 May, 2009

[Voted into WP at March, 2010 meeting.]

The current wording of 9.4.2 [dcl.init.aggr] paragraph 1 does not consider brace-or-equal-initializers on members as affecting whether a class type is an aggregate or not. Because in-class member initializers are essentially syntactic sugar for mem-initializers, and the presence of a user-provided constructor disqualifies a class from being an aggregate, presumably the same should hold true of member initializers.

Proposed resolution (November, 2009):

Change 9.4.2 [dcl.init.aggr] paragraph 1 as follows:

An aggregate is an array or a class (Clause 11 [class]) with no user-provided constructors (11.4.5 [class.ctor]), no brace-or-equal-initializers for non-static data members (11.4 [class.mem]), no private or protected non-static data members (11.8 [class.access]), no base classes (11.7 [class.derived]), and no virtual functions (11.7.3 [class.virtual]).



737. Uninitialized trailing characters in string initialization

Section: 9.4.3  [dcl.init.string]     Status: CD2     Submitter: James Kanze     Date: 26 October, 2008

[Voted into WP at October, 2009 meeting.]

The current specification of string initialization in 9.4.3 [dcl.init.string] leaves uninitialized all characters following the terminating '\0' of a character array with automatic storage duration. This is different from C99, in which string initialization is handled like aggregate initialization and all trailing characters are zeroed (6.7.8 paragraph 21).

(See also issue 694, in which we are considering following C99 in a somewhat similar case of zero-initializing trailing data.)

Proposed resolution (September, 2009):

Add a new paragraph following 9.4.3 [dcl.init.string] paragraph 2:

There shall not be more initializers than there are array elements. [Example:

  char cv[4] = "asdf";    // error

is ill-formed since there is no space for the implied trailing '\0'. —end example]

If there are fewer initializers than there are array elements, then each element not explicitly initialized shall be zero-initialized (9.4 [dcl.init]).




936. Array initialization with new string literals

Section: 9.4.3  [dcl.init.string]     Status: CD2     Submitter: Alisdair Meredith     Date: 11 July, 2009

[Voted into WP at October, 2009 meeting.]

9.4.3 [dcl.init.string] paragraph 1 says,

A char array (whether plain char, signed char, or unsigned char), char16_t array, char32_t array, or wchar_t array can be initialized by a string-literal (optionally enclosed in braces) with no prefix, with a u prefix, with a U prefix, or with an L prefix, respectively...

This formulation does not allow for raw and UTF-8 literals.

Proposed resolution (July, 2009):

Change 9.4.3 [dcl.init.string] paragraph 1 as follows:

A char array (whether plain char, signed char, or unsigned char), char16_t array, char32_t array, or wchar_t array can be initialized by a string-literal (optionally enclosed in braces) with no prefix, with a u prefix, with a U prefix, or with an L prefix narrow character literal, char16_t string literal, char32_t string literal, or wide string literal, respectively; successive, or by an appropriately-typed string literal enclosed in braces. Successive characters of the string-literal value of the string literal initialize the members elements of the array. [Example: ...



589. Direct binding of class and array rvalues in reference initialization

Section: 9.4.4  [dcl.init.ref]     Status: CD2     Submitter: Steve Adamczyk     Date: 26 July 2006

[Voted into WP at October, 2009 meeting.]

The resolutions of issues 391 and 450 say that the reference is “bound to” the class or array rvalue, but it does not say that the reference “binds directly” to the initializer, as it does for the cases that fall under the first bullet in 9.4.4 [dcl.init.ref] paragraph 5. However, this phrasing is important in determining the implicit conversion sequence for an argument passed to a parameter with reference type (12.2.4.2.5 [over.ics.ref]), where paragraph 2 says,

When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the underlying type of the reference according to 12.2.4.2 [over.best.ics]. Conceptually, this conversion sequence corresponds to copy-initializing a temporary of the underlying type with the argument expression.

The above-mentioned issue resolutions stated that no copy is to be made in such reference initializations, so the determination of the conversion sequence does not reflect the initialization semantics.

Simply using the “binds directly” terminology in the new wording may not be the right approach, however, as there are other places in the Standard that also give special treatment to directly-bound references. For example, the first bullet of 7.6.16 [expr.cond] paragraph 3 says,

If E2 is an lvalue: E1 can be converted to match E2 if E1 can be implicitly converted (7.3 [conv]) to the type “reference to T2,” subject to the constraint that in the conversion the reference must bind directly (9.4.4 [dcl.init.ref]) to E1.

The effect of simply saying that a reference “binds directly” to a class rvalue can be seen in this example:

    struct B { };
    struct D: B { };
    D f();
    void g(bool x, const B& br) {
        x ? f() : br;   // result would be lvalue
    }

It is not clear that treating this conditional expression as an lvalue is a desirable outcome, even if the result of f() were to “bind directly” to the const B& reference.

Proposed resolution (June, 2009):

  1. Change 9.4.4 [dcl.init.ref] paragraph 5 as follows:

  2. A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

    In all cases except the last (i.e., creating and initializing a temporary from the initializer expression), the reference is said to bind directly to the initializer expression.

  3. Change 7.6.16 [expr.cond] bullet 3.1 as follows:




656. Direct binding to the result of a conversion operator

Section: 9.4.4  [dcl.init.ref]     Status: CD2     Submitter: Jason Merrill     Date: 23 October 2007

[Voted into WP at October, 2009 meeting.]

Consider the following example:

    struct A { };
    struct B : public A { };
    struct X {
       operator B();
    };
    X x;

    int main() {
       const A& r = x;
       return 0;
    }

It seems like the resolution of issue 391 doesn't actually cover this; X is not reference-compatible with A, so we go past the modified bullet (9.4.4 [dcl.init.ref] paragraph 5, bullet 2, sub-bullet 1), which reads:

If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound to the object represented by the rvalue (see 7.2.1 [basic.lval]) or to a sub-object within that object.

and hit

Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy initialization (9.4 [dcl.init]). The reference is then bound to the temporary.

which seems to require that we create an A temporary copied from the return value of X::operator B() rather than bind directly to the A subobject. I think that the resolution of issue 391 should cover this situation as well, and the EDG compiler seems to agree with me.

(See also issue 896.)

Proposed resolution (September, 2009):

  1. Change 9.4.4 [dcl.init.ref] paragraph 5 as follows:

  2. Editorial note: issue 589 makes edits to the top-level bullet preceding this one. The wording resulting from those edits should be changed for consistency with this wording so that the text there reads, “...in the first case and to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object).”

  3. Change 12.2 [over.match] paragraph 2, last bullet as follows:

  4. Change 12.2.2.7 [over.match.ref] paragraph 1 as follows:

  5. Under the conditions specified in 9.4.4 [dcl.init.ref], a reference can be bound directly to an lvalue or class rvalue that is the result of applying a conversion function to an initializer expression. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1 T” is the underlying type of the reference being initialized, and “cv S” is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:

(Note: this resolution also resolves issue 896.)




664. Direct binding of references to non-class rvalue references

Section: 9.4.4  [dcl.init.ref]     Status: CD2     Submitter: Eric Niebler     Date: 1 December 2007

[Voted into WP at March, 2010 meeting as document N3055.]

According to 9.4.4 [dcl.init.ref] paragraph 5, a reference initialized with a reference-compatible rvalue of class type binds directly to the object. A reference-compatible non-class rvalue reference, however, is first copied to a temporary and the reference binds to that temporary, not to the target of the rvalue reference. This can cause problems when the result of a forwarding function is used in such a way that the address of the result is captured. For example:

    struct ref {
        explicit ref(int&& i): p(&i) { }
        int* p;
    };

    int&& forward(int&& i) {
        return i;
    }

    void f(int&& i) {
        ref r(forward(i));
        // Here r.p is a dangling pointer, pointing to a defunct int temporary
    }

A formulation is needed so that rvalue references are treated like class and array rvalues.

Notes from the February, 2008 meeting:

You can't just treat scalar rvalues like class and array rvalues, because they might not have an associated object. However, if you have an rvalue reference, you know that there is an object, so probably the best way to address this issue is to specify somehow that binding a reference to an rvalue reference does not introduce a new temporary.

(See also issues 690 and 846.)

Proposed resolution (February, 2010):

See paper N3030.




896. Rvalue references and rvalue-reference conversion functions

Section: 9.4.4  [dcl.init.ref]     Status: CD2     Submitter: Steve Adamczyk     Date: 9 May, 2009

[Voted into WP at October, 2009 meeting.]

Consider the following example:

    struct A { } a;
    struct B {
      operator A&&() {
        return static_cast<A&&>(a);
      }
    };
    A&& r = B();

One would expect that r would be bound to the object returned by B::operator A&&(), i.e., a. However, the logic in 9.4.4 [dcl.init.ref] paragraph 5 requires that the result of the conversion function be copied to a temporary and r bound to the temporary.

Probably the way to address this is to add another top-level bullet between the first and second that would essentially mimic the first bullet except dealing with rvalue references: direct binding to reference-compatible rvalues or to the reference-compatible result of a conversion function. (Note that this should only apply to class rvalues; the creation of a temporary for non-class rvalues is necessary to have an object for the reference to bind to.)

(See also issue 656.)

Proposed resolution (September, 2009):

This issue is resolved by the resolution of issue 656.




703. Narrowing for literals that cannot be exactly represented

Section: 9.4.5  [dcl.init.list]     Status: CD2     Submitter: Jason Merrill     Date: 2 July, 2008

[Voted into WP at October, 2009 meeting.]

Both of the following initializations are ill-formed because of narrowing, although they were previously well-formed:

    struct A { int i; } a = { 1.0 };
    struct B { float f; } b = { 1.1 };

The first one doesn't seem like a big problem, as there probably isn't much code that has this kind of aggregate initialization. The second might be of more concern, because 1.1 is not representable in either float or double. Is the resulting loss of precision a kind of narrowing that we want to diagnose?

Notes from the September, 2008 meeting:

The CWG agreed that the second initialization should not be a narrowing error; furthermore, this exemption should apply not only to literals but to any floating-point constant expression. Instead of the current formulation, requiring exact bidirectional convertibility, the Standard should only require that the initializer value be within the representable range of the target type.

Proposed resolution (July, 2009):

Change 9.4.5 [dcl.init.list] paragraph 6 as follows:

A narrowing conversion is an implicit conversion




865. Initializing a std::initializer_list

Section: 9.4.5  [dcl.init.list]     Status: CD2     Submitter: James Widman     Date: 8 April, 2009

[Voted into WP at October, 2009 meeting.]

There are several problems with the wording of 9.4.5 [dcl.init.list] paragraph 4:

When an initializer list is implicitly converted to a std::initializer_list<E>, the object passed is constructed as if the implementation allocated an array of N elements of type E, where N is the number of elements in the initializer list. Each element of that array is initialized with the corresponding element of the initializer list converted to E, and the std::initializer_list<E> object is constructed to refer to that array. If a narrowing conversion is required to convert the element to E, the program is ill-formed.

First, an initializer list is not an expression, so it is not appropriate to refer to “implicitly convert[ing]” it, as is done in the first sentence.

Also, the conversion of the elements of the initializer list to the elements of the array is not specified to be either copy-initialization or direct-initialization. If this is intended to be viewed as an aggregate initialization, it would be copy-initialization, but that needs to be specified more clearly.

Finally, the initializer list can have nested initializer lists, so the references to converting the element also need to be cleaned up.

Proposed resolution (July, 2009):

Change 9.4.5 [dcl.init.list] paragraph 4 as follows:

When an initializer list is implicitly converted to a An object of type std::initializer_list<E> is constructed from an initializer list, the object passed is constructed as if the implementation allocated an array of N elements of type E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list converted to E, and the std::initializer_list<E> object is constructed to refer to that array. If a narrowing conversion is required to convert the element to E initialize any of the elements, the program is ill-formed. [Example:...



934. List-initialization of references

Section: 9.4.5  [dcl.init.list]     Status: CD2     Submitter: Mike Miller     Date: 8 July, 2009

[Voted into WP at October, 2009 meeting.]

According to 9.4.5 [dcl.init.list] paragraph 3,

Otherwise, if T is a reference type, an rvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary.

This means, for an example like

    int i;
    const int& r1{ i };
    int&& r2{ i };

r1 is bound to a temporary containing the value of i, not to i itself, which seems surprising. Also, there's no prohibition here against binding the rvalue reference to an lvalue, as there is in 9.4.4 [dcl.init.ref] paragraph 5 bullet 2, so the initialization of r2 is well-formed, even though the corresponding non-list initialization int&& r3(i) is ill-formed.

There's also a question as to whether this bullet even applies to these examples. According to the decision tree in 9.4 [dcl.init] paragraph 16, initialization of a reference is dispatched to 9.4.4 [dcl.init.ref] in the first bullet, so these cases never make it to the third bullet sending the remaining braced-init-list cases to 9.4.5 [dcl.init.list]. If that's the correct interpretation, there's a problem with 9.4.4 [dcl.init.ref], since it doesn't deal with the braced-init-list cases, and the bullet in 9.4.5 [dcl.init.list] paragraph 3 dealing with references is dead code that's never used.

Proposed resolution (July, 2009):

  1. Move the third bullet of the list in 9.4 [dcl.init] paragraph 16 to the top of the list:

  2. Change 9.4.5 [dcl.init.list] paragraph 3, bullets 4 and 5, as follows:




989. Misplaced list-initialization example

Section: 9.4.5  [dcl.init.list]     Status: CD2     Submitter: Daniel Krügler     Date: 20 October, 2009

[Voted into WP at March, 2010 meeting.]

The final set of declarations in the example following 9.4.5 [dcl.init.list] bullet 3.3 is:

    struct S2 {
      int m1;
      double m2,m3;
    };
    S2 s21 = { 1, 2, 3.0 };    // OK
    S2 s22 { 1.0, 2, 3 };      // error: narrowing
    S2 s23 {};                 // OK: default to 0,0,0

However, S2 is an aggregate. Aggregates are handled in bullet 1, while bullet 3 deals with classes with constructors. This part of the example should be moved to the first bullet.

Proposed resolution (October, 2009):

Move the S2 example from bullet 3 to bullet 1 in 9.4.5 [dcl.init.list] paragraph 3:




990. Value initialization with multiple initializer-list constructors

Section: 9.4.5  [dcl.init.list]     Status: CD2     Submitter: Daniel Krügler     Date: 20 October, 2009

[Voted into WP at March, 2010 meeting as part of document N3079.]

It should always be possible to use the new brace syntax to value-initialize an object. However, the current rules make the following example ill-formed because of ambiguity:

    struct S {
      S();
      S(std::initializer_list<int>);
      S(std::initializer_list<double>);
    };
    S s{};    // Ambiguous initializer-list constructor reference,
              // not value initialization.

Proposed resolution (February, 2010):

Change 9.4.5 [dcl.init.list] paragraph 3 as follows:

List-initialization of an object or reference of type T is defined as follows:




732. Late-specified return types in function definitions

Section: 9.5  [dcl.fct.def]     Status: CD2     Submitter: Daniel Krügler     Date: 7 October, 2008

N2800 comment DE 13

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The grammar in 9.5 [dcl.fct.def] paragraph 2 incorrectly excludes late-specified return types and should be corrected.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




845. What is the “first declaration” of an explicit specialization?

Section: 9.5  [dcl.fct.def]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 20 March, 2009

[Voted into WP at March, 2010 meeting.]

According to 9.5 [dcl.fct.def] paragraph 10,

A deleted definition of a function shall be the first declaration of the function.

The Standard is not currently clear about what the “first declaration” of an explicit specialization of a function template is. For example,

    template<typename T> void f() { }
    template<> void f<int>() = delete;  // First declaration?

Proposed resolution (October, 2009):

A deleted definition of a function shall be the first declaration of the function or, for an explicit specialization of a function template, the first declaration of that specialization.

(This resolution also resolves issue 915.)

Notes from the October, 2009 meeting:

It was observed that this specification is complicated by the fact that the “first declaration” of a function might be in a block-extern declaration.




906. Which special member functions can be defaulted?

Section: 9.5  [dcl.fct.def]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 27 May, 2009

[Voted into WP at March, 2010 meeting.]

The only restriction placed on the use of “=default” in 9.5 [dcl.fct.def] paragraph 9 is that a defaulted function must be a special member function. However, there are many variations of declarations of special member functions, and it's not clear which of those should be able to be defaulted. Among the possibilities:

Presumably, you should only be able to default a function if it is declared compatibly with the implicit declaration that would have been generated.

Proposed resolution (October, 2009):

  1. Change 9.5 [dcl.fct.def] paragraph 9 as follows:

  2. A function definition of the form:

    is called an explicitly-defaulted definition. Only special member functions may be explicitly defaulted, and the implementation shall define them as if they had implicit definitions (11.4.5 [class.ctor], 11.4.7 [class.dtor], 11.4.5.3 [class.copy.ctor]). A function that is explicitly defaulted shall

    [Note: This implies that parameter types, return type, and cv-qualifiers must match the hypothetical implicit declaration. —end note] An explicitly-defaulted function may be declared constexpr only if it would have been implicitly declared as constexpr. If it is explicitly defaulted on its first declaration,

    [Note: Such a special member function may be trivial, and thus its accessibility and explicitness should match the hypothetical implicit definition; see below. —end note] [Example:

      struct S {
        S(int a = 0) = default;              // ill-formed: default argument
        void operator=(const S&) = default;  // ill-formed: non-matching return type
        ~S() throw() = default;              // ill-formed: exception-specification
      private:
        S(S&);                               // OK: private copy constructor
      };
      S::S(S&) = default;                    // OK: defines copy constructor
    

    end example] Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall provide implicit definitions for them (11.4.5 [class.ctor], 11.4.7 [class.dtor], 11.4.5.3 [class.copy.ctor]), which might mean defining them as deleted.A special member function that would be implicitly defined as deleted may be explicitly defaulted only on its first declaration, in which case it is defined as deleted. A special member function is user-provided if it is user-declared and not explicitly defaulted on its first declaration. A user-provided explicitly-defaulted function is defined at the point where it is explicitly defaulted. [Note:...

    [Editorial note: this change incorporates the overlapping portion of the resolution of issue 667.]

  3. Change 11.4.5 [class.ctor] paragraph 6 as follows:

  4. ...[Note: ...An explicitly-defaulted definition has no might have an implicit exception-specification, see 9.5 [dcl.fct.def]. —end note]

This resolution also resolves issue 905. See also issue 667.




908. Deleted global allocation and deallocation functions

Section: 9.5  [dcl.fct.def]     Status: CD2     Submitter: John Spicer     Date: 2 June, 2009

[Voted into WP at October, 2009 meeting.]

According to 9.5 [dcl.fct.def] paragraph 10, a deleted definition of a function must be its first declaration. It is not clear whether this requirement can be satisfied for the global allocation and deallocation functions. According to 6.7.5.5 [basic.stc.dynamic] paragraph 2, they are “implicitly declared in global scope in each translation unit of a program.” However, that does not specify where in the translation unit the declaration is considered to take place. This needs to be clarified.

Proposed resolution (July, 2009):

Change 9.5 [dcl.fct.def] paragraph 10 as follows:

...A deleted definition of a function shall be the first declaration of the function. An implicitly declared allocation or deallocation function (6.7.5.5 [basic.stc.dynamic]) shall not be defined as deleted. [Example:...



915. Deleted specializations of member function templates

Section: 9.5  [dcl.fct.def]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 12 June, 2009

[Voted into WP at March, 2010 meeting.]

It is not clear whether the following definition of an explicit specialization of a member function template is permitted or not:

    template <typenanme T> struct S {
      template <typename U> void f();
    };
    template <> template <typename U>
      void S<int>::f() = delete;

Is the explicit specialization the “first declaration” of the member function template?

(See also issue 845.)

Notes from the July, 2009 meeting:

The intent is that this usage should be supported.

Proposed resolution (October, 2009):

This issue is resolved by the resolution of issue 845.




928. Defaulting a function that would be implicitly defined as deleted

Section: 9.5  [dcl.fct.def]     Status: CD2     Submitter: Alisdair Meredith     Date: 1 July, 2009

[Voted into WP at October, 2009 meeting.]

9.5 [dcl.fct.def] paragraph 9 says,

A special member function that would be implicitly defined as deleted shall not be explicitly defaulted.

It would be more regular (and thus useful in generic programming) if such a member function were itself simply defined as deleted rather than being made ill-formed.

Proposed resolution (July, 2009):

  1. Change 9.5 [dcl.fct.def] paragraph 9 as follows:

  2. Only special member functions may be explicitly defaulted, and the implementation shall define them as if they had implicit definitions (11.4.5 [class.ctor], 11.4.7 [class.dtor], 11.4.5.3 [class.copy.ctor]). A special member function that would be implicitly defined as deleted shall not be explicitly defaulted. A special member function that would be implicitly defined as deleted may be explicitly defaulted only on its first declaration, in which case it is defined as deleted. A special member function is user-provided if...
  3. Change 11.4.5 [class.ctor] paragraph 6 as follows:

  4. A non-user-provided default constructor for a class is implicitly defined when it is used (6.3 [basic.def.odr]) to create an object of its class type (6.7.2 [intro.object]). If the implicitly-defined default constructor is explicitly defaulted but the corresponding implicit declaration would have been deleted, the program is ill-formed. The implicitly-defined or explicitly-defaulted default constructor...
  5. Change 11.4.7 [class.dtor] paragraph 4 as follows:

  6. A program is ill-formed if the class for which a destructor is implicitly defined or explicitly defaulted has: if the implicitly-defined destructor is explicitly defaulted, but the corresponding implicit declaration would have been deleted.

  7. Change 11.4.5.3 [class.copy.ctor] paragraph 7 as follows:

  8. ...[Note: the copy constructor is implicitly defined even if the implementation elided its use (6.7.7 [class.temporary]). —end note] A program is ill-formed if the implicitly-defined copy constructor is explicitly defaulted, but the corresponding implicit declaration would have been deleted.
  9. Change 11.4.5.3 [class.copy.ctor] paragraph 12 as follows:

  10. A non-user-provided copy assignment operator is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type. A program is ill-formed if the implicitly-defined copy assignment operator is explicitly defaulted, but the corresponding implicit declaration would have been deleted.



628. The values of an enumeration with no enumerator

Section: 9.7.1  [dcl.enum]     Status: CD2     Submitter: Gennaro Prota     Date: 15 March 2007

N2800 comment UK 96

[Voted into the WP at the March, 2009 meeting.]

According to 9.7.1 [dcl.enum] paragraph 6, the underlying type of an enumeration with an empty enumeration-list is determined as if the enumeration-list contained a single enumerator with value 0. Paragraph 7, which specifies the values of an enumeration and the minimum size of bit-field needed represent those values needs a similar provision for empty enumeration-lists.

Proposed resolution (March, 2008):

Add the indicated sentence to the end of 9.7.1 [dcl.enum] paragraph 5:

...It is possible to define an enumeration that has values not defined by any of its enumerators. If the enumerator-list is empty, the values of the enumeration are as if the enumeration had a single enumerator with value 0.



862. Undefined behavior with enumerator value overflow

Section: 9.7.1  [dcl.enum]     Status: CD2     Submitter: Daniel Krügler     Date: 7 April, 2009

[Voted into WP at October, 2009 meeting.]

The type of an enumerator that has no initializing value in an enumeration whose underlying type is not fixed is given by the third bullet of 9.7.1 [dcl.enum] paragraph 5:

the type of the initializing value is the same as the type of the initializing value of the preceding enumerator unless the incremented value is not representable in that type, in which case the type is an unspecified integral type sufficient to contain the incremented value.

This does not address the case in which there is no such type, meaning that it is apparently undefined behavior. Other cases in which an enumeration value is unrepresentable are made ill-formed (see the preceding paragraph for an enumeration with a fixed underlying type and the following paragraph for the case in which the minimum and maximum values cannot be represented by a single type). It would be better if this case were ill-formed as well, instead of causing undefined behavior.

Proposed resolution (July, 2009):

Change 9.7.1 [dcl.enum] paragraph 5, bullet 3 as follows:




812. Duplicate names in inline namespaces

Section: 9.8.2  [namespace.def]     Status: CD2     Submitter: JP     Date: 3 March, 2009

N2800 comment JP 14

[Voted into WP at March, 2010 meeting.]

It is not clear from the specification in 9.8.2 [namespace.def] paragraph 8 how a declaration in an inline namespace should be handled if the name is the same as one in the containing namespace or in an parallel inline namespace. For example:

  namespace Q {
    inline namespace V1 {
      int i;
      int j;
    }
    inline namespace V2 {
      int j;
    }
    int i;
  }
  int Q::i = 1;   // Q::i or Q::V1::i?
  int Q::j = 2;   // Q::V1::j or Q::V2::j?

Proposed resolution (July, 2009):

This issue is resolved by the resolution of issue 861.




919. Contradictions regarding inline namespaces

Section: 9.8.2  [namespace.def]     Status: CD2     Submitter: Michael Wong     Date: 19 June, 2009

[Voted into WP at March, 2010 meeting as part of document N3079.]

According to 9.8.2 [namespace.def] paragraph 8,

Members of an inline namespace can be used in most respects as though they were members of the enclosing namespace... Furthermore, each member of the inline namespace can subsequently be explicitly instantiated (13.9.3 [temp.explicit]) or explicitly specialized (13.9.4 [temp.expl.spec]) as though it were a member of the enclosing namespace.

However, that assertion is contradicted for class template specializations by Clause 11 [class] paragraph 11:

If a class-head contains a nested-name-specifier, the class-specifier shall refer to a class that was previously declared directly in the class or namespace to which the nested-name-specifier refers...

It is also contradicted for function template specializations by 6.5.5.3 [namespace.qual] paragraph 6:

In a declaration for a namespace member in which the declarator-id is a qualified-id, given that the qualified-id for the namespace member has the form the unqualified-id shall name a member of the namespace designated by the nested-name-specifier.

Proposed resolution (November, 2009):

  1. Change Clause 11 [class] paragraph 11 as follows:

  2. If a class-head contains a nested-name-specifier, the class-specifier shall refer to a class that was previously declared directly in the class or namespace to which the nested-name-specifier refers, or in an element of the inline namespace set (9.8.2 [namespace.def]) of that namespace (i.e., neither not merely inherited nor or introduced by a using-declaration), and the class-specifier shall appear in a namespace enclosing the previous declaration.
  3. Change 6.5.5.3 [namespace.qual] paragraph 6 as follows:

  4. In a declaration for a namespace member in which the declarator-id is a qualified-id, given that the qualified-id for the namespace member has the form

    the unqualified-id shall name a member of the namespace designated by the nested-name-specifier, or of an element of the inline namespace (9.8.2 [namespace.def]) of that namespace. [Example:...

(Note: this resolution depends on the resolution for issue 861.)




921. Unclear specification of inline namespaces

Section: 9.8.2  [namespace.def]     Status: CD2     Submitter: Michael Wong     Date: 19 June, 2009

[Voted into WP at October, 2009 meeting.]

According to 9.8.2 [namespace.def] paragraph 8,

Specifically, the inline namespace and its enclosing namespace are considered to be associated namespaces (6.5.4 [basic.lookup.argdep]) of one another, and a using-directive (9.8.4 [namespace.udir]) that names the inline namespace is implicitly inserted into the enclosing namespace.

There are two problems with this sentence. First, the concept of namespaces being associated with each other is undefined; 6.5.4 [basic.lookup.argdep] describes how namespaces are associated with types, not with other namespaces. Second, unlike unnamed namespaces, the location of the implicit using-directive is not specified.

Proposed resolution (July, 2009):

Change 9.8.2 [namespace.def] paragraph 8 as follows:

...Specifically, the inline namespace and its enclosing namespace are considered to be associated namespaces (6.5.4 [basic.lookup.argdep]) of one another both added to the set of associated namespaces used in argument-dependent lookup (6.5.4 [basic.lookup.argdep]) whenever one of them is, and a using-directive (9.8.4 [namespace.udir]) that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace (9.8.2.2 [namespace.unnamed]). Furthermore...



926. Inline unnamed namespaces

Section: 9.8.2.2  [namespace.unnamed]     Status: CD2     Submitter: Michael Wong     Date: 29 June, 2009

[Voted into WP at October, 2009 meeting.]

In 9.8.2 [namespace.def] paragraph 1, an unnamed-namespace-definition is defined as

However, there is no provision for the inline keyword in the expansion of unnamed namespaces in 9.8.2.2 [namespace.unnamed] paragraph 1. Strictly interpreted, that would mean that the inline qualifier is ignored for unnamed namespaces.

Proposed resolution (September, 2009):

Change 9.8.2.2 [namespace.unnamed] paragraph 1 as follows:

An unnamed-namespace-definition behaves as if it were replaced by

    inlineopt namespace unique { /* empty body */ }
    using namespace unique ;
    namespace unique { namespace-body }

where inline appears if and only if it appears in the unnamed-namespace-definition, all occurrences of unique in a translation unit are replaced by the same identifier, and this identifier differs from all other identifiers in the entire program.87 [Example:...




986. Transitivity of using-directives versus qualified lookup

Section: 9.8.4  [namespace.udir]     Status: CD2     Submitter: Michael Wong     Date: 19 October, 2009

[Voted into WP at March, 2010 meeting.]

According to 9.8.4 [namespace.udir] paragraph 4,

The using-directive is transitive: if a scope contains a using-directive that nominates a second namespace that itself contains using-directives, the effect is as if the using-directives from the second namespace also appeared in the first.

This is true only for unqualified lookup; the algorithm in 6.5.5.3 [namespace.qual] paragraph 2 gives different results (the transitive closure terminates when a declaration of the name being looked up is found).

Proposed resolution (October, 2009):

Change 9.8.4 [namespace.udir] paragraph 4 as follows:

The For unqualified lookup (6.5.3 [basic.lookup.unqual]), the using-directive is transitive: if a scope contains a using-directive that nominates a second namespace that itself contains using-directives, the effect is as if the using-directives from the second namespace also appeared in the first. [Note: For qualified lookup, see 6.5.5.3 [namespace.qual]. —end note] [Example:...


564. Agreement of language linkage or linkage-specifications?

Section: 9.11  [dcl.link]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 8 March 2006

[Voted into the WP at the March, 2009 meeting.]

The wording of 9.11 [dcl.link] paragraph 5 is suspect:

If two declarations of the same function or object specify different linkage-specifications (that is, the linkage-specifications of these declarations specify different string-literals), the program is ill-formed if the declarations appear in the same translation unit, and the one definition rule (3.2) applies if the declarations appear in different translation units.

But what if only one of the declarations has a linkage-specification, while the other is left with the default C++ linkage? Shouldn't this restriction be phrased in terms of the functions’ or objects’ language linkage rather than linkage-specifications?

(Additional note [wmm]: Is the ODR the proper vehicle for enforcing this requirement? This is dealing with declarations, not necessarily definitions. Shouldn't this say “ill-formed, no diagnostic required” instead of some vague reference to the ODR?)

Proposed resolution (June, 2008):

Change 9.11 [dcl.link] paragraph 5 as follows:

If two declarations of the same function or object declare functions with the same name and parameter-type-list (9.3.4.6 [dcl.fct]) to be members of the same namespace or declare objects with the same name to be members of the same namespace specify different linkage-specifications (that is, the linkage-specifications of these declarations specify different string-literals) and the declarations give the names different language linkages, the program is ill-formed if the declarations appear in the same translation unit, and the one definition rule (6.3 [basic.def.odr]) applies; no diagnostic is required if the declarations appear in different translation units.



814. Attribute to indicate that a function throws nothing

Section: 9.12  [dcl.attr]     Status: CD2     Submitter: US     Date: 3 March, 2009

N2800 comment US 40

[Voted into WP at March, 2010 meeting as paper N3050.]

A function with an exception-specification of throw() must be given a catch(...) clause to enforce its contract, i.e., to call std::unexpected() if it exits with an exception. It would be useful to have an attribute indicating that the function really does throw nothing and thus that the catch(...) clause need not be generated.

(See also issue 830.)

Proposed resolution (September, 2009):

See paper PL22.16/09-0162 = WG21 N2972.




951. Problems with attribute-specifiers

Section: 9.12  [dcl.attr]     Status: CD2     Submitter: Sean Hunt     Date: 5 August, 2009

[Voted into WP at March, 2010 meeting as document N3067.]

There are a number of problems with the treatment of attributes in the current draft. One issue is the failure to permit attributes to appear at various points in the grammar at which one might plausibly expect them:

Another group of problems is the failure to specify to what a given attribute-specifier appertains:

There is also a problem in the specification of the interpretation of an initial attribute-specifier. 9.3.4 [dcl.meaning] paragraph 5 says,

In a declaration attribute-specifieropt T attribute-specifieropt D where D is an unadorned identifier the type of this identifier is “T”. The first optional attribute-specifier appertains to the entity being declared.

This wording only covers the case where the declarator is a simple identifier. It leaves unspecified the meaning of the initial attribute-specifier with more complex declarators for pointers, references, functions, and arrays.

Finally, something needs to be said about the case where attribute-specifiers occur in both the initial position and following the declarator-id: is this permitted, and if so, under what constraints?

(See also issue 968.)

Proposed resolution (February, 2010):

See paper PL22.16/10-0023 = WG21 N3033.




970. Consistent use of “appertain” and “apply”

Section: 9.12  [dcl.attr]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 28 September, 2009

[Voted into WP at March, 2010 meeting.]

The terms “appertain” and “apply” are used in different ways in different subsections of 9.12 [dcl.attr]. A thorough editorial sweep of the entire section is needed to regularize their usage.

Proposed resolution (October, 2009):

  1. Change 9.12.1 [dcl.attr.grammar] paragraph 4 as follows:

  2. ...If an attribute-specifier that appertains to some entity or statement contains an attribute that does not is not allowed to apply to that entity or statement, the program is ill-formed...
  3. Change 9.12.2 [dcl.align] paragraph 1 as follows:

  4. ...The attribute can may be applied to a variable...
  5. Change 9.12.10 [dcl.attr.noreturn] paragraph 1 as follows:

  6. ...The attribute applies may be applied to the declarator-id in a function declaration...
  7. Change _N3225_.7.6.4 [dcl.attr.final] paragraph 1 as follows:

  8. ...The attribute applies may be applied to class definitions...
  9. Change _N3225_.7.6.5 [dcl.attr.override] paragraph 1 as follows:

  10. ...The attribute applies may be applied to virtual member functions...
  11. Change _N3225_.7.6.5 [dcl.attr.override] paragraph 3 as follows:

  12. ...The attribute applies may be applied to class members...
  13. Change _N3225_.7.6.5 [dcl.attr.override] paragraph 5 as follows:

  14. ...The attribute applies may be applied to a class definition.
  15. Change 9.12.4 [dcl.attr.depend] paragraph 1 as follows:

  16. ...The attribute applies may be applied to the declarator-id of a parameter-declaration... The attribute may also applies be applied to the declarator-id of a function declaration...



815. Parameter pack expansion inside attributes

Section: 9.12.1  [dcl.attr.grammar]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 103

[Voted into WP at July, 2009 meeting as N2933.]

Parameter packs should be expanded inside attributes. For example, it would be useful to specify the alignment of each element in a pack expansion using a parallel pack expansion.




957. Alternative tokens and attribute-tokens

Section: 9.12.1  [dcl.attr.grammar]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 26 August, 2009

N2800 comment FR 12

[Voted into WP at March, 2010 meeting.]

9.12.1 [dcl.attr.grammar] paragraph 3 specifies that keywords can be used as attribute-tokens. However, the alternative tokens in 5.5 [lex.digraph], such as bitor and compl, are not keywords. The text should be changed to make the alternative tokens acceptable as attribute-tokens as well.

Proposed resolution, October, 2009:

Change 9.12.1 [dcl.attr.grammar] paragraph 3 as follows:

...A If a keyword (5.11 [lex.key]) or an alternative token (5.5 [lex.digraph]) that satisfies the syntactic requirements of an identifier (5.10 [lex.name]) is contained in an attribute-token, it is considered an identifier...



968. Syntactic ambiguity of the attribute notation

Section: 9.12.1  [dcl.attr.grammar]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 23 September, 2009

[Voted into WP at March, 2010 meeting as document N3063.]

The [[ ... ]] notation for attributes was thought to be completely unambiguous. However, it turns out that two [ characters can be adjacent and not be an attribute-introducer: the first could be the beginning of an array bound or subscript operator and the second could be the beginning of a lambda-introducer. This needs to be explored and addressed.

(See also issue 951.)

Proposed resolution (November, 2009):

Add the following paragraph at the end of 9.3.3 [dcl.ambig.res]:

Two consecutive left square bracket tokens shall appear only when introducing an attribute-specifier. [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;
    int(p[[x]{return x;}()]);  // Error: malformed attribute on a nested
                               // declarator-id and not a function-style cast of
                               // an element of p.
    new int[[]{return x;}()];  // Error even though attributes are not allowed
  }                            // in this context.

end example]




959. Alignment attribute for class and enumeration types

Section: 9.12.2  [dcl.align]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 31 August, 2009

[Voted into WP at March, 2010 meeting.]

According to 9.12.2 [dcl.align] paragraph 1, an alignment attribute can be specified only for a variable or a class data member. The corresponding Microsoft and GNU attributes can be also specified for a class type, and this usage seems to be widespread. It should be permitted with the standard attribute and there seems no good reason not to do so for enumeration types, as well.

Notes from the October, 2009 meeting:

Although there was initial concern for how to integrate the suggested change into the type system, it was observed that current practice is to have the attribute affect only the layout, not the type.

Proposed resolution (February, 2010):

  1. Change 9.12.2 [dcl.align] paragraphs 1-2 as follows:

  2. ...The attribute can be applied to a variable that is neither a function parameter nor declared with the register storage class specifier and to a class data member that is not a bit-field. The attribute can also be applied to the declaration of a class or enumeration type.

    When the alignment attribute is of the form align(assignment-expression):

  3. Change 9.12.2 [dcl.align] paragraphs 4-6 as follows:

  4. When multiple alignment attributes are specified for an object entity, the alignment requirement shall be set to the strictest specified alignment.

    The combined effect of all alignment attributes in a declaration shall not specify an alignment that is less strict than the alignment that would otherwise be required for the object entity being declared.

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

  5. Insert the following as a new paragraph after 9.12.2 [dcl.align] paragraph 6:

  6. [Example:

      // Translation unit #1:
      struct S { int x; } s, p = &s;
    
      // Translation unit #2:
      struct [[align(16)]] S;   // error: definition of S lacks alignment; no
      extern S* p;              //            diagnostic required
    

    end example]

  7. Delete 9.12.2 [dcl.align] paragraph 8:

  8. [Note: the alignment of a union type can be strengthened by applying the alignment attribute to any non-static data member of the union. —end note]



965. Limiting the applicability of the carries_dependency attribute

Section: 9.12.4  [dcl.attr.depend]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 15 September, 2009

[Voted into WP at March, 2010 meeting.]

The current wording for the carries_dependency attribute does not limit it to value-returning functions (when applied to the declarator-id, indicating that the return value is affected), nor does it prohibit use in the declaration of a typedef or function pointer. Arguably these meaningless declarations should be prohibited.

Proposed resolution (October, 2009):

Change 9.12.4 [dcl.attr.depend] paragraph 1 as follows:

...The attribute applies 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 (6.9.2 [intro.multithread]) each lvalue-to-rvalue conversion (7.3.2 [conv.lval]) of that object. The attribute also applies 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.



905. Explicit defaulted copy constructors and trivial copyability

Section: Clause 11  [class]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 27 May, 2009

[Voted into WP at March, 2010 meeting.]

It is presumably possible to declare a defaulted copy constructor to be explicit. Should that render a class not trivially copyable, even though the copy constructor is trivial? That is, does being “trivally copyable” mean that copy initialization, and not just direct initialization, is possible?

A related question is whether the specification of triviality should require that the copy constructor and copy assignment operator must be public. (With the advent of “=default” it is possible to make them non-public, which was not the case when these definitions were crafted.)

Proposed resolution (October, 2009):

This issues is resolved by the resolution of issue 906.




645. Are bit-field and non-bit-field members layout compatible?

Section: 11.4  [class.mem]     Status: CD2     Submitter: Alan Stokes     Date: 9 Aug 2007

[Voted into the WP at the March, 2009 meeting.]

The current wording defining a “common initial sequence” in 11.4 [class.mem] paragraph 17 does not address the case in which one member is a bit-field and the corresponding member is not:

Two standard-layout structs share a common initial sequence if corresponding members have layout-compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members.

Presumably the intent was something like, “(and, if one of the pair is a bit-field, the other is also a bit-field of the same width).”

Proposed Resolution (September, 2008):

Change 11.4 [class.mem] paragraph 18 as follows:

... Two standard-layout structs share a common initial sequence if corresponding members have layout-compatible types (and, for bit-fields, the same widths) and either neither member is a bit-field or both are bit-fields with the same widths for a sequence of one or more initial members.



874. Class-scope definitions of enumeration types

Section: 11.4  [class.mem]     Status: CD2     Submitter: Daniel Krügler     Date: 16 April, 2009

[Voted into WP at October, 2009 meeting.]

According to 11.4 [class.mem] paragraph 1,

The enumerators of an enumeration (9.7.1 [dcl.enum]) defined in the class are members of the class... A member shall not be declared twice in the member-specification, except that a nested class or member class template can be declared and then later defined.

The enumerators of a scoped enumeration are not members of the containing class; the wording should be revised to apply only to unscoped enumerations.

The second part of the cited wording from 11.4 [class.mem] prohibits constructs like:

    class C {
      public:
        enum E: int;
      private:
        enum E: int { e0 };
    };

which might be useful in making the enumeration type, but not its enumerators, accessible.

Notes from the July, 2009 meeting:

According to 11.8.2 [class.access.spec] paragraph 4, the access must be the same for all declarations of a class member. The suggested usage given above violates that requirement: the second declaration of E declares the enumeration itself, not just the enumerators, to be private. The CWG did not feel that the utility of the suggested feature warranted the complexity of an exception to the general rule.

Proposed resolution (July, 2009):

  1. Change 11.4 [class.mem] paragraph 1 as follows:

  2. ...The enumerators of an unscoped enumeration (9.7.1 [dcl.enum]) defined in the class are members of the class... A member shall not be declared twice in the member-specification, except that a nested class or member class template can be declared and then later defined, and except that an enumeration can be first introduced with an opaque-enum-declaration and then later be redeclared with an enum-specifier.
  3. Change the example in 11.8.2 [class.access.spec] paragraph 4 as follows:

  4. When a member is redeclared within its class definition, the access specified at its redeclaration shall be the same as at its initial declaration. [Example:

      struct S {
        class A;
        enum E : int;
      private:
        class A { };          // error: cannot change access
        enum E : int { e0 };  // error: cannot change access
      };
    

    end example]




922. Implicit default constructor definitions and const variant members

Section: 11.4.5  [class.ctor]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 19 June, 2009

[Voted into WP at March, 2010 meeting.]

According to 11.4.5 [class.ctor] paragraph 5,

An implicitly-declared default constructor for class X is defined as deleted if: ... any non-static data member of const-qualified type (or array thereof) does not have a user-provided default constructor, or...

It is not clear if this adequately covers the case in which some variant members are const-qualified but others are not. The intent of the restriction is to prevent creation of an object with uninitialized members that would require a const_cast to set their value later, but const-qualified members of an anonymous union in which other members are not const do not seem to present that problem.

Proposed resolution (October, 2009):

Change 11.4.5 [class.ctor] bullet 5.3 of the second list and add a fourth bullet as follows:

Proposed resolution (November, 2009):

Change 11.4.5 [class.ctor] bullet 5.3 of the second list and add two bullets as follows:




927. Implicitly-deleted default constructors and member initializers

Section: 11.4.5  [class.ctor]     Status: CD2     Submitter: Alisdair Meredith     Date: 1 July, 2009

[Voted into WP at March, 2010 meeting.]

(From message 14555.)

The reasons for which an implicitly-declared default constructor is defined as deleted, given in 11.4.5 [class.ctor] paragraph 4, all deal with cases in which a member cannot be default-initialized. Presumably a brace-or-equal-initializer for such a member would eliminate the need to define the constructor as deleted, but this case is not addressed by the current wording.

Proposed resolution (October, 2009):

Change 11.4.5 [class.ctor] paragraph 5, the second list, as follows:

An implicitly-declared default constructor for class X is defined as deleted if:




667. Trivial special member functions that cannot be implicitly defined

Section: 11.4.5.3  [class.copy.ctor]     Status: CD2     Submitter: James Widman     Date: 14 December 2007

[Voted into WP at March, 2010 meeting as part of document N3079.]

Should the following class have a trivial copy assignment operator?

    struct A {
        int& m;
        A();
        A(const A&);
    };

11.4.5.3 [class.copy.ctor] paragraph 11 does not mention whether the presence of reference members (or cv-qualifiers, etc.) should affect triviality. Should it?

One reason why this matters is that implementations have to make the builtin type trait operator __has_trivial_default_ctor(T) work so that they can support the type trait template std::has_trivial_default_constructor.

Assuming the answer is “yes,” it looks like we probably need similar wording for trivial default and trivial copy ctors.

Notes from the February, 2008 meeting:

Deleted special member functions are also not trivial. Resolution of this issue should be coordinated with the concepts proposal.

Notes from the June, 2008 meeting:

It appears that this issue will be resolved by the concepts proposal directly. The issue is in “review” status to check if that is indeed the case in the final version of the proposal.

Additional notes (May, 2009):

Consider the following example:

    struct Base {
      private:
        ~Base() = default;
    };

    struct Derived: Base {
    };

The implicitly-declared destructor of Derived is defined as deleted because Base::~Base() is inaccessible, but it fulfills the requirements for being trivial. Presumably the Base destructor should be non-trivial, either by directly specifying that it is non-trivial or by specifying that it is user-provided. An alternative would be to make it ill-formed to attempt to declare a defaulted non-public special member function.

Any changes to the definition of triviality should be checked against Clause 11 [class] paragraph 6 for any changes needed there to accommodate the new definitions.

Notes from the July, 2009 meeting:

The July, 2009 resolution of issue 906 addresses the example above (with an inaccessible defaulted destructor): a defaulted special member function can only have non-public access if the defaulted definition is outside the class, making it non-trivial. The example as written above would be ill-formed.

Proposed resolution (October, 2009):

  1. Change 9.5 [dcl.fct.def] paragraph 9 as follows:

  2. ...Only special member functions may be explicitly defaulted. Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall define them as if they had provide implicit definitions for them (11.4.5 [class.ctor], 11.4.7 [class.dtor], 11.4.5.3 [class.copy.ctor]), which might mean defining them as deleted. A special member function that would be implicitly defined as deleted may be explicitly defaulted only on its first declaration, in which case it is defined as deleted. A special member function is user-provided if it is user-declared and not explicitly defaulted on its first declaration. A user-provided explicitly-defaulted function is defined at the point where it is explicitly defaulted. [Note:...
  3. Change 11.4.5 [class.ctor] paragraphs 5-6 as follows:

  4. A default constructor for a class X is a constructor of class X that can be called without an argument. If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted (9.5 [dcl.fct.def]). An implicitly-declared default constructor is an inline public member of its class. A default constructor is trivial if it is not user-provided (9.5 [dcl.fct.def]) and if:

    An implicitly-declared defaulted default constructor for class X is defined as deleted if:

    A default constructor is trivial if it is neither user-provided nor deleted and if:

    Otherwise, the default constructor is non-trivial.

    A non-user-provided default constructor for a class that is defaulted and not deleted is implicitly defined when it is used (6.3 [basic.def.odr]) to create an object of its class type (6.7.2 [intro.object]), or when it is explicitly defaulted after its first declaration. The implicitly-defined or explicitly-defaulted default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer (11.9.3 [class.base.init]) and an empty compound-statement. If that user-written default constructor would be ill-formed, the program is ill-formed. If that user-written default constructor would satisfy the requirements of a constexpr constructor (9.2.6 [dcl.constexpr]), the implicitly-defined default constructor is constexpr. Before the non-user-provided defaulted default constructor for a class is implicitly defined, all the non-user-provided default constructors for its base classes and its non-static data members shall have been implicitly defined. [Note: an implicitly-declared default constructor has an exception-specification (14.5 [except.spec]). An explicitly-defaulted definition has no implicit exception-specification. —end note]

  5. Change 11.4.7 [class.dtor] paragraphs 3-4 as follows:

  6. If a class has no user-declared destructor, a destructor is declared implicitly declared as defaulted (9.5 [dcl.fct.def]). An implicitly-declared destructor is an inline public member of its class. If the class is a union-like class that has a variant member with a non-trivial destructor, an implicitly-declared destructor is defined as deleted (9.5 [dcl.fct.def]). A destructor is trivial if it is not user-provided and if:

    An implicitly-declared defaulted destructor for a class X is defined as deleted if:

    A destructor is trivial if it is neither user-provided nor deleted and if:

    Otherwise, the destructor is non-trivial.

    A non-user-provided destructor that is defaulted and not defined as deleted is implicitly defined when it is used to destroy an object of its class type (6.7.5 [basic.stc]), or when it is explicitly defaulted after its first declaration. A program is ill-formed if the class for which a destructor is implicitly defined or explicitly defaulted has:

    Before the non-user-provided defaulted destructor for a class is implicitly defined, all the non-user-defined non-user-provided destructors for its base classes and its non-static data members shall have been implicitly defined. [Note: an implicitly-declared destructor has an exception-specification (14.5 [except.spec]). An explictly defaulted definition has no implicit exception-specification. —end note]

  7. Change 11.4.5.3 [class.copy.ctor] paragraphs 4-9 as follows:

  8. If the class definition does not explicitly declare a copy constructor, one is declared implicitly implicitly declared as defaulted (9.5 [dcl.fct.def]). Thus...

    ...An implicitly-declared copy constructor is an inline public member of its class. An implicitly-declared defaulted copy constructor for a class X is defined as deleted if X has: ...

    A copy constructor for class X is trivial trivial if it is not neither user-provided nor deleted (9.5 [dcl.fct.def]) and if...

    A non-user-provided copy constructor that is defaulted and not defined as deleted is implicitly defined if it is used to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type116, or when it is explicitly defaulted after its first declaration. [Note: the copy constructor is implicitly defined even if the implementation elided its use (6.7.7 [class.temporary]). —end note]

    Before the non-user-provided defaulted copy constructor for a class is implicitly defined, all non-user-provided copy constructors...

    The implicitly-defined or explicitly-defaulted copy constructor for a non-union class X performs...

    The implicitly-defined or explicitly-defaulted copy constructor for a union X copies the object representation (6.8 [basic.types]) of X.

  9. Change 11.4.5.3 [class.copy.ctor] paragraphs 11-15 as follows:

  10. If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly implicitly declared as defaulted (9.5 [dcl.fct.def])...

    ...An implicitly-declared defaulted copy assignment operator for class X is defined as deleted if X has:...

    A copy assignment operator for class X is trivial if it is not neither user-provided nor deleted and if...

    A non-user-provided copy assignment operator that is defaulted and not defined as deleted is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type, or when it is explicitly defaulted after its first declaration.

    Before the non-user-provided defaulted copy assignment operator for a class is implicitly defined...

    The implicitly-defined or explicitly-defaulted copy assignment operator for a non-union class X performs...

    It is unspecified whether subobjects representing virtual base classes are assigned more than once by the implicitly-defined or explicitly-defaulted copy assignment operator. [Example:...

    The implicitly-defined or explicitly-defaulted copy assignment operator for a union X copies the object representation (6.8 [basic.types]) of X.




680. What is a move constructor?

Section: 11.4.5.3  [class.copy.ctor]     Status: CD2     Submitter: Steve Adamczyk     Date: 3 March, 2008

N2800 comment US 33

[Voted into the WP at the July, 2009 meeting as part of N2927.]

Although the term “move constructor” appears multiple times in the library clauses and is referenced in the newly-added text for the lambda feature, it is not defined anywhere.

Notes from the June, 2008 meeting:

The only reference to “move constructor” in the core language clauses of the Standard is in 7.5.5 [expr.prim.lambda] paragraph 10; there are no semantic implications of the term. This issue will be addressed by using a function signature instead of the term, thus allowing the library section to provide a definition that is appropriate for its needs.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




887. Move construction of thrown object

Section: 11.4.5.3  [class.copy.ctor]     Status: CD2     Submitter: Steve Adamczyk     Date: 6 May, 2009

[Voted into WP at March, 2010 meeting.]

11.4.5.3 [class.copy.ctor] paragraph 16 details the conditions under which a thrown object can be moved instead of copied. However, the optimization as currently described is unsafe. Consider the following example:

    void f() {
        X x;
        try {
            throw x;
        } catch (...) {
        }
        // x may have been moved from but can still be accessed here
    }

When the operation is a throw, as opposed to a return, there must be a restriction that the object potentially being moved be defined within the innermost enclosing try block.

Notes from the July, 2009 meeting:

It is not clear how important this optimization is in the context of throw: how often is a large object with substantial copying overhead thrown? Also, throwing an exception is already a heavyweight operation, so presumably moving instead of copying an object would not make much difference.

Proposed resolution (October, 2009):

Change 11.4.5.3 [class.copy.ctor] paragraph 17 second bullet as follows:




910. Move constructors and implicitly-declared copy constructors

Section: 11.4.5.3  [class.copy.ctor]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 4 June, 2009

[Voted into WP at March, 2010 meeting as document N3053.]

A constructor of the form T::T(T&&) is a candidate function for copy construction; however, the declaration of such a constructor does not inhibit the implicit declaration and definition of a copy constructor. This can lead to surprising results. We should consider suppressing the implicit copy constructor if a move constructor is declared.




653. Copy assignment of unions

Section: 11.4.6  [class.copy.assign]     Status: CD2     Submitter: Jens Maurer     Date: 3 October 2007

[Voted into WP at July, 2009 meeting.]

How does copy assignment for unions work? For example,

  union U {
    int a;
    float b;
  };

  void f() {
    union U u = { 5 };
    union U v;
    v = u;    // what happens here?
  }

11.5 [class.union] is silent on the issue, therefore it seems that 11.4.5.3 [class.copy.ctor] applies. There is no special case for unions, thus paragraph 13 (memberwise assignment of subobjects) seems to apply. That would seem to imply these actions in the compiler-generated copy assignment operator:

  v.a = u.a;
  v.b = u.b;

And this is just wrong. For example, the lifetime of v.a ends once the second assignment reuses the memory of v.a.

We should probably prescribe “memcpy” copying for unions (both for the copy constructor and the assignment operator) unless the user provided his own special member function.

Proposed resolution (March, 2008):

  1. Change 11.4.5.3 [class.copy.ctor] paragraph 8 as follows:

  2. The implicitly-defined or explicitly-defaulted copy constructor for a non-union class X performs a memberwise copy of its subobjects...
  3. Add a new paragraph after 11.4.5.3 [class.copy.ctor] paragraph 8:

  4. The implicitly-defined or explicitly-defaulted copy constructor for a union X where all members have a trivial copy constructor copies the object representation (6.8 [basic.types]) of X. [Note: The behavior is undefined if X is not a trivial type. —end note]
  5. Change 11.4.5.3 [class.copy.ctor] paragraph 13 as follows:

  6. The implicitly-defined or explicitly-defaulted copy assignment operator for a non-union class X performs memberwise assignment of its subobjects...
  7. Add a new paragraph after 11.4.5.3 [class.copy.ctor] paragraph 13:

  8. The implicitly-defined or explicitly-defaulted copy assignment operator for a union X where all members have a trivial copy assignment operator copies the object representation (6.8 [basic.types]) of X. [Note: The behavior is undefined if X is not a trivial type. —end note]

Notes from the September, 2008 meeting:

The proposed wording needs to be updated to reflect the changes adopted in papers N2757 and N2762, resolving issue 683, which require “no non-trivial” special member functions instead of “a trivial” function. Also, the notes regarding undefined behavior are incorrect, because the member functions involved are defined as deleted when there are non-trivial members.

Proposed resolution (October, 2008):

  1. Change 11.4.5.3 [class.copy.ctor] paragraph 8 as follows:

  2. The implicitly-defined or explicitly-defaulted copy constructor for a non-union class X performs a memberwise copy of its subobjects...
  3. Add a new paragraph following 11.4.5.3 [class.copy.ctor] paragraph 8:

  4. The implicitly-defined or explicitly-defaulted copy constructor for a union X copies the object representation (6.8 [basic.types]) of X.
  5. Change 11.4.5.3 [class.copy.ctor] paragraph 13 as follows:

  6. The implicitly-defined or explicitly-defaulted copy assignment operator for a non-union class X performs memberwise assignment of its subobjects...
  7. Add a new paragraph following 11.4.5.3 [class.copy.ctor] paragraph 13:

  8. The implicitly-defined or explicitly-defaulted copy assignment operator for a union X copies the object representation (6.8 [basic.types]) of X.



714. Static const data members and braced-init-lists

Section: 11.4.9.3  [class.static.data]     Status: CD2     Submitter: Steve Adamczyk     Date: 15 September, 2008

[Voted into WP at July, 2009 meeting.]

The recent changes in the handling of initialization have not touched the requirement that the in-class initializer for a const static data member must be of the form = assignment-expression and not a braced-init-list. It would be more consistent and general to allow the braced form as well.

Proposed resolution (March, 2009):

  1. Change 7.7 [expr.const] paragraph 3 as follows:

  2. ...as enumerator initializers (9.7.1 [dcl.enum]), as static member initializers (11.4.9.3 [class.static.data]), and as integral or enumeration non-type template arguments (13.6 [temp.type]).
  3. Change 11.4.9.3 [class.static.data] paragraph 3 as follows:

  4. If a static data member is of const effective literal type, its declaration in the class definition can specify a brace-or-equal-initializer with an in which every initializer-clause that is an assignment-expression is a integral constant expression. A static data member of effective literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer with an in which every initializer-clause that is an assignment-expression is a integral constant expression. [Note: In both these cases, the member may appear in integral constant expressions. end note] The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.

[Drafting note: this change also corrects an editorial error resulting from overlapping changes that inadvertently retained the original restriction that only members of integral type could be initialized inside the class definition.]




716. Specifications that should apply only to non-static union data members

Section: 11.5  [class.union]     Status: CD2     Submitter: Mike Miller     Date: 17 September, 2008

[Voted into WP at July, 2009 meeting.]

Unions are no longer forbidden to have static data members; however, much of the wording of 11.5 [class.union] (and possibly other places in the Standard) is still written with that assumption and refers only to “data members” when clearly non-static data members are in view. From paragraph 1, for example:

In a union, at most one of the data members can be active at any time... The size of a union is sufficient to contain the largest of its data members...

Proposed resolution (March, 2009):

  1. Change the footnote in 6.8.5 [basic.type.qualifier] paragraph 1 as follows:

  2. The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and non-static data members of unions.
  3. Change 7.2.1 [basic.lval] bullet 15.6 as follows:

  4. Change 7.6.9 [expr.rel] bullet 2.5 as follows:

  5. Change 9.12.2 [dcl.align] paragraph 8 as follows:

  6. [Note: the alignment of a union type can be strengthened by applying the alignment attribute to any non-static data member of the union. —end note]
  7. Change 9.4.2 [dcl.init.aggr] paragraph 15 as follows:

  8. When a union is initialized with a brace-enclosed initializer, the braces shall only contain an initializer-clause for the first non-static data member of the union...
  9. Change 11.5 [class.union] paragraph 1 as follows:

  10. In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time. [Note: one special guarantee is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout structs that share a common initial sequence (11.4 [class.mem]), and if an object of this standard-layout union type contains one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of standard-layout struct members; see 11.4 [class.mem]. —end note] The size of a union is sufficient to contain the largest of its non-static data members. Each non-static data member is allocated as if it were the sole member of a struct. A union can have...



608. Determining the final overrider of a virtual function

Section: 11.7.3  [class.virtual]     Status: CD2     Submitter: Mike Miller     Date: 7 December 2006

[Voted into WP at October, 2009 meeting.]

According to 11.7.3 [class.virtual] paragraph 2:

Then in any well-formed class, for each virtual function declared in that class or any of its direct or indirect base classes there is a unique final overrider that overrides that function and every other overrider of that function. The rules for member lookup (6.5.2 [class.member.lookup]) are used to determine the final overrider for a virtual function in the scope of a derived class but ignoring names introduced by using-declarations.

I think that description is wrong on at least a couple of counts. First, consider the following example:

    struct A { virtual void f(); };
    struct B: A { };
    struct C: A { void f(); };
    struct D: B, C { };

What is the “unique final overrider” of A::f() in D? According to 11.7.3 [class.virtual] paragraph 2, we determine that by looking up f in D using the lookup rules in 6.5.2 [class.member.lookup]. However, that lookup determines that f in D is ambiguous, so there is no “unique final overrider” of A::f() in D. Consequently, because “any well-formed class” must have such an overrider, D must be ill-formed.

Of course, we all know that D is not ill-formed. In fact, 11.7.3 [class.virtual] paragraph 10 contains an example that illustrates exactly this point:

struct A {
    virtual void f();
};
struct B1 : A {     // note non-virtual derivation
    void f();
};
struct B2 : A {
    void f();
};
struct D : B1, B2 { // D has two separate A subobjects
};

In class D above there are two occurrences of class A and hence two occurrences of the virtual member function A::f. The final overrider of B1::A::f is B1::f and the final overrider of B2::A::f is B2::f.

It appears that the requirement for a “unique final overrider” in 11.7.3 [class.virtual] paragraph 2 needs to say something about sub-objects. Whatever that “something” is, you can't just say “look up the name in the derived class using 6.5.2 [class.member.lookup].”

There's another problem with using the 6.5.2 [class.member.lookup] lookup to specify the final overrider: name lookup just looks up the name, while the overriding relationship is based not only on the name but on a matching parameter-type-list and cv-qualification. To illustrate this point:

    struct X {
        virtual void f();
    };
    struct Y: X {
        void f(int);
    };
    struct Z: Y { };

What is the “unique final overrider” of X::f() in A? Again, 11.7.3 [class.virtual] paragraph 2 says you're supposed to look up f in Z to find it; however, what you find is Y::f(int), not X::f(), and that's clearly wrong.

Proposed Resolution (December, 2006):

Change 11.7.3 [class.virtual] paragraph 2 as follows:

Then in any well-formed class, for each virtual function declared in that class or any of its direct or indirect base classes there is a unique final overrider that overrides that function and every other overrider of that function. The rules for member lookup (6.5.2 [class.member.lookup]) are used to determine the final overrider for a virtual function in the scope of a derived class but ignoring names introduced by using-declaration s. A virtual member function vf of a class C is a final overrider unless the most derived class (6.7.2 [intro.object]) of which C is a base class (if any) declares or inherits another member function that overrides vf. In a derived class, if a virtual member function of a base class subobject has more than one final overrider, the program is ill-formed.

Proposed resolution (July, 2009):

Change 11.7.3 [class.virtual] paragraph 2 as follows:

...Then in any well-formed class, for each virtual function declared in that class or any of its direct or indirect base classes there is a unique final overrider that overrides that function and every other overrider of that function. The rules for member lookup (6.5.2 [class.member.lookup]) are used to determine the final overrider for a virtual function in the scope of a derived class but ignoring names introduced by using-declarations. A virtual member function C::vf of a class object S is a final overrider unless the most derived class (6.7.2 [intro.object]) of which S is a base class subobject (if any) declares or inherits another member function that overrides vf. In a derived class, if a virtual member function of a base class subobject has more than one final overrider, the program is ill-formed. [Example: ... —end example] [Example:

    struct A { virtual void f(); };
    struct B: A { };
    struct C: A { void f(); };
    struct D: B, C { };    // OK; A::f and C::f are the final overriders
                           // for the B and C subobjects, respectively

end example]




939. Explicitly checking virtual function overriding

Section: 11.7.3  [class.virtual]     Status: CD2     Submitter: FI/US     Date: 14 July, 2009

N2800 comment FI 1
N2800 comment US 41

[Voted into WP at July, 2009 meeting as N2928.]

There should be a way to detect errors in overriding a virtual function.

Proposed resolution (July, 2009):

This issue is resolved by paper PL22.16/09-0118 = WG21 N2928.




960. Covariant functions and lvalue/rvalue references

Section: 11.7.3  [class.virtual]     Status: CD2     Submitter: James Widman     Date: 1 September, 2009

[Voted into WP at March, 2010 meeting.]

11.7.3 [class.virtual] paragraph 5 requires that covariant return types be either both pointers or both references, but it does not specify that references must be both lvalue references or both rvalue references. Presumably this is an oversight.

Proposed resolution (February, 2010):

Change 11.7.3 [class.virtual] bullet 5.1 as follows:

...If a function D::f overrides a function B::f, the return types of the functions are covariant if they satisfy the following criteria:




542. Value initialization of arrays of POD-structs

Section: 11.9  [class.init]     Status: CD2     Submitter: Alisdair Meredith     Date: 27 October 2005

[Voted into the WP at the March, 2009 meeting.]

11.9 [class.init] paragraph 2 says,

When an array of class objects is initialized (either explicitly or implicitly), the constructor shall be called for each element of the array, following the subscript order;

That implies that, given

    struct POD {
      int x;
    };

    POD data[10] = {};

this should call the implicitly declared default ctor 10 times, leaving 10 uninitialized ints, rather than value initialize each member of data, resulting in 10 initialized ints (which is required by 9.4.2 [dcl.init.aggr] paragraph 7).

I suggest rephrasing along the lines:

When an array is initialized (either explicitly or implicitly), each element of the array shall be initialized in turn, following the subscript order;

This would allow for PODs and other classes with a dual nature under value/default initialization, and cover copy initialization for arrays too.

Proposed resolution (October, 2006):

Change 11.9 [class.init] paragraph 3 as follows:

When an array of class objects is initialized (either explicitly or implicitly) and the elements are initialized by constructor, the constructor shall be called for each element of the array, following the subscript order; see 9.3.4.5 [dcl.array].



257. Abstract base constructors and virtual base initialization

Section: 11.9.3  [class.base.init]     Status: CD2     Submitter: Mike Miller     Date: 1 Nov 2000

[Voted into WP at October, 2009 meeting.]

Must a constructor for an abstract base class provide a mem-initializer for each virtual base class from which it is directly or indirectly derived? Since the initialization of virtual base classes is performed by the most-derived class, and since an abstract base class can never be the most-derived class, there would seem to be no reason to require constructors for abstract base classes to initialize virtual base classes.

It is not clear from the Standard whether there actually is such a requirement or not. The relevant text is found in 11.9.3 [class.base.init] paragraph 6:

All sub-objects representing virtual base classes are initialized by the constructor of the most derived class (6.7.2 [intro.object]). If the constructor of the most derived class does not specify a mem-initializer for a virtual base class V, then V's default constructor is called to initialize the virtual base class subobject. If V does not have an accessible default constructor, the initialization is ill-formed. A mem-initializer naming a virtual base class shall be ignored during execution of the constructor of any class that is not the most derived class.

This paragraph requires only that the most-derived class's constructor have a mem-initializer for virtual base classes. Should the silence be construed as permission for constructors of classes that are not the most-derived to omit such mem-initializers?

Christopher Lester, on comp.std.c++, March 19, 2004: If any of you reading this posting happen to be members of the above working group, I would like to encourage you to review the suggestion contained therein, as it seems to me that the final tenor of the submission is both (a) correct (the silence of the standard DOES mandate the omission) and (b) describes what most users would intuitively expect and desire from the C++ language as well.

The suggestion is to make it clearer that constructors for abstract base classes should not be required to provide initialisers for any virtual base classes they contain (as only the most-derived class has the job of initialising virtual base classes, and an abstract base class cannot possibly be a most-derived class).

For example:

struct A {
  A(const int i, const int j) {};
};

struct B1 : virtual public A {
  virtual void moo()=0;
  B1() {};   // (1) Look! not "B1() : A(5,6) {};"
};

struct B2 : virtual public A {
  virtual void cow()=0;
  B2() {};   // (2) Look! not "B2() : A(7,8) {};"
};

struct C : public B1, public B2 {
  C() : A(2,3) {};
  void moo() {};
  void cow() {};
};

int main() {
  C c;
  return 0;
};

I believe that, by not expressly forbidding it, the standard does (and should!) allow the above code. However, as the standard doesn't expressly allow it either (have I missed something?) there appears to be room for misunderstanding. For example, g++ version 3.2.3 (and maybe other versions as well) rejects the above code with messages like:

	In constructor `B1::B1()':
	no matching function for call to `A::A()'
	candidates are: A::A(const A&)
         	        A::A(int, int)

Fair enough, the standard is perhaps not clear enough. But it seems to be a shame that although this issue was first raised in 2000, we are still living with it today.

Note that we can work-around, and persuade g++ to compile the above by either (a) providing a default constructor A() for A, or (b) supplying default values for i and j in A(i,j), or (c) replace the construtors B1() and B2() with the forms shown in the two comments in the above example.

All three of these workarounds may at times be appropriate, but equally there are other times when all of these workarounds are particularly bad. (a) and (b) may be very bad if you are trying to enforce string contracts among objects, while (c) is just barmy (I mean why did I have to invent random numbers like 5, 6, 7 and 8 just to get the code to compile?).

So to to round up, then, my plea to the working group is: "at the very least, please make the standard clearer on this issue, but preferrably make the decision to expressly allow code that looks something like the above"

Proposed resolution (July, 2009):

  1. Add the indicated text (moved from paragraph 11) to the end of 11.9.3 [class.base.init] paragraph 7:

  2. ...The initialization of each base and member constitutes a full-expression. Any expression in a mem-initializer is evaluated as part of the full-expression that performs the initialization. A mem-initializer where the mem-initializer-id names a virtual base class is ignored during execution of a constructor of any class that is not the most derived class.
  3. Change 11.9.3 [class.base.init] paragraph 8 as follows:

  4. If a given non-static data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (11.7.4 [class.abstract]), then

    [Note: An abstract class (11.7.4 [class.abstract]) is never a most derived class, thus its constructors never initialize virtual base classes, therefore the corresponding mem-initializers may be omitted. —end note] After the call to a constructor for class X has completed...

  5. Change 11.9.3 [class.base.init] paragraph 10 as follows:

  6. Initialization shall proceed proceeds in the following order:

    [Note: the declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. —end note]

  7. Remove all normative text in 11.9.3 [class.base.init] paragraph 11, keeping the example:

  8. All subobjects representing virtual base classes are initialized by the constructor of the most derived class (6.7.2 [intro.object]). If the constructor of the most derived class does not specify a mem-initializer for a virtual base class V, then V's default constructor is called to initialize the virtual base class subobject. If V does not have an accessible default constructor, the initialization is ill-formed. A mem-initializer naming a virtual base class shall be ignored during execution of the constructor of any class that is not the most derived class. [Example:...



888. Union member initializers

Section: 11.9.3  [class.base.init]     Status: CD2     Submitter: Alisdair Meredith     Date: 6 May, 2009

[Voted into WP at October, 2009 meeting.]

11.9.3 [class.base.init] paragraph 5 forbids initializing multiple members of a union via mem-initializers:

If a ctor-initializer specifies more than one mem-initializer for the same member, for the same base class or for multiple members of the same union (including members of anonymous unions), the ctor-initializer is ill-formed.

However, there is no corresponding restriction against specifying brace-or-equal-initializers for multiple union members, nor for a non-overlapping pair of brace-or-equal-initializer and mem-initializer. This is presumably an oversight.

Proposed resolution (July, 2009):

  1. Change 11.5 [class.union] paragraph 1 as follows:

  2. ...If a union contains a non-static data member of reference type the program is ill-formed. At most one non-static data member of a union shall have a brace-or-equal-initializer. [Note:...
  3. Change 11.9.3 [class.base.init] paragraph 5 as follows:

  4. ...If a ctor-initializer specifies more than one mem-initializer for the same member, or for the same base class or for multiple members of the same union (including members of anonymous unions), the ctor-initializer is ill-formed.
  5. Change 11.9.3 [class.base.init] paragraph 8 as follows:

  6. ...An attempt to initialize more than one non-static data member of a union renders the program ill-formed. After the call to a constructor for class X has completed...



710. Data races during construction

Section: 11.9.5  [class.cdtor]     Status: CD2     Submitter: Jeffrey Yasskin     Date: 3 May, 2008

[Voted into WP at March, 2010 meeting.]

Consider the following example:

    struct A {
      A() {
        std::thread(&A::Func, this).detach();
      }
      virtual void Func() {
        printf("In A");
      }
    };

    struct B : public A {
      virtual void Func() {
        printf("In B");
      }
    };

    struct C : public B {
      virtual void Func() {
        printf("In C");
      }
    };

    C c;

What is the program allowed to print? Should it be undefined behavior or merely unspecified which of the Func()s is called?

There is a related question about which variables C::Func() can depend on having been constructed. Unless we want to require the equivalent of at least memory_order_consume on the presumed virtual function table pointer, I think the answer is just the members of A.

If I instead just have

    A a;

I think the only reasonable behavior is to print In A.

Finally, given

    struct F {
      F() {
        std::thread(&F::Func, this).detach();
      }
      virtual void Func() {
        print("In F");
      }
    };

    struct G : public F {
    };

    G g;

I can see the behavior being undefined, but I think a lot of people would be confused if it did anything other than print In F.

Suggested resolution:

I think the intent here is that an object should not be used in another thread until any non-trivial constructor has been called. One possible way of saying that would be to add a new paragraph at the end of 11.9.5 [class.cdtor]:

A constructor for a class with virtual functions or virtual base classes modifies a memory location in the object that is accessed by any access to a virtual function or virtual base class or by a dynamic_cast. [Note: This implies that access to an object by another thread while it is being constructed often introduces a data race (see 6.9.2 [intro.multithread]). —end note]

Proposed resolution (October, 2009):

Add the following as a new paragraph at the end of 6.7.3 [basic.life]:

In this section, “before” and “after” refer to the “happens before” relation (6.9.2 [intro.multithread]). [Note: Therefore, undefined behavior results if an object that is being constructed in one thread is referenced from a different thread without adequate synchronization. —end note]



999. “Implicit” or “implied” object argument/parameter?

Section: 12.2  [over.match]     Status: CD2     Submitter: James Widman     Date: 20 November, 2009

[Voted into WP at March, 2010 meeting.]

The terminology used to refer to the parameter for this and its corresponding argument is inconsistent, sometimes using “implied” and sometimes “implicit.” It would be easier to search the text of the Standard if this usage were made regular.

Proposed resolution (February, 2010):

  1. Change the index to refer to “implicit object parameter” and “implied object argument” instead of the current permutations of these terms.

  2. Change 12.2 [over.match] paragraph 1 as follows:

  3. ...how well (for non-static member functions) the object matches the implied implicit object parameter...
  4. Change 12.2.2 [over.match.funcs] paragraph 4 as follows:

  5. ...For conversion functions, the function is considered to be a member of the class of the implicit implied object argument for the purpose of defining the type of the implicit object parameter...
  6. Change the footnote in 12.2.4 [over.match.best] bullet 1.1 as follows:




704. To which postfix-expressions does overload resolution apply?

Section: 12.2.2.2  [over.match.call]     Status: CD2     Submitter: Jens Maurer     Date: 29 July, 2008

[Voted into WP at October, 2009 meeting.]

There are several problems with the phrasing of 12.2.2.2 [over.match.call] paragraphs 1 and 3. Paragraph 1 reads,

Recall from 7.6.1.3 [expr.call], that a function call is a postfix-expression, possibly nested arbitrarily deep in parentheses, followed by an optional expression-list enclosed in parentheses: Overload resolution is required if the postfix-expression is the name of a function, a function template (13.7.7 [temp.fct]), an object of class type, or a set of pointers-to-function.

Aside from the fact that directly addressing the reader (“Recall that...”) is stylistically incongruous with the rest of the Standard, as well as the fact that 7.6.1.3 [expr.call] doesn't mention parentheses at all, this wording does not cover member function calls: a member access expression isn't “the name” of anything. This should perhaps be reworded to refer to being either an id-expression or the id-expression in a member access expression. This could be either by using two lines in the “of the form” citation or in the discussion following the syntax reference.

In addition, paragraph 3 refers to “a postfix-expression of the form &F,” which is an oxymoron: &F is a unary-expression, not a postfix-expression. One possibility would be to explicitly include the parentheses needed in this case, i.e., “a postfix-expression of the form (&F)...”

Proposed resolution (September, 2009):

Replace the entirety of 12.2.2.2 [over.match.call] with the following two paragraphs:

In a function call (7.6.1.3 [expr.call])

if the postfix-expression denotes a set of overloaded functions and/or function templates, overload resolution is applied as specified in 12.2.2.2.2 [over.call.func]. If the postfix-expression denotes an object of class type, overload resolution is applied as specified in 12.2.2.2.3 [over.call.object].

If the postfix-expression denotes the address of a set of overloaded functions and/or function templates, overload resolution is applied using that set as described above. If the function selected by overload resolution is a non-static member function, the program is ill-formed. [Note: The resolution of the address of an overload set in other contexts is described in 12.3 [over.over]. —end note]




604. Argument list for overload resolution in copy-initialization

Section: 12.2.2.4  [over.match.ctor]     Status: CD2     Submitter: Dawn Perchik     Date: 4 November 2006

[Voted into WP at October, 2009 meeting.]

According to 12.2.2.4 [over.match.ctor],

When objects of class type are direct-initialized (9.4 [dcl.init]), or copy-initialized from an expression of the same or a derived class type (9.4 [dcl.init])... [the] argument list is the expression-list within the parentheses of the initializer.

However, in copy initialization (using the “=” notation), there need be no parentheses. What is the argument list in that case?

Proposed resolution (June, 2009):

Change 12.2.2.4 [over.match.ctor] paragraph 1 as follows:

...The argument list is the expression-list or assignment-expression within the parentheses of the initializer initializer.



899. Explicit conversion functions in direct class initialization

Section: 12.2.2.5  [over.match.copy]     Status: CD2     Submitter: Jason Merrill     Date: 13 May, 2009

[Voted into WP at March, 2010 meeting.]

Consider the following example:

    struct C { };

    struct A {
       explicit operator int() const;
       explicit operator C() const;
    };

    struct B {
       int i;
       B(const A& a): i(a) { }
    };

    int main() {
       A a;
       int i = a;
       int j(a);
       C c = a;
       C c2(a);
    }

It's clear that the B constructor and the declaration of j are well-formed and the declarations of i and c are ill-formed. But what about the declaration of c2? This is supposed to work, but it doesn't under the current wording.

C c2(a) is direct-initialization of a class, so constructors are considered. The only possible candidate is the default copy constructor. So we look for a conversion from A to const C&. There is a conversion operator to C, but it is explicit and we are now performing copy-initialization of a reference temporary, so it is not a candidate, and the declaration of c2 is ill-formed.

Proposed resolution (October, 2009):

Change 12.2.2.5 [over.match.copy] paragraph 1 second bullet as follows:




641. Overload resolution and conversion-to-same-type operators

Section: 12.2.3  [over.match.viable]     Status: CD2     Submitter: Nathan Sidwell     Date: 2 Aug 2007

[Voted into the WP at the March, 2009 meeting.]

11.4.8.3 [class.conv.fct] paragraph 1 says,

A conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to (possibly cv-qualified) void.

At what point is this enforced, and how is it enforced?

  1. Does such a user-declared conversion operator participate in overload resolution? Or is it never entered into the overload set?
  2. If it does participate in overload resolution, what happens if it is selected? Is the program ill-formed (and diagnostic required), or is it silently ignored? The above wording doesn't really make it clear.

Consider this test case:

    struct abc;

    struct xyz {
       xyz();

       xyz(xyz &);

       operator xyz& (); // #1
       operator abc& (); // #2
    };

    struct abc : xyz {};

    void foo(xyz &);

    void bar() {
             foo (xyz ());
    }

If such conversion functions are part of the overload set, #1 is a better conversion than #2 to convert the temporary xyz object to a non-const reference required for foo's operand. If such conversion functions are not part of the overload set, then #2 would be selected, and AFAICT the program would be well formed.

If the conversion functions are not part of the overload set, then it would seem one cannot take their address. For instance, adding the following line to the above test case would find no suitable function:

    xyz &(xyz::*ptr) () = &xyz::operator xyz &;

Notes from the October, 2007 meeting:

The intent of 11.4.8.3 [class.conv.fct] paragraph 1 is that overload resolution not be attempted at all for the listed cases; that is, if the target type is void, the object's type, or a base of the object's type, the conversion is done directly without considering any conversion functions. Consequently, the questions about whether the conversion function is part of the overload set or not are moot. The wording will be changed to make this clearer.

Proposed Resolution (October, 2007):

Change the footnote in 11.4.8.3 [class.conv.fct] paragraph 1 as follows:

A conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to (possibly cv-qualified) void. [Footnote: These conversions are considered as standard conversions for the purposes of overload resolution (12.2.4.2 [over.best.ics], 12.2.4.2.5 [over.ics.ref]) and therefore initialization (9.4 [dcl.init]) and explicit casts (7.6.1.9 [expr.static.cast]). A conversion to void does not invoke any conversion function (7.6.1.9 [expr.static.cast]). Even though never directly called to perform a conversion, such conversion functions can be declared and can potentially be reached through a call to a virtual conversion function in a base class —end footnote]

Additional note (March, 2008):

A slight change to the example above indicates that there is a need for a normative change as well as the clarification of the rationale in the October, 2007 proposed resolution. If the declaration of foo were changed to

    void foo(const xyz&);

with the current wording, the call foo(xyz()) would be interpreted as foo(xyz().operator abc&()) instead of binding the parameter directly to the rvalue, which is clearly wrong.

Proposed resolution (March, 2008):

  1. Change the footnote in 11.4.8.3 [class.conv.fct] paragraph 1 as described in the October, 2007 proposed resolution.

  2. Change 9.4.4 [dcl.init.ref] paragraph 5 as follows:

  3. A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

    [Drafting note: this resolution makes the example in the issue description ill-formed.]




877. Viable functions and binding references to rvalues

Section: 12.2.3  [over.match.viable]     Status: CD2     Submitter: Daniel Krügler     Date: 23 April, 2009

[Voted into WP at October, 2009 meeting.]

12.2.3 [over.match.viable] paragraph 3 says,

If the parameter has reference type, the implicit conversion sequence includes the operation of binding the reference, and the fact that a reference to non-const cannot be bound to an rvalue can affect the viability of the function (see 12.2.4.2.5 [over.ics.ref]).

This should say “lvalue reference to non-const,” as is correctly stated in 12.2.4.2.5 [over.ics.ref] paragraph 3.

Proposed resolution (July, 2009):

Change 12.2.3 [over.match.viable] paragraph 3 as follows:

If the parameter has reference type, the implicit conversion sequence includes the operation of binding the reference, and the fact that a an lvalue reference to non-const cannot be bound to an rvalue can affect the viability of the function (see 12.2.4.2.5 [over.ics.ref]).



495. Overload resolution with template and non-template conversion functions

Section: 12.2.4  [over.match.best]     Status: CD2     Submitter: Nathan Sidwell     Date: 20 Dec 2004

[Voted into WP at July, 2009 meeting.]

The overload resolution rules for ranking a template against a non-template function differ for conversion functions in a surprising way. 12.2.4 [over.match.best] lists four checks, the last three concern this report. For the non-conversion operator case, checks 2 and 3 are applicable, whereas for the conversion operator case checks 3 and 4 are applicable. Checks 2 and 4 concern the ranking of argument and return value conversion sequences respectively. Check 3 concerns only the templatedness of the functions being ranked, and will prefer a non-template to a template. Notice that this check happens after argument conversion sequence ranking, but before return value conversion sequence ranking. This has the effect of always selecting a non-template conversion operator, as the following example shows:

    struct C
    {
      inline operator int () { return 1; }
      template <class T> inline operator T () { return 0; }
    };

    inline long f (long x) { return x; }

    int
    main (int argc, char *argv[])
    {
      return f (C ());
    }

The non-templated C::operator int function will be selected, rather than the apparently better C::operator long<long> instantiation. This is a surprise, and resulted in a bug report where the user expected the template to be selected. In addition some C++ compilers have implemented the overload ranking as if checks 3 and 4 were transposed.

Is this ordering accidental, or is there a rationale?

Notes from the April, 2005 meeting:

The CWG agreed that the template/non-template distinction should be the final tie-breaker.

Proposed resolution (March, 2007):

In the second bulleted list of 12.2.4 [over.match.best] paragraph 1, move the second and third bullets to the end of the list, to read as follows:




978. Incorrect specification for copy initialization

Section: 12.2.4.2  [over.best.ics]     Status: CD2     Submitter: Daniel Krügler     Date: 5 October, 2009

[Voted into WP at March, 2010 meeting.]

12.2.4.2 [over.best.ics] paragraph 4 says,

However, when considering the argument of a user-defined conversion function that is a candidate by 12.2.2.4 [over.match.ctor] when invoked for the copying of the temporary in the second step of a class copy-initialization, by 12.2.2.8 [over.match.list] when passing the initializer list as a single argument or when the initializer list has exactly one element and a conversion to some class X or reference to (possibly cv-qualified) X is considered for the first parameter of a constructor of X, or by 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv], or 12.2.2.7 [over.match.ref] in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed.

This is not quite right, as this applies to constructor arguments, not just arguments of user-defined conversion functions. Furthermore, the word “allowed” might be better replaced by something like,

considered (in particular, for the purposes of determining whether the candidate function (that is either a constructor or a conversion function) is viable)

Proposed resolution (October, 2009):

Change 12.2.4.2 [over.best.ics] paragraph 4 as follows:

However, when considering the argument of a constructor or user-defined conversion function that is a candidate by 12.2.2.4 [over.match.ctor] when invoked for the copying of the temporary in the second step of a class copy-initialization, by 12.2.2.8 [over.match.list] when passing the initializer list as a single argument or when the initializer list has exactly one element and a conversion to some class X or reference to (possibly cv-qualified) X is considered for the first parameter of a constructor of X, or by 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv], or 12.2.2.7 [over.match.ref] in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed considered.



953. Rvalue references and function viability

Section: 12.2.4.2.5  [over.ics.ref]     Status: CD2     Submitter: Mike Miller     Date: 18 August, 2009

[Voted into WP at March, 2010 meeting.]

According to 12.2.4.2.5 [over.ics.ref] paragraphs 3-4,

A standard conversion sequence cannot be formed if it requires binding an lvalue reference to non-const to an rvalue (except when binding an implicit object parameter; see the special rules for that case in 12.2.2 [over.match.funcs]). [Note: this means, for example, that a candidate function cannot be a viable function if it has a non-const lvalue reference parameter (other than the implicit object parameter) and the corresponding argument is a temporary or would require one to be created to initialize the lvalue reference (see 9.4.4 [dcl.init.ref]). —end note]

Other restrictions on binding a reference to a particular argument that are not based on the types of the reference and the argument do not affect the formation of a standard conversion sequence, however.

Because this section does not mention attempting to bind an rvalue reference to an lvalue, such a “conversion sequence” might be selected as best and result in an ill-formed program. It should, instead, be treated like trying to bind an lvalue reference to non-const to an rvalue, making the function non-viable.

Proposed resolution (November, 2009):

Change 12.2.4.2.5 [over.ics.ref] paragraph 3 as follows:

A Except for an implicit object parameter, for which see 12.2.2 [over.match.funcs], a standard conversion sequence cannot be formed if it requires binding an lvalue reference to non-const to an rvalue (except when binding an implicit object parameter; see the special rules for that case in 12.2.2 [over.match.funcs]) or binding an rvalue reference to an lvalue. [Note: this means, for example, that a candidate function cannot be a viable function if it has a non-const lvalue reference parameter (other than the implicit object parameter) and the corresponding argument is a temporary or would require one to be created to initialize the lvalue reference (see 9.4.4 [dcl.init.ref]). —end note]



702. Preferring conversion to std::initializer_list

Section: 12.2.4.3  [over.ics.rank]     Status: CD2     Submitter: Jason Merrill     Date: 2 July, 2008

[Voted into WP at July, 2009 meeting.]

We need another bullet in 12.2.4.3 [over.ics.rank], along the lines of:

This is necessary to make the following example work:

    #include <initializer_list>

    struct string {
      string (const char *) {}
      template <class Iter> string (Iter, Iter);
    };

    template <class T, class U>
    struct pair {
      pair (T t, U u) {}
    };

    template<class T, class U>
    struct map {
      void insert (pair<T,U>);
      void insert (std::initializer_list<pair<T,U> >) {}
    };

    int main() {
      map<string,string> m;
      m.insert({ {"this","that"}, {"me","you"} });
    }

Proposed resolution (March, 2009):

Add a new top-level bullet at the end of the current list in 12.2.4.3 [over.ics.rank] paragraph 3:




961. Overload resolution and conversion of std::nullptr_t to bool

Section: 12.2.4.3  [over.ics.rank]     Status: CD2     Submitter: Mike Miller     Date: 2 September, 2009

[Voted into WP at March, 2010 meeting.]

Conversion of a pointer or pointer to member to bool is given special treatment as a tiebreaker in overload resolution in 12.2.4.3 [over.ics.rank] paragraph 4, bullet 1:

It would be reasonable to expect a similar provision to apply to conversions of std::nullptr_t to bool.

Proposed resolution (October, 2009):

Change 12.2.4.3 [over.ics.rank] bullet 4.1 as follows:




749. References to function types with a cv-qualifier or ref-qualifier

Section: 12.5  [over.built]     Status: CD2     Submitter: Alberto Ganesh Barbati     Date: 9 December, 2008

[Voted into WP at July, 2009 meeting.]

12.5 [over.built] paragraph 7 posits the existence of built-in candidate operator* functions “for every function type T.” However, only non-static member function types can contain a cv-qualifier or ref-qualifier (9.3.4.6 [dcl.fct] paragraph 7), and a reference to such a type cannot be initialized (7.6.1.5 [expr.ref] paragraph 4, bullet 3, sub-bullet 2). (See also _N2914_.14.10.4 [concept.support] paragraph 10, which disallows references to function types with cv-qualifiers but is silent on ref-qualifiers.)

Proposed resolution (March, 2009):

  1. Change 12.5 [over.built] paragraph 7 as follows:

  2. For every function type T that does not have cv-qualifiers or a ref-qualifier, there exist candidate operator functions of the form
  3. Change _N2914_.14.10.4 [concept.support] paragraph 7 as follows:

  4. Requires: for every type T that is an object type, a function type that does not have cv-qualifiers or a ref-qualifier, or cv void, a concept map PointeeType<T> is implicitly defined in namespace std.
  5. Change _N2914_.14.10.4 [concept.support] paragraph 11 as follows:

  6. Requires: for every type T that is an object type, a function type that does not have cv-qualifiers or a ref-qualifier, or a reference type, a concept map ReferentType<T> is implicitly defined in namespace std.



879. Missing built-in comparison operators for pointer types

Section: 12.5  [over.built]     Status: CD2     Submitter: Daniel Krügler     Date: 25 April, 2009

[Voted into WP at October, 2009 meeting.]

12.5 [over.built] paragraph 15 restricts the built-in comparison operators to

every T, where T is an enumeration type or pointer to effective object type

This omits both pointers to function types and pointers to void.

Proposed resolution (July, 2009):

  1. Add a new paragraph following 7.6.9 [expr.rel] paragraph 2:

  2. Pointers to void (after pointer conversions) can be compared, with a result defined as follows: If both pointers represent the same address or are both the null pointer value, the result is true if the operator is <= or >= and false otherwise; otherwise the result is unspecified.
  3. Change 7.6.10 [expr.eq] paragraph 1 as follows:

  4. ...Pointers to objects or functions of the same type (after pointer conversions) can be compared for equality...
  5. Change 12.5 [over.built] paragraph 15 as follows:

  6. For every T, where T is an enumeration type or, a pointer to effective object type, or std::nullptr_t, there exist candidate operator functions of the form...



880. Built-in conditional operator for scoped enumerations

Section: 12.5  [over.built]     Status: CD2     Submitter: Daniel Krügler     Date: 25 April, 2009

[Voted into WP at March, 2010 meeting.]

12.5 [over.built] paragraphs 24-25 describe the imaginary built-in conditional operator functions. However, neither paragraph 24 (promoted arithmetic types) nor 25 (pointer and pointer-to-member types) covers scoped enumerations, whose values should be usable in conditional expressions.

(See also issue 835.)

Proposed resolution (October, 2009):

Change 12.5 [over.built] paragraph 25 as follows:

For every type T, where T is a pointer, or pointer-to-member, or scoped enumeration type, there exist candidate operator functions of the form

    T        operator?(bool, T , T );



935. Missing overloads for character types for user-defined literals

Section: 12.6  [over.literal]     Status: CD2     Submitter: Alisdair Meredith     Date: 9 July, 2009

[Voted into WP at March, 2010 meeting.]

The list of overloads for user-defined literal operators given in 12.6 [over.literal] paragraph 3 should include signatures for char, wchar_t, char16_t, and char32_t.

Proposed resolution (November, 2009):

Change 12.6 [over.literal] paragraph 3 as follows:

The declaration of a literal operator shall have a parameter-declaration-clause equivalent to one of the following:

  const char*
  unsigned long long int
  long double
  char
  wchar_t
  char16_t
  char32_t
  const char*, std::size_t
  const wchar_t*, std::size_t
  const char16_t*, std::size_t
  const char32_t*, std::size_t



820. Deprecation of export

Section: Clause 13  [temp]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 115

[Voted into WP at March, 2010 meeting as document N3065.]

Exported templates were a great idea that is generally understood to have failed. In the decade since the standard was adopted, only one implementation has appeared. No current vendors appear interested in creating another. We tentatively suggest this makes the feature ripe for deprecation. Our main concern with deprecation is that it might turn out that exported constrained templates become an important compile-time optimization, as the constraints would be checked once in the exported definition and not in each translation unit consuming the exported declarations.

Notes from the March, 2010 meeting:

It was decided to remove export altogether, rather than deprecating it.




840. Rvalue references as nontype template parameters

Section: 13.2  [temp.param]     Status: CD2     Submitter: Steve Adamczyk     Date: 13 March, 2009

[Voted into WP at October, 2009 meeting.]

Nontype template parameters are currently allowed to have rvalue reference type (13.2 [temp.param] bullet 4.3 just says “reference,” not “lvalue reference”). However, with the change of N2844 voted in (which prohibits rvalue references from binding to lvalues), I can't think of any way to specify a valid template argument for a parameter of rvalue reference type. If that's the case, should we restrict nontype template parameters to lvalue reference types?

Proposed resolution (July, 2009):

Change 13.2 [temp.param] paragraph 4, bullet 3 as follows:




823. Literal types with constexpr conversions as non-type template arguments

Section: 13.4.3  [temp.arg.nontype]     Status: CD2     Submitter: FR     Date: 3 March, 2009

N2800 comment FR 29

[Voted into WP at March, 2010 meeting.]

7.7 [expr.const] permits literal types with a constexpr conversion function to an integral type to be used in an integral constant expression. However, such conversions are not listed in 13.4.3 [temp.arg.nontype] bullet 5.1 among the conversions applied to template-arguments for a non-type template-parameter of integral or enumeration type.

Notes from the March, 2009 meeting:

The original national body comment suggested allowing any literal type as a non-type template argument. The CWG was not in favor of this change, but in the course of discussing the suggestion discovered the problem with template-parameters of integral and enumeration type.

Proposed resolution (October, 2009):

Change 13.4.3 [temp.arg.nontype] bullet 1.1 as follows:




744. Matching template arguments with template template parameters with parameter packs

Section: 13.4.4  [temp.arg.template]     Status: CD2     Submitter: Faisal Vali     Date: 2 November, 2008

[Voted into WP at March, 2010 meeting.]

According to 13.4.4 [temp.arg.template] paragraph 3,

A template-argument matches a template template-parameter (call it P) when each of the template parameters in the template-parameter-list of the template-argument's corresponding class template or template alias (call it A) matches the corresponding template parameter in the template-parameter-list of P. When P's template-parameter-list contains a template parameter pack (13.7.4 [temp.variadic]), the template parameter pack will match zero or more template parameters or template parameter packs in the template-parameter-list of A with the same type and form as the template parameter pack in P (ignoring whether those template parameters are template parameter packs).

The immediately-preceding example, however, assumes that a parameter pack in the parameter will match only a parameter pack in the argument:

    template<class T> class A { /* ... */ };
    template<class T, class U = T> class B { /* ... */ };
    template<class ... Types> class C { /* ... */ };

    template<template<class ...> class Q> class Y { /* ... */ };

    Y<A> ya;  // ill-formed: a template parameter pack does not match a template parameter
    Y<B> yb;  // ill-formed: a template parameter pack does not match a template parameter
    Y<C> yc;  // OK

Proposed resolution (February, 2010):

Change the final three lines of the second example in 13.4.4 [temp.arg.template] paragraph 2 as follows:

    Y<A> ya;  // ill-formed: a template parameter pack does not match a template parameter OK
    Y<B> yb;  // ill-formed: a template parameter pack does not match a template parameter OK
    Y<C> yc;  // OK



408. sizeof applied to unknown-bound array static data member of template

Section: 13.7.2.5  [temp.static]     Status: CD2     Submitter: Nathan Myers     Date: 14 Apr 2003

[Voted into WP at March, 2010 meeting.]

Is this allowed?

  template<typename T>
    struct X
    {
        static int s[];
        int c;
    };

  template<typename T>
    int X<T>::s[sizeof(X<T>)];

  int* p = X<char>::s;

I have a compiler claiming that, for the purpose of sizeof(), X<T> is an incomplete type, when it tries to instantiate X<T>::s. It seems to me that X<char> should be considered complete enough for sizeof even though the size of s isn't known yet.

John Spicer: This is a problematic construct that is currently allowed but which I think should be disallowed.

I tried this with a number of compilers. None of which did the right thing. The EDG front end accepts it, but gives X<...>::s the wrong size.

It appears that most compilers evaluate the "declaration" part of the static data member definition only once when the definition is processed. The initializer (if any) is evaluated for each instantiation.

This problem is solvable, and if it were the only issue with incomplete arrays as template static data members, then it would make sense to solve it, but there are other problems.

The first problem is that the size of the static data member is only known if a template definition of the static data member is present. This is weird to start with, but it also means that sizes would not be available in general for exported templates.

The second problem concerns the rules for specialization. An explicit specialization for a template instance can be provided up until the point that a use is made that would cause an implicit instantiation. A reference like "sizeof(X<char>::s)" is not currently a reference that would cause an implicit instantiation of X<char>::s. This means you could use such a sizeof and later specialize the static data member with a different size, meaning the earlier sizeof gave the wrong result. We could, of course, change the "use" rules, but I'd rather see us require that static data members that are arrays have a size specified in the class or have a size based on their initializer.

Notes from the October 2003 meeting:

The example provided is valid according to the current standard. A static data member must be instantiated (including the processing of its initializer, if any) if there is any reference to it. The compiler need not, however, put out a definition in that translation unit. The standard doesn't really have a concept of a "partial instantiation" for a static data member, and although we considered adding that, we decided that to get all the size information that seems to be available one needs a full instantiation in any case, so there's no need for the concept of a partial instantiation.

Note (June, 2006):

Mark Mitchell suggested the following example:

    template <int> void g();

    template <typename T>
    struct S {
      static int i[];
      void f();
    };

    template <typename T>
    int S<T>::i[] = { 1 };

    template <typename T>
    void S<T>::f() {
      g<sizeof (i) / sizeof (int)>();
    }

    template <typename T>
    int S<int>::i[] = { 1, 2 };

Which g is called from S<int>::f()?

If the program is valid, then surely one would expect g<2> to be called.

If the program is valid, does S<T>::i have a non-dependent type in S<T>::f? If so, is it incomplete, or is it int[1]? (Here, int[1] would be surprising, since S<int>::i actually has type int[2].)

If the program is invalid, why?

For a simpler example, consider:

    template <typename T>
    struct S {
      static int i[];
      const int N = sizeof (i);
    };

This is only valid if the type of i is dependent, meaning that the sizeof expression isn't evaluated until the class is instantiated.

Proposed resolution (February, 2010):

  1. Add the following as a new paragraph following 13.7.2.5 [temp.static] paragraph 1:

  2. An explicit specialization of a static data member declared as an array of unknown bound can have a different bound from its definition, if any. [Example:

      template<class T> struct A {
        static int i[];
      };
      template<class T> int A<T>::i[4];    // 4 elements
      template<> int A<int>::i[] = { 1 };  // 1 element, OK
    

    end example]

  3. Change 13.8.3.3 [temp.dep.expr] paragraph 3 as follows:

  4. An id-expression is type-dependent if it contains:

    or if it names a static data member of the current instantiation that has type “array of unknown bound of T” for some T (13.7.2.5 [temp.static]). Expressions of the following forms are type-dependent only if...




638. Explicit specialization and friendship

Section: 13.7.5  [temp.friend]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 6 July 2007

[Voted into WP at March, 2010 meeting.]

Is this code well-formed?

    template <typename T> struct A {
        struct B;
    };

    class C {
        template <typename T> friend struct A<T>::B;
        static int bar;
    };

    template <> struct A<char> {
        struct B {
            int f() {
                return C::bar;   // Is A<char>::B a friend of C?
            }
        };
    };

According to 13.7.5 [temp.friend] paragraph 5,

A member of a class template may be declared to be a friend of a non-template class. In this case, the corresponding member of every specialization of the class template is a friend of the class granting friendship.

This would tend to indicate that the example is well-formed. However, technically A<char>::B does not “correspond to” the same-named member of the class template: 13.9.4 [temp.expl.spec] paragraph 4 says,

The definition of an explicitly specialized class is unrelated to the definition of a generated specialization. That is, its members need not have the same names, types, etc. as the members of a generated specialization.

In other words, there are no “corresponding members” in an explicit specialization.

Is this the outcome we want for examples like the preceding? There is diversity among implementations on this question, with some accepting the example and others rejecting it as an access violation.

Notes from the July, 2009 meeting:

The consensus of the CWG was to allow the correspondence of similar members in explicit specializations.

Proposed resolution (October, 2009):

Change 13.7.5 [temp.friend] paragraph 5 as follows:

A member of a class template may be declared to be a friend of a non-template class. In this case, the corresponding member of every specialization of the class template is a friend of the class granting friendship. For explicit specializations the corresponding member is the member (if any) that has the same name, kind (type, function, class template or function template), template parameters, and signature as the member of the class template instantiation that would otherwise have been generated. [Example:

  template<class T> struct A {
    struct B { };
    void f();
    struct D {
      void g();
    };
  };
  template<> struct A<int> {
    struct B { };
    int f();
    struct D {
      void g();
    };
  };

  class C {
    template<class T> friend struct A<T>::B;    // grants friendship to A<int>::B even though
                                                // it is not a specialization of A<T>::B
    template<class T> friend void A<T>::f();    // does not grant friendship to A<int>::f()
                                                // because its return type does not match
    template<class T> friend void A<T>::D::g(); // does not grant friendship to A<int>::D::g()
                                                // because A<int>::D is not a specialization of A<T>::D
  };



929. What is a template alias?

Section: 13.7.8  [temp.alias]     Status: CD2     Submitter: Alisdair Meredith     Date: 2 July, 2009

[Voted into WP at October, 2009 meeting.]

Although it is a reasonable assumption that a template-declaration in which the declaration is an alias-declaration declares a template alias, that is not said explicitly in 13.7.8 [temp.alias] nor, apparently, anywhere else.

Proposed resolution (September, 2009):

Change 13.7.8 [temp.alias] paragraph 1 as follows:

A template-declaration in which the declaration is an alias-declaration (clause 7) declares the identifier to be a template alias. A template alias declares template alias is a name for a family of types. The name of the template alias is a template-name.



588. Searching dependent bases of classes local to function templates

Section: 13.8.3  [temp.dep]     Status: CD2     Submitter: James Widman     Date: 21 June 2006

[Voted into the WP at the March, 2009 meeting.]

13.8.3 [temp.dep] paragraph 3 reads,

In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.

This wording applies only to definitions of class templates and members of class templates. That would make the following program ill-formed (but it probably should be well-formed):

    struct B{ void f(int); };

    template<class T> struct D: B { };

    template<class T> void g() {
       struct B{ void f(); };
       struct A: D<T> {
           B m;
       };
       A a;
       a.m.f(); // Presumably, we want ::g()::B::f(), not ::B::f(int)
    }

    int main () {
       g<int>();
       return 0;
    }

I suspect the wording should be something like

In the definition of a class template or a class defined (directly or indirectly) within the scope of a class template or function template, if a base class...

That should also include deeply nested classes in templates, local classes of non-template member functions of member classes of class templates, etc.

Proposed resolution (October, 2006):

Change 13.8.3 [temp.dep] paragraph 3 as follows:

In the definition of a class or class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.



541. Dependent function types

Section: 13.8.3.3  [temp.dep.expr]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 22 October 2005

[Voted into WP at March, 2010 meeting.]

13.8.3.3 [temp.dep.expr] paragraph 3 says,

An id-expression is type-dependent if it contains:

This treatment seems inadequate with regard to id-expressions in function calls:

  1. According to 13.8.3.2 [temp.dep.type] paragraph 6,

    A type is dependent if it is
    • ...
    • a compound type constructed from any dependent type...

    This would apply to the type of a member function of a class template if any of its parameters are dependent, even if the return type is not dependent. However, there is no need for a call to such a function to be a type-dependent expression because the type of the expression is known at definition time.

  2. This wording does not handle the case of overloaded functions, some of which might have dependent types (however defined) and others not.

Notes from the October, 2009 meeting:

The consensus of the CWG was that the first point of the issue is not sufficiently problematic as to require a change.

Proposed resolution (October, 2009):

Change 13.8.3.3 [temp.dep.expr] paragraph 3 as follows:

An id-expression is type-dependent if it contains:




561. Internal linkage functions in dependent name lookup

Section: 13.8.4.2  [temp.dep.candidate]     Status: CD2     Submitter: Joaquín López Muñoz     Date: 17 February 2006

[Voted into WP at March, 2010 meeting.]

According to 13.8.4.2 [temp.dep.candidate],

For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found using the usual lookup rules (6.5.3 [basic.lookup.unqual], 6.5.4 [basic.lookup.argdep]) except that:

It is not at all clear why a call using a template-id would be treated differently from one not using a template-id. Furthermore, is it really necessary to exclude internal linkage functions from the lookup? Doesn't the ODR give implementations sufficient latitude to handle this case without another wrinkle on name lookup?

(See also issue 524.)

Notes from the April, 2006 meeting:

The consensus of the group was that template-ids should not be treated differently from unqualified-ids (although it's not clear how argument-dependent lookup works for template-ids), and that internal-linkage functions should be found by the lookup (although they may result in errors if selected by overload resolution).

Note (June, 2006):

Although the notes from the Berlin meeting indicate that argument-dependent lookup for template-ids is under-specified in the Standard, further examination indicates that that is not the case: the note in 13.10.2 [temp.arg.explicit] paragraph 8 clearly indicates that argument-dependent lookup is to be performed for template-ids, and 6.5.4 [basic.lookup.argdep] paragraph 4 describes the lookup performed:

When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier (6.5.5.3 [namespace.qual]) except that:

Proposed resolution (October, 2009):

  1. Change 13.8.3 [temp.dep] paragraph 1 as follows:

  2. In an expression of the form:

    where the postfix-expression is an unqualified-id but not a template-id, the unqualified-id denotes a dependent name if and only if any of the expressions in the expression-list is a type-dependent expression (13.8.3.3 [temp.dep.expr])...

  3. Change 13.8.4.2 [temp.dep.candidate] paragraph 1 as follows:

  4. For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, or if the function is called using operator notation, the candidate functions are found using the usual lookup rules (6.5.3 [basic.lookup.unqual], 6.5.4 [basic.lookup.argdep]) except that:




969. Explicit instantiation declarations of class template specializations

Section: 13.9.3  [temp.explicit]     Status: CD2     Submitter: Jason Merrill     Date: 29 December, 2008

[Voted into WP at March, 2010 meeting.]

Consider this example:

    template <class T> struct A {
       virtual void f() {}
    };

    extern template struct A<int>;

    int main() {
       A<int> a;
       a.f();
    }

The intent is that the explicit instantiation declaration will suppress any compiler-generated machinery such as a virtual function table or typeinfo data in this translation unit, and that because of 13.9.3 [temp.explicit] paragraph 10,

An entity that is the subject of an explicit instantiation declaration and that is also used in the translation unit shall be the subject of an explicit instantiation definition somewhere in the program; otherwise the program is ill-formed, no diagnostic required.

the use of A<int> in declaring a requires an explicit instantiation definition in another translation unit that will provide the requisite compiler-generated data.

The existing wording of 13.9.3 [temp.explicit] does not express this intent clearly enough, however.

Suggested resolution:

  1. Change 13.9.3 [temp.explicit] paragraph 7 as follows:

  2. An explicit instantiation that names a class template specialization is also an explicit instantion of the same kind (declaration or definition) of each of its members (not including members inherited from base classes) that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, except as described below.
  3. Change 13.9.3 [temp.explicit] paragraph 9 as follows:

  4. An explicit instantiation declaration that names a class template specialization has no effect on the class template specialization itself (except for perhaps resulting in its implicit instantiation). Except for inline functions and class template specializations, other explicit instantiation declarations have the effect of suppressing the implicit instantiation of the entity to which they refer...

Proposed resolution (October, 2009):

Change 13.9.3 [temp.explicit] paragraphs 7-9 as follows:

An explicit instantiation that names a class template specialization is also an explicit instantion of the same kind (declaration or definition) of each of its members (not including members inherited from base classes) that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, except as described below. [Note: In addition, it will typically be an explicit instantiation of certain implementation-dependent data about the class. —end note]

An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is only an explicit instantiation definition of only those members whose definition is visible at the point of instantiation.

An explicit instantiation declaration that names a class template specialization has no effect on the class template specialization itself (except for perhaps resulting in its implicit instantiation). Except for inline functions and class template specializations, other explicit instantiation declarations have the effect of suppressing the implicit instantiation of the entity to which they refer...




980. Explicit instantiation of a member of a class template

Section: 13.9.3  [temp.explicit]     Status: CD2     Submitter: Doug Gregor     Date: 14 October, 2009

[Voted into WP at March, 2010 meeting.]

13.9.3 [temp.explicit] paragraph 1 says,

An explicit instantiation of a function template shall not use the inline or constexpr specifiers.

This wording should be revised to apply to member functions of class templates as well.

Proposed resolution (February, 2010):

Change 13.9.3 [temp.explicit] paragraph 1 as follows:

...An explicit instantiation of a function template or member function of a class template shall not use the inline or constexpr specifiers.



995. Incorrect example for using-declaration and explicit instantiation

Section: 13.9.3  [temp.explicit]     Status: CD2     Submitter: Doug Gregor     Date: 27 Oct, 2009

[Voted into WP at March, 2010 meeting.]

13.9.3 [temp.explicit] paragraph 5 has an example that reads, in significant part,

    namespace N {
      template<class T> class Y {
        void mf() { }
      };
    }

    using N::Y;
    template class Y<int>; // OK: explicit instantiation in namespace N

In fact, paragraph 2 requires that an explicit instantiation with an unqualified name must appear in the same namespace in which the template was declared, so the example is ill-formed.

Proposed resolution (February, 2010):

Change the example in 13.9.3 [temp.explicit] paragraph 5 as follows:

  namespace N {
    template<class T> class Y { void mf() { } };
  }

  template class Y<int>;            // error: class template Y not visible
                                    // in the global namespace

  using N::Y;
  template class Y<int>;            // OK: explicit instantiation in namespace N
  template class Y<int>;            // error: explicit instantiation outside of the
                                    // namespace of the template

  template class N::Y<char*>;       // OK: explicit instantiation in namespace N
  template void N::Y<double>::mf(); // OK: explicit instantiation
                                    // in namespace N



730. Explicit specializations of members of non-template classes

Section: 13.9.4  [temp.expl.spec]     Status: CD2     Submitter: Bronek Kozicki     Date: 3 October, 2008

N2800 comment DE 14

[Voted into WP at October, 2009 meeting.]

The list of entities that can be explicitly specialized in 13.9.4 [temp.expl.spec] paragraph 1 includes member templates of class templates but not member templates of non-template classes. This omission could lead to the conclusion that such member templates cannot be explicitly specialized. (Note, however, that paragraph 3 refers to “an explicit specialization for a member template of [a] class or class template.”)

Proposed resolution (July, 2009):

Change 13.9.4 [temp.expl.spec] paragraph 1 as follows:

An explicit specialization of any of the following:

can be declared...




884. Defining an explicitly-specialized static data member

Section: 13.9.4  [temp.expl.spec]     Status: CD2     Submitter: Daniel Krügler     Date: 29 April, 2009

[Voted into WP at October, 2009 meeting.]

13.9.4 [temp.expl.spec] paragraphs 15-16 contain the following note:

[Note: there is no syntax for the definition of a static data member of a template that requires default initialization.
    template<> X Q<int>::x;
This is a declaration regardless of whether X can be default initialized (9.4 [dcl.init]). —end note]

While this note is still accurate, the C++0x list initialization syntax provides a way around the restriction, which could be useful if the class is not copyable or movable but has a default constructor. Perhaps the note should be updated to mention that possibility?

Proposed resolution (July, 2009):

Change 13.9.4 [temp.expl.spec] paragraphs 15-16 as follows:

An explicit specialization of a static data member of a template is a definition if the declaration includes an initializer; otherwise, it is a declaration. [Note: there is no syntax for the The definition of a static data member of a template that requires default initialization. must use a braced-init-list:

  template<> X Q<int>::x;      // declaration
  template<> X Q<int>::x ();   // error: declares a function
  template<> X Q<int>::x {};   // definition

This is a declaration regardless of whether X can be default initialized (9.4 [dcl.init]).end note]




923. Inline explicit specializations

Section: 13.9.4  [temp.expl.spec]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 19 June, 2009

[Voted into WP at March, 2010 meeting.]

According to 13.9.4 [temp.expl.spec] paragraph 14,

An explicit specialization of a function template is inline only if it is explicitly declared to be...

This could be read to require that the inline keyword must appear in the declaration. However, 9.5 [dcl.fct.def] paragraph 10 says that a deleted function is implicitly inline, so it should be made clear that defining an explicit specialization as deleted makes it inline.

Proposed resolution (November, 2009):

Change 13.9.4 [temp.expl.spec] paragraph 14 as follows:

An explicit specialization of a function template is inline only if it is explicitly declared to be with the inline specifier or defined as deleted, and independently of whether its function template is inline. [Example:...



657. Abstract class parameter in synthesized declaration

Section: 13.10.3  [temp.deduct]     Status: CD2     Submitter: Mike Miller     Date: 31 October 2007

[Voted into WP at October, 2009 meeting.]

A customer of ours recently brought the following example to our attention. There's some question as to whether the Standard adequately addresses this example, and if it does, whether the outcome is what we'd like to see. Here's the example:

    struct Abs {
      virtual void x() = 0;
    };

    struct Der: public Abs {
      virtual void x();
    };

    struct Cnvt {
      template <typename F> Cnvt(F);
    };

    void foo(Cnvt a);
    void foo(Abs &a);

    void f() {
      Der d;
      Abs *a = &d;
      foo(*a);        // #1
      return 0;
    }

The question is how to perform overload resolution for the call at #1. To do that, we need to determine whether foo(Cnvt) is a viable function. That entails deciding whether there is an implicit conversion sequence that converts Abs (the type of *a in the call) to Cnvt (12.2.3 [over.match.viable] paragraph 3), and that involves a recursive invocation of overload resolution.

The initialization of the parameter of foo(Cnvt) is a case of copy-initialization of a class by user-defined conversion, so the candidate functions are the converting constructors of Cnvt (12.2.2.5 [over.match.copy] paragraph 1), of which there are two: the implicitly-declared copy constructor and the constructor template.

According to 13.9.2 [temp.inst] paragraph 8,

If a function template or a member function template specialization is used in a way that involves overload resolution, a declaration of the specialization is implicitly instantiated (13.10.4 [temp.over]).

Template argument deduction results in “synthesizing” (13.10.4 [temp.over] paragraph 1) (or “instantiating,” 13.9.2 [temp.inst] paragraph 8) the declaration

    Cnvt::Cnvt(Abs)

Because Abs is an abstract class, this declaration violates the restriction of 11.7.4 [class.abstract] paragraph 3 (“An abstract class shall not be used as a parameter type...”), and because a parameter of an abstract class type does not cause a deduction failure (it's not in the bulleted list in 13.10.3 [temp.deduct] paragraph 2), the program is ill-formed. This error is reported by both EDG and Microsoft compilers, but not by g++.

It seems unfortunate that the program would be rendered ill-formed by a semantic violation in a declaration synthesized solely for the purpose of overload resolution analysis; foo(Cnvt) would not be selected by overload resolution, so Cnvt::Cnvt(Abs) would not be instantiated.

There's at least some indication that a parameter with an abstract class type should be a deduction failure; an array element of abstract class type is a deduction failure, so one might expect that a parameter would be, also.

(See also issue 339; this question might be addressed as part of the direction described in the notes from the July, 2007 meeting.)

Notes from the June, 2008 meeting:

Paper N2634, adopted at the June, 2008 meeting, replaces the normative list of specific errors accepted as deduction failures by a general statement covering all “invalid types and expressions in the immediate context of the function type and its template parameter types,” so the code is now well-formed. However, the previous list is now a note, and the note should be updated to mention this case.

Proposed resolution (August, 2008):

Add a new bullet following the last bullet of the note in 13.10.3 [temp.deduct] paragraph 8 as follows:




847. Error in rvalue reference deduction example

Section: 13.10.3.2  [temp.deduct.call]     Status: CD2     Submitter: Steve Adamczyk     Date: 27 March, 2009

[Voted into WP at March, 2010 meeting.]

The adoption of paper N2844 made it ill-formed to attempt to bind an rvalue reference to an lvalue. However, the example in 13.10.3.2 [temp.deduct.call] paragraph 3 still reflects the previous specification:

    template <typename T> int f(T&&);
    int i;
    int j = f(i);        // calls f<int&>(i)
    template <typename T> int g(const T&&);
    int k;
    int n = g(k);        // calls g<int>(k)

The last line of that example is now ill-formed, attempting to bind the const int&& parameter of g to the lvalue k.

Proposed resolution (July, 2009):

Replace the example in 13.10.3.2 [temp.deduct.call] paragraph 3 with:

    template<typename T> int f(T&&);
    template<typename T> int g(const T&&);
    int i;
    int n1 = f(i);    // calls f<int&>(int&)
    int n2 = f(0);    // calls f<int>(int&&)
    int n3 = g(i);    // error: would call g<int>(const int&&), which would
                      // bind an rvalue reference to an lvalue

(See also issue 858.)




876. Type references in rvalue reference deduction specification

Section: 13.10.3.2  [temp.deduct.call]     Status: CD2     Submitter: Steve Adamczyk     Date: 20 April, 2009

[Voted into WP at October, 2009 meeting.]

13.10.3.2 [temp.deduct.call] paragraph 3 says,

If P is of the form T&&, where T is a template parameter, and the argument is an lvalue, the type A& is used in place of A for type deduction.

The type references in that sentence are inconsistent with the normal usage in the Standard; they should instead refer to “an rvalue reference to a cv-unqualified template parameter” and “lvalue reference to A.”

Proposed resolution (July, 2009):

Change 13.10.3.2 [temp.deduct.call] paragraph 3 as follows:

If P is a cv-qualified type, the top level cv-qualifiers of P's type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction. If P is of the form T&&, where T is a template parameter, an rvalue reference to a cv-unqualified template parameter and the argument is an lvalue, the type A& “lvalue reference to A is used in place of A for type deduction.



493. Type deduction from a bool context

Section: 13.10.3.4  [temp.deduct.conv]     Status: CD2     Submitter: John Spicer     Date: 17 Dec 2004

[Voted into WP at March, 2010 meeting.]

An expression used in an if statement is implicitly converted to type bool (8.5 [stmt.select]). According to the rules of template argument deduction for conversion functions given in 13.10.3.4 [temp.deduct.conv], the following example is ill-formed:

    struct X {
      template<class T> operator const T&() const;
    };
    int main()
    {
      if( X() ) {}
    }

Following the logic in 13.10.3.4 [temp.deduct.conv], A is bool and P is const T (because cv-qualification is dropped from P before the reference is removed), and deduction fails.

It's not clear whether this is the intended outcome or not.

Notes from the April, 2005 meeting:

The CWG observed that there is nothing special about either bool or the context in the example above; instead, it will be a problem wherever a copy occurs, because cv-qualification is always dropped in a copy operation. This appears to be a case where the conversion deduction rules are not properly symmetrical with the rules for arguments. The example should be accepted.

Proposed resolution (February, 2010):

This issue is resolved by the resolution of issue 976.




913. Deduction rules for array- and function-type conversion functions

Section: 13.10.3.4  [temp.deduct.conv]     Status: CD2     Submitter: Steve Clamage     Date: 10 June, 2009

[Voted into WP at March, 2010 meeting.]

The rules for deducing function template arguments from a conversion function template include provisions in 13.10.3.4 [temp.deduct.conv] paragraph 2 for array and function return types, even though such types are prohibited and cannot occur in the conversion-type-id of a conversion function template. They should be removed.

Proposed resolution (February, 2010):

This issue is resolved by the resolution of issue 976. In particular, under that resolution, if a conversion function returns a reference to an array or function type, the reference will be dropped prior to the adjustments mentioned in this issue, so they are, in fact, needed.




976. Deduction for const T& conversion operators

Section: 13.10.3.4  [temp.deduct.conv]     Status: CD2     Submitter: Jens Maurer     Date: 1 October, 2009

[Voted into WP at March, 2010 meeting.]

Consider this program:

    struct F {
       template<class T>
       operator const T&() { static T t; return t; }
    };

    int main() {
       F f;
       int i = f;   // ill-formed
    }

It's ill-formed, because according to 13.10.3.4 [temp.deduct.conv], we try to match const T with int.

(The reference got removed from P because of paragraph 3, but the const isn't removed, because bullet 2.3 comes before paragraph 3 and thus isn't applied any more.)

Changing the declaration of the conversion operator to

   operator T&() { ... }

makes the program compile, which is counter-intuitive to me: I'm in an rvalue (read-only) context, and I can use a conversion to T&, but I can't use a conversion to const T&?

Proposed resolution (February, 2010):

Change 13.10.3.4 [temp.deduct.conv] paragraphs 1-3 as follows, inserting a new paragraph between the current paragraphs 1 and 2:

Template argument deduction is done by comparing the return type of the conversion function template (call it P; see 9.4 [dcl.init], 12.2.2.6 [over.match.conv], and 12.2.2.7 [over.match.ref] for the determination of that type) with the type that is required as the result of the conversion (call it A) as described in 13.10.3.6 [temp.deduct.type].

If P is a reference type, the type referred to by P is used in place of P for type deduction and for any further references to or transformations of P in the remainder of this section.

If A is not a reference type:

If A is a cv-qualified type, the top level cv-qualifiers of A's type are ignored for type deduction. If A is a reference type, the type referred to by A is used for type deduction. If P is a reference type, the type referred to by P is used for type deduction.

(This resolution also resolves issues 493 and 913.)

[Drafting note: This change intentionally reverses the resolution of issue 322 (and applies it in a different form).]




499. Throwing an array of unknown size

Section: 14.2  [except.throw]     Status: CD2     Submitter: Mike Miller     Date: 19 Jan 2005

[Voted into the WP at the March, 2009 meeting.]

According to 14.2 [except.throw] paragraph 3,

The type of the throw-expression shall not be an incomplete type, or a pointer to an incomplete type other than (possibly cv-qualified) void.

This disallows cases like the following, because str has an incomplete type (an array of unknown size):

    extern const char str[];
    void f() {
        throw str;
    }

The array-to-pointer conversion is applied to the operand of throw, so there's no problem creating the exception object, which is the reason for the restriction on incomplete types. I believe this case should be permitted.

Notes from the April, 2005 meeting:

The CWG agreed that the example should be permitted. Note that the reference to throw-expression in the cited text is incorrect; a throw-expression includes the throw keyword and is always of type void. This wording problem is addressed in the proposed resolution for issue 475.

Proposed resolution (October, 2006)

Change 14.2 [except.throw] paragraph 3 as indicated:

...The type of the throw-expression shall not If the type of the exception object would be an incomplete type, or a pointer to an incomplete type other than (possibly cv-qualified) void the program is ill-formed...



828. Destruction of exception objects

Section: 14.2  [except.throw]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 130

[Voted into WP at March, 2010 meeting.]

14.2 [except.throw] paragraph 4 says,

When the last remaining active handler for the exception exits by any means other than throw; the temporary object is destroyed and the implementation may deallocate the memory for the temporary object...

With std::current_exception() (17.9.7 [propagation] paragraph 7) , it might be possible to refer to the exception object after its last handler exits (if the exception object is not copied). The text needs to be updated to allow for that possibility.

Proposed resolution (September, 2009):

Change 14.2 [except.throw] paragraph 4 as follows:

The memory for the temporary copy of the exception being thrown exception object is allocated in an unspecified way, except as noted in 6.7.5.5.2 [basic.stc.dynamic.allocation]. The temporary persists as long as there is a handler being executed for that exception. In particular, if If a handler exits by executing a throw; statement, that passes control rethrowing, control is passed to another handler for the same exception, so the temporary remains. The exception object is destroyed after either When the last remaining active handler for the exception exits by any means other than throw; rethrowing, or the last object of type std::exception_ptr (17.9.7 [propagation]) that refers to the exception object is destroyed, whichever is later. In the former case, the destruction occurs when the handler exits, immediately after the destruction of the object declared in the exception-declaration in the handler, if any. In the latter case, the destruction occurs before the destructor of std::exception_ptr returns. the temporary object is destroyed and the The implementation may then deallocate the memory for the temporary exception object; any such deallocation is done in an unspecified way. The destruction occurs immediately after the destruction of the object declared in the exception-declaration in the handler.



830. Deprecating exception specifications

Section: 14.5  [except.spec]     Status: CD2     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 136

[Voted into WP at March, 2010 meeting as paper N3051.]

Exception specifications have proven close to worthless in practice, while adding a measurable overhead to programs. The feature should be deprecated. The one exception to the rule is the empty throw specification which could serve a legitimate optimizing role if the requirement to call std::unexpected were relaxed in this case.

Notes from the July, 2009 meeting:

The consensus of the CWG was in favor of deprecating exception specifications. Further discussion, and with a wider constituency, is needed to determine a position on the status of throw().

(See also issue 814.)




973. Function types in exception-specifications

Section: 14.5  [except.spec]     Status: CD2     Submitter: Daniel Krügler     Date: 12 October, 2009

[Voted into WP at March, 2010 meeting.]

There is no prohibition against specifying a function type in an exception-specification, and the normal conversion of a function type to a pointer-to-function type occurs in both throw-expressions (14.2 [except.throw] paragraph 3) and in handlers (14.4 [except.handle] paragraph 2), but that was apparently overlooked in the description of exception-specifications.

Proposed resolution (February, 2010):

Change 14.5 [except.spec] paragraphs 2-3 as follows:

A type denoted in an exception-specification shall not denote an incomplete type. A type denoted in an exception-specification shall not denote a pointer or reference to an incomplete type, other than void*, const void*, volatile void*, or const volatile void*. A type cv T, “array of T,” or “function returning T” denoted in an exception-specification is adjusted to type T, “pointer to T,” or “pointer to function returning T,” respectively.

If any declaration of a function has an exception-specification, all declarations, including the definition and an any explicit specialization, of that function shall have an exception-specification with the same set of type-ids adjusted types. If any declaration of a pointer to function, reference to function, or pointer to member function has an exception-specification, all occurrences of that declaration shall have an exception-specification with the same set of type-ids adjusted types. In an explicit instantiation an exception-specification may be specified, but is not required. If an exception-specification is specified in an explicit instantiation directive, it shall have the same set of type-ids adjusted types as other declarations of that function. A diagnostic is required only if the sets of type-ids adjusted types are different within a single translation unit.




668. Throwing an exception from the destructor of a local static object

Section: 14.6.2  [except.terminate]     Status: CD2     Submitter: Daniel Krügler     Date: 16 December 2007

[Voted into the WP at the March, 2009 meeting.]

The destruction of local static objects occurs at the same time as that of non-local objects (6.9.3.3 [basic.start.dynamic] paragraph 1) and the execution of functions registered with std::atexit (paragraph 3). According to 14.6.2 [except.terminate] paragraph 1, std::terminate is called if a destructor for a non-local object or a function registered with std::atexit exits via an exception, but the Standard is silent about the result of throwing an exception from a destructor for a local static object. Presumably this is an oversight and the same rules should apply to destruction of local static objects.

Proposed resolution (September, 2008):

Change 14.6.2 [except.terminate] paragraph 1, fourth bullet as indicated, and add an additional bullet to follow it:




601. Type of literals in preprocessing expressions

Section: 15.2  [cpp.cond]     Status: CD2     Submitter: Daveed Vandevoorde     Date: 23 October 2006     Liaison: WG14

[Voted into WP at October, 2009 meeting.]

The description of preprocessing expressions in 15.2 [cpp.cond] paragraph 4 says,

The resulting tokens comprise the controlling constant expression which is evaluated according to the rules of 5.19 using arithmetic that has at least the ranges specified in 17.3 [support.limits], except that all signed and unsigned integer types act as if they have the same representation as, respectively, intmax_t or uintmax_t (18.3.2).

However, this does not address the type implicitly assigned to integral literals. For example, in an implementation where int is 32 bits and long long is 64 bits, is a literal like 0xffffffff signed or unsigned? WG14 adopted DR 265 to deal with this issue in the essentially-identical wording in C99; we should probably follow suit for C++.

Proposed Resolution (July, 2009):

Change 15.2 [cpp.cond] paragraph 4 as follows:

...and then each preprocessing token is converted into a token. The resulting tokens comprise the controlling constant expression which is evaluated according to the rules of 7.7 [expr.const] using arithmetic that has at least the ranges specified in 17.3 [support.limits], except that. For the purposes of this token conversion and evaluation all signed and unsigned integer types act as if they have the same representation as, respectively, intmax_t or uintmax_t (_N3035_.18.4.2 [stdinth])[Footnote: Thus on an implementation where std::numeric_limits<int>::max() is 0x7FFF and std::numeric_limits<unsigned int>::max() is 0xFFFF, the integer literal 0x8000 is signed and positive within a #if expression even though it is unsigned in translation phase 7 (5.2 [lex.phases]). —end footnote]. This includes interpreting character literals...



618. Casts in preprocessor conditional expressions

Section: 15.2  [cpp.cond]     Status: CD2     Submitter: Martin Sebor     Date: 12 February 2007     Liaison: WG14

[Voted into WP at October, 2009 meeting.]

15.2 [cpp.cond] paragraph 1 states,

The expression that controls conditional inclusion shall be an integral constant expression except that: it shall not contain a cast...

The prohibition of casts is vacuous and misleading: as pointed out in the footnote in that paragraph,

Because the controlling constant expression is evaluated during translation phase 4, all identifiers either are or are not macro names — there simply are no keywords, enumeration constants, and so on.

As a result, there can be no casts, which require either keywords or identifiers that resolve to types in order to be recognized as casts. The wording on casts should be removed and replaced by a note recognizing this implication.

Notes from the April, 2007 meeting:

The CWG agreed with this suggested resolution; however, the reference is in the “Preprocessing Directives” clause, which WG21 intends to keep in as close synchronization as possible with the corresponding wording in the C Standard. Any change here must therefore be done in consultation with WG14. Clark Nelson will fulfill this liaison function.

It was also noted that the imminent introduction of constexpr also has the potential for a similar kind of confusion, so the proposed resolution should address both casts and constexpr.

Proposed resolution (July, 2009):

Change 15.2 [cpp.cond] paragraph 1 as follows:

The expression that controls conditional inclusion shall be an integral constant expression except that: it shall not contain a cast; identifiers (including those lexically identical to keywords)...



626. Preprocessor string literals

Section: 15.6.3  [cpp.stringize]     Status: CD2     Submitter: Gennaro Prota     Date: 12 September 2006

[Voted into WP at October, 2009 meeting.]

Clause 15 [cpp] refers in several places to “character string literals” without specifying whether they are narrow or wide strings. For instance, what kind of string does the # operator (15.6.3 [cpp.stringize]) produce?

15.7 [cpp.line] paragraph 1 says,

The string literal of a #line directive, if present, shall be a character string literal.

Is “character string literal” intended to mean a narrow string literal? (Also, there is no string-literal mentioned in the grammatical descriptions of #line; paragraph 4 reads,

which is apparently intended to suggest a string literal but does not use the term.)

15.11 [cpp.predefined] should also specify what kind of character string literals are produced by the various string-valued predefined macros.

Notes from the July, 2007 meeting:

The CWG affirmed that all the string literals mentioned in Clause 15 [cpp] are intended to be narrow strings.

Proposed resolution (September, 2008)

  1. Change the footnote in Clause 15 [cpp] paragraph 1 as follows:

  2. Thus, preprocessing directives are commonly called “lines.” These “lines” have no other syntactic significance, as all white space is equivalent except in certain situations during preprocessing (see the # character string literal creation operator in 15.6.3 [cpp.stringize], for example).
  3. Change 15.6.3 [cpp.stringize] paragraph 2 as follows:

  4. If, in the replacement list, a parameter is immediately preceded by a # preprocessing token, both are replaced by a single character ordinary string literal (5.13.5 [lex.string]) preprocessing token that contains the spelling of the preprocessing token sequence for the corresponding argument... Otherwise, the original spelling of each preprocessing token in the argument is retained in the character ordinary string literal, except for special handling for producing the spelling of string literals and character literals: a \ character is inserted before each " and \ character of a character literal or string literal (including the delimiting " characters). If the replacement that results is not a valid character ordinary string literal, the behavior is undefined. The character ordinary string literal corresponding to an empty argument is "". The order of evaluation of # and ## operators is unspecified.
  5. Change 15.6.6 [cpp.scope] paragraph 6 as follows:

  6. To illustrate the rules for creating character ordinary string literals and concatenating tokens, the sequence... or, after concatenation of the character ordinary string literals...
  7. Change 15.7 [cpp.line] paragraph 1 as follows:

  8. The string literal of a #line directive, if present, shall be a character an ordinary string literal.
  9. Change 15.7 [cpp.line] paragraph 4 as follows:

  10. ...and changes the presumed name of the source file to be the contents of the character ordinary string literal.
  11. Change 15.11 [cpp.predefined] paragraph 1 as follows:

  12. __DATE__

    __FILE__

    ...

    __TIME__

Notes from the September, 2008 meeting:

The proposed resolution will be discussed with the C Committee before proceeding, as it is expected that the next revision of the C Standard will also adopt new forms of string literals.

Additional notes (May, 2009):

At its most recent meeting, the C Committee decided to keep the existing term, “character string literal.”

One possibility for maintaining compatible phraseology with the C Standard would be to replace the occurrences of “ordinary string literal” in 5.13.5 [lex.string] with “character string literal,” instead of the extensive set of changes above.

Another possibility would be to leave the references in Clause 15 [cpp] unchanged and just insert a prefatory comment near the beginning that every occurrence of “character string literal” refers to a string-literal with no prefix. (The use of “ordinary string literal” in the preceding edits is problematic in that the phrase includes raw string literals as well as unprefixed literals.)

Proposed resolution (July, 2009):

  1. Change 15.6.3 [cpp.stringize] paragraph 2 as follows:

  2. A character string literal is a string-literal with no prefix. If, in the replacement list, a parameter is immediately preceded by a # preprocessing token...
  3. Change the fifteenth bullet of Annex Clause Annex B [implimits] paragraph 2 as follows:




831. Limit on recursively nested template instantiations

Section: Clause Annex B  [implimits]     Status: CD2     Submitter: DE     Date: 3 March, 2009

N2800 comment DE 25

[Voted into WP at October, 2009 meeting.]

The limit of 17 recursively-nested template instantiations is too small for modern programming practices such as template metaprogramming. It is unclear, however, whether this is a useful metric; see this paper for an example that honors the limit but results in over 750 billion instantiations.

Notes from the July, 2009 meeting:

The consensus of the CWG was to increase the limit to 1024.

Proposed resolution (September, 2009):

Change Clause Annex B [implimits], the fourth bullet from the end, as follows:






Issues with "CD3" Status


1350. Incorrect exception specification for inherited constructors

Section: _N4527_.12.9  [class.inhctor]     Status: CD3     Submitter:     Date: 2011-08-16

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

According to _N4527_.12.9 [class.inhctor] paragraph 3, the exception specification for an inheriting constructor has the same exception specification as the inherited constructor. This ignores the exception specifications of default constructors for base classes and nonstatic data members and of functions called in brace-or-equals-initializers of nonstatic data members.

Proposed resolution (August, 2011):

  1. Delete the indicated bullet of _N4527_.12.9 [class.inhctor] paragraph 2:

  2. Change _N4527_.12.9 [class.inhctor] paragraph 3 as follows:

  3. ...[Note: Default arguments are not inherited. An exception-specification is implied as specified in 14.5 [except.spec].end note]
  4. Change 14.5 [except.spec] paragraph 14 as follows:

  5. An inheriting constructor (_N4527_.12.9 [class.inhctor]) and an implicitly declared special member function ( 11.4.4 [special]) shall have an exception-specification. If f is an inheriting constructor or an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f's implicit definition; f shall allow all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions if every function it directly invokes allows no exceptions. [Note: an instantiation of an inheriting constructor template has an implied exception-specification as if it were a non-template inheriting constructor. —end note] [Example:...



1487. When are inheriting constructors declared?

Section: _N4527_.12.9  [class.inhctor]     Status: CD3     Submitter: Richard Smith     Date: 2012-03-27

[Moved to DR at the April, 2013 meeting.]

According to _N4527_.12.9 [class.inhctor] paragraph 3,

For each non-template constructor in the candidate set of inherited constructors other than a constructor having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly declared with the same constructor characteristics unless there is a user-declared constructor with the same signature in the class where the using-declaration appears.

It is not clear whether that determination is intended to include constructors declared after the point of the using-declaration or not.

Proposed resolution (February, 2013):

  1. Change 11.4 [class.mem] paragraph 2 as follows:

  2. A class is considered a completely-defined object type (6.8 [basic.types]) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, using-declarations introducing inheriting constructors (_N4527_.12.9 [class.inhctor]), and brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.
  3. Change 11.4.4 [special] paragraph 1 as follows:

  4. ...See 11.4.5 [class.ctor], 11.4.7 [class.dtor] and 11.4.5.3 [class.copy.ctor]. —end note] An implicitly-declared special member function is declared at the closing } of the class-specifier. Programs shall not define implicitly-declared special member functions.
  5. Change _N4527_.12.9 [class.inhctor] paragraph 3 as follows:

  6. For each non-template constructor in the candidate set of inherited constructors other than a constructor having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly declared with the same constructor characteristics unless there is a user-declared constructor with the same signature in the complete class where the using-declaration appears. Similarly, for each constructor template in the candidate set of inherited constructors, a constructor template is implicitly declared with the same constructor characteristics unless there is an equivalent user-declared constructor template (13.7.7.2 [temp.over.link]) in the complete class where the using-declaration appears. [Note: Default arguments are not inherited. An exception-specification is implied as specified in 14.5 [except.spec]. —end note]

Additional note (January, 2013):

A question has been raised as to whether it is necessary to prohibit inheriting constructors from base classes that are also enclosing classes when the derived class is defined outside the member-specification of the enclosing class. This issue has been returned to "review" status to allow discussion of this question.

Additional note (February, 2013):

It was observed that it is not permitted to derive from an incomplete class, which prevents the problem intended to be addressed by the prohibition of inheriting constructors from an enclosing class without disallowing such usage when the nested class is defined outside its enclosing class. That restriction has been removed from the proposed resolution.




1440. Acceptable decltype-specifiers used as nested-name-specifiers

Section: _N4567_.5.1.1  [expr.prim.general]     Status: CD3     Submitter: Mike Miller     Date: 2012-01-05

[Moved to DR at the October, 2012 meeting.]

The current wording of the Standard does not describe what happens when a decltype-specifier is used as a nested-name-specifier and the type denoted by the decltype-specifier is neither a class type nor an enumeration type. Such nested-name-specifiers should be prohibited, presumably somewhere around paragraphs 8-10 of _N4567_.5.1.1 [expr.prim.general]. (The corresponding prohibition for named types is handled as part of lookup in 6.5.5 [basic.lookup.qual] paragraph 1.)

Proposed resolution (February, 2012):

Add the following immediately after the grammar in _N4567_.5.1.1 [expr.prim.general] paragraph 8 and move the text following that point into a new paragraph:

The type denoted by a decltype-specifier in a nested-name-specifier shall be a class or enumeration type.

A nested-name-specifier that denotes a class...




1251. C compatibility: casting to unqualified void*

Section: _N4750_.C.1.3  [diff.conv]     Status: CD3     Submitter: Johannes Schaub     Date: 2011-03-04

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The incompatibility described appears not to exist.

Proposed resolution (August, 2011):

Delete the second entry in _N4750_.C.1.3 [diff.conv], i.e., the one headed by

Change: Only pointers to non-const and non-volatile objects may be implicitly converted to void*



2114. Missing description of incompatibility from aggregate NSDMIs

Section: _N4750_.C.3.5  [diff.cpp11.dcl.decl]     Status: CD3     Submitter: Ville Voutilainen     Date: 2015-04-14

The following example illustrates an incompatibility between C++11 and C++14:

  struct S {
    int m = 1;
  };        // C++11: S is non-aggregate
            // C++14: S is AGGREGATE
  struct X {
      operator int();
      operator S();
  };

  int main() {
    X a{};
    S b{a};  // C++11: valid, copy constr S(a.operator S()) is called here
             // C++14: valid, aggregate initialization { a.operator int() }

    printf("%d\n", b.m);
  }

This should be documented in Annex Clause Annex C [diff].

Notes from the May, 2015 meeting:

This will be handled editorially; the status has been set to "review" to check that the editorial change has been made.




1439. Lookup and friend template declarations

Section: _N4868_.9.8.2.3  [namespace.memdef]     Status: CD3     Submitter: Richard Smith     Date: 2012-01-04

[Moved to DR at the October, 2012 meeting.]

According to _N4868_.9.8.2.3 [namespace.memdef] paragraph 3,

If a friend declaration in a non-local class first declares a class or function95 the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (6.5.3 [basic.lookup.unqual]) or by qualified lookup (6.5.5 [basic.lookup.qual]) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship).

This wording does not, but probably should, apply to friend declarations of function templates and class templates as well.

Proposed resolution (February, 2012):

  1. Change 6.5.4 [basic.lookup.argdep] paragraph 1 as follows:

  2. ...in those namespaces, namespace-scope friend function or function template declarations (11.3) not otherwise visible may be found...
  3. Change _N4868_.9.8.2.3 [namespace.memdef] paragraph 3 as follows:

  4. Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class, or function, class template, or function template95 the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (6.5.3 [basic.lookup.unqual]) or by qualified lookup (6.5.5 [basic.lookup.qual]) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function or function template is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (6.5.4 [basic.lookup.argdep]). If the name in a friend declaration...



1477. Definition of a friend outside its namespace

Section: _N4868_.9.8.2.3  [namespace.memdef]     Status: CD3     Submitter: John Spicer     Date: 2012-03-09

[Moved to DR at the April, 2013 meeting.]

According to _N4868_.9.8.2.3 [namespace.memdef] paragraph 3,

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class or function95 the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (6.5.3 [basic.lookup.unqual]) or by qualified lookup (6.5.5 [basic.lookup.qual]) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship).

Taken literally, that would mean the following example is ill-formed:

  namespace N {
    struct A {
      friend int f();
    };
  }
  int N::f() { return 0; }
  int i = N::f();    // ill-formed: N::f not found

because the definition of N::f appears in global scope rather than in namespace scope.

Proposed resolution (October, 2012):

Change _N4868_.9.8.2.3 [namespace.memdef] paragraph 3 as follows:

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class or function95 the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by The friend declaration does not by itself make the name visible to unqualified lookup (6.5.3 [basic.lookup.unqual]) or by qualified lookup (6.5.5 [basic.lookup.qual]). [Note: The name of the friend will be visible in its namespace if until a matching declaration is provided in that at namespace scope (either before or after the class definition granting friendship). end note] If a friend function is called...



1306. Modifying an object within a const member function

Section: _N4868_.11.4.3.2  [class.this]     Status: CD3     Submitter: James Kanze     Date: 2011-04-26

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

According to _N4868_.11.4.3.2 [class.this] paragraph 2,

In a const member function, the object for which the function is called is accessed through a const access path; therefore, a const member function shall not modify the object and its non-static data members.

This is clearly overstating the case: mutable members can be modified, a const_cast can be used, and class member access expressions not involving this can also allow the object to be modified. The effect of the cv-qualification of a member function on the type of *this is clear from the preceding paragraph; this statement appears both unnecessary and incorrect.

Proposed resolution (August, 2011):

Merge _N4868_.11.4.3.2 [class.this] paragraphs 1 and 2 and change the text as follows:

In the body of a non-static (11.4.2 [class.mfct]) member function, the keyword this is a prvalue expression whose value is the address of the object for which the function is called. The type of this in a member function of a class X is X*. If the member function is declared const, the type of this is const X*, if the member function is declared volatile, the type of this is volatile X*, and if the member function is declared const volatile, the type of this is const volatile X*. In [Note: thus in a const member function, the object for which the function is called is accessed through a const access path; therefore, a const member function shall not modify the object and its non-static data members. end note] [Example:...



1438. Non-dereference use of invalid pointers

Section: _N4885_6.7.5.5.4  [basic.stc.dynamic.safety]     Status: CD3     Submitter: Anthony Williams     Date: 2012-01-03

[Moved to DR at the October, 2012 meeting.]

The current Standard says that any use of an invalid pointer value produces undefined behavior (6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 4). This includes not only dereferencing the pointer but even just fetching its value. The reason for this draconian restriction is that some architectures in the past used dedicated address registers for pointer loads and stores and they could fault if, for example, a segment number in a pointer was not currently mapped.

It is not clear whether such restrictions are necessary with architectures currently in use or reasonably foreseen. This should be investigated to see if the restriction can be loosened to apply only to dereferencing the pointer.

Proposed resolution (February, 2012):

Change 6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 4 as follows:

If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value (7.3.12 [conv.ptr]), the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid all pointers referring to any part of the deallocated storage. The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation function have undefined behavior. Any other use of an invalid pointer value has implementation-defined behavior. [Footnote: On some Some implementations, it might define that copying an invalid pointer value causes a system-generated runtime fault. —end footnote]



616. Definition of “indeterminate value”

Section: Clause 3  [intro.defs]     Status: CD3     Submitter: Bjarne Stroustrup     Date: 2 February 2007

[Moved to DR at the April, 2013 meeting.]

The C++ Standard uses the phrase “indeterminate value” without defining it. C99 defines it as “either an unspecified value or a trap representation.” Should C++ follow suit?

In addition, 7.3.2 [conv.lval] paragraph 1 says that applying the lvalue-to-rvalue conversion to an “object [that] is uninitialized” results in undefined behavior; this should be rephrased in terms of an object with an indeterminate value.

Proposed resolution (October, 2012):

  1. Change 7.3.2 [conv.lval] paragraphs 1 and 2 as follows (including changing the running text of paragraph 2 into bullets):

  2. A glvalue (7.2.1 [basic.lval]) of a non-function, non-array type T can be converted to a prvalue.53 If T is an incomplete type, a program that necessitates this conversion is ill-formed. If the object to which the glvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior. If T is a non-class type, the type of the prvalue is the cv-unqualified version of T. Otherwise, the type of the prvalue is T.54

    When an lvalue-to-rvalue conversion occurs in an unevaluated operand or a subexpression thereof (Clause 7 [expr]) the value contained in the referenced object is not accessed. In all other cases, the result of the conversion is determined according to the following rules:

  3. Change 7.6.1.5 [expr.ref] paragraph 4 second bullet as follows:

  4. Change 7.6.4 [expr.mptr.oper] paragraph 6 as follows:

  5. ...The result of a .* expression whose second operand is a pointer to a data member is of the same value category (7.2.1 [basic.lval]) as its first operand an lvalue if the first operand is an lvalue and an xvalue otherwise. The result of a .* expression whose second operand is a pointer to a member function...

This resolution also resolves issues 129, 240, 312, 623, and 1013.

(See also issue 1213.)

Additional note (August, 2012):

It was observed that the phrase in the fourth bullet of the change to 7.3.2 [conv.lval] paragraph 2 that reads “is not a local variable” should probably be changed to “does not have automatic storage duration,” because objects with static storage duration are zero-initialized and thus cannot have an indeterminate value. The issue was returned to "review" status for discussion of this point.




1476. Definition of user-defined type

Section: Clause 3  [intro.defs]     Status: CD3     Submitter: Loïc Joly     Date: 2012-03-08

[Moved to DR at the April, 2013 meeting.]

The Standard uses the phrase, “user-defined type,” but it is not clear what it is intended to mean. For example, 16.4.5.2.1 [namespace.std] paragraph 1 says,

A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type...

Are types defined in the Standard library “user-defined?”

9.2.9.3 [dcl.type.simple] paragraph 2 says,

The auto specifier is a placeholder for a type to be deduced (9.2.9.7 [dcl.spec.auto]). The other simple-type-specifiers specify either a previously-declared user-defined type or one of the fundamental types (6.8.2 [basic.fundamental]).

implying that all non-fundamental types are “user-defined.”

A definition is needed, as well as a survey of uses of the term to ensure consistency with the definition.

Proposed resolution (October, 2012):

  1. Change 9.2.9.3 [dcl.type.simple] paragraph 2 as follows:

  2. The auto specifier is a placeholder for a type to be deduced (9.2.9.7 [dcl.spec.auto]). The other simple-type-specifiers specify either a previously-declared user-defined type, a type determined from an expression, or one of the fundamental types (6.8.2 [basic.fundamental]). Table 10 summarizes the valid combinations of simple-type-specifiers and the types they specify.
  3. Change 7.3 [conv] paragraph 4 as follows:

  4. [Note: For user-defined class types, user-defined conversions are considered as well; see 11.4.8 [class.conv]. In general, an implicit conversion sequence (12.2.4.2 [over.best.ics]) consists of a standard conversion sequence followed by a user-defined conversion followed by another standard conversion sequence. —end note]

  5. Change the example in 12.2.2.3 [over.match.oper] paragraph 1 as follows:

  6.   ...
      void f(void) {
        const char* p= "one" + "two";  // ill-formed because neither
                                       // operand has user-defined class or enumeration type
        int I = 1 + 1;                 // Always evaluates to 2 even if
                                       // user-defined class or enumeration types exist which
                                       // would perform the operation.
      }
    



1531. Definition of “access” (verb)

Section: Clause 3  [intro.defs]     Status: CD3     Submitter: Mike Miller     Date: 2012-07-27

[Moved to DR at the April, 2013 meeting.]

The verb “access” is used in various places in the Standard (see 6.7.3 [basic.life] paragraphs 5 and 6 and 7.2.1 [basic.lval] paragraph 10) but is not defined. C99 defines it as

<execution-time action> to read or modify the value of an object

(See also issue 1530.)

Proposed resolution (March, 2013):

  1. Add the following to Clause 3 [intro.defs]:

  2. access
    <execution-time action> to read or modify the value of an object
  3. Change 6.9.1 [intro.execution] paragraph 12 as follows:

  4. Accessing Reading an object designated by a volatile glvalue (7.2.1 [basic.lval]), modifying an object, calling...
  5. Change 6.9.2 [intro.multithread] paragraph 4 as follows:

  6. Two expression evaluations conflict if one of them modifies a memory location (6.7.1 [intro.memory]) and the other one accesses reads or modifies the same memory location.
  7. Change 6.9.2 [intro.multithread] paragraph 24 as follows:

  8. The implementation may assume that any thread will eventually do one of the following:

  9. Change 7.6.19 [expr.ass] paragraph 8 as follows:

  10. If the value being stored in an object is accessed from read via another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is undefined. [Note:...
[Note: this wording was reviewed during the 2013-03-25 teleconference.]


912. Character literals and universal-character-names

Section: 5.13.3  [lex.ccon]     Status: CD3     Submitter: Alisdair Meredith     Date: 7 June, 2009

[Moved to DR at the October, 2012 meeting.]

According to 5.13.3 [lex.ccon] paragraph 1,

A character literal that does not begin with u, U, or L is an ordinary character literal, also referred to as a narrow-character literal. An ordinary character literal that contains a single c-char has type char, with value equal to the numerical value of the encoding of the c-char in the execution character set.

However, the definition of c-char includes as one possibility a universal-character-name. The value of a universal-character-name cannot, in general, be represented as a char, so this specification is impossible to satisfy.

(See also issue 411 for related questions.)

Additional note (February, 2012):

See the discussion in issue 1422 for a possible interpretation of the existing text.

Proposed resolution (February, 2012):

Change 5.13.3 [lex.ccon] paragraph 1 as follows:

...A character literal that does not begin with u, U, or L is an ordinary character literal, also referred to as a narrow-character literal. An ordinary character literal that contains a single c-char representable in the execution character set has type char, with value equal to the numerical value of the encoding of the c-char in the execution character set. An ordinary character literal that contains more than one c-char is a multicharacter literal. A multicharacter literal, or an ordinary character literal containing a single c-char not representable in the execution character set, is conditionally-supported, has type int, and has an implementation-defined value.

This resolution also resolves issue 1024.




1024. Limits on multicharacter literals

Section: 5.13.3  [lex.ccon]     Status: CD3     Submitter: Alisdair Meredith     Date: 2010-01-31

[Moved to DR at the October, 2012 meeting.]

There is no limit placed on the number of c-chars in a multicharacter literal or a wide-character literal containing multiple c-chars, either in 5.13.3 [lex.ccon] paragraphs 1-2 or in Annex Clause Annex B [implimits]. Presumably this means that an implementation must accept arbitrarily long literals.

An alternative approach might be to state that these literals are conditionally supported with implementation-defined semantics, allowing an implementation to impose a documented limit that makes sense for the particular architecture.

Proposed resolution (February, 2012):

This issue is resolved by the resolution of issue 912.




712. Are integer constant operands of a conditional-expression “used?”

Section: 6.3  [basic.def.odr]     Status: CD3     Submitter: Mike Miller     Date: 9 September, 2008

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

In describing static data members initialized inside the class definition, 11.4.9.3 [class.static.data] paragraph 3 says,

The member shall still be defined in a namespace scope if it is used in the program...

The definition of “used” is in 6.3 [basic.def.odr] paragraph 1:

An object or non-overloaded function whose name appears as a potentially-evaluated expression is used unless it is an object that satisfies the requirements for appearing in a constant expression (7.7 [expr.const]) and the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is immediately applied.

Now consider the following example:

    struct S {
      static const int a = 1;
      static const int b = 2;
    };
    int f(bool x) {
      return x ? S::a : S::b;
    }

According to the current wording of the Standard, this example requires that S::a and S::b be defined in a namespace scope. The reason for this is that, according to 7.6.16 [expr.cond] paragraph 4, the result of this conditional-expression is an lvalue and the lvalue-to-rvalue conversion is applied to that, not directly to the object, so this fails the “immediately applied” requirement. This is surprising and unfortunate, since only the values and not the addresses of the static data members are used. (This problem also applies to the proposed resolution of issue 696.)

Proposed resolution (August, 2011):

Divide 6.3 [basic.def.odr] paragraph 2 into two paragraphs and change as follows:

An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression thereof. The set of potential results of an expression e is defined as:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used unless it x is an object that satisfies the requirements for appearing in a constant expression (7.7 [expr.const]) and ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is immediately applied to e, or e is a discarded-value expression (Clause 7 [expr]). this is odr-used...

[Drafting note: this wording requires S::a to be defined if it is used in an expression like *&S::a.]


1260. Incorrect use of term “overloaded” in description of odr-use

Section: 6.3  [basic.def.odr]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-03-11

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The current wording of 6.3 [basic.def.odr] paragraph 2 uses the term “overloaded” differently from its definition in Clause 12 [over] paragraph 1. For example, names found by argument-dependent lookup are not “overloaded” if they are not declared in the same scope. The phrasing should be reconciled between the two sections.

Proposed resolution (August, 2011):

Change 6.3 [basic.def.odr] paragraph 2 as follows:

...A non-overloaded function whose name appears as a potentially-evaluated expression is odr-used if it is the unique lookup result or it is the selected or a member of a set of candidate overloaded functions (6.5 [basic.lookup], 12.2 [over.match], 12.3 [over.over]), if selected by overload resolution when referred to from a potentially-evaluated expression, is odr-used, unless it is a pure virtual function and its name is not explicitly qualified...



1362. Complete type required for implicit conversion to T&

Section: 6.3  [basic.def.odr]     Status: CD3     Submitter: Richard Smith     Date: 2011-08-16

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The requirement in 6.3 [basic.def.odr] paragraph 4 that a type T must be complete if an expression is implicitly converted to a pointer to T or reference to T inadvertently applies to user-defined conversions, although it was intended only to refer to built-in conversions.

Proposed resolution (August, 2011):

Change the indicated bullet of 6.3 [basic.def.odr] paragraph 4 as follows:




1472. odr-use of reference variables

Section: 6.3  [basic.def.odr]     Status: CD3     Submitter: Richard Smith     Date: 2012-03-01

[Moved to DR at the April, 2013 meeting.]

We have a special case in 6.3 [basic.def.odr] paragraph 2 that variables which satisfy the requirements for appearing in a constant expression are not odr-used if the lvalue-to-rvalue conversion is immediately applied. This special case only applies to objects, and thus does not apply to variables of reference type. This inconsistency seems strange, and there is implementation divergence:

  int n;
  void f() {
   constexpr int &r = n;
   [] { return r; }; // error: r is odr-used but not captured
  }

This code is accepted by g++ but rejected by clang. Should r be odr-used here?

Proposed resolution (October, 2012):

Change 6.3 [basic.def.odr] paragraph 3 as follows:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used unless x is an object that satisfies the requirements for appearing in a constant expression (7.7 [expr.const]) and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is applied to e, or e is a discarded-value expression ( Clause 7 [expr]). this is odr-used...



1511. const volatile variables and the one-definition rule

Section: 6.3  [basic.def.odr]     Status: CD3     Submitter: Richard Smith     Date: 2012-06-18

[Moved to DR at the April, 2013 meeting.]

One of the criteria in 6.3 [basic.def.odr] paragraph 6 for when a entity is allowed to have multiple definitions is:

in each definition of D, corresponding names, looked up according to 6.5 [basic.lookup], shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (12.2 [over.match]) and after matching of partial template specialization (13.10.4 [temp.over]), except that a name can refer to a const object with internal or no linkage if the object has the same literal type in all definitions of D, and the object is initialized with a constant expression (7.7 [expr.const]), and the value (but not the address) of the object is used, and the object has the same value in all definitions of D; and

This wording is possibly not sufficiently clear for an example like:

  const volatile int n = 0;
  inline int get() { return n; }

Presumably this code could not appear in multiple translation units, because the requirement that n “has the same value in all definitions” cannot be satisfied (the value of a volatile variable can change “by means undetectable by the implementation,” per 9.2.9.2 [dcl.type.cv] paragraph 7, so the value of n might be different in each translation unit). However, it might be good to make it explicit that “a const object” is not intended to apply to a volatile-qualified object.

Other points that were raised during the discussion of this issue were that it would probably be better to rephrase “the value (but not the address) of the object is used” in terms of the odr-use of the object, as well as questioning why a const volatile variable implicitly has internal linkage.

Proposed resolution (October, 2012):

  1. Change 6.3 [basic.def.odr] paragraph 6 as follows:

  2. There can be more than one definition...

  3. Change 6.6 [basic.link] paragraph 3 as follows:

  4. A name having namespace scope (6.4.6 [basic.scope.namespace]) has internal linkage if it is the name of




1482. Point of declaration of enumeration

Section: 6.4.2  [basic.scope.pdecl]     Status: CD3     Submitter: Daveed Vandevoorde     Date: 2012-03-20

[Moved to DR at the April, 2013 meeting.]

According to 6.4.2 [basic.scope.pdecl] paragraph 2,

The point of declaration for an enumeration is immediately after the identifier (if any) in either its enum-specifier (9.7.1 [dcl.enum]) or its first opaque-enum-declaration (9.7.1 [dcl.enum]), whichever comes first.

This would make the following example well-formed:

  template<typename T> struct S { typedef char I; };
  enum E: S<E>::I { e };

Presumably that would make E an incomplete enumeration type for the instantiation of S<E> (a concept not otherwise found in the Standard). However, some implementations reject this example, presumably making the point of declaration after the enum-base. We either need to make it ill-formed or describe incomplete enumeration types.

See also issue 977.

Proposed resolution (December, 2012):

  1. Change 6.8 [basic.types] paragraph 5 as follows:

  2. A class that has been declared but not defined, an enumeration type in certain contexts (9.7.1 [dcl.enum]), or an array of unknown size or of incomplete element type, is an incompletely-defined object type.43 Incompletely-defined object types...
  3. Add the following as a new paragraph preceding 9.7.1 [dcl.enum] paragraph 6:

  4. An enumeration whose underlying type is fixed is an incomplete type from its point of declaration (6.4.2 [basic.scope.pdecl]) to immediately after its enum-base (if any), at which point it becomes a complete type. An enumeration whose underlying type is not fixed is an incomplete type from its point of declaration to immediately after the closing } of its enum-specifier, at which point it becomes a complete type.

    For an enumeration whose underlying type is not fixed...

This resolution also resolves issue 977.




1352. Inconsistent class scope and completeness rules

Section: 6.4.7  [basic.scope.class]     Status: CD3     Submitter: Richard Smith     Date: 2011-08-16

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The rules regarding class scope and when the class is considered to be complete (normally implemented by deferred parsing of portions of class member declarations) are inconsistent and need to be clarified.

Proposed resolution (August, 2011):

  1. Change 6.4.7 [basic.scope.class] paragraph 1 as follows:

    1. The potential scope of a name declared in a class consists not only of the declarative region following the name's point of declaration, but also of all function bodies, default arguments, and brace-or-equal-initializers of non-static data members, and default arguments in that class (including such things in nested classes).
  2. Change 6.5.3 [basic.lookup.unqual] paragraph 7 as follows:

  3. A name used in the definition of a class X outside of a member function body, default argument, brace-or-equal-initializer of a non-static data member, or nested class definition29 shall be declared in one of the following ways:...
  4. Change 6.5.3 [basic.lookup.unqual] paragraph 8 as follows:

  5. A For the members of a class X, a name used in a member function body, in a default argument, in the brace-or-equal-initializer of a non-static data member (11.4 [class.mem]), or in the definition of a class member function (11.4.2 [class.mfct]) of class X outside of the definition of X, following the function's member's declarator-id [Footnote: That is, an unqualified name that occurs, for instance, in a type or default argument in the parameter-declaration-clause or in the function body exception-specification. —end footnote], or in the brace-or-equal-initializer of a non-static data member (11.4 [class.mem]) of class X shall be declared in one of the following ways:...
[Drafting note: 11.4 [class.mem] paragraph 2 requires no changes. 6.4.7 [basic.scope.class] bullet 1.5 deals with out-of-class definitions, and bullet 2 ensures that the lookup results for argument types are the same for in-class and out-of-class declarations, so no change is required.]


1310. What is an “acceptable lookup result?”

Section: 6.5.5.2  [class.qual]     Status: CD3     Submitter: Jason Merrill     Date: 2011-05-06

[Moved to DR at the April, 2013 meeting.]

In 6.5.5.2 [class.qual] paragraph 2,

In a lookup in which the constructor is an acceptable lookup result and the nested-name-specifier nominates a class C:

the name is instead considered to name the constructor of class C.

it is not clear what constitutes “an acceptable lookup result.” For instance, is

  struct S { } *sp = new S::S;

well-formed?

The intent of the wording was that S::S would refer to the constructor except in lookups that ignore the names of functions, e.g., in elaborated-type-specifiers and nested-name-specifiers. There doesn't seem to be a good reason to allow a qualified-id naming the injected-class-name. The alternative, i.e., only to find the constructor in a declarator, complicates parsing because the determination of whether the name is a type or a function would require lookahead.

Proposed resolution (August, 2012):

Change 6.5.5.2 [class.qual] paragraph 2 as follows:

In a lookup in which the constructor is an acceptable lookup result function names are not ignored [Footnote: Lookups in which function names are ignored include names appearing in a nested-name-specifier, an elaborated-type-specifier, or a base-specifier. —end footnote] and the nested-name-specifier nominates a class C:

the name is instead considered to name the constructor of class C...




1415. Missing prohibition of block-scope definition of extern object

Section: 6.6  [basic.link]     Status: CD3     Submitter: Richard Smith     Date: 2011-11-13

[Moved to DR at the October, 2012 meeting.]

There does not appear to be wording that prohibits a block-scope extern object declaration from being a definition.

Proposed resolution (February, 2012):

Add the following as a new paragraph following 9.4 [dcl.init] paragraph 4:

[Note: Default arguments are more restricted; see 9.3.4.7 [dcl.fct.default].

The order of initialization of variables with static storage duration is described in 6.9.3 [basic.start] and 8.8 [stmt.dcl]. —end note]

A declaration of a block-scope variable with external or internal linkage that has an initializer is ill-formed.

To zero-initialize an object...




597. Conversions applied to out-of-lifetime non-POD lvalues

Section: 6.7.3  [basic.life]     Status: CD3     Submitter: Mike Miller     Date: 27 September 2006

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

An lvalue referring to an out-of-lifetime non-POD class objects can be used in limited ways, subject to the restrictions in 6.7.3 [basic.life] paragraph 6:

if the original object will be or was of a non-POD class type, the program has undefined behavior if:

There are at least a couple of questionable things in this list. First, there is no “implicit conversion to a reference to a base class,” as assumed by the second bullet. Presumably this is intended to say that the lvalue is bound to a reference to a base class, and the cross-reference should be to 9.4.4 [dcl.init.ref], not to 7.3.12 [conv.ptr] (which deals with pointer conversions). However, even given that adjustment, it is not clear why it is forbidden to bind a reference to a non-virtual base class of an out-of-lifetime object, as that is just an address offset calculation. (Binding to a virtual base, of course, would require access to the value of the object and thus cannot be done outside the object's lifetime.)

The third bullet also appears questionable. It's not clear why static_cast is discussed at all here, as the only permissible static_cast conversions involving reference types and non-POD classes are to references to base or derived classes and to the same type, modulo cv-qualification; if implicit “conversion” to a base class reference is forbidden in the second bullet, why would an explicit conversion be permitted in the third? Was this intended to refer to reinterpret_cast? Also, is there a reason to allow char types but disallow array-of-char types (which are more likely to be useful than a single char)?

Proposed resolution (March, 2008):

  1. Change 6.7.3 [basic.life] paragraph 5 as follows:

  2. ...If the object will be or was of a non-trivial class type, the program has undefined behavior if:

  3. Change 6.7.3 [basic.life] paragraph 6 as follows:

  4. ...if the original object will be or was of a non-trivial class type, the program has undefined behavior if:

[Drafting notes: Paragraph 5 was changed to track the changes to paragraph 6. See also the resolution for issue 658.]




312. “use” of invalid pointer value not defined

Section: 6.7.5.5.3  [basic.stc.dynamic.deallocation]     Status: CD3     Submitter: Martin von Loewis     Date: 20 Sep 2001

[Moved to DR at the April, 2013 meeting.]

6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 4 mentions that the effect of using an invalid pointer value is undefined. However, the standard never says what it means to 'use' a value.

There are a number of possible interpretations, but it appears that each of them leads to undesired conclusions:

  1. A value is 'used' in a program if a variable holding this value appears in an expression that is evaluated. This interpretation would render the sequence
       int *x = new int(0);
       delete x;
       x = 0;
    
    into undefined behaviour. As this is a common idiom, this is clearly undesirable.
  2. A value is 'used' if an expression evaluates to that value. This would render the sequence
       int *x = new int(0);
       delete x;
       x->~int();
    
    into undefined behaviour; according to _N4778_.7.6.1.4 [expr.pseudo], the variable x is 'evaluated' as part of evaluating the pseudo destructor call. This, in turn, would mean that all containers (Clause 24 [containers]) of pointers show undefined behaviour, e.g. 24.3.10.4 [list.modifiers] requires to invoke the destructor as part of the clear() method of the container.

If any other meaning was intended for 'using an expression', that meaning should be stated explicitly.

(See also issue 623.)

Proposed resolution (February, 2012):

This issue is resolved by the resolution of issue 616.




623. Use of pointers to deallocated storage

Section: 6.7.5.5.3  [basic.stc.dynamic.deallocation]     Status: CD3     Submitter: Herb Sutter     Date: 27 February 2007

Any use of a pointer to deleted storage, even if the pointer is not dereferenced, produces undefined behavior (6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 4) . The reason for this restriction is that, on some historical architectures, deallocating an object might free a memory segment, resulting in a hardware exception if a pointer referring to that segment were loaded into a pointer register, and on those architectures use of a pointer register for moving and comparing pointers was the most efficient mechanism for these operations.

It is not clear whether current or foreseeable architectures still require such a draconian restriction or whether it is feasible to relax it only to forbid a smaller range of operations. Of particular concern is the use of atomic pointers, which might be used in race conditions involving deallocation, where the loser of the race might encounter this undefined behavior.

(See also issue 312.)

Rationale (April, 2007):

The current specification is clear and was well-motivated. Analysis of whether this restriction is still needed should be done via a paper and discussed in the Evolution Working Group rather than being handled by CWG as an issue/defect.

Additional note, February, 2014:

This issue was resolved by the resolution of issue 616, which made use of a pointer to deleted storage implementation-defined behavior.




462. Lifetime of temporaries bound to comma expressions

Section: 6.7.7  [class.temporary]     Status: CD3     Submitter: Steve Adamczyk     Date: April 2004

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

Split off from issue 86.

Should binding a reference to the result of a "," operation whose second operand is a temporary extend the lifetime of the temporary?

  const SFileName &C = ( f(), SFileName("abc") );

Notes from the March 2004 meeting:

We think the temporary should be extended.

Proposed resolution (October, 2004):

Change 6.7.7 [class.temporary] paragraph 2 as indicated:

... In all these cases, the temporaries created during the evaluation of the expression initializing the reference, except the temporary that is the overall result of the expression [Footnote: For example, if the expression is a comma expression (7.6.20 [expr.comma]) and the value of its second operand is a temporary, the reference is bound to that temporary.] and to which the reference is bound, are destroyed at the end of the full-expression in which they are created and in the reverse order of the completion of their construction...

[Note: this wording partially resolves issue 86. See also issue 446.]

Notes from the April, 2005 meeting:

The CWG suggested a different approach from the 10/2004 resolution, leaving 6.7.7 [class.temporary] unchanged and adding normative wording to 7.6.20 [expr.comma] specifying that, if the result of the second operand is a temporary, that temporary is the result of the comma expression as well.

Proposed Resolution (November, 2006):

Add the indicated wording to 7.6.20 [expr.comma] paragraph 1:

... The type and value of the result are the type and value of the right operand; the result is of the same value category as its right operand, and is a bit-field if its right operand is a glvalue and a bit-field. If the value of the right operand is a temporary (6.7.7 [class.temporary]), the result is that temporary.



496. Is a volatile-qualified type really a POD?

Section: 6.8  [basic.types]     Status: CD3     Submitter: John Maddock     Date: 30 Dec 2004

[Moved to DR at the April, 2013 meeting.]

In 6.8 [basic.types] paragraph 10, the standard makes it quite clear that volatile qualified types are PODs:

Arithmetic types (6.8.2 [basic.fundamental]), enumeration types, pointer types, and pointer to member types (6.8.4 [basic.compound]), and cv-qualified versions of these types (6.8.5 [basic.type.qualifier]) are collectively called scalar types. Scalar types, POD-struct types, POD-union types (Clause 11 [class]), arrays of such types and cv-qualified versions of these types (6.8.5 [basic.type.qualifier]) are collectively called POD types.

However in 6.8 [basic.types] paragraph 3, the standard makes it clear that PODs can be copied “as if” they were a collection of bytes by memcpy:

For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the value of obj1 is copied into obj2, using the std::memcpy library function, obj2 shall subsequently hold the same value as obj1.

The problem with this is that a volatile qualified type may need to be copied in a specific way (by copying using only atomic operations on multithreaded platforms, for example) in order to avoid the “memory tearing” that may occur with a byte-by-byte copy.

I realise that the standard says very little about volatile qualified types, and nothing at all (yet) about multithreaded platforms, but nonetheless this is a real issue, for the following reason:

The forthcoming TR1 will define a series of traits that provide information about the properties of a type, including whether a type is a POD and/or has trivial construct/copy/assign operations. Libraries can use this information to optimise their code as appropriate, for example an array of type T might be copied with a memcpy rather than an element-by-element copy if T is a POD. This was one of the main motivations behind the type traits chapter of the TR1. However it's not clear how volatile types (or POD's which have a volatile type as a member) should be handled in these cases.

Notes from the April, 2005 meeting:

It is not clear whether the volatile qualifier actually guarantees atomicity in this way. Also, the work on the memory model for multithreading being done by the Evolution Working Group seems at this point likely to specify additional semantics for volatile data, and that work would need to be considered before resolving this issue.

(See also issue 1746.)

Proposed resolution, October, 2012:

  1. Change 6.8 [basic.types] paragraph 9 as follows:

  2. ...Scalar types, trivially copyable class types (Clause 11 [class]), arrays of such types, and cv-qualified non-volatile const-qualified versions of these types (6.8.5 [basic.type.qualifier]) are collectively called trivially copyable types. Scalar types, trivial class types...
  3. Change 9.2.9.2 [dcl.type.cv] paragraphs 6-7 as follows:

  4. What constitutes an access to an object that has volatile-qualified type is implementation-defined. If an attempt is made to refer to an object defined with a volatile-qualified type through the use of a glvalue with a non-volatile-qualified type, the program behavior is undefined.

    [Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. Furthermore, for some implementations, volatile might indicate that special hardware instructions are required to access the object. See 6.9.1 [intro.execution] for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. —end note]

  5. Change 11.4.5.3 [class.copy.ctor] paragraph 12 as follows:

  6. A copy/move constructor for class X is trivial if it is not user-provided, its declared parameter type is the same as if it had been implicitly declared, and if

  7. Change 11.4.5.3 [class.copy.ctor] paragraph 25 as follows:

  8. A copy/move assignment operator for class X is trivial if it is not user-provided, its declared parameter type is the same as if it had been implicitly declared, and if

Notes from the June, 2016 meeting:

The resolution of issue 2094 revert the changes above revert the changes of volatile qualification on trivial copyability.




1361. Requirement on brace-or-equal-initializers of literal types

Section: 6.8  [basic.types]     Status: CD3     Submitter: Richard Smith     Date: 2011-08-16

The requirement in 6.7.3 [basic.life] paragraph 10 that

is mostly redundant with the constexpr constructor requirements in 9.2.6 [dcl.constexpr] paragraph 4 (although 11.9.3 [class.base.init] does not establish a strict equivalence between brace-or-equal-initializers and mem-initializers).

Proposed resolution (April, 2013):

This issue is resolved by the changes in N3652, adopted at the April, 2013 (Bristol) meeting.




1405. constexpr and mutable members of literal types

Section: 6.8  [basic.types]     Status: CD3     Submitter: Richard Smith     Date: 2011-10-21

[Moved to DR at the April, 2013 meeting.]

Currently, literal class types can have mutable members. It is not clear whether that poses any particular problems with constexpr objects and constant expressions, and if so, what should be done about it.

Proposed resolution (February, 2012):

Change 7.7 [expr.const] paragraph 2 as follows:




1453. Volatile members in literal classes?

Section: 6.8  [basic.types]     Status: CD3     Submitter: Richard Smith     Date: 2012-01-02

[Moved to DR at the October, 2012 meeting.]

Can a literal class have a volatile member? For example,

   struct S {
     constexpr S() : n(0) { }
     volatile int n;
   };

   constexpr S s;   // causes volatile write to S::n

Proposed resolution (February, 2012):

Change 6.8 [basic.types] paragraph 10 as follows:

A type is a literal type if it is:




483. Normative requirements on integral ranges

Section: 6.8.2  [basic.fundamental]     Status: CD3     Submitter: Steve Adamczyk     Date: 21 Oct 2004

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

There is no normative requirement on the ranges of the integral types, although the footnote in 6.8.2 [basic.fundamental] paragraph 2 indicates the intent (for int, at least) that they match the values given in the <climits> header. Should there be an explicit requirement of some sort?

(See also paper N1693.)

Proposed resolution (August, 2011):

Change 6.8.2 [basic.fundamental] paragraph 3 as follows:

...collectively called the extended integer types. The signed and unsigned integral types shall satisfy the constraints given in ISO C 5.2.4.2.1.



1302. noexcept applied to expression of type void

Section: 6.8.2  [basic.fundamental]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-04-22

[Moved to DR at the October, 2012 meeting.]

The list of acceptable uses of an expression of type void in 6.8.2 [basic.fundamental] paragraph 9 does not, but should, include an operand of the noexcept operator.

Proposed resolution (August, 2011):

Change 6.8.2 [basic.fundamental] paragraph 9 as follows:

An expression of type void shall be used only as an expression statement (8.3 [stmt.expr]), as an operand of a comma expression (7.6.20 [expr.comma]), as a second or third operand of ?: (7.6.16 [expr.cond]), as the operand of typeid, noexcept, or decltype, as the expression in a return statement (8.7.4 [stmt.return]) for a function with the return type void, or as the operand of an explicit conversion to type cv void.



1515. Modulo 2n arithmetic for implicitly-unsigned types

Section: 6.8.2  [basic.fundamental]     Status: CD3     Submitter: Sean Hunt     Date: 2012-07-03

[Moved to DR at the April, 2013 meeting.]

According to 6.8.2 [basic.fundamental] paragraph 4,

Unsigned integers, declared unsigned, shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer.

It is not clear whether this wording intentionally excludes types like char16_t and char32_t (and, possibly, types char and wchar_t, if those types are unsigned in a given implementation), since the unsigned keyword is not used in their declaration.

Proposed resolution (October, 2012):

Change 6.8.2 [basic.fundamental] paragraph 4 as follows:

Unsigned integers, declared unsigned, shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer.46



1539. Definition of “character type”

Section: 6.8.2  [basic.fundamental]     Status: CD3     Submitter: Beman Dawes     Date: 2012-08-15

[Moved to DR at the April, 2013 meeting.]

The term character type is used in the Standard without definition. It should be defined; however, the use of the term is divergent between the core and library clauses: in the former, it means narrow character types, while in the latter it includes wchar_t, char16_t, and char32_t, so care must be taken in ensuring that no inadvertent changes are implied.

Proposed resolution (October, 2012):

  1. Change _N4885_6.7.5.5.4 [basic.stc.dynamic.safety] paragraph 1 as follows:

  2. A traceable pointer object is

  3. Change 6.8.2 [basic.fundamental] paragraph 1 as follows:

  4. Objects declared as characters (char) shall be large enough to store any member of the implementation's basic character set. If a character from this set is stored in a character object, the integral value of that character object is equal to the value of the single character literal form of that character. It is implementation-defined whether a char object can hold negative values. Characters can be explicitly declared unsigned or signed. Plain char, signed char, and unsigned char are three distinct types, collectively called narrow character types. A char, a signed char, and an unsigned char occupy the same amount of storage and have the same alignment requirements (6.7.6 [basic.align]); that is, they have the same object representation. For narrow character types, all bits of the object representation participate in the value representation. For unsigned narrow character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types. In any particular implementation, a plain char object can take on either the same values as a signed char or an unsigned char; which one is implementation-defined.
  5. Change 6.7.6 [basic.align] paragraph 6 as follows:

  6. The alignment requirement of a complete type can be queried using an alignof expression (7.6.2.6 [expr.alignof]). Furthermore, the types char, signed char, and unsigned char narrow character types (6.8.2 [basic.fundamental]) shall have the weakest alignment requirement. [Note: This enables the narrow character types to be used as the underlying type for an aligned memory area (9.12.2 [dcl.align]). —end note]
  7. Change 9.4.3 [dcl.init.string] paragraph 1 as follows:

  8. A char array (whether plain char, signed char, or unsigned char) An array of narrow character type (6.8.2 [basic.fundamental]), char16_t array, char32_t array, or wchar_t array can be initialized by a narrow character string literal, char16_t string literal, char32_t string literal, or wide string literal, respectively, or by an appropriately-typed string literal enclosed in braces (5.13.5 [lex.string]). Successive characters of the value of the string literal initialize the elements of the array. [Example:



1059. Cv-qualified array types (with rvalues)

Section: 6.8.5  [basic.type.qualifier]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2010-03-20

[Moved to DR at the October, 2012 meeting.]

In spite of the resolution of issue 112, the exact relationship between cv-qualifiers and array types is not clear. There does not appear to be a definitive normative statement answering the question of whether an array with a const-qualified element type is itself const-qualified; the statement in 6.8.5 [basic.type.qualifier] paragraph 5,

Cv-qualifiers applied to an array type attach to the underlying element type, so the notation “cv T,” where T is an array type, refers to an array whose elements are so-qualified. Such array types can be said to be more (or less) cv-qualified than other types based on the cv-qualification of the underlying element types.

hints at an answer but is hardly decisive. For example, is the following example well-formed?

    template <class T> struct is_const {
        static const bool value = false;
    };
    template <class T> struct is_const<const T> {
        static const bool value = true;
    };

    template <class T> void f(T &) {
        char checker[is_const<T>::value];
    }

    int const arr[1] = {};

    int main() {
        f(arr);
    }

Also, when 7.2.1 [basic.lval] paragraph 4 says,

Class prvalues can have cv-qualified types; non-class prvalues always have cv-unqualified types.

does this apply to array rvalues, as it appears? That is, given

    struct S {
        const int arr[10];
    };

is the array rvalue S().arr an array of int or an array of const int?

(The more general question is, when the Standard refers to non-class types, should it be considered to include array types? Or perhaps only arrays of non-class types?)

Proposed resolution (December, 2011):

  1. Change 6.8.5 [basic.type.qualifier] paragraph 5 as follows:

  2. ...Cv-qualifiers applied to an array type attach to the underlying element type, so the notation “cv T,” where T is an array type, refers to an array whose elements are so-qualified. Such array types can be said to be more (or less) cv-qualified than other types based on the cv-qualification of the underlying element types. An array type whose elements are cv-qualified is also considered to have the same cv-qualification as its elements. [Example:
      typedef char CA[5];
      typedef const char CC;
      CC arr1[5] = { 0 };
      const CA arr2 = { 0 };
    

    The type of both arr1 and arr2 is “array of 5 const char,” and the array type is considered to be const-qualified. —end example]

  3. Change 7.2.1 [basic.lval] paragraph 4 as follows:

  4. Class and array prvalues can have cv-qualified types; non-class other prvalues always have cv-unqualified types. Unless otherwise indicated...



1428. Dynamic const objects

Section: 6.8.5  [basic.type.qualifier]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-12-08

[Moved to DR at the October, 2012 meeting.]

The definition of “const object” in 6.8.5 [basic.type.qualifier] paragraph 1 is:

The presence of a const specifier in a decl-specifier-seq declares an object of const-qualified object type; such object is called a const object.

Because the type of an object created by a new-expression is given by a type-id or new-type-id rather than with a decl-specifier-seq, this definition gives the false impression that objects of dynamic storage duration cannot be const objects. The wording should be adjusted to make it clear that they can.

Proposed resolution (February, 2012):

  1. Change 6.8.5 [basic.type.qualifier] paragraph 1 as follows:

  2. The term object type (6.7.2 [intro.object]) includes the cv-qualifiers specified in the decl-specifier-seq (9.2 [dcl.spec]), declarator ( 9.3 [dcl.decl]), type-id (9.3.2 [dcl.name]), or new-type-id (7.6.2.8 [expr.new]) when the object is created. The presence of a const specifier in a decl-specifier-seq declares an object of const-qualified object type; such object is called a const object. The presence of a volatile specifier in a decl-specifier-seq declares an object of volatile-qualified object type; such object is called a volatile object. The presence of both cv-qualifiers in a decl-specifier-seq declares an object of const-volatile-qualified object type; such object is called a const volatile object.

    The cv-qualified or cv-unqualified versions of a type are distinct types; however, they shall have the same representation and alignment requirements (6.8 [basic.types]).51

  3. Change 6.8.5 [basic.type.qualifier] paragraph 3 as follows:

  4. Each non-static, non-mutable, non-reference data member of a const-qualified class object is const-qualified, each non-static, non-reference data member of a volatile-qualified class object is volatile-qualified and similarly for members of a const-volatile class. See 9.3.4.6 [dcl.fct] and _N4868_.11.4.3.2 [class.this] regarding function types...



129. Stability of uninitialized auto variables

Section: 6.9.1  [intro.execution]     Status: CD3     Submitter: Nathan Myers     Date: 26 June 1999

[Moved to DR at the April, 2013 meeting.]

Does the Standard require that an uninitialized auto variable have a stable (albeit indeterminate) value? That is, does the Standard require that the following function return true?

    bool f() {
        unsigned char i;  // not initialized
        unsigned char j = i;
        unsigned char k = i;
        return j == k;    // true iff "i" is stable
    }
6.8.2 [basic.fundamental] paragraph 1 requires that uninitialized unsigned char variables have a valid value, so the initializations of j and k are well-formed and required not to trap. The question here is whether the value of i is allowed to change between those initializations.

Mike Miller: 6.9.1 [intro.execution] paragraph 10 says,

An instance of each object with automatic storage duration (6.7.5.4 [basic.stc.auto] ) is associated with each entry into its block. Such an object exists and retains its last-stored value during the execution of the block and while the block is suspended...
I think that the most reasonable way to read this is that the only thing that is allowed to change the value of an automatic (non-volatile?) value is a "store" operation in the abstract machine. There are no "store" operations to i between the initializations of j and k, so it must retain its original (indeterminate but valid) value, and the result of the program is well-defined.

The quibble, of course, is whether the wording "last-stored value" should be applied to a "never-stored" value. I think so, but others might differ.

Tom Plum: 9.2.9.2 [dcl.type.cv] paragraph 8 says,

[Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. See 6.9.1 [intro.execution] for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. ]
>From this I would infer that non-volatile means "shall not be changed by means undetectable by an implementation"; that the compiler is entitled to safely cache accesses to non-volatile objects if it can prove that no "detectable" means can modify them; and that therefore i shall maintain the same value during the example above.

Nathan Myers: This also has practical code-generation consequences. If the uninitialized auto variable lives in a register, and its value is really unspecified, then until it is initialized that register can be used as a temporary. Each time it's "looked at" the variable has the value that last washed up in that register. After it's initialized it's "live" and cannot be used as a temporary any more, and your register pressure goes up a notch. Fixing the uninit'd value would make it "live" the first time it is (or might be) looked at, instead.

Mike Ball: I agree with this. I also believe that it was certainly never my intent that an uninitialized variable be stable, and I would have strongly argued against such a provision. Nathan has well stated the case. And I am quite certain that it would be disastrous for optimizers. To ensure it, the frontend would have to generate an initializer, because optimizers track not only the lifetimes of variables, but the lifetimes of values assigned to those variables. This would put C++ at a significant performance disadvantage compared to other languages. Not even Java went this route. Guaranteeing defined behavior for a very special case of a generally undefined operation seems unnecessary.

Proposed resolution (February, 2012):

This issue is resolved by the resolution of issue 616.




1003. Acceptable definitions of main

Section: 6.9.3.1  [basic.start.main]     Status: CD3     Submitter: Daniel Krügler     Date: 2009-11-17

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The specification of the forms of the definition of main that an impliementation is required to accept is clear in C99 that the parameter names and the exact syntactic form of the types can vary. Although it is reasonable to assume that a C++ implementation would accept a definition like

    int main(int foo, char** bar) { /* ... */ }

instead of the canonical

    int main(int argc, char* argv[]) { /* ... */ }

it might be a good idea to clarify the intent using wording similar to C99's.

Proposed resolution (August, 2011):

Change 6.9.3.1 [basic.start.main] paragraph 2 as follows:

...All implementations shall allow both of the following definitions of main:

  int main() { /* ... */ }

and

  int main(int argc, char* argv[]) { /* ... */ }

as the type of main (9.3.4.6 [dcl.fct]. In the latter form, for purposes of exposition, the first function parameter is called argc and the second function parameter is called argv, where argc shall be the number of arguments...




1489. Is value-initialization of an array constant initialization?

Section: 6.9.3.2  [basic.start.static]     Status: CD3     Submitter: Steve Adamczyk     Date: 2012-03-29

[Moved to DR at the April, 2013 meeting.]

According to 6.9.3.2 [basic.start.static] paragraph 2,

Constant initialization is performed:

Presumably this would include a value-initialization (i.e., with no expressions) such as

  int a[1000]{};

However, we have recently clarified the degenerate cases of other similar rules referencing “every,” so it wouldn't hurt to be more explicit here.

Proposed resolution (October, 2012):

Change 6.9.3.2 [basic.start.static] paragraph 2 as follows:

Constant initialization is performed:




1261. Explicit handling of cv-qualification with non-class prvalues

Section: Clause 7  [expr]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-03-12

[Moved to DR at the October, 2012 meeting.]

Proposed resolution (December, 2011):

  1. Change 7.2.1 [basic.lval] paragraph 4 as follows (supersedes the corresponding change in the resolution of issue 1059):

  2. Class prvalues can have cv-qualified types; non-class prvalues always have cv-unqualified types. Unless otherwise indicated (7.6.1.3 [expr.call]), prvalues shall always have complete types or the void type; in addition to these types, glvalues can also have incomplete types. [Note: class and array prvalues can have cv-qualified types; other prvalues always have cv-unqualified types. See Clause 7 [expr]. —end note]
  3. Add a new paragraph following Clause 7 [expr] paragraph 5:

  4. If an expression initially has the type “reference to T”...

    If a prvalue initially has the type “cv T,” where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.

  5. Change 7.6.1.3 [expr.call] paragraph 3 as follows:

  6. If the postfix-expression designates a destructor (11.4.7 [class.dtor]), the type of the function call expression is void; otherwise, the type of the function call expression is the return type of the statically chosen function (i.e., ignoring the virtual keyword), even if the type of the function actually called is different. This return type shall be an object type, a reference type or the type cv void.
  7. Change 7.6.1.4 [expr.type.conv] paragraph 2 as follows:

  8. ...[Note: if T is a non-class type that is cv-qualified, the cv-qualifiers are ignored discarded when determining the type of the resulting prvalue (7.2.1 [basic.lval] Clause 7 [expr]). —end note]
  9. Change 7.6.3 [expr.cast] paragraph 1 as follows:

  10. ...[Note: if T is a non-class type that is cv-qualified cv-qualified, the cv-qualifiers are ignored discarded when determining the type of the resulting prvalue; see 7.2.1 [basic.lval] Clause 7 [expr]. —end note]



1383. Clarifying discarded-value expressions

Section: Clause 7  [expr]     Status: CD3     Submitter: Lawrence Crowl     Date: 2011-08-30

[Moved to DR at the October, 2012 meeting.]

There are some points in the description discarded-value expressions that need clarification:

Suggested resolution:

In some contexts, an expression only appears for its side effects. Such an expression is called a discarded-value expression. The expression is evaluated and its value is discarded. The array-to-pointer (7.3.3 [conv.array]) and function-to-pointer (7.3.4 [conv.func]) standard conversions are not applied. The lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is applied if and only if the expression is an lvalue of volatile-qualified type and it has one of the following forms:

[Note: Expressions invoking user-defined operators are not the operations above. Discarded-value expressions apply to class types, which will be ill-formed if there is no volatile copy constructor with which to initialize the temporary. —end note]

Proposed resolution (February, 2012):

Change Clause 7 [expr] paragraph 10 as follows:

...The lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is applied if and only if the expression is an lvalue of volatile-qualified type and it has is one of the following forms:

[Note: Using an overloaded operator causes a function call; the above covers only operators with built-in meaning. If the lvalue is of class type, it must have a volatile copy constructor to initialize the temporary that is the result of the lvalue-to-rvalue conversion. —end note]

Additional note (February, 2012):

A problem was discovered that was not addressed by the proposed resolution that was reviewed at the February, 2012 meeting, so the issue has been moved back to "review" status with revised wording.




240. Uninitialized values and undefined behavior

Section: 7.3.2  [conv.lval]     Status: CD3     Submitter: Mike Miller     Date: 8 Aug 2000

[Moved to DR at the April, 2013 meeting.]

7.3.2 [conv.lval] paragraph 1 says,

If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.

I think there are at least three related issues around this specification:

  1. Presumably assigning a valid value to an uninitialized object allows it to participate in the lvalue-to-rvalue conversion without undefined behavior (otherwise the number of programs with defined behavior would be vanishingly small :-). However, the wording here just says "uninitialized" and doesn't mention assignment.

  2. There's no exception made for unsigned char types. The wording in 6.8.2 [basic.fundamental] was carefully crafted to allow use of unsigned char to access uninitialized data so that memcpy and such could be written in C++ without undefined behavior, but this statement undermines that intent.

  3. It's possible to get an uninitialized rvalue without invoking the lvalue-to-rvalue conversion. For instance:

            struct A {
                int i;
                A() { } // no init of A::i
            };
            int j = A().i;  // uninitialized rvalue
    

    There doesn't appear to be anything in the current IS wording that says that this is undefined behavior. My guess is that we thought that in placing the restriction on use of uninitialized objects in the lvalue-to-rvalue conversion we were catching all possible cases, but we missed this one.

In light of the above, I think the discussion of uninitialized objects ought to be removed from 7.3.2 [conv.lval] paragraph 1. Instead, something like the following ought to be added to 6.8 [basic.types] paragraph 4 (which is where the concept of "value" is introduced):

Any use of an indeterminate value (7.6.2.8 [expr.new], 9.4 [dcl.init], 11.9.3 [class.base.init]) of any type other than char or unsigned char results in undefined behavior.

John Max Skaller:

A().i had better be an lvalue; the rules are wrong. Accessing a member of a structure requires it be converted to an lvalue, the above calculation is 'as if':

    struct A {
        int i;
        A *get() { return this; }
    };
    int j = (*A().get()).i;

and you can see the bracketed expression is an lvalue.

A consequence is:

    int &j= A().i; // OK, even if the temporary evaporates

j now refers to a 'destroyed' value. Any use of j is an error. But the binding at the time is valid.

Proposed Resolution (November, 2006):

  1. Add the indicated words to 6.8 [basic.types] paragraph 4:

    ... For trivial types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values. Any use of an indeterminate value (7.6.2.8 [expr.new], 9.4 [dcl.init], 11.9.3 [class.base.init]) of a type other than unsigned char results in undefined behavior.
  2. Change 7.3.2 [conv.lval] paragraph 1 as follows:

    If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.

Additional note (May, 2008):

The C committee is dealing with a similar issue in their DR338. According to this analysis, they plan to take almost the opposite approach to the one described above by augmenting the description of their version of the lvalue-to-rvalue conversion. The CWG did not consider that access to an unsigned char might still trap if it is allocated in a register and needs to reevaluate the proposed resolution in that light. See also issue 129.

Proposed resolution (February, 2012):

This issue is resolved by the resolution of issue 616.




1013. Uninitialized std::nullptr_t objects

Section: 7.3.2  [conv.lval]     Status: CD3     Submitter: Miller     Date: 2009-12-10

[Moved to DR at the April, 2013 meeting.]

According to 7.3.2 [conv.lval] paragraph 1, an lvalue-to-rvalue conversion on an uninitialized object produces undefined behavior. Since there is only one “value” of type std::nullptr_t, an lvalue-to-rvalue conversion on a std::nullptr_t glvalue does not need to fetch the value from storage. Is there any need for undefined behavior in this case?

Proposed resolution (February, 2012):

This issue is resolved by the resolution of issue 616.




1423. Convertibility of nullptr to bool

Section: 7.3.14  [conv.fctptr]     Status: CD3     Submitter: Dave Abrahams     Date: 2011-12-04

[Moved to DR at the October, 2012 meeting.]

The resolution of issue 654 (found in paper N2656) enabled conversion of rvalues of type std::nullptr_t to bool. It appears that the use cases for this conversion are primarily or exclusively the “contextually converted to bool” cases, with some possibility for inadvertent misuse in other contexts. Paper N2656 mentioned the idea of limiting the conversions to the direct initialization contexts; that possibility should be reexamined.

Proposed resolution (February, 2012):

Change 7.3.14 [conv.fctptr] paragraph 1 as follows:

...any other value is converted to true. A For direct-initialization (9.4 [dcl.init]), a prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.



974. Default arguments for lambdas

Section: 7.5.5  [expr.prim.lambda]     Status: CD3     Submitter: Jason Merrill     Date: 4 September, 2009

N3092 comment US 29

[Moved to DR status at the April, 2013 meeting.]

There does not appear to be any technical difficulty that would require the restriction in 7.5.5 [expr.prim.lambda] paragraph 5 against default arguments in lambda-expressions.

Proposed resolution (August, 2011):

Delete the following sentence from 7.5.5 [expr.prim.lambda] paragraph 5:

Default arguments (9.3.4.7 [dcl.fct.default]) shall not be specified in the parameter-declaration-clause of a lambda-declarator.

Additional note (February, 2012):

EWG requested that the adoption of this proposed resolution be postponed to allow them to discuss it. The issue has thus been returned to "review" status pending EWG action.




975. Restrictions on return type deduction for lambdas

Section: 7.5.5  [expr.prim.lambda]     Status: CD3     Submitter: Jason Merrill     Date: 4 September, 2009

N3092 comment US 30

[Moved to DR status at the April, 2013 meeting as part of paper N3638.]

There does not appear to be any technical difficulty that would require the current restriction that the return type of a lambda can be deduced only if the body of the lambda consists of a single return statement. In particular, multiple return statements could be permitted if they all return the same type.

Proposed resolution (August, 2011):

Change 7.5.5 [expr.prim.lambda] paragraph 4 as follows:

...If a lambda-expression does not include a trailing-return-type, it is as if the trailing-return-type denotes the following type:

[Example:

  auto x1 = [](int i){ return i; }; // OK: return type is int
  auto x2 = []{ return { 1, 2 }; }; // error: the return type is void (a
                                    // braced-init-list is not an expression)
  struct A { int fn1(); const int& fn2(); };
  template <class T> void f () {
    [](T t, bool b){
      if (b)
        return t.fn1();
      else
        return t.fn2();
    };
  }
  template void f<A>();             // OK: lambda return type is int

end example]

Additional note (February, 2012):

EWG requested that the adoption of this proposed resolution be postponed to allow them to discuss it. The issue has thus been returned to "review" status pending EWG action.




1048. auto deduction and lambda return type deduction.

Section: 7.5.5  [expr.prim.lambda]     Status: CD3     Submitter: Jason Merrill     Date: 2010-03-09

[Moved to DR at the April, 2013 meeting as part of paper N3638.]

auto and lambda return types use slightly different rules for determining the result type from an expression. auto uses the rules in 13.10.3.2 [temp.deduct.call], which explicitly drops top-level cv-qualification in all cases, while the lambda return type is based on the lvalue-to-rvalue conversion, which drops cv-qualification only for non-class types. As a result:

    struct A { };

    const A f();

    auto a = f();               // decltype(a) is A
    auto b = []{ return f(); }; // decltype(b()) is const A

This seems like an unnecessary inconsistency.

John Spicer:

The difference is intentional; auto is intended only to give a const type if you explicitly ask for it, while the lambda return type should generally be the type of the expression.

Daniel Krügler:

Another inconsistency: with auto, use of a braced-init-list can deduce a specialization of std::initializer_list; it would be helpful if the same could be done for a lambda return type.

Additional note, February, 2014:

EWG noted that g++ and clang differ in their treatment of this example and referred it back to CWG for resolution.

Proposed resolution, November, 2014:

This issue was actually resolved by paper N3638, adopted at the April, 2013 meeting. It is returned to "review" status to allow consideration of whether the resolution should be considered a change for C++14 or a retroactive change to C++11.

Notes from the November, 2014 meeting:

CWG agreed that the change embodied in paper N3638 should be considered to have been a DR against C++11.




1557. Language linkage of converted lambda function pointer

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD3     Submitter: Scott Meyers     Date: 2012-09-19

[Moved to DR at the April, 2013 meeting.]

7.5.5 [expr.prim.lambda] paragraph 6 does not specify the language linkage of the function type of the closure type's conversion function.

Proposed resolution (October, 2012):

Change 7.5.5 [expr.prim.lambda] paragraph 6 as follows:

The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function with C++ language linkage (9.11 [dcl.link]). having the same parameter and return types as the closure type's function call operator. The value returned...



755. Generalized lambda-captures

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: CD3     Submitter: John Freeman     Date: 11 December, 2008

In the current specification of lambda expressions, a name appearing in a lambda-capture must refer to a local variable or reference with automatic storage duration (7.5.5 [expr.prim.lambda] paragraph 3). This restriction seems unnecessary and possibly confusing.

One possibility would be to extend the syntax of the lambda-capture to be something like

v = expr

with the meaning that the closure object would have a member named v initialized with the value expr. With this extension, the current syntax could be viewed as an abbreviation for

v = v

Rationale, March, 2009:

This idea was discussed and rejected by the EWG.

This issue was addressed by the adoption of N3648, adopted at the April, 2013 (Bristol) meeting.




2162. Capturing this by reference

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: CD3     Submitter: Brian Bi     Date: 2015-07-23

The current wording of 7.5.5 [expr.prim.lambda] paragraphs 15 and 16 does not, but presumably should, indicate that this, if implicitly captured, is always captured by copy and not by reference.

Proposed resolution (February, 2016):

Change 7.5.5 [expr.prim.lambda] paragraph 15 as follows, dividing the existing text into a bulleted list:

An entity is captured by copy if

For each entity captured by copy...

Notes from the February, 2016 meeting:

Paper P0018 is intended to change this area, so this issue is being returned to "review" status to accommodate those changes.




1213. Array subscripting and xvalues

Section: 7.6.1.2  [expr.sub]     Status: CD3     Submitter: Jason Merrill     Date: 2010-10-24

[Moved to DR status at the April, 2013 meeting.]

Because the subscripting operation is defined as indirection through a pointer value, the result of a subscript operator applied to an xvalue array is an lvalue, not an xvalue. This could be surprising to some.

Proposed resolution (December, 2012):

Change 7.6.1.2 [expr.sub] paragraph 1 as follows:

A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall have the type “array of T” or “pointer to T” and the other shall have unscoped enumeration or integral type. The result is an lvalue of type “T.” The type “T” shall be a completely-defined object type.62 The expression E1[E2] is identical (by definition) to *((E1)+(E2)) [Note: see 7.6.2 [expr.unary] and 7.6.6 [expr.add] for details of * and + and 9.3.4.5 [dcl.array] for details of arrays. —end note], except that in the case of an array operand, the result is an lvalue if that operand is an lvalue and an xvalue otherwise.

(See also issue 616.)

Additional note (January, 2013):

The preceding resolution differs from that discussed in the December, 2012 drafting review teleconference by the deletion of the words “an lvalue” in the second sentence. The issue has consequently been moved to "review" status instead of "tentatively ready."




1516. Definition of “virtual function call”

Section: 7.6.1.3  [expr.call]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2012-07-05

[Moved to DR at the April, 2013 meeting.]

The terms “virtual function call” and “virtual call” are used in the Standard but are not defined.

Proposed resolution (August, 2012):

Change 7.6.1.3 [expr.call] paragraph 1 as follows:

If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called. Otherwise, its final overrider (11.7.3 [class.virtual]) in the dynamic type of the object expression is called; such a call is referred to as a virtual function call.



1269. dynamic_cast of an xvalue operand

Section: 7.6.1.7  [expr.dynamic.cast]     Status: CD3     Submitter: Michael Wong     Date: 2011-03-21

[Moved to DR at the October, 2012 meeting.]

7.6.1.7 [expr.dynamic.cast] paragraph 2 allows an expression of any value category when the target type is an rvalue reference. However, paragraph 6 requires that the operand be an lvalue if the runtime check is to be applied. This requirement should presumably be relaxed to require only a glvalue when the target type is an rvalue reference.

Proposed resolution (August, 2011):

Change 7.6.1.7 [expr.dynamic.cast] paragraph 6 as follows:

Otherwise, v shall be a pointer to or an lvalue a glvalue of a polymorphic type (11.7.3 [class.virtual]).

Additional note, January, 2012:

An objection has been raised to the proposed resolution on the basis that it unnecessarily weakens the distinction between rvalues and lvalues, making it easier to create dangling references. Its status has therefore been changed back to "review" to allow further discussion.




1416. Function cv-qualifiers and typeid

Section: 7.6.1.8  [expr.typeid]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-11-17

[Moved to DR at the October, 2012 meeting.]

The requirement in 7.6.1.8 [expr.typeid] paragraph 5 that

The top-level cv-qualifiers of the glvalue expression or the type-id that is the operand of typeid are always ignored

could be misinterpreted as referring to cv-qualifiers in a function type, even though it is clear that a function type is never cv-qualified. A note emphasizing the fact that that is not the case would be helpful.

Proposed resolution (February, 2012):

Change 7.6.1.8 [expr.typeid] paragraph 5 as follows:

The top-level cv-qualifiers of the glvalue expression or the type-id that is the operand of typeid are always ignored. If the type of the expression or type-id is a cv-qualified type, the result of the typeid expression refers to a std::type_info object representing the cv-unqualified type. [Example:...



1320. Converting scoped enumerations to bool

Section: 7.6.1.9  [expr.static.cast]     Status: CD3     Submitter: Jonathan Wakely     Date: 2011-05-18

[Moved to DR at the April, 2013 meeting.]

The specification of static_cast (7.6.1.9 [expr.static.cast]) does not describe conversion of a scoped enumeration value to bool. Presumably it should be handled as for unscoped enumerations, with a zero value becoming false and a non-zero value becoming true.

Proposed resolution (August, 2012):

Change 7.6.1.9 [expr.static.cast] paragraph 9 as follows:

A value of a scoped enumeration type (9.7.1 [dcl.enum]) can be explicitly converted to an integral type. The When that type is cv bool, the resulting value is false if the original value is zero and true for all other values. For the remaining integral types, the value is unchanged if the original value can be represented by the specified type. Otherwise, the resulting value is unspecified.



1412. Problems in specifying pointer conversions

Section: 7.6.1.9  [expr.static.cast]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-11-01

[Moved to DR at the April, 2013 meeting.]

The definedness of various pointer conversions (see 7.6.1.10 [expr.reinterpret.cast] paragraph 7, 11.4 [class.mem] paragraph 20, 7.6.1.9 [expr.static.cast] paragraph 13) relies on the properties of the types involved, such as whether they are standard-layout types and their intrinsic alignment. This creates contradictions and unnecessary unspecified behavior when the actual values of the pointer involved would actually permit the operations. Recasting the specification in terms of the memory model instead of the types of the objects provides a more coherent specification.

Proposed resolution (February, 2012):

  1. Change 7.3.12 [conv.ptr] paragraph 2 as follows:

  2. A prvalue of type “pointer to cv T,” where T is an object type, can be converted to a prvalue of type “pointer to cv void”. The result of converting a “pointer to cv T non-null pointer value of a pointer to object type to a “pointer to cv void” points to the start of the storage location where the object of type T resides, as if the object is a most derived object (6.7.2 [intro.object]) of type T (that is, not a base class subobject) represents the address of the same byte in memory as the original pointer value. The null pointer value is converted to the null pointer value of the destination type.
  3. Change 7.6.1.9 [expr.static.cast] paragraph 13 as follows:

  4. A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T,” where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. The null pointer value is converted to the null pointer value of the destination type. If the original pointer value represents the address A of a byte in memory and A satisfies the alignment requirement of T, then the resulting pointer value represents the same address as the original pointer value, that is, A. The result of any other such pointer conversion is unspecified. A value of type pointer to object converted to “pointer to cv void” and back, possibly with different cv-qualification, shall have its original value...
  5. Change 7.6.1.10 [expr.reinterpret.cast] paragraph 7 as follows:

  6. An object pointer can be explicitly converted to an object pointer of a different type.70 When a prvalue v of object pointer type “pointer to T1 is converted to the object pointer type “pointer to cv T2”, the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (6.8 [basic.types]) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.



1447. static_cast of bit-field lvalue to rvalue reference

Section: 7.6.1.9  [expr.static.cast]     Status: CD3     Submitter: Jason Merrill     Date: 2012-01-16

[Moved to DR at the October, 2012 meeting.]

According to 7.6.1.9 [expr.static.cast] paragraph 3,

A glvalue of type “cv1 T1” can be cast to type “rvalue reference to cv2 T2,” if “cv2 T2” is reference-compatible with “cv1 T1” (9.4.4 [dcl.init.ref]). The result refers to the object or the specified base class subobject thereof.

This specification fails to allow for a bit-field lvalue operand, since the reference cannot refer to a bit-field. Presumably a temporary should be formed and the reference be bound to it.

Proposed resolution (February, 2012):

Change 7.6.1.9 [expr.static.cast] paragraphs 3-4 as follows:

A glvalue of type “cv1 T1” can be cast to type “rvalue reference to cv2 T2” if “cv2 T2” is reference-compatible with “cv1 T1” (9.4.4 [dcl.init.ref]). The If the glvalue is not a bit-field, the result refers to the object or the specified base class subobject thereof; otherwise, the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is applied to the bit-field and the resulting prvalue is used as the expression of the static_cast for the remainder of this section. If T2 is an inaccessible (11.8 [class.access]) or ambiguous (6.5.2 [class.member.lookup]) base class of T1, a program that necessitates such a cast is ill-formed.

Otherwise, an An expression e can be explicitly converted...




1268. reinterpret_cast of an xvalue operand

Section: 7.6.1.10  [expr.reinterpret.cast]     Status: CD3     Submitter: Michael Wong     Date: 2011-03-21

[Moved to DR at the October, 2012 meeting.]

7.6.1.10 [expr.reinterpret.cast] paragraph 11, dealing with casting to reference types, only allows an lvalue operand. Presumably it should allow a glvalue operand when the target is an rvalue reference type.

Proposed resolution (August, 2011):

Change 7.6.1.10 [expr.reinterpret.cast] paragraph 11:

An lvalue A glvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result refers to the same object as the source glvalue, but with the specified type. [Note: That is, for lvalues, a reference cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with the built-in & and * operators (and similarly for reinterpret_cast<T&&>(x)). end note] The result refers to the same object as the source lvalue, but with a different type. The result is an lvalue for an lvalue reference type or an rvalue reference to function type and an xvalue for an rvalue reference to object type. No temporary is created,...

Additional note, January, 2012:

An objection has been raised to the proposed resolution on the basis that it unnecessarily weakens the distinction between rvalues and lvalues, making it easier to create dangling references. Its status has therefore been changed back to "review" to allow further discussion.




342. Terminology: "indirection" versus "dereference"

Section: 7.6.2  [expr.unary]     Status: CD3     Submitter: Jason Merrill     Date: 7 Oct 2001

[Moved to DR at the October, 2012 meeting.]

Split off from issue 315.

Incidentally, another thing that ought to be cleaned up is the inconsistent use of "indirection" and "dereference". We should pick one.

Proposed resolution (December, 2006):

  1. Change 7.6.2.2 [expr.unary.op] paragraph 1 as follows:

  2. The unary * operator performs indirection dereferences a pointer value: the expression to which it is applied shall be a pointer...
  3. Change 9.3.4.5 [dcl.array] paragraph 8 as follows:

  4. The results are added and indirection applied values are added and the result is dereferenced to yield an array (of five integers), which in turn is converted to a pointer to the first of the integers.
  5. Change 9.3.4.6 [dcl.fct] paragraph 9 as follows:

  6. The binding of *fpi(int) is *(fpi(int)), so the declaration suggests, and the same construction in an expression requires, the calling of a function fpi, and then using indirection through dereferencing the (pointer) result to yield an integer. In the declarator (*pif)(const char*, const char*), the extra parentheses are necessary to indicate that indirection through dereferencing a pointer to a function yields a function, which is then called.
  7. Change the index for * and “dereferencing” no longer to refer to “indirection.”

[Drafting note: 28.6.9 [template.indirect.array] requires no change. Many more places in the current wording use “dereferencing” than “indirection.”]

Notes from the August, 2011 meeting:

CWG prefers use of the term “indirection” instead of “dereferencing.” This would be consistent with the usage in the C Standard and would avoid entanglement with the C++ concept of a reference type.

Proposed resolution (February, 2012):

  1. Change 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 2 as follows:

  2. ...The effect of dereferencing indirecting through a pointer returned as a request for zero size is undefined.
  3. Change _N4885_6.7.5.5.4 [basic.stc.dynamic.safety] paragraph 2 as follows:

  4. Change 6.7.3 [basic.life] paragraph 5 as follows:

  5. ...Such Indirection through such a pointer may be dereferenced is permitted but the resulting lvalue may only be used in limited ways...
  6. Change 7.3.13 [conv.mem] paragraph 2 as follows:

  7. ...Since the result has type “pointer to member of D of type cv T”, it can be dereferenced indirection through it with a D object is valid. The result is the same as if indirecting through the pointer to member of B were dereferenced with the B subobject of D. The null member pointer value...
  8. Change 7.6.1.9 [expr.static.cast] paragraph 12 as follows:

  9. ...[Note: although class B need not contain the original member, the dynamic type of the object on with which indirection through the pointer to member is dereferenced performed must contain the original member; see 7.6.4 [expr.mptr.oper]. —end note]
  10. Change 7.6.2.2 [expr.unary.op] paragraph 1 as follows:

  11. ...[Note: indirection through a pointer to an incomplete type (other than cv void) can be dereferenced is valid. The lvalue thus obtained...
  12. Change 7.6.10 [expr.eq] paragraph 2 as follows:

  13. ...Otherwise they compare equal if and only if they would refer to the same member of the same most derived object (6.7.2 [intro.object]) or the same subobject if they were dereferenced indirection with a hypothetical object of the associated class type were performed. [Example:...
  14. Change 9.11 [dcl.link] paragraph 8:

  15. [Note: Because the language linkage is part of a function type, when indirecting through a pointer to C function (for example) is dereferenced, the function to which it the resulting lvalue refers is considered a C function. —end note]
  16. Change 9.3.4.3 [dcl.ref] paragraph 5 as follows:

  17. ...[Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by dereferencing indirection through a null pointer, which causes undefined behavior. As described...
  18. Change 16.4.4.6 [allocator.requirements] table 27:

  19. Variable Definition
    ...
    c a dereferenceable pointer of type C* through which indirection is valid
    ...
  20. Change 20.2.3.3 [pointer.traits.functions] as follows:

  21. Returns: The first member function returns a dereferenceable pointer to r obtained by calling Ptr::pointer_to(r) through which indirection is valid; an instantiation of this function is ill-formed...
  22. Change _N4885_.20.10.5 [util.dynamic.safety] paragraph 10 as follows:

  23. Effects: The n bytes starting at p no longer contain traceable pointer locations, independent of their type. Hence pointers indirection through a pointer located there may not be dereferenced is undefined if the object they point it points to was created by global operator new and not previously declared reachable...
  24. Change 27.11 [specialized.algorithms] paragraph 1 as follows:

  25. ...is required to have the property that no exceptions are thrown from increment, assignment, comparison, or dereference of indirection through valid iterators...
  26. Change 30.4.6.2.3 [locale.time.get.virtuals] paragraph 11 as follows:

  27. Requires: t shall be dereferenceable point to an object.
  28. Change 24.4.4.2 [map.cons] paragraph 3 as follows:

  29. Requires: If the iterator's dereference indirection operator returns an lvalue or a const rvalue pair<key_type, mapped_type>, then both key_type and mapped_type shall be CopyConstructible.
  30. Change 24.4.5.2 [multimap.cons] paragraph 3 as follows:

  31. Requires: If the iterator's dereference indirection operator returns an lvalue or a const rvalue pair<key_type, mapped_type>, then both key_type and mapped_type shall be CopyConstructible.
  32. Change 24.4.6.2 [set.cons] paragraph 4 as follows:

  33. Requires: If the iterator's dereference indirection operator returns an lvalue or a non-const rvalue, then Key shall be CopyConstructible.
  34. Change 24.4.7.2 [multiset.cons] paragraph 3 as follows:

  35. Requires: If the iterator's dereference indirection operator returns an lvalue or a const rvalue, then Key shall be CopyConstructible.
  36. Change 25.5.4 [move.iterators] paragraph 1 as follows:

  37. Class template move_iterator is an iterator adaptor with the same behavior as the underlying iterator except that its dereference indirection operator implicitly converts the value returned by the underlying iterator's dereference indirection operator to an rvalue reference...
  38. Change the title of 32.11.1.4 [re.regiter.deref] as follows:

  39. regex_iterator dereference indirection
  40. Change the title of 32.11.2.4 [re.tokiter.deref] as follows:

  41. regex_token_iterator dereference indirection



1458. Address of incomplete type vs operator&()

Section: 7.6.2.2  [expr.unary.op]     Status: CD3     Submitter: Richard Smith     Date: 2012-02-07

[Moved to DR at the October, 2012 meeting.]

According to 7.6.2.2 [expr.unary.op] paragraph 5,

The address of an object of incomplete type can be taken, but if the complete type of that object is a class type that declares operator&() as a member function, then the behavior is undefined (and no diagnostic is required).

This should actually be “ill-formed, no diagnostic required” instead of undefined behavior, since the problem could be detected by whole-program analysis. Also, it's not clear what this means for constant expressions.

Proposed resolution (February, 2012):

Change 7.6.2.2 [expr.unary.op] paragraph 5 as follows:

The address of an object of incomplete type can be taken, but if the complete type of that object is a class type that declares operator&() as a member function, then the behavior is undefined (and no diagnostic is required). If & is applied to an lvalue of incomplete class type and the complete type declares operator&(), it is unspecified whether the operator has the built-in meaning or the operator function is called. The operand of & shall not be a bit-field.



1553. sizeof and xvalue bit-fields

Section: 7.6.2.5  [expr.sizeof]     Status: CD3     Submitter: Richard Smith     Date: 2012-09-13

[Moved to DR at the April, 2013 meeting.]

According to 7.6.2.5 [expr.sizeof] paragraph 1,

The sizeof operator shall not be applied... to an lvalue that designates a bit-field.

Xvalues can also designate bit-fields and thus should presumably be addressed here as well.

Proposed resolution (October, 2012):

Change 7.6.2.5 [expr.sizeof] paragraph 1 as follows:

The sizeof operator yields the number of bytes in the object representation of its operand. The operand is either an expression, which is an unevaluated operand (Clause 7 [expr]), or a parenthesized type-id. The sizeof operator shall not be applied to an expression that has function or incomplete type, to an enumeration type whose underlying type is not fixed before all its enumerators have been declared, to the parenthesized name of such types, or to an lvalue a glvalue that designates a bit-field. sizeof(char)...



1305. alignof applied to array of unknown size

Section: 7.6.2.6  [expr.alignof]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-04-26

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

According to 7.6.2.6 [expr.alignof] paragraph 1,

An alignof expression yields the alignment requirement of its operand type. The operand shall be a type-id representing a complete object type or an array thereof or a reference to a complete object type.

This (presumably unintentionally) excludes a reference to an array with an unknown bound but a complete element type; the bound is not needed to determine the alignment of the array.

Proposed resolution (August, 2011):

Change 7.6.2.6 [expr.alignof] paragraph 1 as follows:

An alignof expression yields the alignment requirement of its operand type. The operand shall be a type-id representing a complete object type or an array thereof or a reference to a complete object type one of those types.



1354. Destructor exceptions for temporaries in noexcept expressions

Section: 7.6.2.7  [expr.unary.noexcept]     Status: CD3     Submitter: Sebastian Redl     Date: 2011-08-16

[Moved to DR at the October, 2012 meeting.]

The result of the noexcept operator does not consider possible exceptions thrown by the destructors for temporaries created in the operand expression.

Proposed resolution (February, 2012):

  1. Change 6.9.1 [intro.execution] paragraph 10 as follows:

  2. A full-expression is an expression that is not a subexpression of another expression. [Note: in some contexts such as unevaluated operands, a syntactic subexpression is considered a full-expression (Clause 7 [expr]). —end note] If a language construct...
  3. Change Clause 7 [expr] paragraph 7 as follows:

  4. ...An unevaluated operand is not evaluated. An unevaluated operand is considered a full-expression. [Note:...

[Drafting note: This uniformly handles sizeof(A()), noexcept(A()), typeid(A()), and decltype(A()) with regard to the semantic requirements on ~A (accessible and not deleted), which might be checked via SFINAE. A programmer can use decltype(new A) to avoid considering the destructor. If this is undesired, an alternative change just addresses the noexecept issue:]

[Editing note: all the occurrences of “potentially evaluated” in 7.6.2.7 [expr.unary.noexcept] paragraph 3 should be hyphenated.]




292. Deallocation on exception in new before arguments evaluated

Section: 7.6.2.8  [expr.new]     Status: CD3     Submitter: Andrei Iltchenko     Date: 26 Jun 2001

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

According to the C++ Standard section 7.6.2.8 [expr.new] paragraph 21 it is unspecified whether the allocation function is called before evaluating the constructor arguments or after evaluating the constructor arguments but before entering the constructor.

On top of that paragraph 17 of the same section insists that

If any part of the object initialization described above [Footnote: This may include evaluating a new-initializer and/or calling a constructor.] terminates by throwing an exception and a suitable deallocation function is found, the deallocation function is called to free the memory in which the object was being constructed... If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object's memory to be freed...

Now suppose we have:

  1. An implementation that always evaluates the constructor arguments first (for a new-expression that creates an object of a class type and has a new-initializer) and calls the allocation function afterwards.
  2. A class like this:
        struct  copy_throw  {
           copy_throw(const copy_throw&)
           {   throw  std::logic_error("Cannot copy!");   }
           copy_throw(long, copy_throw)
           {   }
           copy_throw()
           {   }
        };
    
  3. And a piece of code that looks like the one below:
        int  main()
        try  {
           copy_throw   an_object,     /* undefined behaviour */
              * a_pointer = ::new copy_throw(0, an_object);
           return  0;
        }
        catch(const std::logic_error&)
        {   }
    

Here the new-expression '::new copy_throw(0, an_object)' throws an exception when evaluating the constructor's arguments and before the allocation function is called. However, 7.6.2.8 [expr.new] paragraph 17 prescribes that in such a case the implementation shall call the deallocation function to free the memory in which the object was being constructed, given that a matching deallocation function can be found.

So a call to the Standard library deallocation function '::operator delete(void*)' shall be issued, but what argument is an implementation supposed to supply to the deallocation function? As per 7.6.2.8 [expr.new] paragraph 17 - the argument is the address of the memory in which the object was being constructed. Given that no memory has yet been allocated for the object, this will qualify as using an invalid pointer value, which is undefined behaviour by virtue of 6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 4.

Suggested resolution:

Change the first sentence of 7.6.2.8 [expr.new] paragraph 17 to read:

If the memory for the object being created has already been successfully allocated and any part of the object initialization described above...

Proposed resolution (March, 2008):

Change 7.6.2.8 [expr.new] paragraph 18 as follows:

If any part of the object initialization described above [Footnote: ...] terminates by throwing an exception, storage has been obtained for the object, and a suitable deallocation function can be found, the deallocation function is called...



1464. Negative array bound in a new-expression

Section: 7.6.2.8  [expr.new]     Status: CD3     Submitter: Mike Miller     Date: 2012-02-12

[Accepted at the April, 2013 meeting.]

Currently, 7.6.2.8 [expr.new] paragraph 7 requires that an attempt to allocate an array with a negative length be diagnosed:

If the value of that expression is less than zero or such that the size of the allocated object would exceed the implementation-defined limit, or if the new-initializer is a braced-init-list for which the number of initializer-clauses exceeds the number of elements to initialize, no storage is obtained and the new-expression terminates by throwing an exception of a type that would match a handler (14.4 [except.handle]) of type std::bad_array_new_length (17.6.4.2 [new.badlength]).

Checking for a negative bound will be lost, however, upon the adoption of paper N3323, as the expression will be converted to std::size_t, an unsigned type. Although the result of this conversion will likely also cause the check to fail (and will always do so when scaled by an element size larger than 1), it is not inconceivable that an implementation could provide a heap that capable of providing more than half the addressable range of std::size_t, and a request for a character array (with an element size of 1) with a negative bound close to LONG_MIN (assuming std::size_t is unsigned long) might actually succeed.

The wording of 7.6.2.8 [expr.new] paragraph 7 should be changed so that the test for a negative bound is applied to the value before conversion to std::size_t, or some other mechanism should be invented to preserve the check for a negative bound.

Additional note (August, 2012):

The goal for addressing this issue should be that an attempt to use an invalid bound (negative, greater than the maximum allowed, or more than the number implied by the initializer) will be ill-formed when the bound is a compile-time constant and will result in an exception otherwise.

Proposed resolution (October, 2012):

  1. Change 6.8.4 [basic.compound] paragraph 2 as follows:

  2. These methods of constructing types can be applied recursively; restrictions are mentioned in 9.3.4.2 [dcl.ptr], 9.3.4.5 [dcl.array], 9.3.4.6 [dcl.fct], and 9.3.4.3 [dcl.ref]. Constructing a type such that the number of bytes in its object representation exceeds the maximum value representable in the type std::size_t (17.2 [support.types]) is ill-formed.
  3. Change 7.6.2.8 [expr.new] paragraph 7 as follows:

  4. The expression in a noptr-new-declarator is erroneous if:

    If the expression, after converting to std::size_t, is a core constant expression and the expression is erroneous, the program is ill-formed. Otherwise, a new-expression with an erroneous expression does not call an allocation function and terminates by throwing an exception of a type that would match a handler (14.4 [except.handle]) of type std::bad_array_new_length (17.6.4.2 [new.badlength]). When the value of the expression in a noptr-new-declarator is zero, the allocation function is called to allocate an array with no elements. If the value of that expression is less than zero or such that the size of the allocated object would exceed the implementation-defined limit, or if the new-initializer is a braced-init-list for which the number of initializer-clauses exceeds the number of elements to initialize, no storage is obtained and the new-expression terminates by throwing an exception of a type that would match a handler (14.4 [except.handle]) of type std::bad_array_new_length (17.6.4.2 [new.badlength]).

(This resolution also resolves issue 1559.)




1559. String too long in initializer list of new-expression

Section: 7.6.2.8  [expr.new]     Status: CD3     Submitter: John Spicer     Date: 2012-09-21

[Moved to DR at the April, 2013 meeting.]

According to 7.6.2.8 [expr.new] paragraph 7,

if the new-initializer is a braced-init-list for which the number of initializer-clauses exceeds the number of elements to initialize, no storage is obtained and the new-expression terminates by throwing an exception of a type that would match a handler (14.4 [except.handle]) of type std::bad_array_new_length (17.6.4.2 [new.badlength]).

This wording does not, but presumably should, require an exception to be thrown in a case like

  void f() {
    int x = 3;
    new char[x]{"abc"};
  }

(See also issue 1464.)

Proposed resolution (October, 2012):

This issue is resolved by the resolution of issue 1464.




1340. Complete type in member pointer expressions

Section: 7.6.4  [expr.mptr.oper]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-08-10

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

Both the .* and ->* operators (7.6.4 [expr.mptr.oper]) require that the class of the second operand be a complete object type. Current implementations do not enforce this requirement, and it is not clear that there is a need for it.

Proposed resolution (August, 2011):

  1. Change 7.6.4 [expr.mptr.oper] paragraph 2 as follows:

  2. The binary operator .* binds its second operand, which shall be of type “pointer to member of T(where T is a completely-defined class type) to its first operand...
  3. Change 7.6.4 [expr.mptr.oper] paragraph 3 as follows:

  4. The binary operator ->* binds its second operand, which shall be of type “pointer to member of T(where T is a completely-defined class type) to its first operand...



1450. INT_MIN % -1

Section: 7.6.5  [expr.mul]     Status: CD3     Submitter: Richard Smith     Date: 2012-01-31

[Moved to DR at the October, 2012 meeting.]

Issue 614 adopted the corresponding C99 wording for 7.6.5 [expr.mul] paragraph 4,

...if the quotient a/b is representable in the type of the result, (a/b)*b + a%b is equal to a.

in an attempt to ensure that INT_MAX % -1 produces undefined behavior (because the result is not specified by the Standard). However, the new C draft makes the undefined behavior explicit:

If the quotient a/b is representable, the expression (a/b) * b + a%b shall equal a; otherwise, the behavior of both a/b and a%b is undefined.

Should C++ adopt similar wording?

Proposed resolution (February, 2012):

Change 7.6.5 [expr.mul] paragraph 4 as follows:

...If the second operand of / or % is zero the behavior is undefined. For integral operands the / operator yields the algebraic quotient with any fractional part discarded;81 if the quotient a/b is representable in the type of the result, (a/b)*b + a%b is equal to a; otherwise, the behavior of both a/b and a%b is undefined.



1504. Pointer arithmetic after derived-base conversion

Section: 7.6.6  [expr.add]     Status: CD3     Submitter: Loïc Joly     Date: 2012-05-20

[Moved to DR at the April, 2013 meeting.]

The current wording is not sufficiently clear that a pointer to a base class subobject of an array element cannot be used in pointer arithmetic.

Proposed resolution (October, 2012):

Add the following as a new paragraph before 7.6.6 [expr.add] paragraph 7:

For addition or subtraction, if the expressions P or Q have type “pointer to cv T", where T is different from the cv-unqualified array element type, the behavior is undefined. [Note: In particular, a pointer to a base class cannot be used for pointer arithmetic when the array contains objects of a derived class type. —end note]

If the value 0 is added to or subtracted...




1457. Undefined behavior in left-shift

Section: 7.6.7  [expr.shift]     Status: CD3     Submitter: Howard Hinnant     Date: 2012-02-04

[Moved to DR at the October, 2012 meeting.]

The current wording of 7.6.7 [expr.shift] paragraph 2 makes it undefined behavior to create the most-negative integer of a given type by left-shifting a (signed) 1 into the sign bit, even though this is not uncommonly done and works correctly on the majority of (twos-complement) architectures:

...if E1 has a signed type and non-negative value, and E1 ⨯ 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

As a result, this technique cannot be used in a constant expression, which will break a significant amount of code.

Proposed resolution (February, 2012):

Change 7.6.7 [expr.shift] paragraph 2 as follows:

...if E1 has a signed type and non-negative value, and E1 ⨯ 2E2 is representable in the corresponding unsigned type of the result type, then that value, converted to the result type, is the resulting value; otherwise, the behavior is undefined.



583. Relational pointer comparisons against the null pointer constant

Section: 7.6.9  [expr.rel]     Status: CD3     Submitter: James Widman     Date: 24 May 2006

[Moved to DR status at the April, 2013 meeting as paper N3624.]

In C, this is ill-formed (cf C99 6.5.8):

    void f(char* s) {
        if (s < 0) { }
    }

...but in C++, it's not. Why? Who would ever need to write (s > 0) when they could just as well write (s != 0)?

This has been in the language since the ARM (and possibly earlier); apparently it's because the pointer conversions (7.3.12 [conv.ptr]) need to be performed on both operands whenever one of the operands is of pointer type. So it looks like the "null-ptr-to-real-pointer-type" conversion is hitching a ride with the other pointer conversions.

Proposed resolution (April, 2013):

This issue is resolved by the resolution of issue 1512.




1512. Pointer comparison vs qualification conversions

Section: 7.6.9  [expr.rel]     Status: CD3     Submitter: Steve Clamage     Date: 2012-06-22

[Moved to DR status at the April, 2013 meeting as paper N3624.]

According to 7.6.9 [expr.rel] paragraph 2, describing pointer comparisons,

Pointer conversions (7.3.12 [conv.ptr]) and qualification conversions (7.3.6 [conv.qual]) are performed on pointer operands (or on a pointer operand and a null pointer constant, or on two null pointer constants, at least one of which is non-integral) to bring them to their composite pointer type.

This would appear to make the following example ill-formed,

  bool foo(int** x, const int** y) {
     return x < y;  // valid ?
  }

because int** cannot be converted to const int**, according to the rules of 7.3.6 [conv.qual] paragraph 4. This seems too strict for pointer comparison, and current implementations accept the example.

Proposed resolution (November, 2012):

The proposed wording is found in document N3478.

(This resolution also resolves issue 583.)




1550. Parenthesized throw-expression operand of conditional-expression

Section: 7.6.16  [expr.cond]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2012-09-04

[Moved to DR at the April, 2013 meeting.]

The current wording of 7.6.16 [expr.cond] paragraph 2 says,

If either the second or the third operand has type void, then the lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are performed on the second and third operands, and one of the following shall hold:

A parenthesized throw-expression is a primary-expression, not a throw-expression. Should a parenthesized throw-expression be considered a throw-expression for this purpose?

Proposed resolution (October, 2012):

Change 7.6.16 [expr.cond] paragraph 2 as follows:

If either the second or the third operand has type void, then the lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are performed on the second and third operands, and one of the following shall hold:

(This resolution also resolves issue 1560.)




1560. Gratuitous lvalue-to-rvalue conversion in conditional-expression with throw-expression operand

Section: 7.6.16  [expr.cond]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2012-09-04

[Moved to DR at the April, 2013 meeting.]

A glvalue appearing as one operand of a conditional-expression in which the other operand is a throw-expression is converted to a prvalue, regardless of how the conditional-expression is used:

If either the second or the third operand has type void, then the lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are performed on the second and third operands, and one of the following shall hold:

This seems to be gratuitous and surprising.

Proposed resolution (October, 2012):

This issue is resolved by the resolution of issue 1550.




1527. Assignment from braced-init-list

Section: 7.6.19  [expr.ass]     Status: CD3     Submitter: Mike Miller     Date: 2012-07-23

[Moved to DR at the April, 2013 meeting.]

According to 7.6.19 [expr.ass] paragraph 9,

A braced-init-list may appear on the right-hand side of

Presumably the phrase “user-defined” is not intended to forbid an example like

  struct A {
    A();
    A ( std::initializer_list<int> ) ;
  };
  void f() {
    A a;
    a = {37};
  }

which relies on an implicitly-declared assignment operator.

Proposed resolution (August, 2012):

Change 7.6.19 [expr.ass] paragraph 9 as follows:

A braced-init-list may appear on the right-hand side of




1538. C-style cast in braced-init-list assignment

Section: 7.6.19  [expr.ass]     Status: CD3     Submitter: Daniel Krügler     Date: 2012-08-14

[Moved to DR at the April, 2013 meeting.]

According to 7.6.19 [expr.ass] paragraph 9,

The meaning of x={v}, where T is the scalar type of the expression x, is that of x=T(v) except that no narrowing conversion (9.4.5 [dcl.init.list]) is allowed. The meaning of x={} is x=T().

This definition adds a gratuitous C-style cast to the right-hand operand, inadvertently allowing such things as base-to-derived conversions and circumvention of access checking.

Proposed resolution (October, 2012):

Change 7.6.19 [expr.ass] paragraph 9 as follows:

The meaning of x={v}, where T is the scalar type of the expression x, is that of x=T(v) except that no narrowing conversion (9.4.5 [dcl.init.list]) is allowed x=T{v}. The meaning of x={} is x=T() x=T{}.



1264. Use of this in constexpr constructor

Section: 7.7  [expr.const]     Status: CD3     Submitter: Jason Merrill     Date: 2011-03-18

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

Proposed resolution (August, 2011):

This issue is resolved by the resolution of issue 1369.




1293. String literals in constant expressions

Section: 7.7  [expr.const]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-04-11

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

It is not clear whether a string literal can be used in a constant expression.

Proposed resolution (August, 2011):

Change 7.7 [expr.const] paragraph 2 as follows:




1311. Volatile lvalues in constant expressions

Section: 7.7  [expr.const]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-05-06

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The current wording of 7.7 [expr.const] paragraph 2 does not, but should, prohibit use of volatile glvalues in constant expressions.

Proposed resolution (August, 2011):

Change 7.7 [expr.const] paragraph 2 as follows:




1312. Simulated reinterpret_cast in constant expressions

Section: 7.7  [expr.const]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-05-06

[Moved to DR at the October, 2012 meeting.]

Although a reinterpret_cast is prohibited in a constant expression, casting to and from void* can achieve the same effect.

Proposed resolution (August, 2011):

Change 7.7 [expr.const] paragraph 2 as follows:

Note, January, 2012:

Additional discussion has occurred, so this issue has been returned to "review" status to allow further consideration.

Proposed resolution (February, 2012):

Change 7.7 [expr.const] paragraph 2 as follows:




1313. Undefined pointer arithmetic in constant expressions

Section: 7.7  [expr.const]     Status: CD3     Submitter: Jens Maurer     Date: 2011-05-07

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The requirements for constant expressions do not currently, but should, exclude expressions that have undefined behavior, such as pointer arithmetic when the pointers do not point to elements of the same array.

Proposed resolution (August, 2011):

Change 7.7 [expr.const] paragraph 2 as follows:




1364. constexpr function parameters

Section: 7.7  [expr.const]     Status: CD3     Submitter: Sean Hunt     Date: 2011-08-17

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

Use of a parameter in a constexpr function appears to be ill-formed, because the lvalue-to-rvalue conversion on the parameter is not one of those permitted in a constant expression.

Proposed resolution (August, 2011):

  1. Change the indicated bullet of 7.7 [expr.const] paragraph 2 as follows:

  2. Delete the final bullet of 9.2.6 [dcl.constexpr] paragraph 3 and move the deleted "." to the preceding sub-bullet:

  3. Delete the final bullet of 9.2.6 [dcl.constexpr] paragraph 4 and change the preceding bullet as follows:




1365. Calling undefined constexpr functions

Section: 7.7  [expr.const]     Status: CD3     Submitter: Sean Hunt     Date: 2011-08-17

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The current wording appears to allow calling a constexpr function that is never defined within the body of a constexpr function. (The wording was intended to allow mutually-recursive constexpr functions but require that the not-yet-defined function be defined before it would be needed in an actual constant expression.)

Proposed resolution (August, 2011):

Change the indicated bullet of 7.7 [expr.const] paragraph 2 as follows:




1367. Use of this in a constant expression

Section: 7.7  [expr.const]     Status: CD3     Submitter: Jason Merrill     Date: 2011-08-17

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The provisions allowing the use of this in a constant expression appear to be unnecessary, as any uses of this in a constant expression that are valid will be replaced by function invocation substitution.

Proposed resolution (August, 2011):

This issue is resolved by the resolution of issue 1369.




1454. Passing constants through constexpr functions via references

Section: 7.7  [expr.const]     Status: CD3     Submitter: Richard Smith     Date: 2011-12-27

[Moved to DR at the October, 2012 meeting.]

The current wording incorrectly appears to make the following example ill-formed:

   constexpr const int &f(const int &n) { return n; }
   constexpr int k = f(0);   // ill-formed

Proposed resolution (February, 2012):

  1. Change 7.7 [expr.const] paragraph 2 as follows:

  2. A conditional-expression is a core constant expression unless it involves one of the following...

  3. Change 7.7 [expr.const] paragraph 3 as follows, dividing it into two paragraphs:

  4. A literal constant expression is a prvalue core constant expression of literal type, but not pointer type. An integral constant expression is a literal constant an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression. [Note: Such expressions may be used as array bounds (9.3.4.5 [dcl.array], 7.6.2.8 [expr.new]), as bit-field lengths (11.4.10 [class.bit]), as enumerator initializers if the underlying type is not fixed (9.7.1 [dcl.enum]), as null pointer constants (7.3.12 [conv.ptr]), and as alignments (9.12.2 [dcl.align]). —end note] A converted constant expression of type T is a literal constant an expression, implicitly converted to a prvalue of type T, where the implicit conversion (if any) is permitted in a literal converted expression is a core constant expression and the implicit conversion sequence contains only user-defined conversions, lvalue-to-rvalue conversions (7.3.2 [conv.lval]), integral promotions (7.3.7 [conv.prom]), and integral conversions (7.3.9 [conv.integral]) other than narrowing conversions (9.4.5 [dcl.init.list]). [Note: such expressions may be used as case expressions (8.5.3 [stmt.switch]), as enumerator initializers if the underlying type is fixed (9.7.1 [dcl.enum]), and as integral or enumeration non-type template arguments (13.4 [temp.arg]). —end note]

    A literal constant expression is a prvalue core constant expression of literal type, but not pointer type (after conversions as required by the context). For a literal constant expression of array or class type, each subobject of its value shall have been initialized by a constant expression. A reference constant expression is an lvalue core constant expression that designates an object with static storage duration or a function. An address constant expression is a prvalue core constant expression (after conversions as required by the context) of type std::nullptr_t or of pointer type that evaluates to the address of an object with static storage duration, to the address of a function, or to a null pointer value, or a prvalue core constant expression of type std::nullptr_t. Collectively, literal constant expressions, reference constant expressions, and address constant expressions are called constant expressions.

  5. Change the second example 9.2.6 [dcl.constexpr] paragraph 5 as follows:

  6.   constexpr int f(bool b)
        { return b ? throw 0 : 0; }                  // OK
      constexpr int f() { throw 0 return f(true); }  // ill-formed, no diagnostic required
      ...
    

This resolution also resolves issue 1455.




1455. Lvalue converted constant expressions

Section: 7.7  [expr.const]     Status: CD3     Submitter: Richard Smith     Date: 2012-01-14

[Moved to DR at the October, 2012 meeting.]

A "converted constant expression" must be a literal constant expression, which is a prvalue, and thus can't be an lvalue. This is unintended; the lvalue-to-rvalue conversion should be applied as necessary.

Proposed resolution (February, 2012):

This issue is resolved by the resolution of issue 1454.




1456. Address constant expression designating the one-past-the-end address

Section: 7.7  [expr.const]     Status: CD3     Submitter: Richard Smith     Date: 2012-01-14

[Moved to DR at the April, 2013 meeting.]

Currently an address constant expression cannot designate the address one past the end of an array. This seems unfortunate.

Proposed resolution (August, 2012):

Change 7.7 [expr.const] paragraph 3 as follows:

...An address constant expression is a prvalue core constant expression of pointer type that evaluates to the address of an object with static storage duration, to the address one past the last element of an array with static storage duration, to the address of a function, or to a null pointer value, or a prvalue core constant expression of type std::nullptr_t...



1480. Constant initialization via non-constant temporary

Section: 7.7  [expr.const]     Status: CD3     Submitter: Daniel Krügler     Date: 2012-03-17

The following initializations appear to be well-formed:

  struct C {
   int m;
   constexpr C(int m ) : m{m} {};
   constexpr int get() { return m; };
  };

  C&& rr = C{1};
  constexpr int k1 = rr.get();

  const C& cl = C{1};
  constexpr int k2 = cl.get();

They appear to fall under the bullet of 7.7 [expr.const] paragraph 2,

The problem in this example is that the referenced temporary object is not a constant, so it would be well-defined for intervening code to modify its value before it was used in the later initialization.

Additional note (February, 2013):

The intent is that non-const references to temporaries should be allowed within constant expressions, e.g., across constexpr function calls, but not as the result of a constant expression.

Proposed resolution (April, 2013):

This issue was rendered moot by paper N3652, adopted at the April, 2013 meeting.




1535. typeid in core constant expressions

Section: 7.7  [expr.const]     Status: CD3     Submitter: Richard Smith     Date: 2012-08-10

[Moved to DR at the April, 2013 meeting.]

One of the criteria in 7.7 [expr.const] paragraph 2 for disqualifying an expression from being a constant expression is:

on the basis that a runtime test for the dynamic type is inconsistent with a constant expression. However, it is only glvalues of polymorphic type that require a runtime test; type-ids and prvalues with a polymorphic type could (and should) be permitted in constant expressions.

Proposed resolution (October, 2012):

Change 7.7 [expr.const] paragraph 2 as follows:




1537. Optional compile-time evaluation of constant expressions

Section: 7.7  [expr.const]     Status: CD3     Submitter: John Spicer     Date: 2012-08-14

[Moved to DR at the April, 2013 meeting.]

According to the note in 7.7 [expr.const] paragraph 4,

[Note: Although in some contexts constant expressions must be evaluated during program translation, others may be evaluated during program execution. Since this International Standard imposes no restrictions on the accuracy of floating-point operations, it is unspecified whether the evaluation of a floating-point expression during translation yields the same result as the evaluation of the same expression (or the same operations on the same values) during program execution.

With the advent of narrowing rules, which require the compiler to evaluate constant expressions in more contexts than was the case in C++03 in order to determine whether an expression is well formed or not, this wording is not sufficiently clear in stating that even in cases where the computation must be done at compile time, the implementation is free to use the result of a runtime calculation rather than preserving the one computed at compile time.

Proposed resolution (October, 2012):

Change 7.7 [expr.const] paragraph 4 as follows:

[Note: Although in some contexts constant expressions must be evaluated during program translation, others may be evaluated during program execution. Since this International Standard imposes no restrictions on the accuracy of floating-point operations, it is unspecified whether the evaluation of a floating-point expression during translation yields the same result as the evaluation of the same expression (or the same operations on the same values) during program execution. [Footnote: Nonetheless, implementations are encouraged to provide consistent results, irrespective of whether the evaluation was actually performed during translation and/or during program execution. —end footnote] [Example:...



631. Jumping into a “then” clause

Section: 8.5.2  [stmt.if]     Status: CD3     Submitter: James Kanze     Date: 24 April 2007

[Moved to DR at the October, 2012 meeting.]

8.5.2 [stmt.if] is silent about whether the else clause of an if statement is executed if the condition is not evaluated. (This could occur via a goto or a longjmp.) C99 covers the goto case with the following provision:

If the first substatement is reached via a label, the second substatement is not executed.

It should probably also be stated that the condition is not evaluated when the “then” clause is entered directly.

Proposed resolution (February, 2012):

Change 8.5.2 [stmt.if] paragraph 1 as follows:

If the condition (8.5 [stmt.select]) yields true the first substatement is executed. If the else part of the selection statement is present and the condition yields false, the second substatement is executed. If the first substatement is reached via a label, the condition is not evaluated and the second substatement is not executed. In the second form...



1442. Argument-dependent lookup in the range-based for

Section: 8.6.5  [stmt.ranged]     Status: CD3     Submitter: Mike Miller     Date: 2012-01-16

[Moved to DR at the April, 2013 meeting.]

It is not clear whether the reference to argument-dependent lookup in 8.6.5 [stmt.ranged] bullet 1.3 should be “pure” argument-dependent lookup (with no unqualified name lookup component) or the usual lookup that is invoked when argument-dependent lookup is done, i.e., unqualified lookup, potentially augmented by the associated namespaces.

Proposed resolution (October, 2012):

Change 8.6.5 [stmt.ranged] bullet 1.3 as follows:




1541. cv void return types

Section: 8.7.4  [stmt.return]     Status: CD3     Submitter: Sean Hunt     Date: 2012-08-21

[Moved to DR at the April, 2013 meeting.]

According to 8.7.4 [stmt.return] paragraph 3,

A return statement with neither an expression nor a braced-init-list can be used only in functions that do not return a value, that is, a function with the return type void, a constructor (11.4.5 [class.ctor]), or a destructor (11.4.7 [class.dtor]).

However, paragraph 3 allows a return type of cv void in cases where the expression in the return statement has type void. Should paragraph 2 follow suit?

Proposed resolution (October, 2012):

Change 8.7.4 [stmt.return] paragraph 2 as follows:

A return statement with neither an expression nor a braced-init-list can be used only in functions that do not return a value, that is, a function with the return type cv void, a constructor (11.4.5 [class.ctor]), or a destructor (11.4.7 [class.dtor]). A return statement with an expression of non-void type...



1544. Linkage of member of unnamed namespace

Section: 9.2.2  [dcl.stc]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2012-08-24

[Moved to DR at the April, 2013 meeting.]

There is a contradiction in the specification of the linkage of members of the unnamed namespace, for example

  namespace {
    int x;
  }

According to 6.6 [basic.link] paragraph 4,

An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage. All other namespaces have external linkage. A name having namespace scope that has not been given internal linkage above has the same linkage as the enclosing namespace if it is the name of

which would give x internal linkage. However, 9.2.2 [dcl.stc] paragraph 7 says,

A name declared in a namespace scope without a storage-class-specifier has external linkage unless it has internal linkage because of a previous declaration and provided it is not declared const.

This would give x external linkage. (This appears to have been a missed edit from the resolution of issue 1113.)

Proposed resolution (October, 2012):

Delete 9.2.2 [dcl.stc] paragraph 7:

A name declared in a namespace scope without a storage-class-specifier has external linkage unless it has internal linkage because of a previous declaration and provided it is not declared const. Objects declared const and not explicitly declared extern have internal linkage.



1437. alignas in alias-declaration

Section: 9.2.4  [dcl.typedef]     Status: CD3     Submitter: Daveed Vandevoorde     Date: 2012-01-02

[Moved to DR at the April, 2013 meeting.]

Consider the following:

    struct S { int i; };
    using A alignas(alignof(long long)) = S;

9.12.2 [dcl.align] paragraph 1 allows an alignment-specifier to be applied to the declaration of a class or enumeration type, which A arguably is. The specification should be clarified to indicate that such a usage is not permitted, however.

Proposed resolution (October, 2012):

Change 9.12.2 [dcl.align] paragraph 1 as follows:

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, the formal parameter of a catch clause an exception-declaration (14.4 [except.handle]), or a variable declared with the register storage class specifier. An alignment-specifier may also be applied to the declaration of a class or enumeration type or definition of a class (in an elaborated-type-specifier (9.2.9.5 [dcl.type.elab]) or class-head ( Clause 11 [class]), respectively) and to the declaration or definition of an enumeration (in an opaque-enum-declaration or enum-head, respectively (9.7.1 [dcl.enum])). An alignment-specifier with an ellipsis is a pack expansion (13.7.4 [temp.variadic]).



1358. Unintentionally ill-formed constexpr function template instances

Section: 9.2.6  [dcl.constexpr]     Status: CD3     Submitter: Richard Smith     Date: 2011-08-16

[Moved to DR status at the April, 2013 meeting.]

The permission granted implementations in 9.2.6 [dcl.constexpr] paragraph 5 to diagnose definitions of constexpr functions that can never be used in a constant expression should not apply to an instantiated constexpr function template.

Notes from the August, 2011 meeting:

The CWG also decided to treat the following example under this issue, although it does not involve a function template:
    int f();   // not constexpr
    struct A {
      int m;
      constexpr A(int i = f()) : m(i) { }
    };
    struct B {
      A a;
    } b;

This is ill-formed, no diagnostic required, because the defaulted default constructor of B will be declared constexpr but can never be invoked in a constant expression. See issue 1360.

Proposed resolution (February, 2012):

  1. Change 9.2.6 [dcl.constexpr] paragraph 5 as follows:

  2. ... —end example]

    For a non-template, non-defaulted constexpr function, if no function argument values exist such that the function invocation substitution would produce a constant expression (7.7 [expr.const]), the program is ill-formed; no diagnostic required. For a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that after function invocation substitution, every constructor call and full-expression in the mem-initializers would be a constant expression (including conversions), the program is ill-formed; no diagnostic required. For a constexpr function template or member function of a class template, if no instantiation would be well-formed when considered as a non-template constexpr function, the program is ill-formed; no diagnostic required. [Example:...

  3. Delete 9.2.6 [dcl.constexpr] paragraph 6:

  4. If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is not a constexpr function or constexpr constructor. [Note: If the function is a member function it will still be const as described below. —end note] If no specialization of the template would yield a constexpr function or constexpr constructor, the program is ill-formed; no diagnostic required.

    Additional notes, February, 2012:

    The proposed resolution inadvertently removes the provision allowing specializations of constexpr templates to violate the requirements of 9.2.6 [dcl.constexpr]. It is being retained in "drafting" status pending additional work.

Proposed resolution (October, 2012):

Change 9.2.6 [dcl.constexpr] paragraphs 5-6 as follows:

...For a non-template, non-defaulted constexpr function, if no function argument values exist such that the function invocation substitution would produce a constant expression (7.7 [expr.const]), the program is ill-formed; no diagnostic required. For a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that after function invocation substitution, every constructor call and full-expression in the mem-initializers would be a constant expression (including conversions), the program is ill-formed; no diagnostic required. [Example: ... —end example]

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is not still a constexpr function or constexpr constructor, even though a call to such a function cannot appear in a constant expression. [Note: If the function is a member function it will still be const as described below. —end note] If no specialization of the template would yield satisfy the requirements for a constexpr function or constexpr constructor when considered as a non-template function or constructor, the program template is ill-formed; no diagnostic required.

Additional note (January, 2013):

Questions arose in the discussion of issue 1581 as to whether this approach — making the specialization of a constexpr function template or member function of a class template still constexpr but unable to be invoked in a constant context — is correct. The implication is that class types might be categorized as literal but not be able to be instantiated at compile time. This issue is therefore returned to "review" status to allow further consideration of this question.




1359. constexpr union constructors

Section: 9.2.6  [dcl.constexpr]     Status: CD3     Submitter: Richard Smith     Date: 2011-08-16

[Moved to DR at the October, 2012 meeting.]

A constexpr constructor is required to initialize all non-static data members (9.2.6 [dcl.constexpr] paragraph 4), which conflicts with the requirement that a constructor for a union is permitted to initialize only a single non-static data member (11.9.3 [class.base.init] paragraph 8).

Proposed resolution (February, 2012):

Change 9.2.6 [dcl.constexpr] paragraph 4 as follows:

In a definition of a constexpr constructor, each of the parameter types shall be a literal type. In addition, either its function-body shall be = delete or = default or it shall satisfy the following constraints:




1366. Deleted constexpr constructors and virtual base classes

Section: 9.2.6  [dcl.constexpr]     Status: CD3     Submitter: Sean Hunt     Date: 2011-08-17

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The requirement that a class with a constexpr constructor cannot have a virtual base only applies to constructors with non-deleted and non-defaulted function-bodys. This seems like an oversight.

Proposed resolution (August, 2011):

Change 9.2.6 [dcl.constexpr] paragraph 4 as follows:

In a The definition of a constexpr constructor, each of the parameter types shall be a literal type. In addition, either its function-body shall be = delete or = default or it shall satisfy the following constraints:

In addition, either its function-body shall be = delete or it shall satisfy the following constraints:




1369. Function invocation substitution of this

Section: 9.2.6  [dcl.constexpr]     Status: CD3     Submitter: Jens Maurer     Date: 2011-08-18

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

Function invocation substitution (9.2.6 [dcl.constexpr] paragraph 5) seems underspecified with respect to this.

Proposed resolution (August, 2011):

  1. Change the indicated bullet of 7.7 [expr.const] paragraph 2 as follows:

  2. Change 9.2.6 [dcl.constexpr] paragraph 5 as follows (converting the running text into a bulleted list):

  3. Function invocation substitution for a call of a constexpr function or of a constexpr constructor means:

    Such substitution...

This resolution also resolves issues 1264 and 1367.




1597. Misleading constexpr example

Section: 9.2.6  [dcl.constexpr]     Status: CD3     Submitter: John Spicer     Date: 2012-12-21

[Addressed by the adoption of paper N3652 at the April, 2013 meeting.]

One of the examples in 9.2.6 [dcl.constexpr] paragraph 3 reads,

  constexpr int prev(int x)
    { return --x; }      // error: use of decrement

According to paragraph 5, this ill-formed, no diagnostic required:

For a constexpr function, if no function argument values exist such that the function invocation substitution would produce a constant expression (7.7 [expr.const]), the program is ill-formed; no diagnostic required.

However, the surrounding errors in the example have required diagnostics, potentially leading the reader to the mistaken conclusion that this error must be diagnosed as well. The example should be removed or the comment updated to reflect its true status.

Proposed resolution (June, 2013):

This issue is no longer relevant after the adoption of the changes to constexpr in paper N3652.




539. Constraints on type-specifier-seq

Section: 9.2.9  [dcl.type]     Status: CD3     Submitter: Mike Miller     Date: 5 October 2005

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The constraints on type-specifiers given in 9.2.9 [dcl.type] paragraphs 2 and 3 (at most one type-specifier except as specified, at least one type-specifier, no redundant cv-qualifiers) are couched in terms of decl-specifier-seqs and declarations. However, they should also apply to constructs that are not syntactically declarations and that are defined to use type-specifier-seqs, including 7.6.2.8 [expr.new], 8.7 [stmt.jump], 9.3.2 [dcl.name], and 11.4.8.3 [class.conv.fct].

Proposed resolution (August, 2011):

Change 9.2.9 [dcl.type] paragraph 3 as follows:

At Except in a declaration of a constructor, destructor, or conversion function, at least one type-specifier that is not a cv-qualifier is required in a declaration unless it declares a constructor, destructor or conversion function shall appear in a complete type-specifier-seq or a complete decl-specifier-seq.92 A type-specifier-seq shall not define...

(Note: paper N2546, voted into the Working Draft in February, 2008, addresses part of this issue.)




1265. Mixed use of the auto specifier

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD3     Submitter: Michael Wong     Date: 2011-03-20

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The current wording of 9.2.9.7 [dcl.spec.auto] does not appear to forbid using the auto specifier for both a function declaration with a trailing return type and a variable definition in the same declaration, e.g.,

    auto f() -> int, i = 0;

(See also issue 1347.)

Proposed resolution (August, 2011):

Change 9.2.9.7 [dcl.spec.auto] paragraph 7 as follows:

If the list of declarators contains more than one declarator, they shall all form declarations of variables. The the type of each declared variable is determined as described above. If, and if the type deduced for the template parameter U is not the same in each deduction, the program is ill-formed. [Example:...



1346. expression-list initializers and the auto specifier

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD3     Submitter: Richard Smith     Date: 2011-08-16

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

9.2.9.7 [dcl.spec.auto] does not address the case when the initializer for an auto variable is a parenthesized expression-list.

Proposed resolution (August, 2011):

Change 9.2.9.7 [dcl.spec.auto] paragraph 3 as follows:

...auto shall appear as one of the decl-specifiers in the decl-specifier-seq and the decl-specifier-seq shall be followed by one or more init-declarators, each of which shall have a non-empty initializer. In an initializer of the form

the expression-list shall be a single assignment-expression. [Example:...




1347. Consistency of auto in multiple-declarator declarations

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD3     Submitter: Richard Smith     Date: 2011-08-16

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The intent of 9.2.9.7 [dcl.spec.auto] paragraph 7 appears to have been that the type represented by auto should be the same for each declarator in the declaration. However, the current wording does not achieve that goal. For example, in

    auto a = 0, b = { 1, 2, 2 };

the auto specifier represents int in the first declarator and std::initializer_list<int> in the second. (See also issue 1265.)

Proposed resolution (August, 2011):

Move the example in 9.2.9.7 [dcl.spec.auto] paragraph 7 into that of paragraph 6 and change paragraph 7 as follows:

...[Example:

  auto x1 = { 1, 2 };   // decltype(x1) is std::initializer_list<int>
  auto x2 = { 1, 2.0 }; // error: cannot deduce element type

  const auto &i = expr;

The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template:

  template <class U> void f(const U& u);

end example]

If the list of declarators init-declarator-list contains more than one declarator init-declarator, the type of each declared variable is determined as described above. If the type deduced for the template parameter U that replaces the occurrence of auto is not the same in each deduction, the program is ill-formed.

[Example:

  const auto &i = expr;

The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template:

  template <class U> void f(const U& u);
  auto x = 5, *y = &x;       // OK: auto is int
  auto a = 5, b = { 1, 2 };  // error: different types for auto

end example]




1588. Deducing cv-qualified auto

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD3     Submitter: Jens Maurer     Date: 2012-11-18

In an example like

  const auto x = 3;

the intent, clearly, is to make const int the type of x. It is not clear, however, that the current wording accomplishes this. Because the deduction is based on that of function calls, and because top-level cv-qualifiers are ignored in such deduction, it appears that 9.2.9.7 [dcl.spec.auto] paragraph 6,

The type deduced for the variable d is then the deduced A determined using the rules of template argument deduction from a function call (13.10.3.2 [temp.deduct.call]), where P is a function template parameter type and the initializer for d is the corresponding argument.

incorrectly gives x the type int.

Proposed resolution (April, 2013):

This issue is resolved by the wording changes in N3638, adopted at the April, 2013 (Bristol) meeting.




1297. Misplaced function attribute-specifier

Section: 9.3  [dcl.decl]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-04-14

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

There is a contradiction between the grammar of 9.3 [dcl.decl] paragraph 4 and that of 9.3.4.6 [dcl.fct] paragraphs 1 and 2 and 9.5.1 [dcl.fct.def.general] paragraph 2 regarding the placement of the optional exception-specification: in the former, it immediately follows the parameter-declaration-clause, while in the latter it follows the exception-specification.

Proposed resolution (August, 2011):

  1. Change the grammar in 9.3 [dcl.decl] paragraph 4 as follows:

  2. Change the grammar snippet in 12.2.2.2.3 [over.call.object] paragraph 2 as follows:




1382. Dead code for constructor names

Section: 9.3  [dcl.decl]     Status: CD3     Submitter: Johannes Schaub     Date: 2011-08-27

[Moved to DR at the October, 2012 meeting.]

Issue 147 changed the name lookup rules so that a lookup that would have found the injected-class-name of a class will refer to the constructor. However, there still appear to be vestiges of the earlier specification that were not removed by the resolution. For example, the grammar in 9.3 [dcl.decl] paragraph 4 contains,

It would seem that there is no longer any need for the second line, since a lookup for a declarator-id will not produce a class-name. Similarly, _N4567_.5.1.1 [expr.prim.general] paragraph 8 still contains the sentence,

Where class-name :: class-name is used, and the two class-names refer to the same class, this notation names the constructor (11.4.5 [class.ctor]).

Proposed resolution (February, 2012):

  1. Change 9.3 [dcl.decl] paragraph 4 as follows:

  2. A class-name has special meaning in a declaration of the class of that name and when qualified by that name using the scope resolution operator :: (5.1 [lex.separate], 11.4.5 [class.ctor], 11.4.7 [class.dtor]).

  3. Change _N4567_.5.1.1 [expr.prim.general] paragraph 8 as follows:

  4. ...[Note: a class member can be referred to using a qualified-id at any point in its potential scope (6.4.7 [basic.scope.class]). —end note] Where class-name :: class-name is used, and the two class-names refer to the same class, this notation names the constructor (11.4.5 [class.ctor]). Where class-name ::~ class-name is used...



1528. Repeated cv-qualifiers in declarators

Section: 9.3  [dcl.decl]     Status: CD3     Submitter: Richard Smith     Date: 2012-07-23

[Moved to DR at the April, 2013 meeting.]

There is no restriction against repeated cv-qualifiers in declarators, although there is (in 9.2.9 [dcl.type] paragraph 2) for those appearing in a decl-specifier-seq. However, most or all current implementations reject such code. Is a change needed?

Proposed resolution (October, 2012):

Change 9.2.9.2 [dcl.type.cv] paragraph 1 as follows:

There are two cv-qualifiers, const and volatile. Each cv-qualifier shall appear at most once in a cv-qualifier-seq. If a cv-qualifier appears in a decl-specifier-seq, the init-declarator-list of the declaration shall not be empty. [Note:...



482. Qualified declarators in redeclarations

Section: 9.3.4  [dcl.meaning]     Status: CD3     Submitter: Daveed Vandevoorde     Date: 03 Nov 2004

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

According to 9.3.4 [dcl.meaning] paragraph 1,

A declarator-id shall not be qualified except for the definition of a member function (11.4.2 [class.mfct]) or static data member (11.4.9 [class.static]) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or the definition of a previously declared explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (11.8.4 [class.friend]). When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers...

This restriction prohibits examples like the following:

    void f();
    void ::f();        // error: qualified declarator

    namespace N {
      void f();
      void N::f() { }  // error: qualified declarator
    }

There doesn't seem to be any good reason for disallowing such declarations, and a number of implementations accept them in spite of the Standard's prohibition. Should the Standard be changed to allow them?

Notes from the April, 2006 meeting:

In discussing issue 548, the CWG agreed that the prohibition of qualified declarators inside their namespace should be removed.

Proposed resolution (October, 2006):

Remove the indicated words from 9.3.4 [dcl.meaning] paragraph 1:

...An unqualified-id occurring in a declarator-id shall be a simple identifier except for the declaration of some special functions (11.4.8 [class.conv], 11.4.7 [class.dtor], 12.4 [over.oper]) and for the declaration of template specializations or partial specializations (13.9 [temp.spec]). A declarator-id shall not be qualified except for the definition of a member function (11.4.2 [class.mfct]) or static data member (11.4.9 [class.static]) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or the definition of a previously declared explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (11.8.4 [class.friend]). When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers, and the member shall not have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id...

[Drafting note: The omission of “outside of its class” here does not give permission for redeclaration of class members; that is still prohibited by 11.4 [class.mem] paragraph 1. The removal of the enumeration of the kinds of declarations in which a qualified-id can appear does allow a typedef declaration to use a qualified-id, which was not permitted before; if that is undesirable, the prohibition can be reinstated here.]




1435. template-id as the declarator for a class template constructor

Section: 9.3.4  [dcl.meaning]     Status: CD3     Submitter: Johannes Schaub     Date: 2011-12-24

[Moved to DR at the April, 2013 meeting.]

The status of a declaration like the following is unclear:

  template<typename T> struct A {
    A<T>();
  };

9.3.4 [dcl.meaning] paragraph 1 appears to say that it is not allowed, but it is not clear.

Proposed resolution (October, 2012):

  1. Change 9.3.4 [dcl.meaning] paragraph 1 as follows:

  2. A list of declarators appears after an optional (9.1 [dcl.pre]) decl-specifier-seq (9.2 [dcl.spec]). Each declarator contains exactly one declarator-id; it names the identifier that is declared. An unqualified-id occurring in a declarator-id shall be a simple identifier except for the declaration of some special functions (11.4.5 [class.ctor], 11.4.8 [class.conv], 11.4.7 [class.dtor], 12.4 [over.oper]) and for the declaration of template specializations or partial specializations (13.9 [temp.spec]). A declarator-id shall not...
  3. Change 11.4.5 [class.ctor] paragraph 1 as follows:

  4. Constructors do not have names. A special declarator syntax is used to declare or define the constructor. The syntax uses:

    in that order. In such a declaration, optional parentheses around the constructor class name are ignored. A declaration of a constructor uses a function declarator (9.3.4.6 [dcl.fct]) of the form

    where the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:

    The class-name shall not be a typedef-name. In a constructor declaration, each decl-specifier in the optional decl-specifier-seq shall be friend, inline, explicit, or constexpr. [Example:...

  5. Delete 11.4.5 [class.ctor] paragraph 3:

  6. A typedef-name shall not be used as the class-name in the declarator-id for a constructor declaration.
  7. Change 11.4.5 [class.ctor] paragraph 4 as follows:

  8. A constructor shall not be virtual (11.7.3 [class.virtual]) or static (11.4.9 [class.static]). A constructor can be invoked for a const, volatile or const volatile object. A constructor shall not be declared const, volatile, or const volatile (_N4868_.11.4.3.2 [class.this]). const and volatile semantics (9.2.9.2 [dcl.type.cv]) are not applied on an object under construction. They come into effect when the constructor for the most derived object (6.7.2 [intro.object]) ends. A constructor shall not be declared with a ref-qualifier.
  9. Change 11.4.5 [class.ctor] paragraph 9 as follows:

  10. No return type (not even void) shall be specified for a constructor. A return statement in the body of a constructor shall not specify a return value. The address of a constructor shall not be taken.
  11. Change 11.4.7 [class.dtor] paragraphs 1-2 as follows:

  12. A special declarator syntax using an optional function-specifier (9.2.3 [dcl.fct.spec]) followed by ~ followed by the destructor's class name followed by an empty parameter list is used to declare the destructor in a class definition. In such a declaration, the ~ followed by the destructor's class name can be enclosed in optional parentheses; such parentheses are ignored. A typedef-name shall not be used as the class-name following the ~ in the declarator for a destructor declaration. A declaration of a destructor uses a function declarator (9.3.4.6 [dcl.fct]) of the form

    where the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:

    The class-name shall not be a typedef-name. A destructor shall take no arguments (9.3.4.6 [dcl.fct]). In a destructor declaration, each decl-specifier of the optional decl-specifier-seq shall be friend, inline, or virtual.

    A destructor is used to destroy objects of its class type. A destructor takes no parameters, and no return type can be specified for it (not even void). The address of a destructor shall not be taken. A destructor shall not be static. A destructor can be invoked for a const, volatile or const volatile object. A destructor shall not be declared const, volatile or const volatile (_N4868_.11.4.3.2 [class.this]). const and volatile semantics (9.2.9.2 [dcl.type.cv]) are not applied on an object under destruction. They stop being in effect when the destructor for the most derived object (6.7.2 [intro.object]) starts. A destructor shall not be declared with a ref-qualifier.

    This resolution also resolves issue 344.




1510. cv-qualified references via decltype

Section: 9.3.4.3  [dcl.ref]     Status: CD3     Submitter: Richard Smith     Date: 2012-06-14

[Moved to DR at the April, 2013 meeting.]

According to 9.3.4.3 [dcl.ref] paragraph 1,

Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef (9.2.4 [dcl.typedef]) or of a template type argument (13.4 [temp.arg]), in which case the cv-qualifiers are ignored.

There does not appear to be a good reason not to extend this to apply to apply to decltype, as well.

Proposed resolution (October, 2012):

Change 9.3.4.3 [dcl.ref] paragraph 1 as follows:

...Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef typedef-name (9.2.4 [dcl.typedef], 13.2 [temp.param]) or of a template type argument (13.4 [temp.arg]) decltype-specifier (9.2.9.3 [dcl.type.simple]), in which case the cv-qualifiers are ignored. [Example:...



332. cv-qualified void parameter types

Section: 9.3.4.6  [dcl.fct]     Status: CD3     Submitter: Michiel Salters     Date: 9 Jan 2002

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

9.3.4.6 [dcl.fct]/2 restricts the use of void as parameter type, but does not mention CV qualified versions. Since void f(volatile void) isn't a callable function anyway, 9.3.4.6 [dcl.fct] should also ban cv-qualified versions. (BTW, this follows C)

Suggested resolution:

A possible resolution would be to add (cv-qualified) before void in

The parameter list (void) is equivalent to the empty parameter list. Except for this special case, (cv-qualified) void shall not be a parameter type (though types derived from void, such as void*, can).

Proposed resolution (August, 2011):

This issue is resolved by the resolution of issue 577.




577. void in an empty parameter list

Section: 9.3.4.6  [dcl.fct]     Status: CD3     Submitter: Ben Hutchings     Date: 22 April 2006

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

9.3.4.6 [dcl.fct] paragraph 2 says,

The parameter list (void) is equivalent to the empty parameter list.

This special case is intended for C compatibility, but C99 describes it differently (6.7.5.3 paragraph 10):

The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.

The C99 formulation allows typedefs for void, while C++ (and C90) accept only the keyword itself in this role. Should the C99 approach be adopted?

Notes from the October, 2006 meeting:

The CWG did not take a formal position on this issue; however, there was some concern expressed over the treatment of function templates and member functions of class templates if the C++ rule were changed: for a template parameter T, would a function taking a single parameter of type T become a no-parameter function if it were instantiated with T = void?

Proposed resolution (August, 2011):

Change 9.3.4.6 [dcl.fct] paragraph 4 as follows:

...If the parameter-declaration-clause is empty, the function takes no arguments. The parameter list (void) A parameter list consisting of a single unnamed parameter of non-dependent type void is equivalent to the an empty parameter list. Except for this special case, a parameter shall not have type cv void shall not be a parameter type (though types derived from void, such as void*, can). If the parameter-declaration-clause terminates...

This resolution also resolves issue 332.




1380. Type definitions in template-parameter parameter-declarations

Section: 9.3.4.6  [dcl.fct]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-08-22

[Moved to DR at the October, 2012 meeting.]

Although 9.3.4.6 [dcl.fct] paragraph 9 forbids defining a type in a parameter declaration, and a template parameter declaration is syntactically a parameter-declaration, the context in 9.3.4.6 [dcl.fct] function declarators. It's therefore not completely clear that that prohibition applies to template parameter declarations as well. This should be clarified.

Proposed resolution (February, 2012):

Change 13.2 [temp.param] paragraph 2 as follows:

...A storage class shall not be specified in a template-parameter declaration. Types shall not be defined in a template-parameter declaration. [Note:...



1394. Incomplete types as parameters of deleted functions

Section: 9.3.4.6  [dcl.fct]     Status: CD3     Submitter: Johannes Schaub     Date: 2011-09-11

[Moved to DR at the October, 2012 meeting.]

Currently, 9.3.4.6 [dcl.fct] paragraph 9 requires that

The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).

There is no reason for this requirement for a function with a deleted definition, and it would be useful to relax this prohibition in such cases.

Proposed resolution (February, 2012):

Change 9.3.4.6 [dcl.fct] paragraph 9 as follows:

Types shall not be defined in return or parameter types. The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function is deleted (9.5.3 [dcl.fct.def.delete]) or the definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).



1226. Converting a braced-init-list default argument

Section: 9.3.4.7  [dcl.fct.default]     Status: CD3     Submitter: Mike Miller     Date: 2010-11-19

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

According to the new wording of 9.3.4.7 [dcl.fct.default] paragraph 5,

A default argument is implicitly converted (7.3 [conv]) to the parameter type.

This is incorrect when the default argument is a braced-init-list. That sentence doesn't seem to be necessary, but if it is kept, it should be recast in terms of initialization rather than conversion.

Proposed resolution (August, 2011):

Delete the first sentence of 9.3.4.7 [dcl.fct.default] paragraph 5:

A default argument is implicitly converted (7.3 [conv]) to the parameter type.



1093. Value-initializing non-objects

Section: 9.4  [dcl.init]     Status: CD3     Submitter: Daniel Krügler     Date: 2010-07-17

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

9.4 [dcl.init] paragraph 7 only describes how to initialize objects:

To value-initialize an object of type T means:

However, 7.6.1.4 [expr.type.conv] paragraph 2 calls for value-initializing prvalues, which in the case of scalar types are not objects:

The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates a prvalue of the specified type, which is value-initialized (9.4 [dcl.init]; no initialization is done for the void() case).

Proposed resolution (August, 2011):

Change 7.6.1.4 [expr.type.conv] paragraph 2 as follows:

The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates a prvalue of the specified type,which is value-initialized (9.4 [dcl.init] type, whose value is that produced by value-initializing (9.4 [dcl.init]) an object of type T; no initialization is done for the void() case). [Note:...



1301. Value initialization of union

Section: 9.4  [dcl.init]     Status: CD3     Submitter: Jason Merrill     Date: 2011-04-18

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

According to 9.4 [dcl.init] paragraph 7,

To value-initialize an object of type T means:

This suggests that for

  struct A { A() = delete; };
  union B { A a };
  int main()
  {
    B();
  }

a B temporary is created and zero-initialized, even though its default constructor is deleted. We should strike "non-union" and also the "if...non-trivial" condition, since we can have a trivial deleted constructor.

Proposed resolution (August, 2011):

  1. Change 9.4 [dcl.init] paragraph 7 as follows:

  2. To value-initialize an object of type T means:

  3. Change 9.4.5 [dcl.init.list] paragraph 3 as follows:

  4. List-initialization of an object or reference of type T is defined as follows:

This resolution also resolves issues 1324 and 1368.




1324. Value initialization and defaulted constructors

Section: 9.4  [dcl.init]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-05-22

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

One would expect that an example like

  struct B {
   B(const B&) = default;
  };

  B b{};

would invoke value-initialization, but (because it does not have a default constructor), the logic ladder of 9.4.5 [dcl.init.list] paragraph 3 makes it aggregate initialization instead:

Proposed resolution (August, 2011):

This issue is resolved by the resolution of issue 1301.




1368. Value initialization and defaulted constructors (part 2)

Section: 9.4  [dcl.init]     Status: CD3     Submitter: Jason Merrill     Date: 2011-06-28

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

According to the current rules of 9.4 [dcl.init], given a class like

  struct A {
    int i;
    A() = default;
    A(int i): i(i) { }
  };

value-initialization leaves A::i uninitialized. This seems like an oversight.

Proposed resolution (August, 2011):

This issue is resolved by the resolution of issue 1301.




1502. Value initialization of unions with member initializers

Section: 9.4  [dcl.init]     Status: CD3     Submitter: Richard Smith     Date: 2012-05-06

[Moved to DR at the April, 2013 meeting.]

According to 9.4 [dcl.init] paragraph 7,

To value-initialize an object of type T means:

In an example like

  union U { int a = 5; };
  int main() { return U().a; }

this means that the value returned is 0, because none of the first three bullets apply. Should the “non-union” restriction be dropped from the second bullet?

Proposed resolution (October, 2012):

Change 9.4 [dcl.init] paragraph 7 as follows:

To value-initialize an object of type T means:




1507. Value initialization with trivial inaccessible default constructor

Section: 9.4  [dcl.init]     Status: CD3     Submitter: Johannes Schaub     Date: 2012-06-01

[Moved to DR at the April, 2013 meeting.]

According to 9.4 [dcl.init] paragraph 7, a trivial default constructor is not used in value initialization, so the following example would appear to be well-formed:

  struct A { private: A() = default; };
  int main() { A(); }

Proposed resolution (February, 2013):

  1. Change 9.4 [dcl.init] paragraph 7 as follows:

  2. To default-initialize an object of type T means:

  3. Change 9.4 [dcl.init] paragraph 8 as follows:

  4. To value-initialize an object of type T means:




1295. Binding a reference to an rvalue bit-field

Section: 9.4.4  [dcl.init.ref]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-04-14

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

Consider the following example:

  struct X {
    unsigned bitfield : 4;
  };
  int main() {
    X x = { 1 };
    unsigned const &ref = static_cast<X &&>(x).bitfield;
  }

According to 9.4.4 [dcl.init.ref] paragraph 5, ref is bound to the bit-field xvalue.

Proposed resolution (August, 2011):

Change the indicated sub-bullet of 9.4.4 [dcl.init.ref] paragraph 5 as follows:




1328. Conflict in reference binding vs overload resolution

Section: 9.4.4  [dcl.init.ref]     Status: CD3     Submitter: Johannes Schaub     Date: 2011-05-31

[Moved to DR at the April, 2013 meeting.]

According to 12.2.2.7 [over.match.ref] paragraph 1, the determination of the candidate functions is based on whether 9.4.4 [dcl.init.ref] requires an lvalue result or an rvalue result. It is not sufficiently clear exactly what this means, particularly with respect to function lvalues and rvalues.

Proposed resolution (August, 2011):

  1. Change 12.2.2.7 [over.match.ref] paragraph 1 as follows:

  2. Change 12.2.4 [over.match.best] paragraph 1 as follows:




1401. Similar types and reference compatibility

Section: 9.4.4  [dcl.init.ref]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-10-03

[Moved to DR at the October, 2012 meeting.]

The definition of reference-compatible types in 9.4.4 [dcl.init.ref] paragraph 4 allows the types to differ in top-level cv-qualification, but it does not encompass the deeper added cv-qualification permitted for “similar types” (7.3.6 [conv.qual]). This seems surprising and could lead to errors resulting from the fact that the reference will be bound to a temporary and not to the original object in the initializer.

Proposed resolution (February, 2012):

  1. Change 9.4.4 [dcl.init.ref] paragraph 4 as follows:

  2. Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is reference-related to “cv2 T2” if T1 is the same type as T2, or T1 is a base class of T2. “cv1 T1” is reference-compatible with “cv2 T2” if T1 is reference-related to T2 and cv1 is the same cv-qualification as, or greater cv-qualification than, cv2. For purposes of overload resolution, cases for which cv1 is greater cv-qualification than cv2 are identified as reference-compatible with added qualification (see 12.2.4.3 [over.ics.rank]). In all cases...
  3. Delete 12.2.4.2.5 [over.ics.ref] paragraph 5:

  4. The binding of a reference to an expression that is reference-compatible with added qualification influences the rank of a standard conversion; see 12.2.4.3 [over.ics.rank] and 9.4.4 [dcl.init.ref].

    [Drafting note: CWG decided not to make a substantive change for this issue, but the investigation discovered that the term defined by these two citations is not actually used and could be removed.]




1270. Brace elision in array temporary initialization

Section: 9.4.5  [dcl.init.list]     Status: CD3     Submitter: Jason Merrill     Date: 2011-03-23

[Moved to DR at the October, 2012 meeting.]

Issue 1232 extended the language to allow creation of array temporaries using initializer lists. However, such initializer lists must be “completely braced;” the elision of braces described in 9.4.2 [dcl.init.aggr] paragraph 11 applies only

In a declaration of the form

  T x = { a };

This restriction prevents plausible uses like

    array<int, 3> f() {
      return { 1, 2, 3 };
    }

Proposed resolution (February, 2012):

Change 9.4.2 [dcl.init.aggr] paragraph 11 as follows:

In a declaration of the form

braces Braces can be elided in an initializer-list as follows. [Footnote: Braces cannot be elided in other uses of list-initialization. —end footnote]




1288. Reference list initialization

Section: 9.4.5  [dcl.init.list]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-04-06

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

One might expect that in an example like

  int i;
  int & ir{i};

ir would bind directly to i. However, according to 9.4.5 [dcl.init.list] paragraph 3, this example creates a temporary of type int and binds the reference to that temporary:

Also, the “or reference” in the last bullet is dead code, as a reference initialization is always handled by the preceding bullet.

Proposed resolution (August, 2011):

Change 9.4.5 [dcl.init.list] paragraph 3 as follows:




1290. Lifetime of the underlying array of an initializer_list member

Section: 9.4.5  [dcl.init.list]     Status: CD3     Submitter: James Dennett     Date: 2011-04-08

[Moved to DR at the October, 2012 meeting.]

A question has arisen over expected behavior when an initializer_list is a non-static data member of a class. Initialization of an initializer_list is defined in terms of construction from an implicitly allocated array whose lifetime "is the same as that of the initializer_list object". That would mean that the array needs to live as long as the initializer_list does, which would on the face of it appear to require the array to be stored in something like a std::unique_ptr<T[]> within the same class (if the member is initialized in this manner).

It would be surprising if that was the intent, but it would make initializer_list usable in this context.

It would also be reasonable if this behaved similarly to binding temporaries to reference members (i.e., "temporary bound to a reference member in a constructor's ctor-initializer (11.9.3 [class.base.init]) persists until the constructor exits."), though this approach would probably prevent use of an initializer_list member in that context.

Proposed resolution (February, 2012):

  1. Change 9.4.5 [dcl.init.list] paragraphs 5-6 as follows:

  2. An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated an a temporary array of N elements of type E, where...

    The lifetime of the array is the same as that of the initializer_list object. The array has the same lifetime as any other temporary object (6.7.7 [class.temporary]), except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary. [Example:

      typedef std::complex<double> cmplx;
      std::vector<cmplx> v1 = { 1, 2, 3 };
    
      void f() {
        std::vector<cmplx> v2{ 1, 2, 3 };
        std::initializer_list<int> i3 = { 1, 2, 3 };
      }
    
      struct A {
        std::initializer_list<int> i4;
        A(): i4{1,2,3} { }  // creates an A with a dangling reference
      };
    

    For v1 and v2, the initializer_list object is a parameter in a function call, so the and array created for { 1, 2, 3 } have has full-expression lifetime. For i3, the initializer_list object is a variable, so the and array have automatic persists for the lifetime of the variable. For i4, the initializer_list object is initialized in a constructor's ctor-initializer, so the array persists only until the constructor exits, and so any use of the elements of i4 after the constructor exits produces undefined behavior. —end example] [Note: The implementation is free to allocate the array in read-only memory if an explicit array with the same initializer could be so allocated. —end note]

  3. Change 6.7.7 [class.temporary] paragraph 5 as follows:

  4. The second context is when a reference is bound to a temporary. [Footnote: The same rules apply to initialization of an initializer_list object (9.4.5 [dcl.init.list]) with its underlying temporary array. —end footnote] The temporary to which...



1418. Type of initializer_list backing array

Section: 9.4.5  [dcl.init.list]     Status: CD3     Submitter: Johannes Schaub     Date: 2011-11-19

[Moved to DR at the October, 2012 meeting.]

According to 9.4.5 [dcl.init.list] paragraph 5, the elements of the backing array for an object of type std::initializer_list<E> are of type E. This is contradicted by the wording of 17.10 [support.initlist] paragraph 2.

Proposed resolution (February, 2012):

Change 9.4.5 [dcl.init.list] paragraph 5 as follows:

An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated an array of N elements of type const E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list<E> object is constructed to refer to that array. If a narrowing conversion is required to initialize any of the elements, the program is ill-formed. [Example:

  struct X {
    X(std::initializer_list<double> v);
  };
  X x{ 1,2,3 };

The initialization will be implemented in a way roughly equivalent to this:

  const double __a[3] = {double{1}, double{2}, double{3}};
  X x(std::initializer_list<double>(__a, __a+3));

assuming that the implementation can construct an initializer_list object with a pair of pointers. —end example]




1449. Narrowing conversion of negative value to unsigned type

Section: 9.4.5  [dcl.init.list]     Status: CD3     Submitter: Richard Smith     Date: 2012-01-28

[Moved to DR at the October, 2012 meeting.]

According to 9.4.5 [dcl.init.list] paragraph 7, an implicit conversion

from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type.

As is made plain in the examples in that paragraph, a conversion of a negative value to an unsigned type is intended to be a narrowing conversion; however, the phrase “actual value after conversion” makes this intent unclear, especially since the round-trip conversion between signed and unsigned types might well yield the original value.

Proposed resolution (February, 2012):

Change 9.4.5 [dcl.init.list] paragraph 7 as follows:

A narrowing conversion is an implicit conversion

[Note:...




1494. Temporary initialization for reference binding in list-initialization

Section: 9.4.5  [dcl.init.list]     Status: CD3     Submitter: Steve Adamczyk     Date: 2012-04-12

[Moved to DR at the April, 2013 meeting.]

One of the bullets in 9.4.5 [dcl.init.list] paragraph 3 reads,

Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary.

This does not say whether the initialization of the temporary is direct-list-initialization, copy-list-initialization, or the same kind of list-initialization as the top-level initialization.

Proposed resolution (December, 2012):

Change 9.4.5 [dcl.init.list] paragraph 3 as follows:




1506. Value category of initializer_list object

Section: 9.4.5  [dcl.init.list]     Status: CD3     Submitter: Steve Adamczyk     Date: 2012-05-29

[Moved to DR at the April, 2013 meeting.]

One of the bullets in 9.4.5 [dcl.init.list] paragraph 3 says,

Otherwise, if T is a specialization of std::initializer_list<E>, an initializer_list object is constructed as described below and used to initialize the object according to the rules for initialization of an object from a class of the same type (9.4 [dcl.init]).

This does not, but should, say whether the initializer_list object is treated as an lvalue or prvalue for the purpose of the 9.4 [dcl.init] initialization.

Proposed resolution (October, 2012):

Change 9.4.5 [dcl.init.list] paragraph 3 as follows:

List-initialization of an object or reference of type T is defined as follows:




1522. Access checking for initializer_list array initialization

Section: 9.4.5  [dcl.init.list]     Status: CD3     Submitter: John Spicer     Date: 2012-07-12

[Moved to DR at the April, 2013 meeting.]

In constructing an initializer_list object from an initializer list, 9.4.5 [dcl.init.list] paragraph 5 says of the underlying array,

Each element of that array is copy-initialized with the corresponding element of the initializer list

It would probably be good to mention that the copy/move constructor for this copy must be accessible in the context in which the initialization occurs.

Proposed resolution (October, 2012):

Change 9.4.5 [dcl.init.list] paragraph 4 as follows:

...Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list<E> object is constructed to refer to that array. [Note: A constructor or conversion function selected for the copy shall be accessible (11.8 [class.access]) in the context of the initializer list. —end note] If a narrowing conversion is required...



2150. Initializer list array lifetime

Section: 9.4.5  [dcl.init.list]     Status: CD3     Submitter: Hubert Tong     Date: 2015-06-26

The resolution of issue 1696 appears to have removed the wording that makes the initialization of A::i4 in 9.4.5 [dcl.init.list] paragraph 6 create a dangling reference:

The array has the same lifetime as any other temporary object (6.7.7 [class.temporary]), except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary. [Example:

  typedef std::complex<double> cmplx;
  std::vector<cmplx> v1 = { 1, 2, 3 };

  void f() {
    std::vector<cmplx> v2{ 1, 2, 3 };
    std::initializer_list<int> i3 = { 1, 2, 3 };
  }

  struct A {
    std::initializer_list<int> i4;
    A() : i4{ 1, 2, 3 } {} // creates an A with a dangling reference
  };

For v1 and v2, the initializer_list object is a parameter in a function call, so the array created for { 1, 2, 3 } has full-expression lifetime. For i3, the initializer_list object is a variable, so the array persists for the lifetime of the variable. For i4, the initializer_list object is initialized in a constructor's ctor-initializer, so the array persists only until the constructor exits, and so any use of the elements of i4 after the constructor exits produces undefined behavior. —end example]

Binding a reference to a temporary in a mem-initializer or default member initializer is now ill-formed, per 11.9.3 [class.base.init] paragraphs 8 and 11, which undercuts the description here.

Notes from the October, 2015 meeting:

The example is incorrect and will be fixed editorially. The issue is left in "review" status to check that the editorial change has been made.




1327. virt-specifier in a defaulted definition

Section: 9.5.2  [dcl.fct.def.default]     Status: CD3     Submitter: Ryou Ezoe     Date: 2011-05-29

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The grammar for defaulted and deleted functions in 9.5.2 [dcl.fct.def.default] and 9.5.3 [dcl.fct.def.delete] does not provide for virt-specifiers. Is there a reason for this omission, or was it inadvertent?

Proposed resolution (August, 2011):

  1. Change 9.5.2 [dcl.fct.def.default] paragraph 1 as follows:

  2. A function definition of the form:

    is called an explicitly-defaulted definition...

  3. Change 9.5.3 [dcl.fct.def.delete] paragraph 1 as follows:

  4. A function definition of the form:

    is called a deleted definition...




1333. Omission of const in a defaulted copy constructor

Section: 9.5.2  [dcl.fct.def.default]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-06-21

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

Paragraph 1 of 9.5.2 [dcl.fct.def.default] allows an explicitly-defaulted copy constructor or copy assignment operator to have a parameter type that is a reference to non-const, even if the corresponding implicitly-declared function would have a reference to const. However, paragraph 2 says that a copy constructor or copy assignment operator that is defaulted on its first declaration, the parameter type must be exactly the same. Is there a good reason for the stricter rule for a function that is defaulted on its first declaration?

Proposed resolution (August, 2011):

  1. Change 9.5.2 [dcl.fct.def.default] paragraph 2 as follows:

  2. ...If a function is explicitly defaulted on its first declaration,

  3. Change 11.4.5.3 [class.copy.ctor] paragraph 12 as follows:

  4. A copy/move constructor for class X is trivial if it is not user-provided, its declared parameter type is the same as if it had been implicitly declared, and if...
  5. Change 11.4.5.3 [class.copy.ctor] paragraph 25 as follows:

  6. A copy/move assignment operator for class X is trivial if it is not user-provided, its declared parameter type is the same as if it had been implicitly declared, and if...



1355. Aggregates and “user-provided” constructors

Section: 9.5.2  [dcl.fct.def.default]     Status: CD3     Submitter: Sean Hunt     Date: 2011-08-16

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The definition of “user-provided” given in 9.5.2 [dcl.fct.def.default] paragraph 4 applies only to special member functions, while the definition of an aggregate in 9.4.2 [dcl.init.aggr] paragraph 1 relies on that term in identifying constructors that make a class a non-aggregate. As a result, a class with a non-special constructor is considered an aggregate.

Proposed resolution (August, 2011):

Change 9.5.2 [dcl.fct.def.default] paragraph 4 as follows:

A special member function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration...

[Drafting note: This makes a class with only a deleted initializer-list constructor an aggregate.]




977. When is an enumeration type complete?

Section: 9.7.1  [dcl.enum]     Status: CD3     Submitter: Daniel Krügler     Date: 3 October, 2009

[Moved to DR at the April, 2013 meeting.]

There is disagreement among implementations as to when an enumeration type is complete. For example,

    enum E { e = E() };

is rejected by some and accepted by another. The Standard does not appear to resolve this question definitively.

See also issue 1482.

Proposed resolution (December, 2012):

This issue is resolved by the resolution of issue 1482.




565. Conflict rules for using-declarations naming function templates

Section: 9.9  [namespace.udecl]     Status: CD3     Submitter: Paolo Carlini     Date: 9 March 2006

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The Standard does not appear to specify what happens for code like the following:

    namespace one {
      template<typename T> void fun(T);
    }

    using one::fun;

    template<typename T> void fun(T);

9.9 [namespace.udecl] paragraph 13 does not appear to apply because it deals only with functions, not function templates:

If a function declaration in namespace scope or block scope has the same name and the same parameter types as a function introduced by a using-declaration, and the declarations do not declare the same function, the program is ill-formed.

John Spicer: For function templates I believe the rule should be that if they have the same function type (parameter types and return type) and have identical template parameter lists, the program is ill-formed.

Proposed resolution (August, 2011):

Change 9.9 [namespace.udecl] paragraph 14 as follows:

If a function declaration in namespace scope or block scope has the same name and the same parameter types parameter-type-list (9.3.4.6 [dcl.fct]) as a function introduced by a using-declaration, and the declarations do not declare the same function, the program is ill-formed. If a function template declaration in namespace scope has the same name, parameter-type-list, return type, and template parameter list as a function template introduced by a using-declaration, the program is ill-formed. [Note: Two using-declarations may introduce functions with the same name and the same parameter types parameter-type-list. If, for a call to an unqualified function name, function overload resolution selects the functions introduced by such using-declarations, the function call is ill-formed. [Example:...



1475. Errors in [[carries_dependency]] example

Section: 9.12.4  [dcl.attr.depend]     Status: CD3     Submitter: Stephan Lavavej     Date: 2012-03-06

[Moved to DR at the April, 2013 meeting.]

The example in 9.12.4 [dcl.attr.depend] paragraph 4 reads,

  /* 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);
  }

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

  /* Translation unit B. */

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

  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));
  }

There appear to be two errors in this example. First, g is declared as returning int in TU A but int* in TU B. Second, paragraph 6 says,

Function g's second argument has a carries_dependency attribute

but that is not reflected in the example.

Proposed resolution (August, 2012):

  1. Change the example in 9.12.4 [dcl.attr.depend] paragraph 4 as follows:

  2.   /* 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);
      }
    
      [[carries_dependency]] 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);
      [[carries_dependency]] int* 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));
      }
    
  3. Change 9.12.4 [dcl.attr.depend] paragraph 6 as follows:

  4. Function g's second argument parameter has a carries_dependency attribute, but its first argument 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.



1318. Syntactic ambiguities with final

Section: Clause 11  [class]     Status: CD3     Submitter: Johannes Schaub     Date: 2011-05-14

[Moved to DR at the April, 2013 meeting.]

The ambiguity in an example like

  struct A final { };

is resolved by 5.10 [lex.name] paragraph 2 to be the declaration of a variable named final:

any ambiguity as to whether a given identifier has a special meaning is resolved to interpret the token as a regular identifier.

Similarly, in an example like

  struct C { constexpr operator int() { return 5; } };
  struct A {
   struct B final : C{};
  };

final is taken as the name of a bit-field member rather than as the name of a nested class.

Proposed resolution (October, 2012):

  1. Change 5.10 [lex.name] paragraph 2 as follows:

  2. The identifiers in Table 3 have a special meaning when appearing in a certain context. When referred to in the grammar, these identifiers are used explicitly rather than using the identifier grammar production. Unless otherwise specified, any ambiguity as to whether a given identifier has a special meaning is resolved to interpret the token as a regular identifier.
  3. Change Clause 11 [class] paragraph 3 as follows:

  4. If a class is marked with the class-virt-specifier final and it appears as a base-type-specifier in a base-clause (11.7 [class.derived]), the program is ill-formed. Whenever a class-key is followed by a class-head-name, the identifier final, and a colon or left brace, final is interpreted as a class-virt-specifier. [Example:

      struct A;
      struct A final {};      // OK: definition of struct A,
                              // not value-initialization of variable final
    
      struct X {
       struct C { constexpr operator int() { return 5; } };
       struct B final : C{};  // OK: definition of nested class B,
                              // not declaration of a bit-field member final
      };
    

    end example]




1363. Triviality vs multiple default constructors

Section: Clause 11  [class]     Status: CD3     Submitter: Sean Hunt     Date: 2011-08-16

[Moved to DR at the October, 2012 meeting.]

The requirements for a trivial class include having “a trivial default constructor” (Clause 11 [class] paragraph 6). However, with an explicitly-defaulted default constructor and other constructors with default arguments, it is possible to have multiple default constructors. Such a class cannot be default-initialized and thus should probably be considered non-trivial.

Proposed resolution (February, 2012):

Change Clause 11 [class] paragraph 6 as follows:

...A trivial class is a class that has a trivial default constructor (11.4.5 [class.ctor]), has no non-trivial default constructors, and is trivially copyable...



1411. More on global scope :: in nested-name-specifier

Section: Clause 11  [class]     Status: CD3     Submitter: David Blaikie     Date: 2011-10-28

[Moved to DR at the April, 2013 meeting.]

The resolution of issue 355 failed to update the grammar for class-head-name in Clause 11 [class] paragraph 1 and removed the optional :: from class-or-decltype in 11.7 [class.derived] paragraph 1, with the result that it is still not possible to globally-qualify a class name in a class definition, and the ability to globally-qualify a base class name has been lost.

Proposed resolution (February, 2012):

Change the grammar in _N4567_.5.1.1 [expr.prim.general] paragraph 8 as follows:




1308. Completeness of class type within an exception-specification

Section: 11.4  [class.mem]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-05-03

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

According to 11.4 [class.mem] paragraph 2,

A class is considered a completely-defined object type (6.8 [basic.types]) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, exception-specifications, and brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.

With the advent of the noexcept operator, treating the class type as complete in exception-specifications is obviously not possible, e.g.,

  struct X {
    // should X be considered as complete here?
    static void create() noexcept(noexcept(X()));
    X() noexcept(!noexcept(X::create()));
  };

Proposed resolution (August, 2011):

  1. Change 11.4 [class.mem] paragraph 2 as follows:

  2. A class is considered a completely-defined object type (6.8 [basic.types]) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, exception-specifications, and brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.
  3. Change 14.5 [except.spec] paragraph 2 as follows:

  4. ...A type denoted in an exception-specification shall not denote an incomplete type other than a class currently being defined. A type denoted in an exception-specification shall not denote a pointer or reference to an incomplete type, other than cv void*, const void*, volatile void*, or const volatile void* or a pointer or reference to a class currently being defined. A type cv T, “array of T”, or “function returning T” denoted in an exception-specification is adjusted to type T, “pointer to T”, or “pointer to function returning T”, respectively.

Note:

This change was subsequently removed by the resolution of issue 1330.




1357. brace-or-equal-initializers for function and typedef members

Section: 11.4  [class.mem]     Status: CD3     Submitter: Richard Smith     Date: 2011-08-16

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The grammar allows a brace-or-equal-initializer for any class member with a member-declarator, including typedef members and member function declarations, and there is no semantic restriction forbidding those forms, either.

Proposed resolution (August, 2011):

In 11.4 [class.mem], delete paragraph 4 and change paragraph 5 as follows:

A member can be initialized using a constructor; see 11.4.5 [class.ctor]. [Note: See 11.4.4 [special] for a description of constructors and other special member functions. —end note]

A member can be initialized using a brace-or-equal-initializer shall appear only in the declaration of a data member. (For static data members, see 11.4.9.3 [class.static.data]; for non-static data members, see 11.9.3 [class.base.init]).




1425. Base-class subobjects of standard-layout structs

Section: 11.4  [class.mem]     Status: CD3     Submitter: Ville Voutilainen     Date: 2011-12-07

[Moved to DR at the April, 2013 meeting.]

According to 11.4 [class.mem] paragraph 20,

A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa.

Given a standard-layout struct in which the non-static data members are in a base class (and hence the derived class is empty), it is not clear what the “initial member” is. Presumably the intent behind allowing such standard-layout classes was to treat the base class object and its first non-static data member as the initial member of the derived class, but this does not appear to be spelled out anywhere.

Proposed resolution (February, 2012):

Change 11.4 [class.mem] paragraph 20 as follows:

A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member. Otherwise, its address is the same as the address of its first base class subobject (if any). [Note:...



535. Copy construction without a copy constructor

Section: 11.4.5.3  [class.copy.ctor]     Status: CD3     Submitter: Mike Miller     Date: 7 October 2005

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

Footnote 112 (11.4.5.3 [class.copy.ctor] paragraph 2) says,

Because a template constructor is never a copy constructor, the presence of such a template does not suppress the implicit declaration of a copy constructor. Template constructors participate in overload resolution with other constructors, including copy constructors, and a template constructor may be used to copy an object if it provides a better match than other constructors.

However, many of the stipulations about copy construction are phrased to refer only to “copy constructors.” For example, 11.4.5.3 [class.copy.ctor] paragraph 14 says,

A program is ill-formed if the copy constructor... for an object is implicitly used and the special member function is not accessible (11.8 [class.access]).

Does that mean that using an inaccessible template constructor to copy an object is permissible, because it is not a “copy constructor?” Obviously not, but each use of the term “copy constructor” in the Standard should be examined to determine if it applies strictly to copy constructors or to any constructor used for copying. (A similar issue applies to “copy assignment operators,” which have the same relationship to assignment operator function templates.)

Proposed Resolution (August, 2011):

  1. Change 6.3 [basic.def.odr] paragraph 2 as follows:

  2. ...[Note: This covers calls to named functions (7.6.1.3 [expr.call]), operator overloading (Clause 12 [over]), user-defined conversions (11.4.8.3 [class.conv.fct]), allocation function for placement new (7.6.2.8 [expr.new]), as well as non-default initialization (9.4 [dcl.init]). A copy constructor or move constructor selected to copy or move an object of class type is odr-used even if the call is actually elided by the implementation (11.4.5.3 [class.copy.ctor]). —end note] ... A copy-assignment function for a class An assignment operator function in a class is odr-used by an implicitly-defined copy-assignment or move-assignment function for another class as specified in 11.4.5.3 [class.copy.ctor]. A move-assignment function for a class is odr-used by an implicitly-defined move-assignment function for another class as specified in 11.4.5.3 [class.copy.ctor]. A default constructor...
  3. Delete 11.4.5 [class.ctor] paragraph 9:

  4. A copy constructor (11.4.5.3 [class.copy.ctor]) is used to copy objects of class type. A move constructor (11.4.5.3 [class.copy.ctor]) is used to move the contents of objects of class type.

  5. Change 6.7.7 [class.temporary] paragraph 1 as follows:

  6. ...[Note: even if there is no call to the destructor or copy/move constructor, all the semantic restrictions, such as accessibility (11.8 [class.access]) and whether the function is deleted (9.5.3 [dcl.fct.def.delete]), shall be satisfied. this includes accessibility (11.8 [class.access]) and whether it is deleted, for the constructor selected and for the destructor. However, in the special case of a function call...
  7. Change 11.4.5.3 [class.copy.ctor] paragraph 13 as follows:

  8. A copy/move constructor that is defaulted and not defined as deleted is implicitly defined if it is odr-used (6.3 [basic.def.odr]) to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type [Footnote: See 9.4 [dcl.init] for more details on direct and copy initialization. —end footnote] or when it is explicitly defaulted...

  9. Change 11.4.5.3 [class.copy.ctor] paragraph 31 as follows:
  10. When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor selected for the copy/move operation and/or the destructor for the object have side effects...
  11. Change 12.2.4.2.3 [over.ics.user] paragraph 4 as follows:

  12. A conversion of an expression of class type to the same class type is given Exact Match rank, and a conversion of an expression of class type to a base class of that type is given Conversion rank, in spite of the fact that a copy/move constructor (i.e., a user-defined conversion function) is called for those cases.
  13. Change 14.2 [except.throw] paragraph 3 as follows:

  14. A throw-expression copy-initializes (9.4 [dcl.init]) a temporary object, called the exception object...
  15. Change 14.2 [except.throw] paragraph 5 as follows:

  16. When the thrown object is a class object, the copy/move constructor selected for the copy-initialization and the destructor shall be accessible, even if the copy/move operation is elided (11.4.5.3 [class.copy.ctor]).

[Drafting note: 7.6.19 [expr.ass] paragraph 4, Clause 11 [class] paragraph 4, 11.5 [class.union] paragraph 1, 6.7.7 [class.temporary] paragraph 2, 11.4.5.3 [class.copy.ctor] paragraphs 1-2, and 14.5 [except.spec] paragraph 14 do not require any changes.]




1402. Move functions too often deleted

Section: 11.4.5.3  [class.copy.ctor]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-10-03

[Moved to DR status at the April, 2013 meeting as paper N3667.]

Paragraphs 11 and 23 of 11.4.5.3 [class.copy.ctor] make a defaulted move constructor and assignment operator, respectively, deleted if there is a subobject with no corresponding move function and for which the copy operation is non-trivial. This seems excessive and unnecessary. For example:

    template<typename T>
     struct wrap
     {
      wrap() = default;

    #ifdef USE_DEFAULTED_MOVE
      wrap(wrap&&) = default;
    #else
      wrap(wrap&& w) : t(static_cast<T&&>(w.t)) { }
    #endif

      wrap(const wrap&) = default;

      T t;
     };

    struct S {
      S(){}
      S(const S&){}
      S(S&&){}
    };

    typedef wrap<const S> W;

    W get() { return W(); }  // Error, if USE_DEFAULTED_MOVE is defined, else OK

In this example the defaulted move constructor of wrap is selected by overload resolution, but this move-constructor is deleted, because S has no trivial copy-constructor.

I think that we overshoot here with the delete rules: I see no problem for the defaulted move-constructor in this example. Our triviality-deduction rules already cover this case (11.4.5.3 [class.copy.ctor] paragraph 12: W::W(W&&) is not trivial) and our exception-specification rules (14.5 [except.spec] paragraph 14) already correctly deduce a noexcept(false) specification for W::W(W&&).

It would still be OK to prevent that a move-constructor would be generated for the following example where no user-declared defaulted copy/move members are present:

    template<typename T>
     struct wrap_2
     {
      wrap_2() = default;
      T t;
     };

    typedef wrap_2<const S> W2;

    W2 get() { return W2(); }  // OK, selects copy constructor

if we want. This would mean that we add a new bullet to 11.4.5.3 [class.copy.ctor] paragraph 9 and paragraph 20.

Proposed resolution (February, 2012):

  1. Change 11.4.5.3 [class.copy.ctor] paragraph 9 as follows:

  2. If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if

    [Note:...

  3. Change 11.4.5.3 [class.copy.ctor] paragraph 11 as follows:

  4. An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (9.5.3 [dcl.fct.def.delete]) if X has:

  5. Change 11.4.5.3 [class.copy.ctor] paragraph 20 as follows:

  6. If the definition of a class X does not explicitly declare a move assignment operator, one will be implicitly declared as defaulted if and only if

    Example:...

  7. Change 11.4.5.3 [class.copy.ctor] paragraph 23 as follows:

  8. A defaulted copy/move assignment operator for class X is defined as deleted if X has:

Additional notes (August, 2012):

The proposed resolution was extensively discussed and additional alternatives were suggested. A paper is being produced for the October, 2012 meeting describing the various options, so the issue has been returned to "review" status to wait for the outcome of that deliberation.

See also the discussion of issue 1491 for additional considerations.

Proposed resolution (December, 2012):

  1. Change 11.4.5.3 [class.copy.ctor] paragraph 9 as follows:

  2. If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if

    [Note:...

  3. Change 11.4.5.3 [class.copy.ctor] paragraph 11 as follows:

  4. ...A defaulted copy/move constructor for a class X is defined as deleted (9.5.3 [dcl.fct.def.delete]) if X has:

    A defaulted move constructor that is defined as deleted is ignored by overload resolution (12.2 [over.match]). [Note: A deleted move constructor would otherwise interfere with initialization from an rvalue which can use the copy constructor instead. —end note]

  5. Change 11.4.5.3 [class.copy.ctor] paragraph 20 as follows:

  6. If the definition of a class X does not explicitly declare a move assignment operator, one will be implicitly declared as defaulted if and only if

    [Example:...

  7. Change 11.4.5.3 [class.copy.ctor] paragraph 23 as follows:

  8. A defaulted copy/move assignment operator for class X is defined as deleted if X has:

    A defaulted move assignment operator that is defined as deleted is ignored by overload resolution (12.2 [over.match], 12.3 [over.over]).

This resolution also resolves issue 1491.




1491. Move construction and rvalue reference members

Section: 11.4.5.3  [class.copy.ctor]     Status: CD3     Submitter: Richard Smith     Date: 2012-03-30

According to 11.4.5.3 [class.copy.ctor] paragraph 11, the last bullet, a defaulted move constructor for a class is defined as deleted if

a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.

This means that an example like

  struct S { S(); int &&r; } s{S()};

is ill-formed. This is probably not intended.

(Note that the February, 2012 proposed resolution for issue 1402 also makes this example ill-formed, because the move constructor is not declared and the copy constructor is defined as deleted because of the rvalue-reference member.)

Additional note, February, 2014:

This issue is resolved by the resolution of issue 1402, which removed the problematic sentence.




344. Naming destructors

Section: 11.4.7  [class.dtor]     Status: CD3     Submitter: Jamie Schmeiser     Date: 25 April 2002

Note that destructors suffer from similar problems as those of constructors dealt with in issue 194 and in 263 (constructors as friends). Also, the wording in 11.4.7 [class.dtor], paragraph 1 does not permit a destructor to be defined outside of the memberlist.

Change 11.4.7 [class.dtor], paragraph 1 from

...A special declarator syntax using an optional function-specifier (9.2.3 [dcl.fct.spec]) followed by ~ followed by the destructor's class name followed by an empty parameter list is used to declare the destructor in a class definition. In such a declaration, the ~ followed by the destructor's class name can be enclosed in optional parentheses; such parentheses are ignored....

to

...A special declarator syntax using an optional sequence of function-specifiers (9.2.3 [dcl.fct.spec]), an optional friend keyword, an optional sequence of function-specifiers (9.2.3 [dcl.fct.spec]) followed by an optional :: scope-resolution-operator followed by an optional nested-name-specifier followed by ~ followed by the destructor's class name followed by an empty parameter list is used to declare the destructor. The optional nested-name-specifier shall not be specified in the declaration of a destructor within the member-list of the class of which the destructor is a member. In such a declaration, the optional :: scope-resolution-operator followed by an optional nested-name-specifier followed by ~ followed by the destructor's class name can be enclosed in optional parentheses; such parentheses are ignored....

Proposed resolution:

This issue is resolved by the resolution of issue 1435.




1605. Misleading parenthetical comment for explicit destructor call

Section: 11.4.7  [class.dtor]     Status: CD3     Submitter: Mike Miller     Date: 2013-01-13

[Moved to DR at the April, 2013 meeting.]

According to 11.4.7 [class.dtor] paragraph 13,

The invocation of a destructor is subject to the usual rules for member functions (11.4.2 [class.mfct]), that is, if the object is not of the destructor's class type and not of a class derived from the destructor's class type, the program has undefined behavior (except that invoking delete on a null pointer has no effect).

While true, the final parenthetical comment concerns a delete-expression, not an explicit destructor call. Its presence here could mislead a careless reader to think that invoking a destructor with a null pointer is harmless. It should be deleted.

Proposed resolution (February, 2013):

Change 11.4.7 [class.dtor] paragraph 13 as follows:

In an explicit destructor call, the destructor name appears as a ~ followed by a type-name or decltype-specifier that denotes the destructor's class type. The invocation of a destructor is subject to the usual rules for member functions (11.4.2 [class.mfct]),; that is, if the object is not of the destructor's class type and not of a class derived from the destructor's class type (including when the destructor is invoked via a null pointer value), the program has undefined behavior (except that invoking delete on a null pointer has no effect). [Note: invoking delete on a null pointer does not call the destructor; see 7.6.2.9 [expr.delete]. —end note] [Example:...



1336. Definition of “converting constructor”

Section: 11.4.8.2  [class.conv.ctor]     Status: CD3     Submitter: Niels Dekker     Date: 2011-07-03

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

Although the normative wording of 11.4.8.2 [class.conv.ctor] paragraph 1 defining a converting constructor says

A constructor declared without the function-specifier explicit specifies a conversion from the types of its parameters to the type of its class.

implying that a constructor with multiple parameters can be a converting constructor, it would be helpful if the example contained such a constructor.

Proposed resolution (August, 2011):

  1. Change the example in 11.4.8.2 [class.conv.ctor] paragraph 1 as follows:

  2.   struct X {
          X(int);
          X(const char*, int =0);
          X(int, int);
      };
    
      void f(X arg) {
        X a = 1;          // a = X(1)
        X b = "Jessie";   // b = X("Jessie",0)
        a = 2;            // a = X(2)
        f(3);             // f(X(3))
        f({1, 2});        // f(X(1,2))
      }
    
  3. Change the example in 11.4.8.2 [class.conv.ctor] paragraph 2 as follows:

  4.   struct Z {
        explicit Z();
        explicit Z(int);
        explicit Z(int, int);
      };
    
      Z a;                       // OK: default-initialization performed
      Z a1 = 1;                  // error: no implicit conversion
      Z a3 = Z(1);               // OK: direct initialization syntax used
      Z a2(1);                   // OK: direct initialization syntax used
      Z* p = new Z(1);           // OK: direct initialization syntax used
      Z a4 = (Z)1;               // OK: explicit cast used
      Z a5 = static_cast<Z>(1);  // OK: explicit cast used
      Z a6 = { 3, 4 };           // error: no implicit conversion
    



675. Signedness of bit-field with typedef or template parameter type

Section: 11.4.10  [class.bit]     Status: CD3     Submitter: Richard Corden     Date: 11 February, 2008

[Moved to DR at the October, 2012 meeting.]

Is the signedness of x in the following example implementation-defined?

    template <typename T> struct A {
        T x : 7;
    };

    template struct A<long>;

A similar example could be created with a typedef.

Lawrence Crowl: According to 11.4.10 [class.bit] paragraph 3,

It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int or long bit-field is signed or unsigned.

This clause is conspicuously silent on typedefs and template parameters.

Clark Nelson: At least in C, the intention is that the presence or absence of this redundant keyword is supposed to be remembered through typedef declarations. I don't remember discussing it in C++, but I would certainly hope that we don't want to do something different. And presumably, we would want template type parameters to work the same way.

So going back to the original example, in an instantiation of A<long>, the signedness of the bit-field is implementation-defined, but in an instantiation of A<signed long>, the bit-field is definitely signed.

Peter Dimov: How can this work? Aren't A<long> and A<signed long> the same type?

(See also issue 739.)

Proposed resolution (February, 2012):

This issue is resolved by the resolution of issue 739.


739. Signedness of plain bit-fields

Section: 11.4.10  [class.bit]     Status: CD3     Submitter: Mike Miller     Date: 3 November, 2008

[Moved to DR at the October, 2012 meeting.]

11.4.10 [class.bit] paragraph 3 says,

It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int or long bit-field is signed or unsigned.

The implications of this permission for an implementation that chooses to treat plain bit-fields as unsigned are not clear. Does this mean that the type of such a bit-field is adjusted to the unsigned variant or simply that sign-extension is not performed when the value is fetched? C99 is explicit in specifying the former (6.7.2 paragraph 5: “for bit-fields, it is implementation-defined whether the specifier int designates the same type as signed int or the same type as unsigned int”), while C90 takes the latter approach (6.5.2.1: “Whether the high-order bit position of a (possibly qualified) 'plain' int bit-field is treated as a sign bit is implementation-defined”).

(See also issue 675 and issue 741.)

Additional note, May, 2009:

As an example of the implications of this question, consider the following declaration:

    struct S {
      int i: 2;
      signed int si: 2;
      unsigned int ui: 2;
    } s;

Is it implementation-defined which expression, cond?s.i:s.si or cond?s.i:s.ui, is an lvalue (the lvalueness of the result depends on the second and third operands having the same type, per 7.6.16 [expr.cond] paragraph 4)?

Proposed resolution (August, 2011):

Change 11.4.10 [class.bit] paragraph 3 as follows:

A bit-field shall not be a static member. A bit-field shall have integral or enumeration type (6.8.2 [basic.fundamental]). It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int, long, or long long bit-field is signed or unsigned. For a bit-field with a non-dependent type (13.8.3.2 [temp.dep.type]) that is specified to be plain (neither explicitly signed nor unsigned) short, int, long, or long long or a typename-name that is so defined (possibly through multiple levels of typedefs), it is implementation-defined whether the type of the bit-field is the corresponding signed or unsigned type. [Example:

  struct B {
    long x : 3;
    typedef signed int si;
    si y : 1;
    typedef int i;
    i z : 1;
  };

  template<class T>
  struct A {
    T x : 7;
  };

It is implementation-defined whether B::x has type signed long or unsigned long. B::y has type signed int. It is implementation-defined whether B::z has type signed int or unsigned int. A<int>::x and A<signed int>::x designate the same entity of type signed int. A<unsigned int>::x has type unsigned int. —end example]

A bool value...

This resolution also resolves issue 675.

Note, January, 2012:

Additional questions have been raised about the proposed resolution, so the status was returned to "review" to allow further discussion.

Proposed resolution (February, 2012):

  1. Change 11.4.10 [class.bit] paragraph 3 as follows:

  2. A bit-field shall not be a static member. A bit-field shall have integral or enumeration type (6.8.2 [basic.fundamental]). It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int, long, or long long bit-field is signed or unsigned. A bool value can successfully be stored...
  3. Add the following as a new section in C.7.7 [diff.class]:

  4. 11.4.10 [class.bit]

    Change: Bit-fields of type plain int are signed.

    Rationale: Leaving the choice of signedness to implementations could lead to inconsistent definitions of template specializations. For consistency, the implementation freedom was eliminated for non-dependent types, too.

    Effect on original feature: The choice is implementation-defined in C, but not so in C++.

    Difficulty of converting: Syntactic transformation.

    How widely used: Seldom.

This resolution also resolves issue 675.




1375. Reference to anonymous union?

Section: 11.5  [class.union]     Status: CD3     Submitter: Daveed Vandevoorde     Date: 2011-08-16

[Moved to DR at the October, 2012 meeting.]

According to 11.5 [class.union] paragraph 7,

A union for which objects or pointers are declared is not an anonymous union.

This should also apply to references, which are now possible because decltype allows writing an initializer for an unnamed union:

    char buf[100];
    union {
      int i;
    } &r = (decltype(r)) buf;

Proposed resolution (February, 2012):

Change 11.5 [class.union] paragraph 7:

A union for which objects, or pointers, or references are declared is not an anonymous union. [Example:

  void f() {
    union { int aa; char* p; } obj, *ptr = &obj;
    aa = 1;      // error
    ptr->aa = 1; // OK
  }



1250. Cv-qualification of incomplete virtual function return types

Section: 11.7.3  [class.virtual]     Status: CD3     Submitter: Jonathan Wakely     Date: 2011-03-03

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

According to 11.7.3 [class.virtual] paragraph 8,

If the return type of D::f differs from the return type of B::f, the class type in the return type of D::f shall be complete at the point of declaration of D::f or shall be the class type D.

This provision was intended to deal with covariant return types but inadvertently affects types that vary only in cv-qualification:

    struct A;
    struct B {
        virtual const A* f();
    };
    struct D : B {
        A* f();    // ill-formed
    };

Proposed resolution (August, 2011):

Change 11.7.3 [class.virtual] paragraph 8 as follows:

If the class type in the covariant return type of D::f differs from the return type that of B::f, the class type in the return type of D::f shall be complete at the point of declaration of D::f or shall be the class type D. When the overriding function...



1345. Initialization of anonymous union class members

Section: 11.9.3  [class.base.init]     Status: CD3     Submitter: Sean Hunt     Date: 2011-08-16

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

11.9.3 [class.base.init] paragraph 8 appears to indicate that a class member that is an anonymous union is to be default initilized.

Proposed resolution (August, 2011):

Change the indicated bullet of 11.9.3 [class.base.init] paragraph 8 as follows:




1385. Syntactic forms of conversion functions for surrogate call functions

Section: 12.2.2.3  [over.match.oper]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-08-31

[Moved to DR at the October, 2012 meeting.]

In 12.2.2.2.3 [over.call.object] paragraph 2, the non-explicit conversion functions considered for producing surrogate call functions are those of the form

This (presumably inadvertently) excludes conversion functions with a ref-qualifier and/or an exception-specification.

Proposed resolution (February, 2012):

Change 12.2.2.2.3 [over.call.object] paragraph 2 as follows:

In addition, for each non-explicit conversion function declared in T of the form

where cv-qualifier is the same...




1556. Constructors and explicit conversion functions in direct initialization

Section: 12.2.2.5  [over.match.copy]     Status: CD3     Submitter: Edward Catmur     Date: 2012-09-18

[Moved to DR at the April, 2013 meeting.]

Issue 899 added wording to the second bullet of 12.2.2.5 [over.match.copy] paragraph 1 permitting use of explicit conversion functions when calling a copy constructor in a direct-initialization context. Issue 1087 addressed the problem in the earlier resolution that the phrase “copy constructor” did not include move constructors and template constructors. However, the term “copy constructor” implicitly (and correctly) restricted the constructor parameter to be the constructor's class, i.e., the class of the object being directly initialized. The new phrasing, “a constructor that takes a reference to possibly cv-qualified T as its first argument,” incorrectly assumed that T referred to the type of the object being initialized; however, that is not the case, and a converting constructor from T to the type of the object being initialized is inadvertently permitted. The wording needs a further tweak to restore the intended context.

Proposed resolution (October, 2012):

Change the second bullet of 12.2.2.5 [over.match.copy] paragraph 1 as follows:

...Assuming that “cv1 T” is the type of the object being initialized, with T a class type, the candidate functions are selected as follows:




1392. Explicit conversion functions for references and non-references

Section: 12.2.2.7  [over.match.ref]     Status: CD3     Submitter: Jason Merrill     Date: 2011-09-08

[Moved to DR at the October, 2012 meeting.]

In 12.2.2.6 [over.match.conv], dealing with non-reference initialization, direct initialization considers as candidate functions only those that

yield type T or a type that can be converted to type T with a qualification conversion

By contrast, 12.2.2.7 [over.match.ref], dealing with reference binding, requires only that the type returned be reference-compatible with the target, permitting both qualification conversions and derived-to-base conversions. This discrepancy is presumably unintentional.

Proposed resolution (February, 2012):

Change 12.2.2.7 [over.match.ref] paragraph 1 as follows:

...the candidate functions are selected as follows:




1409. What is the second standard conversion sequence of a list-initialization sequence?

Section: 12.2.4.2.6  [over.ics.list]     Status: CD3     Submitter: Sebastian Redl     Date: 2011-10-24

[Moved to DR at the October, 2012 meeting.]

Both paragraphs 3 and 4 (for non-aggregate and aggregate types, respectively) of 12.2.4.2.6 [over.ics.list] say that the implicit conversion sequence is a user-defined conversion sequence, but neither specifies that the second standard conversion sequence is the identity conversion, as is presumably intended. This makes ranking by 12.2.4.3 [over.ics.rank] bullet 3.2 unncessarily unclear.

Proposed resolution (February, 2012):

Change 12.2.4.2.6 [over.ics.list] paragraphs 3-4 as follows:

Otherwise, if the parameter is a non-aggregate class X and overload resolution per 12.2.2.8 [over.match.list] chooses a single best constructor of X to perform the initialization of an object of type X from the argument initializer list, the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion sequence an identity conversion. If multiple constructors are viable but none is better than the others, the implicit conversion sequence is the ambiguous conversion sequence...

Otherwise, if the parameter has an aggregate type which can be initialized from the initializer list according to the rules for aggregate initialization (9.4.2 [dcl.init.aggr]), the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion sequence an identity conversion. [Example:...




1543. Implicit conversion sequence for empty initializer list

Section: 12.2.4.2.6  [over.ics.list]     Status: CD3     Submitter: Steve Adamczyk     Date: 2012-08-21

[Moved to DR at the April, 2013 meeting.]

According to 12.2.4.2.6 [over.ics.list] paragraph 6, when passing an initializer-list argument to a non-class parameter,

if the initializer list has no elements, the implicit conversion sequence is the identity conversion.

However, there is no similar provision for an empty initializer list passed to a specialization of std::initializer_list or an array, as described in paragraph 2:

If the parameter type is std::initializer_list<X> or “array of X134 and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X.

It is not clear what the result should be for a list with no elements. For example, given

  void f(int) { printf("int\n"); }
  void f(std::initializer_list<int>) { printf("init list\n"); }
  int main() {
      f({});
  }

current implementations result in init list being printed, presumably on the basis of the last bullet of 12.2.4.3 [over.ics.rank] paragraph 3:

That would imply that both conversion sequences are the identity conversion, which is reasonable, but it should be stated clearly if that is the intent.

Proposed resolution (October, 2012):

Change 12.2.4.2.6 [over.ics.list] paragraph 2 as follows:

If the parameter type is std::initializer_list<X> or “array of X134 and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X, or if the initializer list has no elements, the identity conversion. This conversion can be a user-defined conversion even in the context of a call to an initializer-list constructor. [Example:

  void f(std::initializer_list<int>);
  f( {} );        // OK: f(initializer_list<int>) identity conversion
  f( {1,2,3} );   // OK: f(initializer_list<int>) identity conversion
  f( {'a','b'} ); // OK: f(initializer_list<int>) integral promotion
  f( {1.0} );     // error: narrowing
  ...



1298. Incorrect example in overload resolution

Section: 12.2.4.3  [over.ics.rank]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-04-15

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The following example appears in 12.2.4.3 [over.ics.rank] paragraph 3:

  template<class T> int f(T&);
  template<class T> int f(T&&);
  void g();
  int i1 = f(g); // calls f(T&)

This is not correct. Because of the special deduction rule for rvalue reference parameters in 13.10.3.2 [temp.deduct.call] paragraph 3 and the reference-collapsing rules of 9.3.4.3 [dcl.ref] paragraph 6, the parameter type for both will be void(&)().

Proposed resolution (August, 2011):

Change the example in 12.2.4.3 [over.ics.rank] paragraph 3 bullet 1 sub-bullet 5 as follows:

  template<class T> int f(T&);
  template<class T> int f(T&&);
  int f(void(&)());     // #1
  int f(void(&&)());    // #2
  void g();
  int i1 = f(g);        // calls f(T&) #1



1374. Qualification conversion vs difference in reference binding

Section: 12.2.4.3  [over.ics.rank]     Status: CD3     Submitter: Michael Wong     Date: 2011-08-15

[Moved to DR at the April, 2013 meeting.]

The rule in 12.2.4.3 [over.ics.rank] paragraph 3 for ranking based on a difference in qualification conversion applies only if they "differ only in their qualification conversion".

It is unclear as to whether the property of being a reference binding is a factor in determining if there is a difference between conversion sequences. Notice that 12.2.4.2.5 [over.ics.ref] maps reference bindings to other forms of implicit conversion sequences, but does not state that the property of being a reference binding is preserved; however, 12.2.4.3 [over.ics.rank] has cases which depend on whether certain standard conversion sequences are reference bindings or not and on the specifics of the bindings.

In the following, picking T2 && would bind an rvalue to an rvalue reference. Picking T1 & would bind an rvalue to an lvalue reference, but the qualification conversion to T1 is "better". Which is better?

    typedef int *      *      *const *const T1;
    typedef int *const *const *const *const T2;
    void foo(T1 &);
    void foo(T2 &&) { }

    int main() {
       foo((int ****)0);
       return 0;
    }

Notes from the February, 2012 meeting:

The CWG agreed that bullets 3 and 4 should be reversed, to check the reference binding first and then for qualification conversion.

Proposed resolution (February, 2012):

Move 12.2.4.3 [over.ics.rank] paragraph 3, first bullet, third sub-bullet, after the current fifth sub-bullet, as follows:

Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of the following rules applies:




1408. What is “the same aggregate initialization?”

Section: 12.2.4.3  [over.ics.rank]     Status: CD3     Submitter: Sebastian Redl     Date: 2011-10-24

[Moved to DR at the October, 2012 meeting.]

Bullet 2 of 12.2.4.3 [over.ics.rank] paragraph 3 reads,

It is not clear what “the same aggregate initialization” means — does this require that the same aggregate type is the target type?

Proposed resolution (February, 2012):

Change 12.2.4.3 [over.ics.rank] bullet 3.2 as follows:




1410. Reference overload tiebreakers should apply to rvalue references

Section: 12.2.4.3  [over.ics.rank]     Status: CD3     Submitter: Michael Wong     Date: 2011-10-26

[Moved to DR at the October, 2012 meeting.]

In bullet 3 of paragraph 4 of 12.2.4.3 [over.ics.rank] are two sub-bullets dealing with overload tiebreakers:

Presumably both of these tiebreakers should apply to rvalue references as well as lvalue references.

Proposed resolution (February, 2012):

Change 12.2.4.3 [over.ics.rank] bullet 4.3 as follows:




1563. List-initialization and overloaded function disambiguation

Section: 12.3  [over.over]     Status: CD3     Submitter: Daniel Krügler     Date: 2012-10-08

[Moved to DR at the April, 2013 meeting.]

Presumably, 12.3 [over.over] paragraph 1,

A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. The function selected is the one whose type is identical to the function type of the target type required in the context. [Note: That is, the class of which the function is a member is ignored when matching a pointer-to-member-function type. —end note] The target can be

should apply to an example like

  double bar(double) { return 0.0; }
  float bar(float) { return 0.0f; }

  using fun = double(double);
  fun &foo{bar};

However, there is implementation variance in whether the use of bar is accepted or not, and the omission of a cross-reference to 9.4.5 [dcl.init.list] might be read as indicating that list-initialization is not included.

Proposed resolution (February, 2013):

Change 12.3 [over.over] paragraph 1 as follows:

...The target can be




1481. Increment/decrement operators with reference parameters

Section: 12.4.7  [over.inc]     Status: CD3     Submitter: Ryou Ezoe     Date: 2012-03-20

[Moved to DR at the April, 2013 meeting.]

The phrase in 12.4.7 [over.inc] paragraph 1,

a non-member function with one parameter of class or enumeration type

inadvertently excludes reference parameters, and in fact, no mention of the type is necessary in light of 12.4 [over.oper] paragraph 6:

An operator function shall either be a non-static member function or be a non-member function and have at least one parameter whose type is a class, a reference to a class, an enumeration, or a reference to an enumeration.

Proposed resolution (August, 2012):

Change 12.4.7 [over.inc] paragraph 1 as follows:

The user-defined function called operator++ implements the prefix and postfix ++ operator. If this function is a member function with no parameters, or a non-member function with one parameter of class or enumeration type, it defines the prefix increment operator ++ for objects of that type. If the function...



1473. Syntax of literal-operator-id

Section: 12.6  [over.literal]     Status: CD3     Submitter: Richard Smith     Date: 2012-03-05

[Moved to DR at the April, 2013 meeting.]

The current grammar requires that there be no whitespace between the literal and the ud-suffix, e.g., ""_abc, but that there be whitespace between the "" and the identifier in a literal-operator-id, e.g., operator "" _abc. This seems unfortunate. Would it be possible to provide an alternate production,

with the requirement that the string-literal be empty?

The current wording is also unclear regarding interactions with phases of translation. We have the following production:

As this is after translation phase 6, would something like

  operator "" "" _foo

be accepted?

Proposed resolution (October, 2012):

  1. Change 12.6 [over.literal] as follows:

  2. Change 12.6 [over.literal] paragraph 1 as follows:

  3. The string-literal or user-defined-string-literal in a literal-operator-id shall have no encoding-prefix and shall contain no characters other than the implicit terminating '\0'. The ud-suffix of the user-defined-string-literal or the identifier in a literal-operator-id is called a literal suffix identifier. [Note: some literal suffix identifiers are reserved for future standardization; see 16.4.5.3.6 [usrlit.suffix]. —end note]
  4. Change the example in 12.6 [over.literal] paragraph 8 as follows:

  5.   void operator "" _km(long double);                  // OK
      string operator "" _i18n(const char*, std::size_t); // OK
      template <char...> int operator "" \u03C0();        // OK: UCN for lowercase pi
      float operator ""E(const char*);                    // error: ""E (with no intervening space)
                                                          // is a single token OK
      float operator " " B(const char*);                  // error: non-adjacent quotesempty string-literal
      string operator "" 5X(const char*, std::size_t);    // error: invalid literal suffix identifier
      double operator "" _miles(double);                  // error: invalid parameter-declaration-clause
      template <char...> int operator "" _j(const char*); // error: invalid parameter-declaration-clause
    



1479. Literal operators and default arguments

Section: 12.6  [over.literal]     Status: CD3     Submitter: Jason Merrill     Date: 2012-03-13

[Moved to DR at the April, 2013 meeting.]

It appears that an example like

  int operator"" _a (const char *, std::size_t = 0);
  int operator"" _a (const char *);

  int i = 123_a;

is ambiguous: although only the second declaration is a raw literal operator, the corresponding call

  operator"" _a ("123")

could match either declaration. Should default arguments for literal operators be prohibited?

Proposed resolution (October, 2012):

Change 12.6 [over.literal] paragraph 3 as follows:

The declaration of a literal operator shall have a parameter-declaration-clause equivalent to one of the following:

...

If a parameter has a default argument (9.3.4.7 [dcl.fct.default]), the program is ill-formed.




1275. Incorrect comment in example of template parameter pack restriction

Section: 13.2  [temp.param]     Status: CD3     Submitter: Johannes Schaub     Date: 2011-03-25

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

The following example from 13.2 [temp.param] paragraph 11 is incorrect:

  // U cannot be deduced or specified
  template<class... T, class... U> void f() { }
  template<class... T, class U> void g() { }

In fact, U can be deduced to an empty sequence by 13.10.2 [temp.arg.explicit] paragraph 3:

A trailing template parameter pack (13.7.4 [temp.variadic]) not otherwise deduced will be deduced to an empty sequence of template arguments.

Proposed resolution (August, 2011):

Change 13.2 [temp.param] paragraph 11 as follows:

...A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template or has a default argument (13.10.3 [temp.deduct]). [Example:

  template<class T1 = int, class T2> class B;   // error

  // U cannot be deduced from the parameter-type-list or specified
  template<class... T, class... U> void f() { } // error
  template<class... T, class U> void g() { }    // error

end example]




1398. Non-type template parameters of type std::nullptr_t

Section: 13.4.3  [temp.arg.nontype]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-09-27

[Moved to DR at the October, 2012 meeting.]

Although 13.2 [temp.param] paragraph 4 explicitly allows non-type template parameters of type std::nullptr_t, they cannot actually be used: 13.4.3 [temp.arg.nontype] paragraph 1 does not allow a template-argument of type std::nullptr_t, and paragraph 5 does not describe any conversions from other types to std::nullptr_t.

Proposed resolution (February, 2012):

Add the following new bullet in 13.4.3 [temp.arg.nontype] paragraph 1:




1533. Function pack expansion for member initialization

Section: 13.7.4  [temp.variadic]     Status: CD3     Submitter: Jonathan Caves     Date: 2012-08-06

[Moved to DR at the April, 2013 meeting.]

The list of pack expansion contexts in 13.7.4 [temp.variadic] paragraph 4 includes a mem-initializer-list, with no restriction on whether the mem-initializer corresponds to a base class or a member. However, it appears from 11.9.3 [class.base.init] paragraph 15 that such a pack expansion is intended for bases:

A mem-initializer followed by an ellipsis is a pack expansion (13.7.4 [temp.variadic]) that initializes the base classes specified by a pack expansion in the base-specifier-list for the class.

This is not conclusive, however, and use of a pack expansion with a mem-initializer for a member could be used with packs containing zero or one element:

  class S { };

  template<typename... T> class X {
  public:
    X(T ...args) : data_(args)... { }
  private:
    S data_;
  };

  int main() {
    S s;
    X<> x1;
    X<S> x2(s);
  }

The Standard should be clarified as to whether such a usage is permitted or not.

Proposed resolution (October, 2012):

Change 13.7.4 [temp.variadic] paragraph 4 as follows:

...Pack expansions can occur in the following contexts:




1495. Partial specialization of variadic class template

Section: 13.7.6  [temp.spec.partial]     Status: CD3     Submitter: Jason Merrill     Date: 2012-04-16

[Moved to DR at the April, 2013 meeting.]

Consider an example like

  template <int B, typename Type1, typename... Types>
  struct A;

  template<typename... Types>
  struct A<0, Types...> { };

  A<0,int,int> t;

In this case, the partial specialization seems well-formed by the rules in 13.7.6 [temp.spec.partial], but it is not more specialized than the primary template. However, 13.7.6.2 [temp.spec.partial.match] says that if exactly one matching specialization is found, it is used, which suggests that the testcase is well-formed. That seems undesirable; I think a partial specialization that is not more specialized than the primary template should be ill-formed.

If the example is rewritten so that both versions are partial specializations, i.e.,

  template <int B, typename... Types>
  struct A;

  template <int B, typename Type1, typename... Types>
  struct A<B, Type1, Types...> { }

  template<typename... Types>
  struct A<0, Types...> { };

  A<0,int,int> t;

There is implementation variance, with gcc and clang reporting an ambiguity and EDG choosing the second specialization.

Proposed resolution (October, 2012):

Add the following as a new bullet in 13.7.6.1 [temp.spec.partial.general] paragraph 9:




2035. Multi-section example is confusing

Section: 13.7.6.2  [temp.spec.partial.match]     Status: CD3     Submitter: CWG     Date: 2014-11-06

The example in 13.7.6.2 [temp.spec.partial.match] paragraph 2 reads,

  A<int, int, 1>   a1;   // uses #1
  A<int, int*, 1>  a2;   // uses #2, T is int, I is 1
  A<int, char*, 5> a3;   // uses #4, T is char
  A<int, char*, 1> a4;   // uses #5, T1 is int, T2 is char, I is 1
  A<int*, int*, 2> a5;   // ambiguous: matches #3 and #5

However, the referenced numbers are defined two pages earlier, in 13.7.6.1 [temp.spec.partial.general] paragraph 3. This is confusing and should be changed.

Notes from the May, 2015 meeting:

This will be handled editorially; the status has been set to "review" to check that the editorial change has been made.




1321. Equivalency of dependent calls

Section: 13.7.7.2  [temp.over.link]     Status: CD3     Submitter: Jason Merrill     Date: 2011-05-18

[Moved to DR at the October, 2012 meeting.]

Consider the following example:

  int g(int);

  template <class T> decltype(g(T())) f();

  int g();

  template <class T> decltype(g(T())) f() { return g(T()); }

  int i = f<int>();

Do the two fs declare the same function template? According to 13.7.7.2 [temp.over.link] paragraph 5,

Two expressions involving template parameters are considered equivalent if two function definitions containing the expressions would satisfy the one definition rule (6.3 [basic.def.odr]), except that the tokens used to name the template parameters may differ as long as a token used to name a template parameter in one expression is replaced by another token that names the same template parameter in the other expression.

The relevant portion of 6.3 [basic.def.odr] paragraph 5 says,

in each definition of D, corresponding names, looked up according to 6.5 [basic.lookup], shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (12.2 [over.match]) and after matching of partial template specialization (13.10.4 [temp.over]), except that a name can refer to a const object with internal or no linkage if the object has the same literal type in all definitions of D, and the object is initialized with a constant expression (7.7 [expr.const]), and the value (but not the address) of the object is used, and the object has the same value in all definitions of D

This could be read either way, since overload resolution isn't done at this point. Either we consider the result of the unqualified name lookup and say that the expressions aren't equivalent or we need a new rule for equivalence and merging of dependent calls.

Proposed resolution (December, 2011):

  1. Change 13.7.7.2 [temp.over.link] paragraph 5 as follows:

  2. Two expressions involving template parameters are considered equivalent if two function definitions containing the expressions would satisfy the one definition rule (6.3 [basic.def.odr]), except that the tokens used to name the template parameters may differ as long as a token used to name a template parameter in one expression is replaced by another token that names the same template parameter in the other expression. For determining whether two dependent names (13.8.3 [temp.dep]) are equivalent, only the name itself is considered, not the result of name lookup in the context of the template. If multiple declarations of the same function template differ in the result of this name lookup, the result for the first declaration is used. [Example:

      template <int I, int J> void f(A<I+J>); // #1
      template <int K, int L> void f(A<K+L>); // same as #1
    
      template <class T> decltype(g(T())) h();
      int g(int);
      template <class T> decltype(g(T())) h() // redeclaration of h() uses the earlier lookup
        { return g(T()); }                    // ...although the lookup here does find g(int)
      int i = h<int>();                       // template argument substitution fails; g(int)
                                              // was not in scope at the first declaration of h()
    

    end example] Two expressions...

  3. Change 13.8.3 [temp.dep] paragraph 1 as follows:

  4. ...In an expression of the form:

    where the postfix-expression is an id-expression unqualified-id, the id-expression unqualified-id denotes a dependent name if

    if an operand...

  5. Change 13.8.4.2 [temp.dep.candidate] paragraph 1 as follows:

  6. For a function call that depends on a template parameter where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules (6.5.3 [basic.lookup.unqual], 6.5.4 [basic.lookup.argdep], 6.5.5 [basic.lookup.qual]) except that:

    If the function name is an unqualified-id and the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior.




1406. ref-qualifiers and added parameters of non-static member function templates

Section: 13.7.7.3  [temp.func.order]     Status: CD3     Submitter: Richard Smith     Date: 2011-10-21

[Moved to DR at the October, 2012 meeting.]

In describing the partial ordering of function templates, 13.7.7.3 [temp.func.order] paragraph 3 says,

If only one of the function templates is a non-static member, that function template is considered to have a new first parameter inserted in its function parameter list. The new parameter is of type “reference to cv A,” where cv are the cv-qualifiers of the function template (if any) and A is the class of which the function template is a member. [Note: This allows a non-static member to be ordered with respect to a nonmember function and for the results to be equivalent to the ordering of two equivalent nonmembers. —end note]

The Standard appears to be silent as to whether the reference is an lvalue or rvalue reference; presumably that should be determined by the ref-qualifier of the member function, if any.

Proposed resolution (February, 2012):

Change 13.7.7.3 [temp.func.order] paragraph 3 as follows:

To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs (13.7.4 [temp.variadic]) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template. If only one of the function templates is a non-static member of some class A, that function template is considered to have a new first parameter inserted in its function parameter list. The Given cv as the cv-qualifiers of the function template (if any), the new parameter is of type “rvalue reference to cv A,if the optional ref-qualifier of the function template is &&, or of type “lvalue reference to cv A” otherwise where cv are the cv-qualifiers of the function template (if any) and A is the class of which the function template is a member. [Note:...



1296. Ill-formed template declarations (not just definitions)

Section: 13.8  [temp.res]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-04-14

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

According to 13.8 [temp.res] paragraph 8,

Knowing which names are type names allows the syntax of every template definition to be checked. No diagnostic shall be issued for a template definition for which a valid specialization can be generated. If no valid specialization can be generated for a template definition, and that template is not instantiated, the template definition is ill-formed, no diagnostic required. If every valid specialization of a variadic template requires an empty template parameter pack, the template definition is ill-formed, no diagnostic required. If a type used in a non-dependent name...

It seems that these points could and should apply to template declarations that are not definitions, as well.

Proposed resolution (August, 2011):

Change 13.8 [temp.res] paragraph 8 as follows:

Knowing which names are type names allows the syntax of every template definition to be checked. No diagnostic shall be issued for a template definition for which a valid specialization can be generated. If no valid specialization can be generated for a template definition, and that template is not instantiated, the template definition is ill-formed, no diagnostic required. If every valid specialization of a variadic template requires an empty template parameter pack, the template definition is ill-formed, no diagnostic required. If a type used...



1471. Nested type of non-dependent base

Section: 13.8.3.2  [temp.dep.type]     Status: CD3     Submitter: Johannes Schaub     Date: 2012-02-26

[Moved to DR at the April, 2013 meeting.]

Even though A::C is a nested type and member of the current instantiation, and thus dependent by the rules of 13.8.3.2 [temp.dep.type] paragraph 8, there does not seem to be a good reason for making it so:

  struct B {
   struct C { };
  };

  template<typename T> struct A : B {
   A::C c;
  };

Proposed resolution (October, 2012):

[Some existing uses of the term “member of the current instantiation” are consistent with the definition in 13.8.3.2 [temp.dep.type] paragraph 4; others are intended to refer to members of the “current instantiation,” as defined in paragraph 1. The following resolution changes the latter to use the phrase “member of a class that is the current instantiation.”]

  1. Change 13.8.3.2 [temp.dep.type] paragraph 4 as follows:

  2. A name is a member of the current instantiation if it is

    [Example: ... —end example]

    A name is a dependent member of the current instantiation if it is a member of the current instantiation which, when looked up, refers to at least one member of a class that is the current instantiation.

  3. Change 13.8.3.2 [temp.dep.type] paragraph 5 as follows:

  4. A name is a member of an unknown specialization if it is

  5. Change 13.8.3.2 [temp.dep.type] paragraph 8 as follows:

  6. A type is dependent if it is

  7. Change 13.8.3.3 [temp.dep.expr] paragraph 3 as follows:

  8. An id-expression is type-dependent if it contains

    or if it names a static data dependent member of the current instantiation that has is a static data member of type “array of unknown bound of T” for some T (13.7.2.5 [temp.static]). Expressions of the following forms...

  9. Change 13.8.3.4 [temp.dep.constexpr] paragraph 2 as follows (assumes the base text is as modified by issue 1413):

  10. An id-expression is value-dependent if:

    Expressions of the following form...

  11. Change 13.8.3.4 [temp.dep.constexpr] paragraph 5 as follows (assumes the base text is as modified by issue 1413):

  12. An expression of the form &qualified-id where the qualified-id's nested-name-specifier names a dependent member of the current instantiation is value-dependent.



903. Value-dependent integral null pointer constants

Section: 13.8.3.4  [temp.dep.constexpr]     Status: CD3     Submitter: Doug Gregor     Date: 22 May, 2009

[Moved to DR status at the April, 2013 meeting.]

Consider the following example:

    void f(int*);
    void f(...);

    template <int N> void g() {
      f(N);
    }

    int main() {
      g<0>();
      g<1>();
    }

The call to f in g is not type-dependent, so the overload resolution must be done at definition time rather than at instantiation time. As a result, both of the calls to g will result in calls to f(...), i.e., N will not be a null pointer constant, even if the value of N is 0.

It would be most consistent to adopt a rule that a value-dependent expression can never be a null pointer constant, even in cases like

    template <int N> void g() {
      int* p = N;
    }

This would always be ill-formed, even when N is 0.

John Spicer: It's clear that this treatment is required for overload resolution, but it seems too expansive given that there are other cases in which the value of a template parameter can affect the validity of the program, and an implementation is forbidden to issue a diagnostic on a template definition unless there are no possible valid specializations.

Notes from the July, 2009 meeting:

There was a strong consensus among the CWG that only the literal 0 should be considered a null pointer constant, not any arbitrary zero-valued constant expression as is currently specified.

Proposed resolution (October, 2012):

  1. Change 7.3.12 [conv.ptr] paragraph 1 as follows:

  2. A null pointer constant is an integral constant expression (7.7 [expr.const]) prvalue of integer type that evaluates to integer literal (5.13.2 [lex.icon]) with value zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted...
  3. Change 7.7 [expr.const] paragraph 3 as follows:

  4. ...[Note: Such expressions may be used as array bounds (9.3.4.5 [dcl.array], 7.6.2.8 [expr.new]), as bit-field lengths (11.4.10 [class.bit]), as enumerator initializers if the underlying type is not fixed (9.7.1 [dcl.enum]), as null pointer constants (7.3.12 [conv.ptr]), and as alignments (9.12.2 [dcl.align]). —end note]...
  5. Change 9.4 [dcl.init] paragraph 5 as follows:

  6. To zero-initialize an object or reference of type T means:

  7. Change 13.4.3 [temp.arg.nontype] paragraph 5 as follows:

  8. Change 14.4 [except.handle] paragraph 3 as follows:

  9. ...[Note: A throw-expression whose operand is an integral constant expression of integer type that evaluates to integer literal with value zero does not match a handler of pointer or pointer to member type. —end note]. [Example: ...
  10. Add a new section to C.6 [diff.cpp03] as follows:

  11. C.2.x Clause 4: standard conversions [diff.cpp03.conv]

    7.3.12 [conv.ptr]
    Change: Only literals are integer null pointer constants
    Rationale: Removing surprising interactions with templates and constant expressions
    Effect on original feature: Valid C++ 2003 code may fail to compile or produce different results in this International Standard, as the following example illustrates:

      void f(void *);  // #1
      void f(...);     // #2
      template<int N> void g() {
          f(0*N);      // calls #2; used to call #1
      }
    

Additional note (January, 2013):

Concerns were raised at the Portland (October, 2012) meeting that the value false has been used in existing code as a null pointer constant, and such code would be broken by this change. This issue has been returned to "review" status to allow discussion of whether to accommodate such code or not.




1413. Missing cases of value-dependency

Section: 13.8.3.4  [temp.dep.constexpr]     Status: CD3     Submitter: Richard Smith     Date: 2011-11-09

[Moved to DR at the April, 2013 meeting.]

The list of cases in 13.8.3.4 [temp.dep.constexpr] paragraph 2 in which an identifier is value-dependent should also include:

Proposed resolution (October, 2012):

  1. Change 13.8.3.4 [temp.dep.constexpr] paragraph 2 as follows and move the text following the bulleted list into a new paragraph:

  2. An identifier id-expression is value-dependent if it is:

    Expressions of the following form...

  3. Change 13.8.3.4 [temp.dep.constexpr] paragraph 5 as follows:

  4. An id-expression is value-dependent if it names a member of an unknown specialization. An expression of the form &qualified-id where the qualified-id's nested-name-specifier names the current instantiation is value-dependent.



1532. Explicit instantiation and member templates

Section: 13.9.3  [temp.explicit]     Status: CD3     Submitter: Johannes Schaub     Date: 2012-08-04

[Moved to DR at the April, 2013 meeting.]

According to 13.9.3 [temp.explicit] paragraph 8,

An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its members (not including members inherited from base classes) that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, except as described below.

This could be read as an indication that member class templates and member function templates are instantiated (as templates) when their containing class template is instantiated. For example,

  template<typename T> struct A {
    template<typename U> void f() {
      T t;
      t.f();
    }
  };

  template struct A<int>;

In this view, the result would be a member function template definition in class A<int> equivalent to

  template<typename U> void f() {
    int t;
    t.f();
  }

Such a template could never be validly instantiated and thus would presumably fall under the rule in 13.8 [temp.res] paragraph 8,

If no valid specialization can be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic required.

The wording of 13.9.3 [temp.explicit] paragraph 1 appears not to allow member templates to be instantiated as templates, however, mentioning only a “member template specialization” as a possibility:

A class, a function or member template specialization can be explicitly instantiated from its template. A member function, member class or static data member of a class template can be explicitly instantiated from the member definition associated with its class template.

This appears to be a contradiction, and although a diagnostic for a member template such as the example above would be helpful, most or all current implementations do not do so. Either the wording of paragraph 1 should be changed to allow explicit instantiation of a member template as a template, analogous to explicitly specializing a member template as a template, or paragraph 8 should be clarified to exclude member templates from the members explicitly instantiated when the containing class template is explicitly instantiated.

Proposed resolution (October, 2012):

Change 13.9.3 [temp.explicit] paragraph 8 as follows:

An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its members (not including members inherited from base classes and members that are templates) that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, except as described below. [Note: In addition, it will typically be an explicit instantiation of certain implementation-dependent data about the class. —end note]



1227. Mixing immediate and non-immediate contexts in deduction failure

Section: 13.10.3  [temp.deduct]     Status: CD3     Submitter: Daniel Krügler     Date: 2010-11-27

[Moved to DR at the October, 2012 meeting.]

Consider the following example:

    template <int> struct X {
      typedef int type;
    };
    template <class T> struct Y { };
    template <class T> struct Z {
      static int const value = Y<T>::value;
    };

    template <class T> typename X<Y<T>::value + Z<T>::value>::type f(T);
    int f(...);

    int main() {
      sizeof f(0);
    }

The problem here is that there is a combination of an invalid expression in the immediate context (Y<T>::value) and in the non-immediate context (within Z<T> when evaluating Z<T>::value). The Standard does not appear to state clearly whether this program is well-formed (because the error in the immediate context causes deduction failure) or ill-formed (because of the error in the non-immediate context).

Notes from the March, 2011 meeting:

Some members expressed a desire to allow implementations latitude in whether examples like this should be deduction failure or a diagnosable error, just as the order of evaluation of arithmetic operands is largely unconstrained. Others felt that specifying something like a depth-first left-to-right traversal of the expression or declaration would be better. Another possibility suggested was to enforce ordering only at comma operators. No consensus was achieved.

CWG agreed that the arguments should be processed in left-to-right order. Some popular existing code (e.g., Boost) depends on this ordering.

Proposed resolution (February, 2012):

Change 13.10.3 [temp.deduct] paragraph 7 as follows:

The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside sizeof, decltype, and other contexts that allow non-constant expressions. The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered. [Note: The equivalent substitution in exception specifications is done only when the function is instantiated, at which point a program is ill-formed if the substitution results in an invalid type or expression. —end note] [Example:

  template <class T> struct A { using X = typename T::X; };
  template <class T> typename T::X f(typename A<T>::X);
  template <class T> void f(...) { }
  template <class T> auto g(typename A<T>::X) -> typename T::X;
  template <class T> void g(...) { }

  void h() {
    f<int>(0); // OK, substituting return type causes deduction to fail
    g<int>(0); // error, substituting parameter type instantiates A<int>
  }

end example]




1262. Default template arguments and deduction failure

Section: 13.10.3  [temp.deduct]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-03-16

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

Proposed resolution (August, 2011):

Change 13.10.3 [temp.deduct] paragraph 5 as follows:

The resulting substituted and adjusted function type is used as the type of the function template for template argument deduction. If a template argument has not been deduced, its default template argument, if any, is used and its corresponding template parameter has a default argument, the template argument is determined by substituting the template arguments determined for preceding template parameters into the default argument. If the substitution results in an invalid type, as described above, type deduction fails. [Example:...



1330. Delayed instantiation of noexcept specifiers

Section: 13.10.3  [temp.deduct]     Status: CD3     Submitter: Jason Merrill     Date: 2011-06-05

[Moved to DR at the April, 2013 meeting.]

See also issues 595 and 287.

The use of noexcept specifiers can cause instantiation of classes and functions that are not actually needed in the program, just to be able to complete the declaration. The actual value of the expression in the noexcept-specification is only needed if the function is defined (i.e., instantiated) or called, so it would significantly reduce the number of instantiations (and avoid certain kinds of errors when the value is currently required before a class is complete) if exception-specifications were treated like default arguments and only instantiated when they are actually needed.

Proposed resolution (February, 2012):

  1. Change 13.7 [temp.decls] paragraph 2 as follows:

  2. For purposes of name lookup and instantiation, default arguments and exception-specifications of function templates and default arguments and exception-specifications of member functions of class templates are considered definitions; each default argument or exception-specification is a separate definition which is unrelated to the function template definition or to any other default arguments or exception-specifications.
  3. Change 13.8 [temp.res] paragraph 11 as follows:

  4. [Note: For purposes of name lookup, default arguments and exception-specifications of function templates and fdefault arguments and exception-specifications of member functions of class templates are considered definitions (14.5). —end note]
  5. Add a new paragraph following 13.8.4.1 [temp.point] paragraph 2:

  6. If a function template or member function of a class template is called in a way which uses the definition of a default argument of that function template or member function, the point of instantiation of the default argument is the point of instantiation of the function template or member function specialization.

    For an exception-specification of a function template specialization or specialization of a member function of a class template, if the exception-specification is implicitly instantiated because it is needed by another template specialization and the context that requires it depends on a template parameter, the point of instantiation of the exception-specification is the point of instantiation of the specialization that requires it. Otherwise, the point of instantiation for such an exception-specification immediately follows the namespace scope declaration or definition that requires the exception-specification.

  7. Change 13.9.2 [temp.inst] paragraph 1 as follows:

  8. ...The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, or default arguments, or exception-specifications, of the class member functions, member classes...
  9. Add a new paragraph following 13.9.2 [temp.inst] paragraph 13:

  10. Each default argument is instantiated independently. [Example: ... —end example]

    If the exception-specification of a specialization of a function template or member function of a class template has not yet been instantiated, but is needed (e.g., to instantiate the function definition, to evaluate a noexcept-expression (7.6.2.7 [expr.unary.noexcept]), or to compare against the exception-specification of another declaration), the dependent names are looked up, the semantic constraints are checked, and the instantiation of any template used in the exception-specification is done as if it were being done as part of instantiating the declaration of the specialization. An exception-specification is not instantiated in order to calculate the exception-specification of a defaulted function in a derived class until the exception-specification of the derived member function becomes necessary.

  11. Change the note 13.10.3 [temp.deduct] paragraph 7 as follows:

  12. ...[Note: The equivalent substitution in exception specifications is done only when the function exception-specification is instantiated, at which point a program is ill-formed if the substitution results in an invalid type or expression. —end note]
  13. Add a new paragraph following 14.5 [except.spec] paragraph 15:

  14. A deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation]) with no explicit exception-specification is treated as if it were specified with noexcept(true).

    The exception-specification of a function template specialization is not instantiated along with the function declaration; it is instantiated when needed (13.9.2 [temp.inst]). The exception-specification of an implicitly-declared special member function is also evaluated as needed. [Note: Therefore, an implicit declaration of a member function of a derived class does not require the exception-specification of a base member function to be instantiated. —end note]

Notes from the February, 2012 meeting:

There should be a specific definition of when an exception-specification is needed and must thus be instantiated.

Additional discussion (September, 2012):

Daveed Vandevoorde brought up two additional points. First, the rule should be crafted so that an example like the following is ill-formed:

  template<class T> T f() noexcept(sizeof(T) < 4);

  int main() {
    decltype(f<void>()) *p;
  }

Even though the exception-specification is not needed here, it should be instantiated (because of the unevaluated reference to f) in order to catch the ill-formed sizeof(T).

In addition, the proposed change creates an asymmetry between class templates and ordinary classes:

  struct S {
    void f() noexcept(sizeof(g()) < 8); // Invalid forward reference.
    static int g();
  };

but

  template<typename> struct X {
      void f() noexcept(sizeof(g()) < 8); // Okay.
      static int g();
  };

If the proposed change is adopted, the rules for exception-specifications in ordinary classes should be revised to make the parallel usage well-formed.

Proposed resolution (October, 2012):

  1. Change 6.4.7 [basic.scope.class] paragraph 1 #1 as follows:

  2. The potential scope of a name declared in a class consists not only of the declarative region following the name's point of declaration, but also of all function bodies, default arguments, exception-specifications, and brace-or-equal-initializers of non-static data members in that class (including such things in nested classes).
  3. Change 6.5.3 [basic.lookup.unqual] paragraph 7 as follows:

  4. A name used in the definition of a class X outside of a member function body, default argument, exception-specification, brace-or-equal-initializer of a non-static data member, or nested class definition29 shall be declared in one of the following ways:...
  5. Change 6.5.3 [basic.lookup.unqual] paragraph 8 as follows:

  6. For the members of a class X, a name used in a member function body, in a default argument, in an exception-specification, in the brace-or-equal-initializer of a non-static data member (11.4 [class.mem]), or in the definition of a class member outside of the definition of X, following the member's declarator-id31 , shall be declared in one of the following ways:...
  7. Change 11.4 [class.mem] paragraph 2 as follows:

  8. A class is considered a completely-defined object type (6.8 [basic.types]) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, exception-specifications, and brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.
  9. Change 13.7 [temp.decls] paragraph 2 as follows:

  10. For purposes of name lookup and instantiation, default arguments and exception-specifications of function templates and default arguments and exception-specifications of member functions of class templates are considered definitions; each default argument or exception-specification is a separate definition which is unrelated to the function template definition or to any other default arguments or exception-specifications.
  11. Change 13.8 [temp.res] paragraph 11 as follows:

  12. [Note: For purposes of name lookup, default arguments and exception-specifications of function templates and default arguments and exception-specifications of member functions of class templates are considered definitions (13.7 [temp.decls]). —end note]
  13. Add the following as a new paragraph after 13.8.4.1 [temp.point] paragraph 2:

  14. If a function template or member function of a class template is called in a way which uses the definition of a default argument of that function template or member function, the point of instantiation of the default argument is the point of instantiation of the function template or member function specialization.

    For an exception-specification of a function template specialization or specialization of a member function of a class template, if the exception-specification is implicitly instantiated because it is needed by another template specialization and the context that requires it depends on a template parameter, the point of instantiation of the exception-specification is the point of instantiation of the specialization that requires it. Otherwise, the point of instantiation for such an exception-specification immediately follows the namespace scope declaration or definition that requires the exception-specification.

  15. Change 13.9.2 [temp.inst] paragraph 1 as follows:

  16. Unless a class template specialization has been explicitly instantiated (13.9.3 [temp.explicit]) or explicitly specialized (13.9.4 [temp.expl.spec]), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program. The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, or default arguments, or exception-specifications of the class member functions, member classes, scoped member enumerations, static data members and member templates; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions. However, for the purpose...
  17. Insert the following as a new paragraph immediately preceding 13.9.2 [temp.inst] paragraph 14:

  18. The exception-specification of a function template specialization is not instantiated along with the function declaration; it is instantiated when needed (14.5 [except.spec]). If such an exception-specification is needed but has not yet been instantiated, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the exception-specification is done as if it were being done as part of instantiating the declaration of the specialization at that point.

    [Note: 13.8.4.1 [temp.point] defines the point of instantiation of a template specialization. —end note]

  19. Change 13.10.3 [temp.deduct] paragraph 7 as follows:

  20. The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside sizeof, decltype, and other contexts that allow non-constant expressions. [Note: The equivalent substitution in exception specifications is done only when the function exception-specification is instantiated, at which point a program is ill-formed if the substitution results in an invalid type or expression. —end note]
  21. Change 14.5 [except.spec] paragraph 2 as follows:

  22. ...A type denoted in an exception-specification shall not denote an incomplete type other than a class currently being defined. A type denoted in an exception-specification shall not denote a pointer or reference to an incomplete type, other than cv void* or a pointer or reference to a class currently being defined. A type cv T, “array of T”, or “function returning T” denoted in an exception-specification is adjusted to type T, “pointer to T”, or “pointer to function returning T” respectively.
  23. Add the following as a new paragraph following 14.5 [except.spec] paragraph 15:

  24. A deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation]) with no explicit exception-specification is treated as if it were specified with noexcept(true).

    An exception-specification is considered to be needed when:

    The exception-specification of a defaulted special member function is evaluated as described above only when needed; similarly, the exception-specification of a specialization of a function template or member function of a class template is instantiated only when needed.

[Note: this resolution reverses the decision in issue 1308.]




1462. Deduction failure vs “ill-formed, no diagnostic required”

Section: 13.10.3  [temp.deduct]     Status: CD3     Submitter: John Spicer     Date: 2012-02-08

[Moved to DR at the April, 2013 meeting.]

The relationship between errors that render a program ill-formed but for which no diagnostic is required and things that cause deduction failure is not clearly specified. Presumably failures that need not be diagnosed cannot be the basis for SFINAE, lest different implementations perform deduction differently depending on how thoroughly they handle such cases. This should be spelled out explicitly.

Proposed resolution (October, 2012):

Change 13.10.3 [temp.deduct] paragraph 8 as follows:

If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments. [Note: If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution process. —end note] Only invalid types and expressions...



1388. Missing non-deduced context following a function parameter pack

Section: 13.10.3.2  [temp.deduct.call]     Status: CD3     Submitter: James Widman     Date: 2011-09-02

[Moved to DR at the October, 2012 meeting.]

Presumably 13.10.3.6 [temp.deduct.type] paragraph 5 should include a bullet for a function parameter or function parameter pack that follows a function parameter pack.

Proposed resolution (February, 2012):

Change 13.10.3.2 [temp.deduct.call] paragraph 1 as follows:

...For a function parameter pack that does not occur at the end of the parameter-declaration-list, the type of the parameter pack is a non-deduced context. When a function parameter pack appears in a non-deduced context (13.10.3.6 [temp.deduct.type]), the type of that parameter pack is never deduced. [Example:

  template<class ... Types> void f(Types& ...);
  template<class T1, class ... Types> void g(T1, Types ...);
  template<class T1, class ... Types> void g1(Types ..., T1);

  void h(int x, float& y) {
    const int z = x;
    f(x, y, z);                  // Types is deduced to int, float, const int
    g(x, y, z);                  // T1 is deduced to int; Types is deduced to float, int
    g1(x, y, z);                 // error: Types is not deduced
    g1<int, int, int>(x, y, z);  // OK, no deduction occurs
  }

end example]

This resolution also resolves issue 1399.




1399. Deduction with multiple function parameter packs

Section: 13.10.3.2  [temp.deduct.call]     Status: CD3     Submitter: Jason Merrill     Date: 2011-09-29

[Moved to DR at the October, 2012 meeting.]

Consider:

    template <class... T>
    void f(T..., int, T...) { }

    int main() {
       f(0);          // OK
       f<int>(0,0,0); // OK
       f(0,0,0);      // error
    }

It seems clear that the third call is ill-formed because by the time we get to the second function parameter pack we've already assumed that T is empty, so deducing anything for T would be nonsensical. But I don't think this is expressed anywhere in the standard.

One way to handle this would be to say that a template parameter pack is not deducible if it is used in a function parameter pack not at the end of the parameter list.

Proposed resolution (February, 2012):

This issue is resolved by the resolution of issue 1388.




1372. Cross-references incorrect in conversion function template argument deduction

Section: 13.10.3.4  [temp.deduct.conv]     Status: CD3     Submitter: Michael Wong     Date: 2011-08-15

[Moved to DR at the October, 2012 meeting.]

According to 13.10.3.4 [temp.deduct.conv] paragraph 1,

Template argument deduction is done by comparing the return type of the conversion function template (call it P; see 9.4 [dcl.init], 12.2.2.6 [over.match.conv], and 12.2.2.7 [over.match.ref] for the determination of that type) with the type that is required as the result of the conversion (call it A) as described in 13.10.3.6 [temp.deduct.type].

It would seem that the cross-references should apply to the determination of the type “required as the result of the conversion” (i.e., A) instead of the return type of the conversion function.

Proposed resolution (February, 2012):

Change 13.10.3.4 [temp.deduct.conv] paragraph 1 as follows:

Template argument deduction is done by comparing the return type of the conversion function template (call it P; see 9.4 [dcl.init], 12.2.2.6 [over.match.conv], and 12.2.2.7 [over.match.ref] for the determination of that type) with the type that is required as the result of the conversion (call it A; see 9.4 [dcl.init], 12.2.2.6 [over.match.conv], and 12.2.2.7 [over.match.ref] for the determination of that type) as described in 13.10.3.6 [temp.deduct.type].



1387. Missing non-deduced context for decltype

Section: 13.10.3.6  [temp.deduct.type]     Status: CD3     Submitter: James Widman     Date: 2011-09-02

[Moved to DR at the October, 2012 meeting.]

Presumably 13.10.3.6 [temp.deduct.type] paragraph 5 should include a bullet for a decltype-specifier whose expression references a template parameter.

Proposed resolution (February, 2012):

Change 13.10.3.6 [temp.deduct.type] paragraph 5 as follows:

The non-deduced contexts are:




1431. Exceptions from other than throw-expressions

Section: Clause 14  [except]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-12-16

[Moved to DR at the October, 2012 meeting.]

There are a number of places in the Standard that appear to assume that exceptions are only thrown by throw-expressions. Various other constructs, such as dynamic_casts, typeid, new-expressions, etc., can also throw exceptions, so a more general term should be coined and applied in place of throw-expression wherever necessary.

Proposed resolution (February, 2012):

  1. Change 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 3 as follows:

  2. ...Any other allocation function that fails to allocate storage shall indicate failure only by throwing an exception (14.2 [except.throw]) of a type that would match a handler (14.4 [except.handle]) of type std::bad_alloc (17.6.4.1 [bad.alloc]).
  3. Change 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 4 as follows:

  4. ...[Note: In particular, a global allocation function is not called to allocate storage for objects with static storage duration (6.7.5.2 [basic.stc.static]), for objects or references with thread storage duration (6.7.5.3 [basic.stc.thread]), for objects of type std::type_info (7.6.1.8 [expr.typeid]), or for the copy of an object thrown by a throw expression an exception object (14.2 [except.throw]). —end note]
  5. Change 7.6.1.7 [expr.dynamic.cast] paragraph 9 as follows:

  6. The value of a failed cast to pointer type is the null pointer value of the required result type. A failed cast to reference type throws an exception (14.2 [except.throw]) of a type that would match a handler (14.4 [except.handle]) of type std::bad_cast (17.7.4 [bad.cast]).
  7. Change 7.6.1.8 [expr.typeid] paragraph 2 as follows:

  8. ...If the glvalue expression is obtained by applying the unary * operator to a pointer68 and the pointer is a null pointer value (7.3.12 [conv.ptr]), the typeid expression throws the an exception (14.2 [except.throw]) of a type that would match a handler of type std::bad_typeid exception (17.7.5 [bad.typeid]).
  9. Change 7.6.2.8 [expr.new] paragraph 7 as follows:

  10. When the value of the expression in a noptr-new-declarator is zero, the allocation function is called to allocate an array with no elements. If the value of that expression is less than zero or such that the size of the allocated object would exceed the implementation-defined limit, or if the new-initializer is a braced-init-list for which the number of initializer-clauses exceeds the number of elements to initialize, no storage is obtained and the new-expression terminates by throwing throws an exception (14.2 [except.throw]) of a type that would match a handler (14.4 [except.handle]) of type std::bad_array_new_length (17.6.4.2 [new.badlength]).
  11. Change Clause 14 [except] paragraph 1 as follows:

  12. ...A handler will be invoked only by a throw-expression invoked throwing an exception in code executed in the handler's try block or in functions called from the handler's try block ...
  13. Change Clause 14 [except] paragraph 2 as follows:

  14. A try-block is a statement (Clause 8 [stmt.stmt]). A throw-expression is of type void. Code that executes a throw-expression is said to “throw an exception;” code that subsequently gets control is called a “handler.” [Note:...
  15. Change 14.2 [except.throw] paragraph 1 as follows:

  16. Throwing an exception transfers control to a handler. [Note: An exception can be thrown from one of the following contexts: throw-expression (see below), allocation functions (6.7.5.5.2 [basic.stc.dynamic.allocation]), dynamic_cast (7.6.1.7 [expr.dynamic.cast]), typeid (7.6.1.8 [expr.typeid]), new-expression (7.6.2.8 [expr.new]), and standard library functions (16.3.2.4 [structure.specifications]). —end note] An object is passed and the type of that object determines which handlers can catch it. [Example:...
  17. Change 14.2 [except.throw] paragraph 3 as follows:

  18. A throw-expression Throwing an exception copy-initializes (9.4 [dcl.init], 11.4.5.3 [class.copy.ctor]) a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T”, respectively. The temporary is an lvalue and is used to initialize the variable named in the matching handler (14.4 [except.handle]). If the type of the exception object would be an incomplete type or a pointer to an incomplete type other than (possibly cv-qualified) void the program is ill-formed. Except for these restrictions and the restrictions on type matching mentioned in 14.4 [except.handle], the operand of throw is treated exactly as a function argument in a call (7.6.1.3 [expr.call]) or the operand of a return statement. Evaluating a throw-expression with an operand throws an exception; the type of the exception object is determined by removing any top-level cv-qualifiers from the static type of the operand and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T,” respectively.
  19. Change 14.2 [except.throw] paragraph 4 as follows:

  20. ...[Note: an a thrown exception thrown by a throw-expression does not propagate to other threads unless caught, stored, and rethrown using appropriate library functions; see 17.9.7 [propagation] and 33.10 [futures]. —end note]
  21. Change 14.2 [except.throw] paragraph 8 as follows:

  22. A throw-expression with no operand rethrows the currently handled exception (14.4 [except.handle]). The exception is reactivated with the existing temporary exception object; no new temporary exception object is created. The exception is no longer considered to be caught; therefore, the value of std::uncaught_exception() will again be true. [Example:...
  23. Change 14.3 [except.ctor] paragraph 1 as follows:

  24. As control passes from a throw-expression the point where an exception is thrown to a handler, destructors are invoked for all automatic objects constructed since the try block was entered...
  25. Change 14.3 [except.ctor] paragraph 3 as follows:

  26. The process of calling destructors for automatic objects constructed on the path from a try block to a throw-expression the point where an exception is thrown is called “stack unwinding.” If a destructor...
  27. Change 14.4 [except.handle] paragraph 17 as follows:

  28. When the handler declares a non-constant an object, any changes to that object will not affect the temporary object that was initialized by execution of the throw-expression exception object. When the handler declares a reference to a non-constant an object, any changes to the referenced object are changes to the temporary object initialized when the throw-expression was executed exception object and will have effect should that object be rethrown.
  29. Change 17.9.5.4 [terminate] paragraph 1 as follows:

  30. Remarks: Called by the implementation when exception handling must be abandoned for any of several reasons (14.6.2 [except.terminate]), in effect immediately after evaluating the throw-expression (17.9.5.1 [terminate.handler]) throwing the exception. May also be called directly by the program.



1503. Exceptions during copy to exception object

Section: 14.2  [except.throw]     Status: CD3     Submitter: Daniel Krügler     Date: 2012-05-11

[Moved to DR at the April, 2013 meeting.]

According to 14.2 [except.throw] paragraph 7,

If the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught, calls a function that exits via an exception, std::terminate is called (14.6.2 [except.terminate]).

This wording was overlooked in the resolution for issue 475 and should be changed, along with the following example, to indicate that std::terminate will be called for an uncaught exception only after initialization of the exception object is complete.

Proposed resolution (August, 2012):

Change 14.2 [except.throw] paragraph 7 as follows:

If the exception handling mechanism, after completing evaluation of the expression to be thrown the initialization of the exception object but before the exception is caught activation of a handler for the exception, calls a function that exits via an exception, std::terminate is called (14.6.2 [except.terminate]). [Example:

  struct C {
    C() { }
    C(const C&) { throw 0; }
      if (std::uncaught_exception()) {
        throw 0;    // throw during copy to handler's exception-declaration object (14.4 [except.handle])
      }
    }
  };

  int main() {
    try {
      throw C();   // calls std::terminate() if construction of the handler's
                   // exception-declaration object is not elided (11.4.5.3 [class.copy.ctor])
    } catch(C) { }
  }

end example]




388. Catching base*& from a throw of derived*

Section: 14.4  [except.handle]     Status: CD3     Submitter: John Spicer     Date: 28 Oct 2002

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

I have a question about exception handling with respect to derived to base conversions of pointers caught by reference.

What should the result of this program be?

  struct S             {};
  struct SS : public S {};

  int main()
  {
  	SS ss;
  	int result = 0;
  	try
  	{
  		throw &ss; // throw object has type SS*
  		           // (pointer to derived class)
  	}
  	catch (S*& rs) // (reference to pointer to base class)
  	{
  		result = 1;
  	}
  	catch (...)
  	{
  		result = 2;
  	}
  	return result;
  }

The wording of 14.4 [except.handle] paragraph 3 would seem to say that the catch of S*& does not match and so the catch ... would be taken.

All of the compilers I tried (EDG, g++, Sun, and Microsoft) used the catch of S*& though.

What do we think is the desired behavior for such cases?

My initial reaction is that this is a bug in all of these compilers, but the fact that they all do the same thing gives me pause.

On a related front, if the handler changes the parameter using the reference, what is caught by a subsequent handler?

  extern "C" int printf(const char *, ...);
  struct S             {};
  struct SS : public S {};
  SS ss;

  int f()
  {
  	try
  	{
  		throw &ss;
  	}
  	catch (S*& rs) // (reference to pointer to base class)
  	{
  		rs = 0;
  		throw;
  	}
  	catch (...)
  	{
  	}
  	return 0;
  }

  int main()
  {
  	try { f(); }
  	catch (S*& rs) {
  		printf("rs=%p, &ss=%p\n", rs, &ss);
  	}
  }

EDG, g++, and Sun all catch the original (unmodified) value. Microsoft catches the modified value. In some sense the EDG/g++/Sun behavior makes sense because the later catch could catch the derived class instead of the base class, which would be difficult to do if you let the catch clause update the value to be used by a subsequent catch.

But on this non-pointer case, all of the compilers later catch the modified value:

  extern "C" int printf(const char *, ...);
  int f()
  {
  	try
  	{
  		throw 1;
  	}
  	catch (int& i)
  	{
  		i = 0;
  		throw;
  	}
  	catch (...)
  	{
  	}
  	return 0;
  }

  int main()
  {
  	try { f(); }
  	catch (int& i) {
  		printf("i=%p\n", i);
  	}
  }

To summarize:

  1. Should "base*const&" be able to catch a "derived*"? The current standard seems to say "no" but parallels to how calls work, and existing practice, suggest that the answer should be "yes".
  2. Should "base*&" be able to catch a "derived*". Again, the standard seems seems to say "no". Parallels to how calls work still suggest "no", but existing practice suggests "yes".
  3. If either of the above is "yes", what happens if you modify the pointer referred to by the reference. This requires a cast to remove const for case #2.
  4. On a related front, if you catch "derived*&" when a "derived*" is thrown, what happens if you modify the pointer referred to by the reference? EDG/g++/Sun still don't modify the underlying value that would be caught by a rethrow in this case. This case seems like it should be the same as the "int&" example above, but is not on the three compilers mentioned.

(See also issue 729.)

Notes from the October, 2009 meeting:

The consensus of the CWG was that it should not be possible to catch a pointer to a derived class using a reference to a base class pointer, and that a handler that takes a reference to non-const pointer should allow the pointer to be modified by the handler.

Proposed resolution (March, 2010):

Change 14.4 [except.handle] paragraph 3 as follows:

A handler is a match for an exception object of type E if

(This resolution also resolves issue 729.)

Notes from the March, 2011 meeting:

This resolution would require an ABI change and was thus deferred for further consideration.




729. Qualification conversions and handlers of reference-to-pointer type

Section: 14.4  [except.handle]     Status: CD3     Submitter: John Spicer     Date: 6 October, 2008

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

Given the following example:

    int f() {
        try { /* ... */ }
        catch(const int*&) {
            return 1;
        }
        catch(int*&) {
            return 2;
        }
        return 3;
    }

can f() return 2? That is, does an int* exception object match a const int*& handler?

According to 14.4 [except.handle] paragraph 3, it does not:

A handler is a match for an exception object of type E if

Only the third bullet allows qualification conversions, but only the first bullet applies to a handler of reference-to-pointer type. This is consistent with how other reference bindings work; for example, the following is ill-formed:

    int* p;
    const int*& r = p;

(The consistency is not complete; the reference binding would be permitted if r had type const int* const &, but a handler of that type would still not match an int* exception object.)

However, implementation practice seems to be in the other direction; both EDG and g++ do match an int* with a const int*&, and the Microsoft compiler issues an error for the presumed hidden handler in the code above. Should the Standard be changed to reflect existing practice?

(See also issue 388.)

Notes from the October, 2009 meeting:

The CWG agreed that matching the exception object with a handler should, to the extent possible, mimic ordinary reference binding in cases like this.

Proposed resolution (February, 2010):

This issue is resolved by the resolution of issue 388.




1267. Rvalue reference types in exception-specifications

Section: 14.5  [except.spec]     Status: CD3     Submitter: Michael Wong     Date: 2011-03-21

[Moved to DR at the October, 2012 meeting.]

The types that may appear in an exception-specification (14.5 [except.spec] paragraph 2) include rvalue reference types, although they are excluded as handler types (14.4 [except.handle] paragraph 1) . This appears to have been an oversight.

Proposed resolution (February, 2012):

Change 14.5 [except.spec] paragraph 2 as follows:

...A type denoted in an exception-specification shall not denote an incomplete type or an rvalue reference type. A type denoted in an exception-specification shall not denote a pointer or reference...



1282. Underspecified destructor exception-specification

Section: 14.5  [except.spec]     Status: CD3     Submitter: Daniel Krügler     Date: 2011-03-28

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

It is not clear whether the unexpected handler will be invoked in the following example:

  #include <iostream>
  #include <exception>

  struct A { ~A() throw() { } };
  struct B { ~B() noexcept { } };
  struct C : A, B { ~C() { throw 0; } };

  void unexpected_observer() {
   std::cerr << "unexpected called" << std::endl;
   std::terminate();
  }

  int main() {
   std::set_unexpected(unexpected_observer);
   C c;
  }

The problem is 14.5 [except.spec] paragraph 14 only says that the exception-specification of C::~C “shall allow no exceptions,” which could mean either throw() or noexcept(true).

Proposed resolution (August, 2011):

Change 14.5 [except.spec] paragraph 14 as follows:

An implicitly declared special member function (11.4.4 [special]) shall have has an exception-specification. If f is an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f's implicit definition; f shall allow allows all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions has the exception-specification noexcept(true) if every function it directly invokes allows no exceptions. [Example:

  struct A {
    A();
    A(const A&) throw();
    A(A&&) throw();
    ~A() throw(X);
  };
  struct B {
    B() throw();
    B(const B&) throw();
    B(B&&) throw(Y);
    ~B() throw(Y);
  };
  struct D : public A, public B {
      // Implicit declaration of D::D();
      // Implicit declaration of D::D(const D&) throw() noexcept(true);
      // Implicit declaration of D::D(D&&) throw(Y);
      // Implicit declaration of D::~D() throw(X, Y);
  };

Furthermore, if...




1381. Implicitly-declared special member functions and default nothrow

Section: 14.5  [except.spec]     Status: CD3     Submitter: David Svoboda     Date: 2011-08-26

[Moved to DR at the October, 2012 meeting.]

According to 14.5 [except.spec] paragraph 14,

An implicitly declared special member function (11.4.4 [special]) shall have an exception-specification. If f is an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f's implicit definition; f shall allow all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions if every function it directly invokes allows no exceptions.

It would be clearer if this description made explicit the intent that special member functions that invoke no other functions are to allow no exceptions.

Proposed resolution (February, 2012):

Change 14.5 [except.spec] paragraph 14 as follows:

...and f has the exception-specification noexcept(true) if every function it directly invokes allows no exceptions. [Note: It follows that f has the exception-specification noexcept(true) if it invokes no other functions. —end note] [Example:

  struct A {
    A();
    A(const A&) throw();
    A(A&&) throw();
    ~A() throw(X);
  };
  struct B {
    B() throw();
    B(const B&) throw() = default;  // Declaration of B::B(const B&) noexcept(true)
    B(B&&) throw(Y);
    ~B() throw(Y);
  };
  ...



1370. identifier-list cannot contain ellipsis

Section: 15.6  [cpp.replace]     Status: CD3     Submitter: Nikolay Ivchenkov     Date: 2011-08-14

[Moved to DR at the October, 2012 meeting.]

15.6 [cpp.replace] paragraph 12 says,

If there is a ... in the identifier-list in the macro definition...

However, an identifier-list cannot contain an ellipsis according to the grammar in Clause 15 [cpp] paragraph 1.

Proposed resolution (February, 2012):

Change 15.6 [cpp.replace] paragraph 12 as follows:

If there is a ... in the identifier-list immediately preceding the ) in the function-like macro definition, then the trailing arguments, including any separating comma preprocessing tokens, are merged to form a single item: the variable arguments variable arguments. The number of arguments so combined is such that, following merger, the number of arguments is one more than the number of parameters in the macro definition (excluding the ...).



1329. Recursive deduction substitutions

Section: Clause Annex B  [implimits]     Status: CD3     Submitter: Jason Merrill     Date: 2011-06-02

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

It is not clear whether the implementation limit on recursion in template instantiation applies only to instantiation itself or also to recursion that occurs during template argument deduction.

Proposed resolution (August, 2011):

Change Clause Annex B [implimits] as follows:




223. The meaning of deprecation

Section: Clause Annex D  [depr]     Status: CD3     Submitter: Mike Miller     Date: 19 Apr 2000

[Moved to DR at the April, 2013 meeting.]

During the discussion of issues 167 and 174, it became apparent that there was no consensus on the meaning of deprecation. Some thought that deprecating a feature reflected an intent to remove it from the language. Others viewed it more as an encouragement to programmers not to use certain constructs, even though they might be supported in perpetuity.

There is a formal-sounding definition of deprecation in Annex Clause Annex D [depr] paragraph 2:

deprecated is defined as: Normative for the current edition of the Standard, but not guaranteed to be part of the Standard in future revisions.
However, this definition would appear to say that any non-deprecated feature is "guaranteed to be part of the Standard in future revisions." It's not clear that that implication was intended, so this definition may need to be amended.

This issue is intended to provide an avenue for discussing and resolving those questions, after which the original issues may be reopened if that is deemed desirable.

Proposed resolution (August, 2012):

Change Clause Annex D [depr] paragraph 2 as follows:

These are deprecated features, where deprecated is defined as: Normative for the current edition of the Standard, but not guaranteed to be part of the Standard in having been identified as a candidate for removal from future revisions.





Issues with "CD4" Status


1882. Reserved names without library use

Section: _N4140_.17.6.4.3.2  [global.names]     Status: CD4     Submitter: Mike Miller     Date: 2014-02-26

[Moved to DR at the November, 2014 meeting.]

The section of the Standard reserving names that begin with two underscores or an underscore and a capital letter, _N4140_.17.6.4.3.2 [global.names], applies only to “programs that use the facilities of the C++ standard library” (16.4.5.1 [constraints.overview]). However, implementations rely on this restriction for mangling, even when no standard library facilities are used. Should this requirement be moved to the core language section?

(There is a related issue with user-defined literal suffixes, 16.4.5.3.6 [usrlit.suffix]. However, these are already mentioned normatively in the core language section, so it could be argued that the question of library usage does not apply.)

Proposed resolution (October, 2014):

  1. Change 5.10 [lex.name] paragraph 3 as follows:

  2. In addition, some identifiers are reserved for use by C++ implementations and standard libraries (_N4140_.17.6.4.3.2 [global.names]) and shall not be used otherwise; no diagnostic is required.

  3. Change the footnote in 9.5.1 [dcl.fct.def.general] paragraph 8 as follows:

  4. [Footnote: Implementations are permitted to provide additional predefined variables with names that are reserved to the implementation (_N4140_.17.6.4.3.2 [global.names] 5.10 [lex.name]). If a predefined variable is not odr-used (6.3 [basic.def.odr]), its string value need not be present in the program image. —end footnote]
  5. Change the example in 12.6 [over.literal] paragraph 8 as follows:

  6.   double operator""_Bq(double);  // OK: does not use the reserved name identifier _Bq (_N4140_.17.6.4.3.2 [global.names] 5.10 [lex.name])
      double operator"" _Bq(double); // uses the reserved name identifier _Bq (_N4140_.17.6.4.3.2 [global.names] 5.10 [lex.name])
    
  7. Delete _N4140_.17.6.4.3.2 [global.names]:

  8. Certain sets of names and function signatures are always reserved to the implementation:




1573. Inherited constructor characteristics

Section: _N4527_.12.9  [class.inhctor]     Status: CD4     Submitter: Jason Merrill     Date: 2012-10-15

[Moved to DR at the November, 2014 meeting.]

Issue 1350 clarified that the exception-specification for an inheriting constructor is determined like defaulted functions, but we should also say something similar for deleted, and perhaps constexpr.

Also, the description of the semantics of inheriting constructors don't seem to allow for C-style variadic functions, so the text should be clearer that such constructors are only inherited without their ellipsis.

Proposed resolution (February, 2014):

  1. Change _N4527_.12.9 [class.inhctor] paragraph 1 as follows:

  2. A using-declaration (9.9 [namespace.udecl]) that names a constructor implicitly declares a set of inheriting constructors. The candidate set of inherited constructors from the class X named in the using-declaration consists of actual constructors and notional constructors that result from the transformation of defaulted parameters and ellipsis parameter specifications as follows:

  3. Change _N4527_.12.9 [class.inhctor] paragraph 2 as follows:

  4. The constructor characteristics of a constructor or constructor template are

  5. Change _N4527_.12.9 [class.inhctor] paragraph 4 as follows:

  6. A constructor so declared has the same access as the corresponding constructor in X. It is constexpr if the user-written constructor (see below) would satisfy the requirements of a constexpr constructor (9.2.6 [dcl.constexpr]). It is deleted if the corresponding constructor in X is deleted (9.5 [dcl.fct.def] 9.5.3 [dcl.fct.def.delete]) or if a defaulted default constructor (11.4.5 [class.ctor]) would be deleted, except that the construction of the direct base class X is not considered in the determination. An inheriting constructor shall not be explicitly instantiated (13.9.3 [temp.explicit]) or explicitly specialized (13.9.4 [temp.expl.spec]).



1645. Identical inheriting constructors via default arguments

Section: _N4527_.12.9  [class.inhctor]     Status: CD4     Submitter: Richard Smith     Date: 2013-03-18

[Adopted at the October, 2015 meeting as P0136R1.]

For an example like

  struct A {
    constexpr A(int, float = 0);
    explicit A(int, int = 0);
    A(int, int, int = 0) = delete;
  };

  struct B : A {
    using A::A;
  };

it is not clear from _N4527_.12.9 [class.inhctor] what should happen: is B::B(int) constexpr and/or explicit? Is B::B(int, int) explicit and/or deleted? Although the rationale given in the note in paragraph 7,

If two using-declarations declare inheriting constructors with the same signatures, the program is ill-formed (11.4 [class.mem], _N4868_.12.2 [over.load]), because an implicitly-declared constructor introduced by the first using-declaration is not a user-declared constructor and thus does not preclude another declaration of a constructor with the same signature by a subsequent using-declaration.

might be thought to apply, paragraph 1 talks about a set of candidate constructors based on their parameter types, so presumably such a set would contain only a single declaration of A::A(int) and one for A::A(int, int). The constructor characteristics of that declaration, however, are not specified.

One possibility might be to declare such a constructor, resulting from the transformation of more than one base class constrctor, to be deleted, so there would be an error only if it were used.

Notes from the April, 2013 meeting:

CWG agreed with the direction of defining such constructors as deleted.

Additional note, June, 2014:

See issue 1941 for an alternative approach to this problem.




1715. Access and inherited constructor templates

Section: _N4527_.12.9  [class.inhctor]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2013-07-16

[Adopted at the October, 2015 meeting as P0136R1.]

Consider the following example:

  template<class T> struct S {
  private:
    typedef int X;
    friend struct B;
  };

  struct B {
    template<class T> B(T, typename T::X);
  };

  struct D: B {
    using B::B;
  };

  S<int> s;
  B b(s, 2); // Okay, thanks to friendship.
  D d(s, 2); // Error: friendship is not inherited.

My understanding is that the construction of d fails because typename T::X expands to S<int>::X in this case, and that is not accessible from D.

However, I'm not sure that makes sense from a usability perspective. The user of D just wanted to be able to wrap class B, and the fact that friendship was granted to B to enable its constructor parameter seems like just an implementation detail that D shouldn't have to cope with.

Would it perhaps be better to suspend access checking during the instantiation of inheriting member function template declarations (not definitions), since real access problems (e.g., the selection of a private constructor) would presumably be revealed when doing the full instantiation?

Proposed resolution (February, 2014):

Change _N4527_.12.9 [class.inhctor] paragraph 4 as follows:

A constructor so declared has the same access as the corresponding constructor in X. It is deleted if the corresponding constructor in X is deleted (9.5 [dcl.fct.def] 9.5.3 [dcl.fct.def.delete]). While performing template argument substitution (13.10.3 [temp.deduct]) for constructor templates so declared, name lookup, overload resolution, and access checking are performed in the context of the corresponding constructor template of X. [Example:

   struct B {
     template<class T> B(T, typename T::Q);
   };

   class S {
     using Q = int;
     template<class T>
     friend B::B(T, typename T::Q);
   };

   struct D : B {
     using B::B;
   };

   B b(S(), 1); // OK: B::B is a friend of S
   D d(S(), 2); // OK: access control is in the context of B::B

end example] An inheriting constructor shall not be explicitly instantiated (13.9.3 [temp.explicit]) or explicitly specialized (13.9.4 [temp.expl.spec]).

Additional note (June, 2014):

This issue is being returned to "review" status in light of a suggestion for an alternative approach to the problem; see issue 1941.




1736. Inheriting constructor templates in a local class

Section: _N4527_.12.9  [class.inhctor]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2013-08-13

[Adopted at the October, 2015 meeting as P0136R1.]

A local class cannot, according to 13.7.3 [temp.mem] paragraph 2, have member templates. Presumably, then, an example like the following is ill-formed:

  struct S {
    template<class T> S(T) {
      struct L: S {
        using S::S;
      };
    }
  };

It is accepted by current implementations, however. Does something need to be said about this case in _N4527_.12.9 [class.inhctor], either to explicitly allow or forbid it, or is the restriction in 13.7.3 [temp.mem] sufficient?




1941. SFINAE and inherited constructor default arguments

Section: _N4527_.12.9  [class.inhctor]     Status: CD4     Submitter: David Krauss     Date: 2014-06-12

[Adopted at the October, 2015 meeting as P0136R1.]

Default arguments are a common mechanism for applying SFINAE to constructors. However, default arguments are not carried over when base class constructors are inherited; instead, an overload set of constructors with various numbers of arguments is created in the derived class. This seems problematic.

One possibility would be to change the mechanism for how constructors are inherited; a using-declaration might actually introduce the base class constructors into the derived class, as other using-declarations do, and if a base class constructor were selected, the remaining derived class members would be default-initialized.

This approach would also address issues 1645, as duplicated constructors would simply fail during overload resolution, and 1715, since there would be no synthesized constructors for which access checking would be needed.

The effect of such an approach on ABIs, including mangling, would need to be considered.

See also issue 1959.




1959. Inadvertently inherited copy constructor

Section: _N4527_.12.9  [class.inhctor]     Status: CD4     Submitter: David Krauss     Date: 2014-06-30

[Adopted at the October, 2015 meeting as P0136R1.]

Consider the following example:

  struct a {
    a() = default;
    a( a const & ) { std::cout << "copy\n"; }
    template< typename t >
    a( t ) { std::cout << "convert\n"; }
  };

  struct b : a {
    using a::a;
  };

  a x;
  b y = x;

The copy constructor is invoked by the inherited constructor template, making it effectively inherited, contrary to the intent of _N4527_.12.9 [class.inhctor] paragraph 3. std::function is affected by this issue.

A kernel of a resolution might be to inherit the copy and move constructors as deleted. Then they will be more specialized than any template, and the user won't get conversion-from-base behavior unless they explicitly declare it. However, reference binding in overload resolution is a potential gap. Something like b::b( a & ) = delete with a non-const parameter would not add safety if it's not chosen.

See also issue 1941.




1991. Inheriting constructors vs default arguments

Section: _N4527_.12.9  [class.inhctor]     Status: CD4     Submitter: Hubert Tong     Date: 2014-08-27

[Adopted at the October, 2015 meeting as P0136R1.]

The creation of inheriting constructors does not, but should, consider the default arguments of constructors in the inheriting class. For example,

  struct A {
    A(int, int);
  };

  struct B : A {
    using A::A;
    B(int, int, int = 0); // does not suppress creation of B(int, int) from A(int, int)
  };



1929. template keyword following namespace nested-name-specifier

Section: _N4567_.5.1.1  [expr.prim.general]     Status: CD4     Submitter: David Krauss     Date: 2014-05-15

[Moved to DR at the May, 2015 meeting.]

It is not clear whether the template keyword should be accepted in an example like

  template<typename> struct s {};
  ::template s<void> q; // innocuous disambiguation?

Although it is accepted by the grammar, the verbiage in _N4567_.5.1.1 [expr.prim.general] paragraph 10 does not mention the possibility, while the preceding paragraph dealing with class qualification calls it out explicitly.

Notes from the June, 2014 meeting:

CWG agreed that this usage should be accepted.

Proposed resolution (November, 2014):

Change _N4567_.5.1.1 [expr.prim.general] paragraph 10 as follows (the base wording is as modified by issue 1887):

The nested-name-specifier :: names the global namespace. A nested-name-specifier that names a namespace (9.8 [basic.namespace]), optionally followed by the keyword template (13.3 [temp.names]), and then followed by the name of a member of that namespace (or the name of a member of a namespace made visible by a using-directive), is a qualified-id...



1920. Qualification mismatch in pseudo-destructor-name

Section: _N4778_.7.6.1.4  [expr.pseudo]     Status: CD4     Submitter: David Majnemer     Date: 2014-05-01

[Moved to DR at the May, 2015 meeting.]

An example like

  typedef int T;
  typedef const T CT;

  void blah2(T *a) {
   a->CT::~T();
  }

is ill-formed, because _N4778_.7.6.1.4 [expr.pseudo] paragraph 2 requires that the two type-names in the qualified-id be the same type. The corresponding case for a real destructor, however, is allowed because of the provision in 11.3 [class.name] paragraph 5 ignoring cv-qualifiers in a typedef-name referring to a class type. The specification for pseudo-destructors should be adjusted accordingly.

Proposed resolution (November, 2014):

Change _N4778_.7.6.1.4 [expr.pseudo] paragraph 2 as follows:

...The cv-unqualified versions of the object type and of the type designated by the pseudo-destructor-name shall be the same type. Furthermore, the two type-names in a pseudo-destructor-name of the form

shall designate the same scalar type (ignoring cv-qualification).




2063. Type/nontype hiding in class scope

Section: _N4868_.6.4.1  [basic.scope.declarative]     Status: CD4     Submitter: Hubert Tong     Date: 2014-12-20

[Adopted at the February, 2016 meeting.]

The type/nontype hiding rules (“struct stat hack”) do not apply in class scope. This is a C compatibility issue:

  struct A {
    struct B { int x; } b;
    int B;    // Permitted in C
  };

Since the type/nontype hiding rules exist for C compatibility, should this example be supported?

Proposed resolution (September, 2015):

Change _N4868_.6.4.1 [basic.scope.declarative] paragraph 4 as follows:

Given a set of declarations in a single declarative region, each of which specifies the same unqualified name,




1021. Definitions of namespace members

Section: _N4868_.9.8.2.3  [namespace.memdef]     Status: CD4     Submitter: Michael Wong     Date: 2010-01-14

[Moved to DR at the November, 2014 meeting.]

According to _N4868_.9.8.2.3 [namespace.memdef] paragraphs 1 and 2 read,

Members (including explicit specializations of templates (13.9.4 [temp.expl.spec])) of a namespace can be defined within that namespace.

Members of a named namespace can also be defined outside that namespace by explicit qualification (6.5.5.3 [namespace.qual]) of the name being defined, provided that the entity being defined was already declared in the namespace and the definition appears after the point of declaration in a namespace that encloses the declaration's namespace.

It is not clear what these specifications mean for the following pair of examples:

    namespace N {
        struct A;
    }
    using N::A;
    struct A { };

Although this does not satisfy the “by explicit qualification” requirement, it is accepted by major implementations.

    struct S;
    namespace A {
        using ::S;
        struct S  { };
    }

Is this a definition “within that namespace,” or should that wording be interpreted as “directly within” the namespace?

See also issue 1838.

Proposed Resolution (July, 2014):

This issue is resolved by the resolution of issue 1838.




1838. Definition via unqualified-id and using-declaration

Section: _N4868_.9.8.2.3  [namespace.memdef]     Status: CD4     Submitter: Richard Smith     Date: 2014-01-17

[Moved to DR at the November, 2014 meeting.]

The Standard is not clear about what happens when an entity is declared but not defined in an inner namespace and declared via a using-declaration in an outer namespace, and a definition of an entity with that name as an unqualified-id appears in the outer namespace. Is this a legitimate definition of the inner-namespace entity, as it would be if the definition used a qualified-id, or is the definition a member of the outer namespace and thus in conflict with the using-declaration? There is implementation divergence on the treatment of such definitions.

See also issues 1708 and 1021.

Notes from the February, 2014 meeting:

CWG agreed that the definition in such cases is a member of the outer namespace, not a redeclaration of the name introduced in that namespace by the using-declaration.

Proposed Resolution (July, 2014):

  1. Change _N4868_.9.8.2.3 [namespace.memdef] paragraph 1 as follows:

  2. Members (including explicit specializations of templates (13.9.4 [temp.expl.spec])) of a namespace can be defined within that namespace. A declaration in a namespace N (excluding declarations in nested scopes) whose declarator-id is an unqualified-id declares (or redeclares) a member of N, and may be a definition. [Note: An explicit instantiation (13.9.3 [temp.explicit]) or explicit specialization (13.9.4 [temp.expl.spec]) of a template does not introduce a name and thus may be declared using an unqualified-id in a member of the enclosing namespace set, if the primary template is declared in an inline namespace. —end note] [Example:

      namespace X {
        void f() { /* ... */ }  // OK: introduces X::f()
    
        namespace M {
          void g();             // OK: introduces X::M::g()
        }
        using M::g;
        void g();               // error: conflicts with X::M::g()
      }
    

    end example]

  3. Change _N4868_.9.8.2.3 [namespace.memdef] paragraph 3 as follows:

  4. Every name first declared in a namespace is a member of that namespace. If a friend declaration...

This resolution also resolves issues 1021 and 987.




2124. Signature of constructor template

Section: 3.54  [defns.signature.member.templ]     Status: CD4     Submitter: Hubert Tong     Date: 2015-05-05

[Adopted at the February, 2016 meeting.]

According to 3.54 [defns.signature.member.templ], the signature of a class member function template includes:

name, parameter type list (9.3.4.6 [dcl.fct]), class of which the function is a member, cv-qualifiers (if any), ref-qualifier (if any), return type, and template parameter list

However, a constructor template does not have a return type. This may be relevant to friend declaration matching.

Proposed resolution (October, 2015):

Change 3.54 [defns.signature.member.templ] as follows:

signature
<class member function template> name, parameter type list (9.3.4.6 [dcl.fct]), class of which the function is a member, cv-qualifiers (if any), ref-qualifier (if any), return type (if any), and template parameter list



1999. Representation of source characters as universal-character-names

Section: 5.2  [lex.phases]     Status: CD4     Submitter: Richard Smith     Date: 2014-09-09

[Moved to DR at the May, 2015 meeting.]

According to 5.2 [lex.phases] paragraph 1, first phase,

Any source file character not in the basic source character set (5.3 [lex.charset]) is replaced by the universal-character-name that designates that character. (An implementation may use any internal encoding, so long as an actual extended character encountered in the source file, and the same extended character expressed in the source file as a universal-character-name (i.e., using the \uXXXX notation), are handled equivalently except where this replacement is reverted in a raw string literal.)

This wording is obviously not intended to exclude the use of characters with code points larger than 0xffff, but the reference to “the \uXXXX notation” might suggest that the \Uxxxxxxxx form is not allowed.

Proposed resolution (April, 2015):

Change 5.2 [lex.phases] paragraph 1 number 1 as follows:

...(An implementation may use any internal encoding, so long as an actual extended character encountered in the source file, and the same extended character expressed in the source file as a universal-character-name (i.e. e.g., using the \uXXXX notation), are handled equivalently except where this replacement is reverted in a raw string literal.)



1796. Is all-bits-zero for null characters a meaningful requirement?

Section: 5.3  [lex.charset]     Status: CD4     Submitter: Tony van Eerd     Date: 2013-10-02

[Moved to DR at the November, 2014 meeting.]

According to 5.3 [lex.charset] paragraph 3,

The basic execution character set and the basic execution wide-character set shall each contain all the members of the basic source character set, plus control characters representing alert, backspace, and carriage return, plus a null character (respectively, null wide character), whose representation has all zero bits.

It is not clear that a portable program can examine the bits of the representation; instead, it would appear to be limited to examining the bits of the numbers corresponding to the value representation (6.8.2 [basic.fundamental] paragraph 1). It might be more appropriate to require that the null character value compare equal to 0 or '\0' rather than specifying the bit pattern of the representation.

There is a similar issue for the definition of shift, bitwise and, and bitwise or operators: are those specifications constraints on the bit pattern of the representation or on the values resulting from the interpretation of those patterns as numbers?

Proposed resolution (February, 2014):

Change 5.3 [lex.charset] paragraph 3 as follows:

The basic execution character set and the basic execution wide-character set shall each contain all the members of the basic source character set, plus control characters representing alert, backspace, and carriage return, plus a null character (respectively, null wide character), whose representation has all zero bits value is 0. For each basic execution character set...



2000. header-name outside #include directive

Section: 5.4  [lex.pptoken]     Status: CD4     Submitter: Richard Smith     Date: 2014-09-09

[Moved to DR at the October, 2015 meeting.]

The “max munch” rule could be read to require the characters <int> in vector<int> to be parsed as a header-name rather than as three distinct tokens. 5.8 [lex.header] paragraph 1 says,

Header name preprocessing tokens shall only appear within a #include preprocessing directive (15.3 [cpp.include]).

However, that is not sufficiently clear that header-names are only to be recognized in that context.

Proposed resolution (May, 2015):

  1. Change 5.4 [lex.pptoken] bullet 3.3 as follows:

  2. Change 5.8 [lex.header] paragraph 1 as follows:

  3. [Note: Header name preprocessing tokens shall only appear within a #include preprocessing directive (15.3 [cpp.include] see 5.4 [lex.pptoken]). end note] The sequences in both forms...



1963. Implementation-defined identifier characters

Section: 5.10  [lex.name]     Status: CD4     Submitter: Richard Smith     Date: 2014-07-07

[Moved to DR at the May, 2015 meeting.]

The grammar in 5.10 [lex.name] includes the production,

The rule for “other implementation-defined characters” is a holdover from before the introduction of universal-character-names, intended to allow the use of non-ASCII national characters in identifiers. However, since all characters outside the basic source character set are now conceptually mapped to universal-character-names in translation phase 1, there is no longer a need for such a provision and it should be removed.

Proposed resolution (April, 2015):

Change the grammar in 5.10 [lex.name] as follows:




1802. char16_t string literals and surrogate pairs

Section: 5.13.5  [lex.string]     Status: CD4     Submitter: Jeffrey Yasskin     Date: 2013-10-30

[Moved to DR at the November, 2014 meeting.]

The intent of char16_t string literals, as evident from 5.13.5 [lex.string] paragraph 9, is that they be encoded in UTF-16, that is, including surrogate pairs to represent code points outside the basic multi-lingual plane:

A single c-char may produce more than one char16_t character in the form of surrogate pairs.

Paragraph 15, however, is inconsistent with this approach, saying,

Escape sequences and universal-character-names in non-raw string literals have the same meaning as in character literals (5.13.3 [lex.ccon]), except that the single quote ' is representable either by itself or by the escape sequence \', and the double quote " shall be preceded by a \.

The reason is that code points outside the basic multi-lingual plane are ill-formed in char16_t character literals:

A character literal that begins with the letter u, such as u'y', is a character literal of type char16_t. The value of a char16_t literal containing a single c-char is equal to its ISO 10646 code point value, provided that the code point is representable with a single 16-bit code unit. (That is, provided it is a basic multi-lingual plane code point.) If the value is not representable within 16 bits, the program is ill-formed.

It should be clarified that this restriction does not apply to char16_t string literals.

Proposed resolution (February, 2014):

Change 5.13.5 [lex.string] paragraph 16 as follows:

Escape sequences and universal-character-names in non-raw string literals have the same meaning as in character literals (5.13.3 [lex.ccon]), except that the single quote ' is representable either by itself or by the escape sequence \', and the double quote " shall be preceded by a \, and except that a universal-character-name in a char16_t string literal may yield a surrogate pair. In a narrow string literal...



1810. Invalid ud-suffixes

Section: 5.13.9  [lex.ext]     Status: CD4     Submitter: Gabriel Dos Reis     Date: 2013-11-13

[Moved to DR at the November, 2014 meeting.]

In explaining the relationship between preprocessing tokens and tokens, 5.4 [lex.pptoken] paragraph 4 contains the following example:

[Example: The program fragment 1Ex is parsed as a preprocessing number token (one that is not a valid floating or integer literal token), even though a parse as the pair of preprocessing tokens 1 and Ex might produce a valid expression (for example, if Ex were a macro defined as +1).

This analysis does not take into account the addition of user-defined literals. In fact, 1Ex matches the rule for a user-defined-integer-literal, which is then ill-formed because it uses a reserved ud-suffix (5.13.9 [lex.ext] paragraph 10), as well as (presumably) because of a lookup failure for a matching literal operator, raw literal operator, or literal operator template.

More generally, it might be preferable to eliminate the restriction on the use of a reserved ud-suffix and rely simply on the fact that it is ill-formed to declare a literal operator, raw literal operator, or literal operator template with a reserved literal suffix identifier (16.4.5.3.6 [usrlit.suffix], cf 12.6 [over.literal] paragraph 1).

Proposed resolution (June, 2014):

  1. Change 5.4 [lex.pptoken] paragraph 4 as follows:

  2. [Example: The program fragment 1Ex 0xe+foo is parsed as a preprocessing number token (one that is not a valid floating or integer literal token), even though a parse as the pair of three preprocessing tokens 1 0xe, +, and Ex foo might produce a valid expression (for example, if Ex foo were a macro defined as +1). Similarly, the program fragment 1E1 is parsed as a preprocessing number (one that is a valid floating literal token), whether or not E is a macro name. —end example]
  3. Delete 5.13.9 [lex.ext] paragraph 10:

  4. Some identifiers appearing as ud-suffixes are reserved for future standardization (16.4.5.3.6 [usrlit.suffix]). A program containing such a ud-suffix is ill-formed, no diagnostic required.
  5. Change 12.6 [over.literal] paragraph 1 as follows:

  6. The string-literal or user-defined-string-literal in a literal-operator-id shall have no encoding-prefix and shall contain no characters other than the implicit terminating '\0'. The ud-suffix of the user-defined-string-literal or the identifier in a literal-operator-id is called a literal suffix identifier. [Note: some Some literal suffix identifiers are reserved for future standardization; see 16.4.5.3.6 [usrlit.suffix]. end note] A declaration whose literal-operator-id uses such a literal suffix identifier is ill-formed; no diagnostic required.
  7. Change 16.4.5.3.6 [usrlit.suffix] paragraph 1 as follows:

  8. Literal suffix identifiers (12.6 [over.literal]) that do not start with an underscore are reserved for future standardization.

Additional note, May, 2014:

It has been suggested that the change to 5.4 [lex.pptoken] paragraph 4 in the proposed resolution would be simpler and better if it did not venture into questions about user-defined literals but simply relied on a string that is a valid pp-number but not a valid floating-point number, as was the case before the introduction of user-defined literals, e.g., 1.2.3.4. The issue has been returned to "review" status for discussion of this suggestion.




1870. Contradictory wording about definitions vs explicit specialization/instantiation

Section: 6.2  [basic.def]     Status: CD4     Submitter: Hubert Tong     Date: 2014-02-15

[Moved to DR at the November, 2014 meeting.]

Sections 13.9.3 [temp.explicit] and 13.9.4 [temp.expl.spec] describe cases of explicit instantiation directives and explicit specializations, respectively, that are not definitions. However, the description in 6.2 [basic.def] does not include these distinctions, classifying all declarations other than those listed as definitions. These should be harmonized.

Proposed Resolution (July, 2014):

Change 6.2 [basic.def] paragraph 2 as follows:

A declaration is a definition unless it... an empty-declaration ( 9.1 [dcl.pre]), or a using-directive (9.8.4 [namespace.udir]), an explicit instantiation declaration (13.9.3 [temp.explicit]), or an explicit specialization (13.9.4 [temp.expl.spec]) whose declaration is not a definition.



1614. Address of pure virtual function vs odr-use

Section: 6.3  [basic.def.odr]     Status: CD4     Submitter: Richard Smith     Date: 2013-01-31

[Moved to DR at the November, 2014 meeting.]

According to 6.3 [basic.def.odr] paragraph 3,

A function whose name appears as a potentially-evaluated expression is odr-used if it is the unique lookup result or the selected member of a set of overloaded functions (6.5 [basic.lookup], 12.2 [over.match], 12.3 [over.over]), unless it is a pure virtual function and its name is not explicitly qualified.

In the following example, consequently, S::f is odr-used but not defined, and (because it is an undefined odr-used inline function) a diagnostic is required:

  namespace {
    struct S {
      inline virtual void f() = 0;
    };
   void (S::*p) = &S::f;
  }

However, S::f cannot be called through such a pointer-to-member, so forming a pointer-to-member should not cause a pure virtual function to be odr-used. There is implementation divergence on this point.

Proposed resolution (April, 2013):

Change 6.3 [basic.def.odr] paragraph 3 as follows:

...A virtual member function is odr-used if it is not pure. A function whose name appears as a potentially-evaluated expression is odr-used if it is the unique lookup result or the selected member of a set of overloaded functions (6.5 [basic.lookup], 12.2 [over.match], 12.3 [over.over]), unless it is a pure virtual function and either its name is not explicitly qualified or the expression forms a pointer to member (5.3.1). [Note:...



1926. Potential results of subscript operator

Section: 6.3  [basic.def.odr]     Status: CD4     Submitter: Marcel Wid     Date: 2014-05-13

[Moved to DR at the May, 2015 meeting.]

The definition of the potential results of an expression in 6.3 [basic.def.odr] paragraph 2 do not, but should, include the subscript operator.

Proposed resolution (November, 2014):

Change 6.3 [basic.def.odr] paragraph 2 as follows:

...The set of potential results of an expression e is defined as follows:




2085. Invalid example of adding special member function via default argument

Section: 6.3  [basic.def.odr]     Status: CD4     Submitter: Hubert Tong     Date: 2015-02-13

[Adopted at the February, 2016 meeting.]

The example in 6.3 [basic.def.odr] bullet 6.6 reads,

  //translation unit 1:
  struct X {
    X(int);
    X(int, int);
  };
  X::X(int = 0) { }
  class D: public X { };
  D d2;     // X(int) called by D()

  //translation unit 2:
  struct X {
    X(int);
    X(int, int);
  };
  X::X(int = 0, int = 0) { }
  class D: public X { };   // X(int, int) called by D();
                           // D()'s implicit definition
                           // violates the ODR

Creating a special member function via default arguments added in an out-of-class definition, as is done here, is no longer permitted, so at a minimum the example should be removed. It is not clear whether there remain any cases to which the normative wording of bullet 6.6 would apply:

If not, the entire bullet should be removed.

Proposed resolution (September, 2015):

Change 6.3 [basic.def.odr] bullet 6.6 as follows:




2104. Internal-linkage constexpr references and ODR requirements

Section: 6.3  [basic.def.odr]     Status: CD4     Submitter: James Widman     Date: 2015-03-17

[Adopted at the February, 2016 meeting.]

In an example like:

  extern int i;
  namespace {
    constexpr int& r = i;
  }
  inline int f() { return r; }

use of f() in multiple translation units results in an ODR violation because of use of the internal-linkage reference r. It would be helpful if 6.3 [basic.def.odr] paragraph 6 could be amended to “look through” a constexpr reference in determining whether an inline function violates the ODR or not.

Proposed resolution (January, 2016):

Change 6.3 [basic.def.odr] bullet 6.2 as follows, dividing the running text into a bulleted list:

...Given such an entity named D defined in more than one translation unit, then




1875. Reordering declarations in class scope

Section: 6.4.7  [basic.scope.class]     Status: CD4     Submitter: Richard Smith     Date: 2014-02-19

[Moved to DR at the May, 2015 meeting.]

The rules for class scope in 6.4.7 [basic.scope.class] paragraph 1 include the following:

  1. A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.

  2. If reordering member declarations in a class yields an alternate valid program under (1) and (2), the program is ill-formed, no diagnostic is required.

The need for rule #3 is not clear; it would seem that any otherwise-valid reordering would have to violate rule #2 in order to yield a different interpretation. Taken literally, rule #3 would also apply to simply reordering nonstatic data members with no name dependencies at all. Can it be simply removed?

Proposed resolution (June, 2014):

Delete the third item of 6.4.7 [basic.scope.class] paragraph 1 and renumber the succeeding items:

  1. If reordering member declarations in a class yields an alternate valid program under (1) and (2), the program is ill-formed, no diagnostic is required.




1753. decltype-specifier in nested-name-specifier of destructor

Section: 6.5.5  [basic.lookup.qual]     Status: CD4     Submitter: John Spicer     Date: 2013-09-18

[Moved to DR at the November, 2014 meeting.]

One of the forms of pseudo-destructor-name is

Presumably the intent of this form is to allow the nested-name-specifier to designate a namespace; otherwise the

production would be used.

Since one of the forms of nested-name-specifier is

one can write something like p->decltype(x)::~Y(). However, the lookup rules in 6.5.5 [basic.lookup.qual] paragraph 6 are inappropriate for the decltype-specifier case:

If a pseudo-destructor-name (_N4778_.7.6.1.4 [expr.pseudo]) contains a nested-name-specifier, the type-names are looked up as types in the scope designated by the nested-name-specifier.

Since this form appears to be useless (use of a decltype-specifier is permitted after a ~, but only with no nested-name-specifer — but see issue 1586), perhaps it should be made ill-formed.

Proposed resolution (February, 2014):

Change the grammar in 7.6.1 [expr.post] paragraph 1 as follows:




1603. Errors resulting from giving unnamed namespaces internal linkage

Section: 6.6  [basic.link]     Status: CD4     Submitter: Richard Smith     Date: 2013-01-09

[Moved to DR at the November, 2014 meeting.]

In C++03, all namespace-scope names had external linkage unless explicitly declared otherwise (via static, const, or as a member of an anonymous union). C++11 now specifies that members of an unnamed namespace have internal linkage (see issue 1113). This change invalidated a number of assumptions scattered throughout the Standard that need to be adjusted:

  1. 6.6 [basic.link] paragraph 5 says,

  2. a member function, static data member, a named class or enumeration of class scope, or an unnamed class or enumeration defined in a class-scope typedef declaration such that the class or enumeration has the typedef name for linkage purposes (9.2.4 [dcl.typedef]), has external linkage if the name of the class has external linkage.

    There is no specification for the linkage of such members of a class with internal linkage. Formally, at least, that leads to the statement in paragraph 8 that such members have no linkage. This omission also contradicts the note in 11.4.2 [class.mfct] paragraph 3:

    [Note: Member functions of a class in namespace scope have external linkage. Member functions of a local class (11.6 [class.local]) have no linkage. See 6.6 [basic.link]. —end note]

    as well as the statement in 11.4.9.3 [class.static.data] paragraph 5,

    Static data members of a class in namespace scope have external linkage (6.6 [basic.link]).
  3. The footnote in 6.6 [basic.link] paragraph 8 says,

  4. A class template always has external linkage, and the requirements of 13.4.2 [temp.arg.type] and 13.4.3 [temp.arg.nontype] ensure that the template arguments will also have appropriate linkage.

    This is incorrect, since templates in unnamed namespaces now have internal linkage and template arguments are no longer required to have external linkage.

  5. The statement in 9.2.2 [dcl.stc] paragraph 7 is now false:

  6. A name declared in a namespace scope without a storage-class-specifier has external linkage unless it has internal linkage because of a previous declaration and provided it is not declared const.
  7. The entire treatment of unique in 9.8.2.2 [namespace.unnamed] is no longer necessary, and the footnote is incorrect:

  8. Although entities in an unnamed namespace might have external linkage, they are effectively qualified by a name unique to their translation unit and therefore can never be seen from any other translation unit.

    Names in unnamed namespaces never have external linkage.

  9. According to 11.8.4 [class.friend] paragraph 4,

  10. A function first declared in a friend declaration has external linkage (6.6 [basic.link]).

    This presumably is incorrect for a class that is a member of an unnamed namespace.

  11. According to Clause 13 [temp] paragraph 4,

  12. A non-member function template can have internal linkage; any other template name shall have external linkage.

    Taken literally, this would mean that a template could not be a member of an unnamed namespace.

Proposed resolution (April, 2013):

  1. Change 6.6 [basic.link] paragraph 5 as follows:

  2. In addition, a member function, static data member, a named class or enumeration of class scope, or an unnamed class or enumeration defined in a class-scope typedef declaration such that the class or enumeration has the typedef name for linkage purposes (9.2.4 [dcl.typedef]), has external linkage if the name of the class has external linkage the same linkage, if any, as the name of the class of which it is a member.
  3. Change the footnote in 6.6 [basic.link] paragraph 8 as follows:

  4. 33) A class template always has external linkage, and the requirements of 13.4.2 [temp.arg.type] and 13.4.3 [temp.arg.nontype] ensure that the template arguments will also have appropriate linkage has the linkage of the innermost enclosing class or namespace in which it is declared.
  5. Change 9.8.2.2 [namespace.unnamed] paragraph 1 as follows:

  6. An unnamed-namespace-definition behaves as if it were replaced by

      inlineopt namespace unique { /* empty body */ }
      using namespace unique ;
      namespace unique { namespace-body }
    

    where inline appears if and only if it appears in the unnamed-namespace-definition, and all occurrences of unique in a translation unit are replaced by the same identifier, and this identifier differs from all other identifiers in the entire program. [Footnote: Although entities in an unnamed namespace might have external linkage, they are effectively qualified by a name unique to their translation unit and therefore can never be seen from any other translation unit. —end footnote] translation unit. [Example:...

  7. Change the note in 11.4.2 [class.mfct] paragraph 3 as follows:

  8. [Note: Member functions of a class in namespace scope have external linkage the linkage of that class. Member functions of a local class (11.6 [class.local]) have no linkage. See 6.6 [basic.link]. —end note]
  9. Change 11.4.9.3 [class.static.data] paragraph 5 as follows:

  10. Static data members of a class in namespace scope have external linkage the linkage of that class (6.6 [basic.link]).
  11. Change 11.8.4 [class.friend] paragraph 4 as follows:

  12. A function first declared in a friend declaration has external linkage the linkage of the namespace of which it is a member (6.6 [basic.link]). Otherwise, the function retains its previous linkage (9.2.2 [dcl.stc]).
  13. Change Clause 13 [temp] paragraph 4 as follows:

  14. A template name has linkage (6.6 [basic.link]). A non-member function template can have internal linkage; any other template name shall have external linkage. Specializations (explicit or implicit) of a template that has internal linkage are distinct from all specializations in other translation units...



1686. Which variables are “explicitly declared const?”

Section: 6.6  [basic.link]     Status: CD4     Submitter: Daniel Krügler     Date: 2013-05-17

[Moved to DR at the November, 2014 meeting.]

According to 6.6 [basic.link] paragraph 3,

A name having namespace scope (6.4.6 [basic.scope.namespace]) has internal linkage if it is the name of

It would be more precise and less confusing if the phrase “explicitly declared const” were replaced by saying that its type is const-qualified. This change would also allow removal of the reference to constexpr, which was added by issue 1112 because constexpr variables are implicitly const-qualified but not covered by the “explicitly declared” phrasing.

Proposed resolution (September, 2013):

Change the second bullet of 6.6 [basic.link] paragraph 3 as follows:




2151. Exception object is not created

Section: 6.7.2  [intro.object]     Status: CD4     Submitter: Hubert Tong     Date: 2015-06-26

[Adopted at the June, 2016 meeting as document P0137R1.]

According to 6.7.2 [intro.object] paragraph 1,

An object is created by a definition (6.2 [basic.def]), by a new-expression (7.6.2.8 [expr.new]) or by the implementation (6.7.7 [class.temporary]) when needed.

This should probably also include a throw-expression, with a cross-reference to 14.2 [except.throw].

Notes from the October, 2015 meeting:

This issue is expected to be resolved by the resolution of issue 1776.

Proposed resolution (June, 2016):

This issues is resolved by the resolution of issue 1776.




1116. Aliasing of union members

Section: 6.7.3  [basic.life]     Status: CD4     Submitter: US     Date: 2010-08-02

[Adopted at the June, 2016 meeting as document P0137R1.]

N3092 comment US 27

Related to issue 1027, consider:

    int f() {
      union U { double d; } u1, u2;
      (int&)u1.d = 1;
      u2 = u1;
      return (int&)u2.d;
    }

Does this involve undefined behavior? 6.7.3 [basic.life] paragraph 4 seems to say that it's OK to clobber u1 with an int object. Then union assignment copies the object representation, possibly creating an int object in u2 and making the return statement well-defined. If this is well-defined, compilers are significantly limited in the assumptions they can make about type aliasing. On the other hand, the variant where U has an array of unsigned char member must be well-defined in order to support std::aligned_storage.

Suggested resolution: Clarify that this case is undefined, but that adding an array of unsigned char to union U would make it well-defined — if a storage location is allocated with a particular type, it should be undefined to create an object in that storage if it would be undefined to access the stored value of the object through the allocated type.

(See also issues 1027 and 1338.)

Proposed resolution (August, 2010):

  1. Change 6.7.3 [basic.life] paragraph 1 as follows:

  2. ...The lifetime of an object of type T begins when storage with the proper alignment and size for type T is obtained, and either:

    The lifetime of an object of type T ends...

  3. Change 6.7.3 [basic.life] paragraph 4 as follows:

  4. A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (7.6.2.9 [expr.delete]) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior. If a program obtains storage for an object of a particular type A (e.g. with a variable definition or new-expression) and later reuses that storage for an object of another type B such that accessing the stored value of the B object through a glvalue of type A would have undefined behavior (7.2.1 [basic.lval]), the behavior is undefined. [Example:

      int i;
      (double&)i = 1.0; // undefined behavior
    
      struct S { unsigned char alignas(double) ar[sizeof (double)]; } s;
      (double&)s = 1.0; // OK, can access stored double through s because it has an unsigned char subobject
    

    end example]

  5. Change 7.2.1 [basic.lval] paragraph 10 as follows:

  6. If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined52:

This resolution also resolves issue 1027.

Additional note (August, 2012):

Concerns have been raised regarding the interaction of this change with facilities like std::aligned_storage and memory pools. Care must be taken to achieve the proper balance between supporting type-based optimization techniques and allowing practical storage management.

Additional note (January, 2013):

Several questions have been raised about the wording above . In particular:

  1. Since aggregates and unions cannot have base classes, why are base classes mentioned?

  2. Since unions can now have special member functions, is it still valid to assume that they alias all their member types?

  3. Shouldn't standard-layout classes also be considered and not just aggregates?

Additional note, February, 2014:

According to 6.7.2 [intro.object] paragraph 1, an object (i.e., a “region of storage”) is created by one of only three means:

An object is created by a definition (6.2 [basic.def]), by a new-expression (7.6.2.8 [expr.new]) or by the implementation (6.7.7 [class.temporary]) when needed. The properties of an object are determined when the object is created.

This does not allow for obtaining the storage in other ways, such as via malloc, in determining the lifetime of an object with vacuous initialization (6.7.3 [basic.life] paragraph 1).

In addition, 6.7.3 [basic.life] paragraph 1 does not require the storage obtained for an object of type T to be accessed via an lvalue of type T in order to be considered an object of that type. The treatment of “effective type” by C may be helpful here.

Additional note, May, 2015:

We never say what the active member of a union is, how it can be changed, and so on. The Standard doesn't make clear whether the following is valid:

    union U { int a; short b; } u = { 0 };
    int x = u.a; // presumably this is OK, but we never say that a is the active member
    u.b = 0;     // not clear whether this is valid 

The closest we come to talking about this is the non-normative example in 11.5 [class.union] paragraph 4, which suggests that a placement new is needed.

It's also not clear whether a has two subobjects or only one (corresponding to the active member).




1284. Should the lifetime of an array be independent of that of its elements?

Section: 6.7.3  [basic.life]     Status: CD4     Submitter: Gabriel Dos Reis     Date: 2011-04-02

[Adopted at the February, 2016 meeting.]

The note in 6.7.3 [basic.life] paragraph 2 reads,

[Note: The lifetime of an array object starts as soon as storage with proper size and alignment is obtained, and its lifetime ends when the storage which the array occupies is reused or released. 11.9.3 [class.base.init] describes the lifetime of base and member subobjects. —end note]

This wording reflects an earlier version of paragraph 1 that deferred the start of an object's lifetime only for initialization of objects of class type. The note simply emphasized the implication that that the lifetime of a POD type or an array began immediately, even if lifetime of an array's elements began later.

The decomposition of POD types removed the mention of PODs, leaving only the array types, and when the normative text was changed to include aggregates whose members have non-trivial initialization, the note was overlooked.

It is not clear whether it would be better to update the note to emphasize the distinction between aggregates with non-trivial initialization and those without or to delete it entirely.

A possible related normative change to consider is whether the specification of paragraph 1 is sufficiently clear with respect to multidimensional arrays. The current definition of “non-trivial initialization” is:

An object is said to have non-trivial initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor.

Presumably the top-level array of an N-dimensional array whose ultimate element type is a class type with non-trivial initialization would also have non-trivial initialization, but it's not clear that this wording says that.

A more radical change that came up in the discussion was whether the undefined behavior resulting from an lvalue-to-rvalue conversion of an uninitialized object in 7.3.2 [conv.lval] paragraph 1 would be better dealt with as a lifetime violation instead.

Proposed resolution (October, 2015):

Change 6.7.3 [basic.life] paragraphs 1 and 2 as follows:

The lifetime of an object is a runtime property of the object. An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its members subobjects is initialized by a constructor other than a trivial default constructor. [Note: initialization...

[Note: The lifetime of an array object starts as soon as storage with proper size and alignment is obtained, and its lifetime ends when the storage which the array occupies is reused or released. 11.9.3 [class.base.init] describes the lifetime of base and member subobjects. —end note]




1751. Non-trivial operations vs non-trivial initialization

Section: 6.7.3  [basic.life]     Status: CD4     Submitter: Nico Josuttis     Date: 2013-09-15

[Moved to DR at the November, 2014 meeting.]

The description of is_trivially_constructible in 21.3.5.4 [meta.unary.prop] paragraph 3 says,

is_constructible<T, Args...>::value is true and the variable definition for is_constructible, as defined below, is known to call no operation that is not trivial ( 6.8 [basic.types], 11.4.4 [special]).

This risks confusion when compared with the wording in 6.7.3 [basic.life] paragraph 1,

An object is said to have non-trivial initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor. [Note: initialization by a trivial copy/move constructor is non-trivial initialization. —end note]

The latter was written long before “trivial” became an important concept in its own right and uses the term differently from how it is used elsewhere in the Standard (as evidenced by the note referring to copy/move construction). The intent is to capture the idea that there is some actual initialization occurring; it should be rephrased to avoid the potential of confusion with the usage of “trivial” elsewhere.

Proposed resolution (February, 2014):

Change 6.7.3 [basic.life] paragraph 1 as follows:

The lifetime of an object is a runtime property of the object. An object is said to have non-trivial initialization non-vacuous initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor. [Note: initialization by a trivial copy/move constructor is non-trivial non-vacuous initialization. —end note] The lifetime of an object of type T begins when:

The lifetime of an object...




1776. Replacement of class objects containing reference members

Section: 6.7.3  [basic.life]     Status: CD4     Submitter: Finland     Date: 2013-09-24

[Adopted at the June, 2016 meeting as document P0137R1.]

N3690 comment FI 15

The rules given in 6.7.3 [basic.life] paragraph 7 for when an object's lifetime can be ended and a new object created in its storage include the following restriction:

the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type

The intent of this restriction is to permit optimizers to rely upon the original values of const and reference members in their analysis of subsequent code. However, this makes it difficult to implement certain desirable functionality such as optional<T>; see this discussion for more details.

This rule should be reconsidered, at least as far as it applies to unions. If it is decided to keep the rule, acceptable programming techniques for writing safe code when replacing such objects should be outlined in a note.

(See also issue 1404, which will become moot if the restriction is lifted.)

Notes from the October, 2015 meeting:

See also paper P0137 and issue 2182.

Notes from the February, 2017 meeting.

The resolution of this issue also resolves issues 636 and 2151.




2012. Lifetime of references

Section: 6.7.5  [basic.stc]     Status: CD4     Submitter: Mike Miller     Date: 2014-09-29

[Adopted at the February, 2016 meeting.]

According to 6.7.5 [basic.stc] paragraph 3,

The storage duration categories apply to references as well. The lifetime of a reference is its storage duration.

This is clearly not correct; references can have static storage duration but be dynamically initialized. Consider an example like:

  extern int& r1;
  int& f();
  int& r2 = r1;  // #1
  int& r1 = f();
  int i = r2;    // #2

r1 is not initialized until after its use at #1, so the initialization of r2 should produce undefined behavior, as should the use of r2 at #2.

The description of the lifetime of a reference should be deleted from 6.7.5 [basic.stc] and it should be described properly in 6.7.3 [basic.life].

Proposed resolution (September, 2015):

  1. Change 6.7.5 [basic.stc] paragraph 3 as follows:

  2. The storage duration categories apply to references as well. The lifetime of a reference is its storage duration.
  3. Change 6.7.3 [basic.life] paragraph 1 as follows:

  4. The lifetime of an object or reference is a runtime property of the object or reference. An object is said to have...
  5. Add the following as a new paragraph following 6.7.5 [basic.stc] paragraph 2:

  6. [Note: The lifetime of an array object starts as soon as storage with proper size and alignment is obtained, and its lifetime ends when the storage which the array occupies is reused or released. 11.9.3 [class.base.init] describes the lifetime of base and member subobjects. —end note]

    The lifetime of a reference begins when its initialization is complete. The lifetime of a reference ends as if it were a scalar object.

  7. Change 6.7.3 [basic.life] paragraph 3 as follows:

  8. The properties ascribed to objects and references throughout this International Standard apply for a given object or reference only during its lifetime. [Note:...

    Change Clause 7 [expr] paragraph 5 as follows:

    If an expression initially has the type “reference to T” (9.3.4.3 [dcl.ref], 9.4.4 [dcl.init.ref]), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression. [Note: Before the lifetime of the reference has started or after it has ended, the behavior is undefined (see 6.7.3 [basic.life]). —end note]

Drafting note: there is no change to 6.7.3 [basic.life] paragraph 4:

A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type...



1956. Reuse of storage of automatic variables

Section: 6.7.5.4  [basic.stc.auto]     Status: CD4     Submitter: Daniel Krügler     Date: 2014-06-29

[Moved to DR at the May, 2015 meeting.]

According to 6.7.5.4 [basic.stc.auto] paragraph 3,

If a variable with automatic storage duration has initialization or a destructor with side effects, it shall not be destroyed before the end of its block, nor shall it be eliminated as an optimization even if it appears to be unused, except that a class object or its copy/move may be eliminated as specified in 11.4.5.3 [class.copy.ctor].

This is intended to be a requirement for the implementation, but it could be read as prohibiting the reuse of the storage of an automatic variable by the program using a placement new-expression.

Proposed resolution (November, 2014):

Change 6.7.5.4 [basic.stc.auto] paragraph 3 as follows:

If a variable with automatic storage duration has initialization or a destructor with side effects, it an implementation shall not be destroyed destroy it before the end of its block, nor shall it be eliminated eliminate it as an optimization, even if it appears to be unused, except that a class object or its copy/move may be eliminated as specified in 11.4.5.3 [class.copy.ctor].



1338. Aliasing and allocation functions

Section: 6.7.5.5.2  [basic.stc.dynamic.allocation]     Status: CD4     Submitter: Jason Merrill     Date: 2011-08-03

[Moved to DR at the November, 2014 meeting.]

In 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 2, allocation functions are constrained to return a pointer that is different from any previously returned pointer that has not been passed to a deallocation function. This does not, for instance, prohibit returning a pointer to storage that is part of another object, for example, a pool of storage. The potential implications of this for aliasing should be spelled out.

(See also issues 1027 and 1116.)

Additional note (March, 2013):

One possibility to allow reasonable optimizations would be to require that allocation packages hide their storage in file-static variables, perhaps by adding wording such as:

Furthermore, p0 shall point to an object distinct from any other object that is accessible outside the implementation of the allocation and deallocation functions.

Additional note, April, 2013:

Concern was expressed that a pool class might provide an interface for iterating over all the pointers that were given out from the pool, and this usage should be supported.

Notes from the September, 2013 meeting:

CWG agreed that changes for this issue should apply only to non-placement forms.

Proposed resolution (February, 2014):

Change 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 2 as follows:

...If the request succeeds, the value returned shall be a non-null pointer value (7.3.12 [conv.ptr]) p0 different from any previously returned value p1, unless that value p1 was subsequently passed to an operator delete. Furthermore, for the library allocation functions in 17.6.3.2 [new.delete.single] and 17.6.3.3 [new.delete.array], p0 shall point to a block of storage disjoint from the storage for any other object accessible to the caller. The effect of indirecting through a pointer returned as a request for zero size is undefined.36



2019. Member references omitted from description of storage duration

Section: 6.7.5.6  [basic.stc.inherit]     Status: CD4     Submitter: David Krauss     Date: 2014-10-08

[Moved to DR at the October, 2015 meeting.]

According to 6.7.5.6 [basic.stc.inherit] paragraph 1,

The storage duration of member subobjects, base class subobjects and array elements is that of their complete object (6.7.2 [intro.object]).

This wording does not cover member references, which should also have the same storage duration as the object of which they are a member.

Proposed resolution (May, 2015):

Change 6.7.5.6 [basic.stc.inherit] paragraph 1 as follows:

The storage duration of member subobjects, base class subobjects and array elements reference members is that of their complete object (6.7.2 [intro.object]).



1696. Temporary lifetime and non-static data member initializers

Section: 6.7.7  [class.temporary]     Status: CD4     Submitter: Richard Smith     Date: 2013-05-31

[Moved to DR at the November, 2014 meeting.]

Presumably a temporary bound to a reference in a non-static data member initializer should be treated analogously with what happens in a ctor-initializer, but the current wording of 6.7.7 [class.temporary] paragraph 5 is not clear on this point.

See also issue 1815 for similar questions regarding aggregate initialization.

Proposed resolution (June, 2014):

  1. Add the following after 9.4.2 [dcl.init.aggr] paragraph 7:

  2. If a reference member is initialized from its brace-or-equal-initializer and a potentially-evaluated subexpression thereof is an aggregate initialization that would use that brace-or-equal-initializer, the program is ill-formed. [Example:

      struct A;
      extern A a;
      struct A {
        const A& a1 { A{a,a} };   // OK
        const A& a2 { A{} };      // error
      };
      A a{a,a};                   // OK
    

    If an aggregate class C contains a subaggregate...

  3. Delete the first bullet of 6.7.7 [class.temporary] paragraph 5:

  4. The second context is when a reference is bound to a temporary.117 The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:

  5. Insert the following as a new paragraph after 11.9.3 [class.base.init] paragraph 7:

  6. A temporary expression bound to a reference member in a mem-initializer is ill-formed. [Example:

      struct A {
        A() : v(42) { }  // error
        const int& v;
      };
    

    end example]

    In a non-delegating constructor, if a given potentially constructed subobject...

  7. Insert the following as a new paragraph after 11.9.3 [class.base.init] paragraph 9:

  8. A temporary expression bound to a reference member from a brace-or-equal-initializer is ill-formed. [Example:

      struct A {
        A() = default;          // OK
        A(int v) : v(v) { }     // OK
        const int& v = 42;      // OK
      };
      A a1;                     // error: ill-formed binding of temporary to reference
      A a2(1);                  // OK, unfortunately
    

    end example]

    In a non-delegating constructor, the destructor for each potentially constructed subobject...

This resolution also resolves issue 1815.




1697. Lifetime extension and copy elision

Section: 6.7.7  [class.temporary]     Status: CD4     Submitter: Richard Smith     Date: 2013-06-01

[Adopted as paper P0135R1 at the June, 2016 meeting.]

In an example like,

  struct S { ~S(); };
  struct X { X(); X(const X&); };
  struct T { S &&s; X x; };
  void f();
  void g() { T t = T{ {}, {} }; f(); }

it appears that the current wording allows two ways of handling this:

  1. The copy to t in g is not elided. X(const X&) is called, then ~S() is called, then f() is called.

  2. The copy to t in g is elided, so the temporary and t are the same object. Thus, the S object's lifetime is extended to the lifetime of the reference t.s, so first f() is called, then ~S() is called (and X(const X&) is not called).

However, EDG and g++ produce a third behavior: they do not call X(const X&), but they destroy the S() temporary at the end of its full-expression. The current wording does not appear to permit this behavior, but it seems preferable that lifetime extension does not depend on whether copy elision is done.




2107. Lifetime of temporaries for default arguments in array copying

Section: 6.7.7  [class.temporary]     Status: CD4     Submitter: Hubert Tong     Date: 2015-03-19

[Adopted at the February, 2016 meeting.]

The lifetime of temporaries introduced for default arguments in array copying is not specified clearly. Presumably it should be treated like default arguments in default constructors (6.7.7 [class.temporary] paragraph 4), which deletes each element's set of default argument temporaries before construction of the next element.

Proposed resolution (September, 2015):

Change 6.7.7 [class.temporary] paragraphs 4-5 as follows:

There are two three contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context is when a default constructor is called to initialize an element of an array with no corresponding initializer (9.4 [dcl.init]). The second context is when a copy constructor is called to copy an element of an array while the entire array is copied (7.5.5 [expr.prim.lambda], 11.4.5.3 [class.copy.ctor]). If In either case, if the constructor has one or more default arguments, the destruction of every temporary created in a default argument is sequenced before the construction of the next array element, if any.

The second third context is when a reference is bound to a temporary.117 The temporary to which the reference is bound...




1951. Cv-qualification and literal types

Section: 6.8  [basic.types]     Status: CD4     Submitter: Richard Smith     Date: 2014-06-19

[Moved to DR at the May, 2015 meeting.]

6.8 [basic.types] paragraph 10 isn't clear whether a const-qualified class type can be a literal type, and the same for const void.

Proposed resolution (April, 2015):

Change 6.8 [basic.types] paragraph 10 as follows:

A type is a literal type if it is:




2096. Constraints on literal unions

Section: 6.8  [basic.types]     Status: CD4     Submitter: Agustín K-ballo Bergé     Date: 2015-03-11

[Adopted at the February, 2016 meeting.]

According to 6.8 [basic.types] bullet 10.5.3, all the members of a class type must be of non-volatile literal types. This seems overly constraining for unions; it would seem to be sufficient if at least one of its non-static members were of a literal type.

Proposed resolution (September, 2015):

Change 6.8 [basic.types] bullet 10.5 as follows:

A type is a literal type if it is:




1797. Are all bit patterns of unsigned char distinct numbers?

Section: 6.8.2  [basic.fundamental]     Status: CD4     Submitter: Tony van Eerd     Date: 2013-10-02

[Moved to DR at the November, 2014 meeting.]

According to 6.8.2 [basic.fundamental] paragraph 1,

For unsigned narrow character types, all possible bit patterns of the value representation represent numbers.

Presumably the intent is that each distinct bit pattern represents a different number, but this should be made explicit.

Proposed resolution (February, 2014):

Change 6.8.2 [basic.fundamental] paragraph 1 as follows:

...For unsigned narrow character types, all each possible bit patterns pattern of the value representation represent numbers represents a distinct number. These requirements...



2006. Cv-qualified void types

Section: 6.8.4  [basic.compound]     Status: CD4     Submitter: Richard Smith     Date: 2014-09-16

[Moved to DR at the October, 2015 meeting.]

According to 6.8.4 [basic.compound] paragraph 3,

The type of a pointer to void or a pointer to an object type is called an object pointer type. [Note: A pointer to void does not have a pointer-to-object type, however, because void is not an object type. —end note]

This wording excludes cv-qualified void types. There are other references in the Standard to “void type” that are apparently intended to include cv-qualified versions as well.

Proposed resolution (May, 2015):

  1. Change 6.8 [basic.types] paragraph 5 as follows:

  2. ...Incompletely-defined object types and the void types cv void are incomplete types (6.8.2 [basic.fundamental])...
  3. Change 6.8 [basic.types] paragraph 8 as follows:

  4. An object type is a (possibly cv-qualified) type that is not a function type, not a reference type, and not a void type cv void.
  5. Change 6.8.2 [basic.fundamental] paragraph 9 as follows:

  6. The void type has an empty set of values. The A type cv void type is an incomplete type that cannot be completed; such a type has an empty set of values. It is used as the return type for functions that do not return a value. Any expression can be explicitly converted to type cv void (7.6.3 [expr.cast]). An expression of type cv void shall be used only as an expression statement (8.3 [stmt.expr]), as an operand of a comma expression (7.6.20 [expr.comma]), as a second or third operand of ?: (7.6.16 [expr.cond]), as the operand of typeid, noexcept, or decltype, as the expression in a return statement (8.7.4 [stmt.return]) for a function with the return type cv void, or as the operand of an explicit conversion to type cv void.
  7. Change bullet 1.3 of 6.8.4 [basic.compound] as follows:

  8. Change 6.8.4 [basic.compound] paragraph 3 as follows:

  9. The type of a pointer to cv void or a pointer to an object type is called an object pointer type. [Note:...



1949. “sequenced after” instead of “sequenced before”

Section: 6.9.1  [intro.execution]     Status: CD4     Submitter: Richard Smith     Date: 2014-06-18

[Moved to DR at the October, 2015 meeting.]

The term “sequenced after” is used in both the core and library clauses instead of the more-correct “sequenced before.”

Proposed resolution (May, 2015):

  1. Change 6.9.1 [intro.execution] paragraph 13 as follows:

  2. Sequenced before is an asymmetric, transitive, pair-wise relation between evaluations executed by a single thread (6.9.2 [intro.multithread]), which induces a partial order among those evaluations. Given any two evaluations A and B, if A is sequenced before B (or, equivalently, B is sequenced after A), then the execution of A shall precede the execution of B. If A is not sequenced before B...
  3. Change 6.9.2 [intro.multithread] paragraph 14 as follows:

  4. An evaluation A happens before an evaluation B (or, equivalently, B happens after A) if:...
  5. Change 6.9.1 [intro.execution] paragraph 15 as follows:

  6. ...Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function For each function invocation F, for every evaluation A that occurs within F and every evaluation B that does not occur within F but is evaluated on the same thread and as part of the same signal handler (if any), either A is sequenced before B or B is sequenced before A.9 [Note: if A and B would not otherwise be sequenced then they are indeterminately sequenced. —end note] Several contexts...
  7. Change 6.9.3.2 [basic.start.static] paragraph 4 as follows:

  8. It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done happens before the first statement of main. If the initialization is deferred to some point in time happen after the first statement of main, it shall occur happens before the first odr-use (6.3 [basic.def.odr]) of any function or variable...
  9. Change 6.9.3.2 [basic.start.static] paragraph 5 as follows:

  10. It is implementation-defined whether the dynamic initialization of a non-local variable with static or thread storage duration is done sequenced before the first statement of the initial function of the thread. If the initialization is deferred to some point in time sequenced after the first statement of the initial function of the thread, it shall occur is sequenced before the first odr-use (6.3 [basic.def.odr]) of any variable with thread storage duration defined in the same translation unit as the variable to be initialized.
  11. Change 8.6.4 [stmt.for] paragraph 1 as follows:

  12. ...[Note: Thus the first statement specifies initialization for the loop; the condition (8.5 [stmt.select]) specifies a test, made sequenced before each iteration, such that the loop is exited when the condition becomes false; the expression often specifies incrementing that is done sequenced after each iteration. —end note]
  13. Add the following as a new paragraph at the end of Clause 14 [except]:

  14. In this section, “before” and “after” refer to the “sequenced before” relation (6.9.1 [intro.execution]).



2146. Scalar object vs memory location in definition of “unsequenced”

Section: 6.9.1  [intro.execution]     Status: CD4     Submitter: Jens Maurer     Date: 2015-06-22

[Adopted at the February, 2016 meeting.]

According to 6.9.1 [intro.execution] paragraph 15,

If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, and they are not potentially concurrent (6.9.2 [intro.multithread]), the behavior is undefined.

Should this refer to “memory location,” which also encompasses contiguous bit-fields, as the definition of data races in 6.9.2 [intro.multithread] does? For example,

  struct S {
    int x : 4;
    int y : 4;
    int z : 4;
  };

  void f(int, int, int);
  int g(int, S&);

  int main(int argc, char ** argv) {
    S s = { argc, argc+1, argc+2 };
    f(++s.x, g(++s.y, s), ++s.z);
  }

Proposed resolution (February, 2016):

Change 6.9.1 [intro.execution] paragraph 15 as follows:

...If a side effect on a scalar object memory location (6.7.1 [intro.memory]) is unsequenced relative to either another side effect on the same scalar object memory location or a value computation using the value of any object in the same scalar object memory location, and they are not potentially concurrent (6.9.2 [intro.multithread]), the behavior is undefined. [Note: The next section...



2026. Zero-initialization and constexpr

Section: 6.9.3  [basic.start]     Status: CD4     Submitter: Jason Merrill     Date: 2014-10-20

[Moved to DR at the October, 2015 meeting.]

According to 6.9.3.2 [basic.start.static] paragraph 2,

Variables with static storage duration (6.7.5.2 [basic.stc.static]) or thread storage duration (6.7.5.3 [basic.stc.thread]) shall be zero-initialized (9.4 [dcl.init]) before any other initialization takes place.

Does this apply to constant initialization as well? For example, should the following be well-formed, relying on the presumed zero-initialization preceding the constant initialization?

  constexpr int i = i;
  struct s {
    constexpr s() : v(v) { }
    int v;
  };
  constexpr s s1;

Notes from the November, 2014 meeting:

CWG agreed that constant initialization should be considered as happening instead of zero initialization in these cases, making the declarations ill-formed.

Proposed resolution (May, 2015):

  1. Rename 6.9.3.2 [basic.start.static] and make the indicated changes, moving parts of its content to a new section immediately following, as indicated below:

  2. 3.6.2 IStatic initialization of non-local variables [basic.start.init.static]

    There are two broad classes of named non-local variables: those with static storage duration (6.7.5.2 [basic.stc.static]) and those with thread storage duration (6.7.5.3 [basic.stc.thread]). Non-local variables Variables with static storage duration are initialized as a consequence of program initiation. Non-local variables Variables with thread storage duration are initialized as a consequence of thread execution. Within each of these phases of initiation, initialization occurs as follows.

    Variables with static storage duration (6.7.5.2 [basic.stc.static]) or thread storage duration (6.7.5.3 [basic.stc.thread]) shall be zero-initialized (9.4 [dcl.init]) before any other initialization takes place. A constant initializer for an object o is an expression that is a constant expression, except that it may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types [Note: such a class may have a non-trivial destructor —end note]. Constant initialization is performed:

    If constant initialization is not performed, a variable with static storage duration (6.7.5.2 [basic.stc.static]) or thread storage duration (6.7.5.3 [basic.stc.thread]) is zero-initialized (9.4 [dcl.init]). Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place. Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, and otherwise is ordered [Note: an explicitly specialized static data member or variable template specialization has ordered initialization. —end note]. Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definitions in the translation unit. If a program starts a thread (33.4 [thread.threads]), the subsequent initialization of a variable is unsequenced with respect to the initialization of a variable defined in a different translation unit. Otherwise, the initialization of a variable is indeterminately sequenced with respect to the initialization of a variable defined in a different translation unit. If a program starts a thread, the subsequent unordered initialization of a variable is unsequenced with respect to every other dynamic initialization. Otherwise, the unordered initialization of a variable is indeterminately sequenced with respect to every other dynamic initialization. [Note: This definition permits initialization of a sequence of ordered variables concurrently with another sequence. —end note] [Note: The dynamic initialization of non-local variables is described in 3.6.3 [basic.start.dynamic]; that of local static variables is described in 8.8 [stmt.dcl]. —end note]

    An implementation is permitted to perform the initialization of a non-local variable with static or thread storage duration as a static initialization even if such initialization is not required to be done statically, provided that

    [Note: As a consequence, if the initialization of an object obj1 refers to an object obj2 of namespace scope potentially requiring dynamic initialization and defined later in the same translation unit, it is unspecified whether the value of obj2 used will be the value of the fully initialized obj2 (because obj2 was statically initialized) or will be the value of obj2 merely zero-initialized. For example,

      inline double fd() { return 1.0; }
      extern double d1;
      double d2 = d1;   // unspecified:
                        // may be statically initialized to 0.0 or
                        // dynamically initialized to 0.0 if d1 is
                        // dynamically initialized, or 1.0 otherwise
      double d1 = fd(); // may be initialized statically or dynamically to 1.0
    

    end note]

  3. Insert a new section after 6.9.3.2 [basic.start.static]:

  4. 3.6.3 Dynamic initialization of non-local variables [basic.start.dynamic]
  5. Move part of 6.9.3.2 [basic.start.static] paragraph 2 as paragraph 1 of the new section:

  6. Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, and otherwise is ordered [Note: an explicitly specialized static data member or variable template specialization has ordered initialization. —end note]. Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definitions in the translation unit. If a program starts a thread (33.4 [thread.threads]), the subsequent initialization of a variable is unsequenced with respect to the initialization of a variable defined in a different translation unit. Otherwise, the initialization of a variable is indeterminately sequenced with respect to the initialization of a variable defined in a different translation unit. If a program starts a thread, the subsequent unordered initialization of a variable is unsequenced with respect to every other dynamic initialization. Otherwise, the unordered initialization of a variable is indeterminately sequenced with respect to every other dynamic initialization. [Note: This definition permits initialization of a sequence of ordered variables concurrently with another sequence. —end note]
  7. Move paragraphs 4-6 of 6.9.3.2 [basic.start.static] as paragraphs 2-4 of the new section:

  8. It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first odr-use (6.3 [basic.def.odr]) of any function or variable defined in the same translation unit as the variable to be initialized. [Footnote: A non-local variable with static storage duration having initialization with side-effects must be initialized even if it is not odr-used (6.3 [basic.def.odr], 6.7.5.2 [basic.stc.static]). —end footnote] [Example:

      // - File 1 -
      #include "a.h"
      #include "b.h"
      B b;
      A::A(){
        b.Use();
      }
    
      // - File 2 -
      #include "a.h"
      A a;
    
      // - File 3 -
      #include "a.h"
      #include "b.h"
      extern A a;
      extern B b;
      int main() {
        a.Use();
        b.Use();
    }
    

    It is implementation-defined whether either a or b is initialized before main is entered or whether the initializations are delayed until a is first odr-used in main. In particular, if a is initialized before main is entered, it is not guaranteed that b will be initialized before it is odr-used by the initialization of a, that is, before A::A is called. If, however, a is initialized at some point after the first statement of main, b will be initialized prior to its use in A::A. —end example]

    It is implementation-defined whether the dynamic initialization of a non-local variable with static or thread storage duration is done before the first statement of the initial function of the thread. If the initialization is deferred to some point in time after the first statement of the initial function of the thread, it shall occur before the first odr-use (6.3 [basic.def.odr]) of any variable with thread storage duration defined in the same translation unit as the variable to be initialized.

    If the initialization of a non-local variable with static or thread storage duration exits via an exception, std::terminate is called (14.6.2 [except.terminate]).

  9. Change 8.8 [stmt.dcl] paragraph 4 as follows:
  10. The zero-initialization (9.4 [dcl.init]) of all block-scope variables with static storage duration (6.7.5.2 [basic.stc.static]) or thread storage duration (6.7.5.3 [basic.stc.thread]) is performed before any other initialization takes place. Constant initialization (6.9.3.2 [basic.start.static]) of a block-scope entity with static storage duration, if applicable, is performed before its block is first entered. An implementation is permitted to perform early initialization of other block-scope variables with static or thread storage duration under the same conditions that an implementation is permitted to statically initialize a variable with static or thread storage duration in namespace scope (6.9.3.2 [basic.start.static]). Otherwise such a variable is initialized Dynamic initialization of a block-scope variable with static storage duration (6.7.5.2 [basic.stc.static]) or thread storage duration (6.7.5.3 [basic.stc.thread]) is performed the first time control passes...

Editing note: all existing cross-references to 6.9.3.2 [basic.start.static] must be examined to determine which of the two current sections should be targeted.




1886. Language linkage for main()

Section: 6.9.3.1  [basic.start.main]     Status: CD4     Submitter: Richard Smith     Date: 2014-03-04

[Moved to DR at the May, 2015 meeting.]

There does not appear to be any restriction on giving main() an explicit language linkage, but it should probably be either ill-formed or conditionally-supported.

Proposed resolution (November, 2014):

  1. Change 6.9.3.1 [basic.start.main] paragraph 2 as follows:

  2. An implementation shall not predefine the main function. This function shall not be overloaded. It Its type shall have C++ language linkage and it shall have a declared return type of type int, but otherwise its type is implementation-defined. An implementation shall allow both...
  3. Change 6.9.3.1 [basic.start.main] paragraph 3 as follows:

  4. The function main shall not be used within a program. The linkage (6.6 [basic.link]) of main is implementation-defined. A program that defines main as deleted or that declares main to be inline, static, or constexpr is ill-formed. The main function shall not be declared with a linkage-specification (9.11 [dcl.link]). A program that declares a variable main at global scope or that declares the name main with C language linkage (in any namespace) is ill-formed. The name main is not otherwise reserved...



1744. Unordered initialization for variable template specializations

Section: 6.9.3.2  [basic.start.static]     Status: CD4     Submitter: Richard Smith     Date: 2013-09-03

[Moved to DR at the November, 2014 meeting.]

According to 6.9.3.2 [basic.start.static] paragraph 2,

Definitions of explicitly specialized class template static data members have ordered initialization. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) have unordered initialization.

This is not clear whether it is referring to static data members of explicit specializations of class templates or to explicit specializations of static data members of class template specializations. It also does not apply to static data member templates and non-member variable templates.

Proposed resolution (February, 2014):

Change 6.9.3.2 [basic.start.static] paragraph 2 as follows:

...Dynamic initialization of a non-local variable with static storage duration is either ordered or unordered. Definitions of explicitly specialized class template static data members have ordered initializa-tion. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) have unordered initialization. Other non-local variables with static storage duration have ordered initialization unordered if the variable is an implicitly or explicitly instantiated specialization, and otherwise is ordered [Note: an explicitly specialized static data member or variable template specialization has ordered initialization. —end note]. Variables with ordered initialization...



1834. Constant initialization binding a reference to an xvalue

Section: 6.9.3.2  [basic.start.static]     Status: CD4     Submitter: Richard Smith     Date: 2014-01-13

[Moved to DR at the November, 2014 meeting.]

According to 6.9.3.2 [basic.start.static] paragraph 2,

Constant initialization is performed:

This wording should also permit the reference to be bound to an xvalue, e.g., a subobject of a temporary, and not just to a complete temporary.

Proposed resolution (February, 2014):

Change 6.9.3.2 [basic.start.static] paragraph 2 as follows (note that this resolution incorporates the overlapping change from the resolution of issue 1299)::

...Constant initialization is performed:




238. Precision and accuracy constraints on floating point

Section: Clause 7  [expr]     Status: CD4     Submitter: Christophe de Dinechin     Date: 31 Jul 2000

[Adopted at the February, 2016 meeting.]

It is not clear what constraints are placed on a floating point implementation by the wording of the Standard. For instance, is an implementation permitted to generate a "fused multiply-add" instruction if the result would be different from what would be obtained by performing the operations separately? To what extent does the "as-if" rule allow the kinds of optimizations (e.g., loop unrolling) performed by FORTRAN compilers?

Proposed resolution (September, 2015):

Change 6.8.2 [basic.fundamental] paragraph 8 as follows:

There are three floating point types: float, double, and long double. The type double provides at least as much precision as float, and the type long double provides at least as much precision as double. The set of values of the type float is a subset of the set of values of the type double; the set of values of the type double is a subset of the set of values of the type long double. The value representation of floating-point types is implementation-defined. [Note: This International Standard imposes no requirements on the accuracy of floating-point operations; see also 17.3 [support.limits]. —end note] Integral and floating types are collectively called arithmetic types. Specializations of the standard library template std::numeric_limits (17.3 [support.limits]) shall specify the maximum and minimum values of each arithmetic type for an implementation.



636. Dynamic type of objects and aliasing

Section: 7.2.1  [basic.lval]     Status: CD4     Submitter: Gabriel Dos Reis     Date: 23 May 2007

The aliasing rules given in 7.2.1 [basic.lval] paragraph 10 rely on the concept of “dynamic type.” The problem is that the dynamic type of an object often cannot be determined (or even sufficiently constrained) at the point at which an optimizer needs to be able to determine whether aliasing might occur or not. For example, consider the function

    void foo(int* p, double* q) {
        *p = 42;
        *q = 3.14;
    }

An optimizer, on the basis of the existing aliasing rules, might decide that an int* and a double* cannot refer to the same object and reorder the assignments. This reordering, however, could result in undefined behavior if the function foo is called as follows:

   void goo() {
      union {
         int i;
         double d;
      } t;

      t.i = 12;

      foo(&t.i, &t.d);

      cout << t.d << endl;
   };

Here, the reference to t.d after the call to foo will be valid only if the assignments in foo are executed in the order in which they were written; otherwise, the union will contain an int object rather than a double.

One possibility would be to require that if such aliasing occurs, it be done only via member names and not via pointers.

Notes from the July, 2007 meeting:

This is the same issue as C's DR236. The CWG expressed a desire to address the issue the same way C99 does. The issue also occurs in C++ when placement new is used to end the lifetime of one object and start the lifetime of a different object occupying the same storage.

Proposed resolution (March, 2017):

This issue is resolved by the resolution of issue 1776.




2122. Glvalues of void type

Section: 7.2.1  [basic.lval]     Status: CD4     Submitter: CWG     Date: 2015-05-05

[Adopted at the February, 2016 meeting.]

According to 7.2.1 [basic.lval] paragraph 4,

Unless otherwise indicated (7.6.1.3 [expr.call]), prvalues shall always have complete types or the void type; in addition to these types, glvalues can also have incomplete types.

This wording inadvertently implies that glvalues can have type void, which is not correct.

Proposed resolution (January, 2016):

Change 7.2.1 [basic.lval] paragraph 4 as follows:

Unless otherwise indicated (7.6.1.3 [expr.call]), prvalues a prvalue shall always have complete types type or the void type; in addition to these types, glvalues can also have incomplete types. A glvalue shall not have type cv void. [Note: class A glvalue may have complete or incomplete non-void type. Class and array prvalues can have cv-qualified types; other prvalues always have cv-unqualified types. See Clause 7 [expr]. —end note]



1981. Implicit contextual conversions and explicit

Section: 7.3  [conv]     Status: CD4     Submitter: Richard Smith     Date: 2014-08-08

[Moved to DR at the October, 2015 meeting.]

According to 7.3 [conv] paragraph 5,

Certain language constructs require conversion to a value having one of a specified set of types appropriate to the construct. An expression e of class type E appearing in such a context is said to be contextually implicitly converted to a specified type T and is well-formed if and only if e can be implicitly converted to a type T that is determined as follows: E is searched for conversion functions whose return type is cv T or reference to cv T such that T is allowed by the context. There shall be exactly one such T.

This description leaves open two questions: first, can explicit conversion functions be used for this conversion? Second, assuming that they cannot, is the restriction to “exactly one such T” enforced before or after exclusion of explicit conversion functions?

Notes from the November, 2014 meeting:

CWG felt that explicit conversion functions should be removed from consideration before determining the set of types for the conversion.

Proposed resolution (May, 2015):

Change 7.3 [conv] paragraph 5 as follows:

...An expression e of class type E appearing in such a context is said to be contextually implicitly converted to a specified type T and is well-formed if and only if e can be implicitly converted to a type T that is determined as follows: E is searched for non-explicit conversion functions whose return type is cv T or reference to cv T such that T is allowed by the context. There shall be exactly one such T.



2140. Lvalue-to-rvalue conversion of std::nullptr_t

Section: 7.3.2  [conv.lval]     Status: CD4     Submitter: Richard Smith     Date: 2015-06-12

[Adopted at the February, 2016 meeting.]

The current rules in 7.3.2 [conv.lval] paragraph 2 do not require fetching the value in memory of an object of type std::nullpt_t in order to produce its prvalue. This choice has implications that may not have been considered for questions like whether use of a std::nullptr_t that is an inactive member of a union results in undefined behavior or whether a volatile std::nullptr_t variable in a discarded-value expression produces a side effect.

Proposed resolution (January, 2016):

Change 7.3.2 [conv.lval] bullet 2.3 as follows:

...In all other cases, the result of the conversion is determined according to the following rules:




330. Qualification conversions and pointers to arrays of pointers

Section: 7.3.6  [conv.qual]     Status: CD4     Submitter: Roger Orr     Date: 2 Jan 2002

[Moved to DR as N4261 at the November, 2014 meeting.]

Section 7.3.6 [conv.qual] covers the case of multi-level pointers, but does not appear to cover the case of pointers to arrays of pointers. The effect is that arrays are treated differently from simple scalar values.

Consider for example the following code: (from the thread "Pointer to array conversion question" begun in comp.lang.c++.moderated)

  int main()
  {
     double *array2D[2][3];

     double       *       (*array2DPtr1)[3] = array2D;     // Legal
     double       * const (*array2DPtr2)[3] = array2DPtr1; // Legal
     double const * const (*array2DPtr3)[3] = array2DPtr2; // Illegal
  }
and compare this code with:-
  int main()
  {
     double *array[2];

     double       *       *ppd1 = array; // legal
     double       * const *ppd2 = ppd1;  // legal
     double const * const *ppd3 = ppd2;  // certainly legal (4.4/4)
  }

The problem appears to be that the pointed to types in example 1 are unrelated since nothing in the relevant section of the standard covers it - 7.3.6 [conv.qual] does not mention conversions of the form "cv array of N pointer to T" into "cv array of N pointer to cv T"

It appears that reinterpret_cast is the only way to perform the conversion.

Suggested resolution:

Artem Livshits proposed a resolution :-

"I suppose if the definition of "similar" pointer types in 7.3.6 [conv.qual] paragraph 4 was rewritten like this:

T1 is cv1,0 P0 cv1,1 P1 ... cv1,n-1 Pn-1 cv1,n T

and

T2 is cv1,0 P0 cv1,1 P1 ... cv1,n-1 Pn-1 cv1,n T

where Pi is either a "pointer to" or a "pointer to an array of Ni"; besides P0 may be also a "reference to" or a "reference to an array of N0" (in the case of P0 of T2 being a reference, P0 of T1 may be nothing).

it would address the problem.

In fact I guess Pi in this notation may be also a "pointer to member", so 7.3.6 [conv.qual]/{4,5,6,7} would be nicely wrapped in one paragraph."

Additional note, February, 2014:

Geoffrey Romer: LWG plans to resolve US 16/LWG 2118, which concerns qualification-conversion of unique_ptr for array types, by effectively punting the issue to core: unique_ptr<T[]> will be specified to be convertible to unique_ptr<U[]> only if T(*)[] is convertible to U(*)[]. LWG and LEWG have jointly decided to adopt the same approach for shared_ptr<T[]> and shared_ptr<T[N]> in the Fundamentals TS. This will probably substantially raise the visibility of core issue 330, which concerns the fact that array types support only top-level qualification conversion of the element type, so it'd be nice if CWG could bump up the priority of that issue.

See also issue 1865.

Proposed resolution (October, 2014):

The resolution is contained in paper N4261.




1816. Unclear specification of bit-field values

Section: 7.3.9  [conv.integral]     Status: CD4     Submitter: Hubert Tong     Date: 2013-12-02

[Moved to DR at the November, 2014 meeting.]

7.3.9 [conv.integral] paragraph 3 says, regarding integral conversions,

If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.

The values that can be represented in a bit-field are not well specified, except for the correspondence with the values of an enumeration in 9.7.1 [dcl.enum]. In particular, it is not clear whether a bit-field has a sign bit and whether bit-fields may have padding bits.

Another point to note in this wording: paragraph 1 describes the context as

A prvalue of an integer type can be converted to a prvalue of another integer type.

However, prvalues cannot be bit-fields, so the applicability of the mention of “bit-field width” in paragraph 3 is unclear.

Proposed resolution (February, 2014):

  1. Change 7.3.9 [conv.integral] paragraph 3 as follows:

  2. If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.
  3. Change 7.6.1.6 [expr.post.incr] paragraph 1 as follows:

  4. ...The result is a prvalue. The type of the result is the cv-unqualified version of the type of the operand. If the operand is a bit-field that cannot represent the incremented value, the resulting value of the bit-field is implementation-defined. See also 7.6.6 [expr.add] and 7.6.19 [expr.ass].
  5. Change 7.6.19 [expr.ass] paragraph 6 as follows:

  6. When the left operand of an assignment operator denotes a reference to T, the operation assigns to the object of type T denoted by the reference is a bit-field that cannot represent the value of the expression, the resulting value of the bit-field is implementation-defined..
  7. Change the final bullet of 9.4 [dcl.init] paragraph 17 as follows:

  8. The semantics of initializers are as follows...




1942. Incorrect reference to trailing-return-type

Section: 7.5.5  [expr.prim.lambda]     Status: CD4     Submitter: Mike Miller     Date: 2014-06-16

[Moved to DR at the May, 2015 meeting.]

According to 7.5.5 [expr.prim.lambda] paragraph 4,

If a lambda-expression does not include a lambda-declarator, it is as if the lambda-declarator were (). The lambda return type is auto, which is replaced by the trailing-return-type if provided...

trailing-return-type is a syntactic nonterminal that includes the -> and thus cannot be used directly to refer to the type. It should instead say something like, ...the type specified by the trailing-return-type.

The reference in 9.3.4.6 [dcl.fct] paragraph 2, “...returning trailing-return-type” should be similarly adjusted.

Proposed resolution (November, 2014):

  1. Change 7.5.5 [expr.prim.lambda] paragraph 4 as follows:

  2. If a lambda-expression does not include a lambda-declarator, it is as if the lambda-declarator were (). The lambda return type is auto, which is replaced by the type specified by the trailing-return-type if provided and/or deduced from return statements as described in 9.2.9.7 [dcl.spec.auto]. [Example:...
  3. Change 9.3.4.6 [dcl.fct] paragraph 2 as follows:

  4. The type of the declarator-id in D is “derived-declarator-type-list function of (parameter-declaration-clause) cv-qualifier-seqopt ref-qualifieropt returning trailing-return-type U, where U is the type specified by the trailing-return-type. The optional attribute-specifier-seq...



1722. Should lambda to function pointer conversion function be noexcept?

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD4     Submitter: Ville Voutilainen     Date: 2013-07-31

[Moved to DR at the October, 2015 meeting.]

According to 7.5.5 [expr.prim.lambda] paragraph 6,

The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function with C++ language linkage (9.11 [dcl.link]) having the same parameter and return types as the closure type's function call operator.

This does not specify whether the conversion function is noexcept(true) or noexcept(false). It might be helpful to nail that down.

Proposed resolution (May, 2015):

Change 7.5.5 [expr.prim.lambda] paragraph 6 as follows:

The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function with C++ language linkage (9.11 [dcl.link]) having the same parameter and return types as the closure type's function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type's function call operator. For a generic lambda with no lambda-capture, the closure type has a public non-virtual non-explicit const conversion function template to pointer to function. The conversion function template... [Example:

  auto GL = [](auto a) { std::cout << a; return a; };
  int (*GL_int)(int) = GL; // OK: through conversion function template
  GL_int(3);               // OK: same as GL(3)

end example] The conversion function or conversion function template is public, non-virtual, non-explicit, const, and has a non-throwing exception specification (14.5 [except.spec]).




1780. Explicit instantiation/specialization of generic lambda operator()

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD4     Submitter: Hubert Tong     Date: 2013-09-26

[Moved to DR at the November, 2014 meeting.]

Similarly to issue 1738, it is not clear whether it is permitted to explicitly instantiate or specialize the call operator of a polymorphic lambda (via decltype).

Proposed resolution (February, 2014):

Add the following as a new paragraph following 7.5.5 [expr.prim.lambda] paragraph 21:

The closure type associated with a lambda-expression has an implicitly-declared destructor (11.4.7 [class.dtor]).

A member of a closure type shall not be explicitly instantiated (13.9.2 [temp.inst]), explicitly specialized (13.9.3 [temp.explicit]), or named in a friend declaration (11.8.4 [class.friend]).




1891. Move constructor/assignment for closure class

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD4     Submitter: Jonathan Caves     Date: 2014-03-10

[Moved to DR at the November, 2014 meeting.]

According to 7.5.5 [expr.prim.lambda] paragraph 20,

The closure type associated with a lambda-expression has a deleted (9.5.3 [dcl.fct.def.delete]) default constructor and a deleted copy assignment operator. It has an implicitly-declared copy constructor (11.4.5.3 [class.copy.ctor]) and may have an implicitly-declared move constructor (11.4.5.3 [class.copy.ctor]).

However, according to 11.4.5.3 [class.copy.ctor] paragraph 9,

If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if

It is not clear how this applies to the closure class. Would it be better to state that the closure class has a defaulted move constructor and a defaulted move assignment operator? There is already wording that handles the case if they are ultimately defined as deleted.

Proposed resolution (October, 2014):

Change 7.5.5 [expr.prim.lambda] paragraph 20 as follows:

The closure type associated with a lambda-expression has a deleted (9.5.3 [dcl.fct.def.delete]) no default constructor and a deleted copy assignment operator. It has an implicitly-declared a defaulted copy constructor (11.4.5.3 [class.copy.ctor]) and may have an implicitly-declared and a defaulted move constructor (11.4.5.3 [class.copy.ctor]). [Note: The copy/move constructor is implicitly defined in the same way as any other implicitly declared copy/move constructor would be implicitly defined These special member functions are implicitly defined as usual, and might therefore be defined as deleted. —end note]



2095. Capturing rvalue references to functions by copy

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: CD4     Submitter: Hubert Tong     Date: 2015-03-07

[Adopted at the February, 2016 meeting.]

According to 7.5.5 [expr.prim.lambda] paragraph 15,

An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that is not of the form & identifier or & identifier initializer. For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the type of the corresponding captured entity if the entity is not a reference to an object, or the referenced type otherwise.

It's not clear how to handle capture by copy when the entity is an rvalue reference to function. In particular, this appears to be a contradiction with 7.5.5 [expr.prim.lambda] paragraph 3,

An implementation shall not add members of rvalue reference type to the closure type.

Proposed resolution (September, 2015):

Change 7.5.5 [expr.prim.lambda] paragraph 15 as follows:

An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that is not of the form & identifier or & identifier initializer. For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the referenced type if the entity is a reference to an object, an lvalue reference to the referenced function type if the entity is a reference to a function, or the type of the corresponding captured entity if the entity is not a reference to an object, or the referenced type otherwise. [Note: If the captured entity is a reference to a function, the corresponding data member is also a reference to a function. —end note] A member of an anonymous union shall not be captured by copy.



1880. When are parameter objects destroyed?

Section: 7.6.1.3  [expr.call]     Status: CD4     Submitter: Hubert Tong     Date: 2014-02-25

[Adopted at the June, 2016 meeting as part of paper P0135R1.]

According to 7.6.1.3 [expr.call] paragraph 4,

The lifetime of a parameter ends when the function in which it is defined returns. The initialization and destruction of each parameter occurs within the context of the calling function.

This presumably means that the destruction of the parameter object occurs before the end of the full-expression, unlike temporaries. This is not what current implementations do, however. It is not clear that a change to treat parameter objects like temporaries, to match existing practice, would be an improvement, however, as it would result in ABI breakage for implementations that destroy parameters in the called function.

See also issue 1935 for a related question regarding the handling of arguments to a placement allocation function and placement deallocation function.

Notes from the June, 2014 meeting:

WG decided to make it unspecified whether parameter objects are destroyed immediately following the call or at the end of the full-expression to which the call belongs. This approach also resolves issue 1935.




1885. Return value of a function is underspecified

Section: 7.6.1.3  [expr.call]     Status: CD4     Submitter: Jens Maurer     Date: 2014-02-28

[Moved to DR at the November, 2014 meeting.]

The intent is that a function call is a temporary expression whose result is a temporary, but that appears not to be said anywhere. It should also be clarified that a return statement in a function with a class return type copy-initializes the temporary that is the result. The sequencing of the initialization of the returned temporary, destruction of temporaries in the return expression, and destruction of automatic variables should be make explicit.

Proposed resolution (October, 2014):

Change 8.7.4 [stmt.return] paragraphs 2-3 as follows:

A return statement with neither an expression nor a braced-init-list can be used only in functions that do not return a value, that is, The expression or braced-init-list of a return statement is called its operand. A return statement with no operand shall be used only in a function with the whose return type is cv void, a constructor (11.4.5 [class.ctor]), or a destructor (11.4.7 [class.dtor]). A return statement with an operand of type void shall be used only in a function whose return type is cv void. A return statement with an expression of non-void type can be used only any other operand shall be used only in functions returning a value; the value of the expression is returned to the caller of the function. The value of the expression is implicitly converted to the return type of the function in which it appears a function whose return type is not cv void; the return statement initializes the object or reference to be returned by copy-initialization (9.4 [dcl.init]) from the operand. [Note: A return statement can involve the construction and copy or move of a temporary object (6.7.7 [class.temporary]). [Note: A copy or move operation associated with a return statement may be elided or considered as an rvalue for the purpose of overload resolution in selecting a constructor (11.4.5.3 [class.copy.ctor]). —end note] A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization (9.4.5 [dcl.init.list]) from the specified initializer list. [Example:

  std::pair<std::string,int> f(const char* p, int x) {
    return {p,x};
  }

end example] Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.

A return statement with an expression of type void can be used only in functions with a return type of cv void; the expression is evaluated just before the function returns to its caller. The copy-initialization of the returned entity is sequenced before the destruction of temporaries at the end of the full-expression established by the operand of the return statement, which, in turn, is sequenced before the destruction of local variables (8.7 [stmt.jump]) of the block enclosing the return statement.

(See also the related changes in the resolution of issue 1299.)




2176. Destroying the returned object when a destructor throws

Section: 7.6.1.3  [expr.call]     Status: CD4     Submitter: Richard Smith     Date: 2015-09-28

[Adopted at the February, 2016 meeting.]

Consider the following example:

  #include <stdio.h>

  struct X {
    X() { puts("X()"); }
    X(const X&) { puts("X(const X&)"); }
    ~X() { puts("~X()"); }
  };

  struct Y { ~Y() noexcept(false) { throw 0; } };

  X f() {
    try {
      Y y;
      return {};
    } catch (...) {
    }
    return {};
  }

  int main() {
    f();
  }

Current implementations print X() twice but ~X() only once. That is obviously wrong, but it is not clear that the current wording covers this case.

Proposed resolution (February, 2016):

Change 14.3 [except.ctor] paragraph 2 as follows:

The destructor is invoked for each automatic object of class type constructed, but not yet destroyed, since the try block was entered. If an exception is thrown during the destruction of temporaries or local variables for a return statement (8.7.4 [stmt.return]), the destructor for the returned object (if any) is also invoked. The automatic objects are destroyed in the reverse order of the completion of their construction. [Example:

  struct A { };

  struct Y { ~Y() noexcept(false) { throw 0; } };

  A f() {
    try {
      A a;
      Y y;
      A b;
      return {};   // #1
    } catch (...) {
    }
    return {};     // #2
  }

At #1, the returned object of type A is constructed. Then, the local variable b is destroyed (8.7 [stmt.jump]). Next, the local variable y is destroyed, causing stack unwinding, resulting in the destruction of the returned object, followed by the destruction of the local variable a. Finally, the returned object is constructed again at #2. —end example]




1832. Casting to incomplete enumeration

Section: 7.6.1.9  [expr.static.cast]     Status: CD4     Submitter: Richard Smith     Date: 2014-01-16

[Moved to DR at the November, 2014 meeting.]

The specification of casting to an enumeration type in 7.6.1.9 [expr.static.cast] paragraph 10 does not require that the enumeration type be complete. Should it? (There is variation among implementations.)

Proposed resolution (February, 2014):

Change 7.6.1.9 [expr.static.cast] paragraph 10 as follows:

A value of integral or enumeration type can be explicitly converted to an a complete enumeration type. The value is...



1800. Pointer to member of nested anonymous union

Section: 7.6.2.2  [expr.unary.op]     Status: CD4     Submitter: Richard Smith     Date: 2013-10-22

[Moved to DR at the November, 2014 meeting.]

According to 7.6.2.2 [expr.unary.op] paragraph 3,

The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a qualified-id naming a non-static member m of some class C with type T, the result has type “pointer to member of class C of type T” and is a prvalue designating C::m.

It is not clear whether this wording applies to variant members of C (i.e., members of nested anonymous unions) or only to its non-variant members. For example, given

  struct A { union { int n; }; };
  auto x = &A::n;

should the type of x be int A::* or int A::anon::*? Current implementations choose the former.

Proposed resolution (February, 2014):

Change 7.6.2.2 [expr.unary.op] paragraph 3 as follows:

The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a qualified-id naming a non-static or variant member m of some class C with type T, the result has type “pointer to member of class C of type T” and is a prvalue designating C::m. Otherwise...



1971. Unclear disambiguation of destructor and operator~

Section: 7.6.2.2  [expr.unary.op]     Status: CD4     Submitter: Hubert Tong     Date: 2014-07-15

[Moved to DR at the May, 2015 meeting.]

There is language in 7.6.2.2 [expr.unary.op] paragraph 10 to disambiguate destructor references from references to operator~:

There is an ambiguity in the unary-expression ~X(), where X is a class-name or decltype-specifier. The ambiguity is resolved in favor of treating ~ as a unary complement rather than treating ~X as referring to a destructor.

However, it is not clear whether this is intended to apply to an example like,

  struct X {
    X(int);
    operator int();
    void foo() {
      ~X(0);
    }
  };

where the form of reference has an apparent argument.

Proposed resolution (November, 2014):

Change 7.6.2.2 [expr.unary.op] paragraph 10 as follows:

The operand of ~ shall have integral or unscoped enumeration type; the result is the one's complement of its operand. Integral promotions are performed. The type of the result is the type of the promoted operand. There is an ambiguity in the unary-expression ~X(), where X is in the grammar when ~ is followed by a class-name or decltype-specifier. The ambiguity is resolved in favor of by treating ~ as a the unary complement operator rather than treating ~X as referring to as the start of an unqualified-id naming a destructor. [Note: Because the grammar does not permit an operator to follow the ., ->, or :: tokens, a ~ followed by a class-name or decltype-specifier in a member access expression or qualified-id is unambiguously parsed as a destructor name. —end note]



1653. Removing deprecated increment of bool

Section: 7.6.2.3  [expr.pre.incr]     Status: CD4     Submitter: Bjarne Stroustrup     Date: 2013-04-19

[Adopted at the October, 2015 meeting as P0002R1.]

The preincrement (7.6.2.3 [expr.pre.incr]) and postincrement (7.6.1.6 [expr.post.incr]) operators can be applied to operands of type bool, setting the operand to true, but this use is deprecated. Can it now be removed altogether?




1465. noexcept and std::bad_array_new_length

Section: 7.6.2.7  [expr.unary.noexcept]     Status: CD4     Submitter: Daniel Krügler     Date: 2012-02-12

[Moved to DR at the November, 2014 meeting.]

The list of causes for a false result of the noexcept operator does not include a new-expression with a non-constant array bound, which could result in an exception even if the allocation function that would be called is declared not to throw (see 7.6.2.8 [expr.new] paragraph 7).

Proposed resolution (June, 2012):

This issue is resolved by the resolution of issue 1351.




1748. Placement new with a null pointer

Section: 7.6.2.8  [expr.new]     Status: CD4     Submitter: Marc Glisse     Date: 2013-09-11

[Moved to DR at the November, 2014 meeting.]

According to 7.6.2.8 [expr.new] paragraph 15,

[Note: unless an allocation function is declared with a non-throwing exception-specification (14.5 [except.spec]), it indicates failure to allocate storage by throwing a std::bad_alloc exception (Clause 14 [except], 17.6.4.1 [bad.alloc]); it returns a non-null pointer otherwise. If the allocation function is declared with a non-throwing exception-specification, it returns null to indicate failure to allocate storage and a non-null pointer otherwise. —end note] If the allocation function returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.

This wording applies even to the non-replaceable placement forms defined in 17.6.3.4 [new.delete.placement] that simply return the supplied pointer as the result of the allocation function. Compilers are thus required to check for a null pointer and avoid the initialization if one is used. This test is unnecessary overhead; it should be the user's responsibility to ensure that a null pointer is not used in these forms of placement new, just as for other cases when a pointer is dereferenced.

Proposed resolution (February, 2014):

Change 7.6.2.8 [expr.new] paragraph 15 as follows:

[Note: unless an allocation function is declared with a non-throwing exception-specification (14.5 [except.spec]), it indicates failure to allocate storage by throwing a std::bad_alloc exception (Clause 14 [except], 17.6.4.1 [bad.alloc]); it returns a non-null pointer otherwise. If the allocation function is declared with a non-throwing exception-specification, it returns null to indicate failure to allocate storage and a non-null pointer otherwise. —end note] If the allocation function is a reserved placement allocation function (17.6.3.4 [new.delete.placement]) that returns null, the behavior is undefined. Otherwise, if the allocation function returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.




1851. decltype(auto) in new-expressions

Section: 7.6.2.8  [expr.new]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2014-02-04

[Moved to DR at the November, 2014 meeting.]

9.2.9.7 [dcl.spec.auto] paragraph 5 says that a placeholder type (presumably including decltype(auto)) can appear in a new-expression. However, 7.6.2.8 [expr.new] mentions only auto, not decltype(auto).

Proposed resolution (February, 2014):

Change 7.6.2.8 [expr.new] paragraph 2 as follows:

If the auto type-specifier a placeholder type (9.2.9.7 [dcl.spec.auto]) appears in the type-specifier-seq of a new-type-id or type-id of a new-expression, the new-expression shall contain...



1992. new (std::nothrow) int[N] can throw

Section: 7.6.2.8  [expr.new]     Status: CD4     Submitter: Martin Sebor     Date: 2014-08-27

[Adopted at the February, 2016 meeting.]

According to 7.6.2.8 [expr.new] paragraph 7,

If the expression, after converting to std::size_t, is a core constant expression and the expression is erroneous, the program is ill-formed. Otherwise, a new-expression with an erroneous expression does not call an allocation function and terminates by throwing an exception of a type that would match a handler (14.4 [except.handle]) of type std::bad_array_new_length (17.6.4.2 [new.badlength]).

This wording makes no provision for an expression like

  new (std::nothrow) int[N]

which most programmers would intuitively expect not to throw an exception under any condition.

Proposed resolution (May, 2015) [SUPERSEDED]:

Change the last part of 7.6.2.8 [expr.new] paragraph 7 as follows, converting the running text into bullets, and making the last sentence into a paragraph 8:

...If the expression, is erroneous after converting to std::size_t,:

When the value of the expression is zero, the allocation function is called to allocate an array with no elements.

Notes from the October, 2015 meeting:

The text in 15.4 paragraph 15 should also be changed.

Proposed resolution (January, 2016):

  1. Change 7.6.2.8 [expr.new] paragraph 7 as follows, dividing the running text into bullets and making the last sentence into a new paragraph:

  2. The expression in a noptr-new-declarator is erroneous if:

    If the expression, is erroneous after converting to std::size_t,:

    When the value of the expression is zero, the allocation function is called to allocate an array with no elements.

  3. Change 14.5 [except.spec] paragraph 14 as follows:

  4. The set of potential exceptions of an expression e is empty if e is a core constant expression (7.7 [expr.const]). Otherwise, it is the union of the sets of potential exceptions of the immediate subexpressions of e, including default argument expressions used in a function call, combined with a set S defined by the form of e, as follows:

    [Example:...

  5. Change the example in 14.5 [except.spec] bullet 17.2 as follows:

  6.   struct A {
        A(int = (A(5), 0)) noexcept;
        A(const A&) throw();
        A(A&&) throw();
        ~A() throw(X);
      };
      struct B {
        B() throw();
        B(const B&) = default; // exception specification contains no types
        B(B&&, int = (throw Y(), 0)) noexcept;
        ~B() throw(Y);
      };
      int n = 7;
      struct D : public A, public B {
        int * p = new (std::nothrow) int[n];
      // exception specification of D::D() contains X and std::bad_array_new_length
        // exception specification of D::D(const D&) contains no types
        // exception specification of D::D(D&&) contains Y
        // exception specification of D::~D() contains X and Y
      };
      struct exp : std::bad_alloc {};
      void *operator new[](size_t) throws(exp);
      struct E : public A {
        int * p = new int[n];
        // exception specification of E::E() contains X, exp, and std::bad_array_new_length
    };
    



2130. Over-aligned types in new-expressions

Section: 7.6.2.8  [expr.new]     Status: CD4     Submitter: Richard Smith     Date: 2015-05-28

[Adopted at the February, 2016 meeting.]

According to 7.6.2.8 [expr.new] paragraph 1,

It is implementation-defined whether over-aligned types are supported (6.7.6 [basic.align]).

However, there is no mechanism for informing an allocation function of the required alignment for over-aligned types. Nevertheless, 6.7.6 [basic.align] paragraph 9 says:

Additionally, a request for runtime allocation of dynamic storage for which the requested alignment cannot be honored shall be treated as an allocation failure.

This seems contradictory.

Proposed resolution (October, 2015):

Change 6.7.6 [basic.align] paragraph 9 as follows:

If a request for a specific extended alignment in a specific context is not supported by an implementation, the program is ill-formed. Additionally, a request for runtime allocation of dynamic storage for which the requested alignment cannot be honored shall be treated as an allocation failure.



2141. Ambiguity in new-expression with elaborated-type-specifier

Section: 7.6.2.8  [expr.new]     Status: CD4     Submitter: Hubert Tong     Date: 2015-06-12

[Adopted at the February, 2016 meeting.]

Consider the following example:

  struct A { };

  void foo() {
    new struct A { };
  }

This could be either an elaborated-type-specifier followed by a braced-init-list or a class-specifier. There does not appear to be a disambiguation rule for this case.

One possibility for addressing this could be to use trailing-type-specifier-seq instead of type-specifier-seq in new-type-id. That could also be a purely syntactic alternative to the resolution of issue 686: change all uses of type-specifier-seq to trailing-type-specifier-seq and provide a new grammar production for use in alias-declaration.

Proposed resolution (February, 2016):

  1. Change the grammar in 9.1 [dcl.pre] paragraph 1 as follows:

  2. Change 9.1 [dcl.pre] paragraph 8 as follows:

  3. Each init-declarator in the init-declarator-list contains exactly one declarator-id, which is the name declared by that init-declarator and hence one of the names declared by the declaration. The defining-type-specifiers (9.2.9 [dcl.type]) in the decl-specifier-seq and the recursive declarator structure of the init-declarator describe a type (9.3.4 [dcl.meaning]), which is then associated with the name being declared by the init-declarator.
  4. Change the grammar in 9.2 [dcl.spec] paragraph 1 as follows:

  5. Change 9.2 [dcl.spec] paragraph 3 as follows:

  6. If a type-name is encountered while parsing a decl-specifier-seq, it is interpreted as part of the decl-specifier-seq if and only if there is no previous defining-type-specifier other than a cv-qualifier in the decl-specifier-seq. The sequence...
  7. Change 9.2.4 [dcl.typedef] paragraph 1 as follows:

  8. Declarations containing the decl-specifier typedef declare identifiers that can be used later for naming fundamental (6.8.2 [basic.fundamental]) or compound (6.8.4 [basic.compound]) types. The typedef specifier shall not be combined in a decl-specifier-seq with any other kind of specifier except a defining-type-specifier, and it shall not be used in the decl-specifier-seq of a parameter-declaration (9.3.4.6 [dcl.fct]) nor in the decl-specifier-seq of a function-definition (9.5 [dcl.fct.def]).
  9. Change 9.2.4 [dcl.typedef] paragraph 2 as follows:

  10. A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It The defining-type-specifier-seq of the defining-type-id may define a class or enumeration only if the alias-declaration is not the declaration of a template-declaration. Such a typedef-name has the same semantics as if it were introduced by the typedef specifier. In particular, it does not define a new type. [Example:
  11. Change 9.2.9 [dcl.type] paragraph 1 as follows:

  12. The optional attribute-specifier-seq in a type-specifier-seq or a trailingdefining-type-specifier-seq appertains to the type denoted by the preceding type-specifiers or defining-type-specifiers (9.3.4 [dcl.meaning]). The attribute-specifier-seq affects the type only for the declaration it appears in, not other declarations involving the same type.

  13. Change 9.2.9.3 [dcl.type.simple] paragraph 2 as follows:

  14. As a general rule, at most one defining-type-specifier is allowed in the complete decl-specifier-seq of a declaration or in a type-specifier-seq or trailingdefining-type-specifier-seq. The only exceptions to this rule are the following:...
  15. Change 9.2.9 [dcl.type] paragraph 3 as follows:

  16. Except in a declaration of a constructor, destructor, or conversion function, at least one defining-type-specifier that is not a cv-qualifier shall appear in a complete type-specifier-seq or a complete decl-specifier-seq.95 A type-specifier-seq shall not define a class or enumeration unless it appears in the type-id of an alias-declaration (9.2.4 [dcl.typedef]) that is not the declaration of a template-declaration.
  17. Change the grammar in 9.3 [dcl.decl] paragraph 4 as follows:

  18. Change the grammar in 9.3.2 [dcl.name] paragraph 1 as follows:

  19. Change 11.4.8.3 [class.conv.fct] paragraph 1 as follows:

  20. ...A decl-specifier in the decl-specifier-seq of a conversion function (if any) shall be neither a defining-type-specifier nor static. Type of the conversion function



1788. Sized deallocation of array of non-class type

Section: 7.6.2.9  [expr.delete]     Status: CD4     Submitter: Richard Smith     Date: 2013-09-29

[Moved to DR at the November, 2014 meeting.]

The changes from N3778 require use of a sized deallocator for a case like

  char *p = new char[32];
  void f() { delete [] p; }

That is unimplementable under current ABIs, which do not store the array size for such allocations. It should instead be unspecified or implementation-defined whether the sized form of operator[] is used for a pointer to a type other than a class with a non-trivial destructor or array thereof.

Proposed resolution (February, 2014) [SUPERSEDED]:

Change 7.6.2.9 [expr.delete] paragraph 10 as follows:

If the type is complete and if deallocation function lookup finds both a usual deallocation function with only a pointer parameter and a usual deallocation function with both a pointer parameter and a size parameter, then

Additional note, February, 2014:

It is not clear that this resolution accurately reflects the intent of the issue. In particular, it changes deletion of a pointer to incomplete type from requiring use of the single-parameter version to being implementation-defined. Also, the “type of the object to be deleted” in the array case is always an array type and thus cannot be “a complete class type with a non-trivial destructor.” The issue has consequently been returned to "review" status.

Proposed resolution (June, 2014):

Change 7.6.2.9 [expr.delete] paragraph 10 as follows:

If the type is complete and if deallocation function lookup finds both a usual deallocation function with only a pointer parameter and a usual deallocation function with both a pointer parameter and a size parameter, then the selected deallocation function shall be the one with two parameters. Otherwise, the selected deallocation function shall be the function with one parameter. the function to be called is selected as follows:



242. Interpretation of old-style casts

Section: 7.6.3  [expr.cast]     Status: CD4     Submitter: Mike Miller     Date: 30 Aug 2000

[Adopted at the February, 2016 meeting.]

The meaning of an old-style cast is described in terms of const_cast, static_cast, and reinterpret_cast in 7.6.3 [expr.cast] paragraph 5. Ignoring const_cast for the moment, it basically says that if the conversion performed by a given old-style cast is one of those performed by static_cast, the conversion is interpreted as if it were a static_cast; otherwise, it's interpreted as if it were a reinterpret_cast, if possible. The following example is given in illustration:

    struct A {};
    struct I1 : A {};
    struct I2 : A {};
    struct D : I1, I2 {};
    A *foo( D *p ) {
	return (A*)( p ); // ill-formed static_cast interpretation
    }

The obvious intent here is that a derived-to-base pointer conversion is one of the conversions that can be performed using static_cast, so (A*)(p) is equivalent to static_cast<A*>(p), which is ill-formed because of the ambiguity.

Unfortunately, the description of static_cast in 7.6.1.9 [expr.static.cast] does NOT support this interpretation. The problem is in the way 7.6.1.9 [expr.static.cast] lists the kinds of casts that can be performed using static_cast. Rather than saying something like "All standard conversions can be performed using static_cast," it says

An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration "T t(e);" is well-formed, for some invented temporary variable t.

Given the declarations above, the hypothetical declaration

    A* t(p);

is NOT well-formed, because of the ambiguity. Therefore the old-style cast (A*)(p) is NOT one of the conversions that can be performed using static_cast, and (A*)(p) is equivalent to reinterpret_cast<A*>(p), which is well-formed under 7.6.1.10 [expr.reinterpret.cast] paragraph 7.

Other situations besides ambiguity which might raise similar questions include access violations, casting from virtual base to derived, and casting pointers-to-members when virtual inheritance is involved.

Proposed resolution (October, 2015):

  1. Change 7.6.1.9 [expr.static.cast] paragraph 2 as follows:

  2. An lvalue of type “cv1 B”, where B is a class type, can be cast to type “reference to cv2 D”, where D is a class derived ( 11.7 [class.derived]) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (7.3.12 [conv.ptr]), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and. If B is neither a virtual base class of D nor or a base class of a virtual base class of D, or if no valid standard conversion from “pointer to D” to “pointer to B” exists (7.3.12 [conv.ptr]), the program is ill-formed. The result has type “cv2 D”. An xvalue of type “cv1 Bmay can be cast to type “rvalue reference to cv2 D” with the same constraints as for an lvalue of type “cv1 B”. If the object of type “cv1 B” is actually a subobject of an object of type D, the result refers to the enclosing object of type D. Otherwise, the behavior is undefined. [Example:...
  3. Change 7.6.1.9 [expr.static.cast] paragraph 4 as follows:

  4. An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t (9.4 [dcl.init]) there is an implicit conversion sequence (12.2.4.2 [over.best.ics]) from e to T, or if overload resolution for a direct-initialization (9.4 [dcl.init]) of an object or reference of type T from e would find at least one viable function (12.2.3 [over.match.viable]). The effect of such an explicit conversion is the same as performing the declaration and initialization

      T t(e);
    

    for some invented temporary variable t (9.4 [dcl.init]) and then using the temporary variable as the result of the conversion. [Note: The conversion is ill-formed when attempting to convert an expression of class type to an inaccessible or ambiguous base class. —end note] The expression e is used as a glvalue if and only if the initialization uses it as a glvalue.

  5. Change 7.6.1.9 [expr.static.cast] paragraph 11 as follows:

  6. A prvalue of type “pointer to cv1 B”, where B is a class type, can be converted to a prvalue of type “pointer to cv2 D”, where D is a class derived (11.7 [class.derived]) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (7.3.12 [conv.ptr]), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and. If B is neither a virtual base class of D nor or a base class of a virtual base class of D, or if no valid standard conversion from “pointer to D” to “pointer to B” exists (7.3.12 [conv.ptr]), the program is ill-formed. The null pointer value (7.3.12 [conv.ptr]) is converted to the null pointer value of the destination type. If the prvalue of type “pointer to cv1 B” points to a B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the behavior is undefined.
  7. Change 7.6.1.9 [expr.static.cast] paragraph 12 as follows:

  8. A prvalue of type “pointer to member of D of type cv1 T” can be converted to a prvalue of type “pointer to member of B of type cv2 T, where B is a base class ( 11.7 [class.derived]) of D, if a valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (7.3.13 [conv.mem]), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.70 If no valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (7.3.13 [conv.mem]), the program is ill-formed. The null member pointer value (7.3.13 [conv.mem]) is converted to the null member pointer value of the destination type. If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the behavior is undefined. [Note: although class B need not contain the original member, the dynamic type of the object with which indirection through the pointer to member is performed must contain the original member; see 7.6.4 [expr.mptr.oper]. —end note]



1865. Pointer arithmetic and multi-level qualification conversions

Section: 7.6.6  [expr.add]     Status: CD4     Submitter: Geoffrey Romer     Date: 2014-02-15

[Moved to DR at the November, 2014 meeting.]

The resolution of issue 1504 added 7.6.6 [expr.add] paragraph 7:

For addition or subtraction, if the expressions P or Q have type “pointer to cv T”, where T is different from the cv-unqualified array element type, the behavior is undefined.

This wording was intended to address derived-base conversion in pointer arithmetic, but it inadvertently categorized as undefined behavior previously well-defined pointer arithmetic on pointers that are the result of multi-level qualification conversions. For example:

  void f() {
    int i = 0;
    int* arr[3] = {&i, &i, &i};
    int const * const * aptr = arr;
    assert(aptr[2] == &i);
  }

This now has undefined behavior because the type of *aptr is “pointer to const int,” which is different from the cv-unqualified array element type, “pointer to int.”

See also issue 330.

Proposed Resolution (July, 2014):

Change 7.6.6 [expr.add] paragraph 7 as follows:

For addition or subtraction, if the expressions P or Q have type “pointer to cv T”, where T is different from the cv-unqualified and the array element type are not similar (7.3.6 [conv.qual]), the behavior is undefined. [Note: In particular, a pointer to a base class cannot be used for pointer arithmetic when the array contains objects of a derived class type. —end note]



1596. Non-array objects as array[1]

Section: 7.6.9  [expr.rel]     Status: CD4     Submitter: Daniel Krügler     Date: 2012-12-20

[Moved to DR at the November, 2014 meeting.]

The provision to treat non-array objects as if they were array objects with a bound of 1 is given only for pointer arithmetic in C++ (7.6.6 [expr.add] paragraph 4). C99 supplies similar wording for the relational and equality operators, explicitly allowing pointers resulting from such implicit-array treatment to be compared. C++ should follow suit.

Proposed resolution (August, 2013):

  1. Change 7.6.2.2 [expr.unary.op] paragraph 3 as follows:

  2. ...Otherwise, if the type of the expression is T, the result has type “pointer to T” and is a prvalue that is the address of the designated object (6.7.1 [intro.memory]) or a pointer to the designated function. [Note: In particular, the address of an object of type “cv T” is “pointer to cv T”, with the same cv-qualification. —end note] For purposes of pointer arithmetic (7.6.6 [expr.add]) and comparison (7.6.9 [expr.rel], 7.6.10 [expr.eq]), an object that is not an array element whose address is taken in this way is considered to belong to an array with one element of type T. [Example:

      struct A { int i; };
      struct B : A { };
      ... &B::i ...        // has type int A::*
      int a;
      int* p1 = &a;
      int* p2 = p1 + 1;    // Defined behavior
      bool b = p2 > p1;    // Defined behavior, with value true
    

    end example] [Note: a pointer to member...

  3. Delete 7.6.6 [expr.add] paragraph 4:

  4. For the purposes of these operators, a pointer to a nonarray object behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.
  5. Change 7.6.6 [expr.add] paragraph 5 as follows:

  6. When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object [Footnote: An object that is not an array element is considered to belong to a single-element array for this purpose; see 7.6.2.2 [expr.unary.op] —end footnote], and the array is large enough, the result points to an element...
  7. Change 7.6.9 [expr.rel] paragraph 3 as follows:

  8. Comparing pointers to objects [Footnote: An object that is not an array element is considered to belong to a single-element array for this purpose; see 7.6.2.2 [expr.unary.op] —end footnote] is defined as follows:...

[Drafting note: No change is proposed for 7.6.10 [expr.eq], since the comparison is phrased in terms of “same address”, not in terms of array elements, so the handling of one-past-the-end addresses falls out of the specification of pointer arithmetic.]




1652. Object addresses in constexpr expressions

Section: 7.6.10  [expr.eq]     Status: CD4     Submitter: Richard Smith     Date: 2013-04-15

[Moved to DR at the May, 2015 meeting.]

Pointer equality is defined by reference to the addresses of the objects designated by the pointer values, reflecting the implementation technique of most/all compilers. However, this definition is intrinsically a runtime property, and such a description is inappropriate with respect to constexpr expressions, which must deal with pointer comparisons without necessarily knowing the runtime layout of the objects involved. A better definition usable at compile time is needed.

Proposed resolution (November, 2014):

Change 7.6.10 [expr.eq] paragraph 2, converting the existing running text into bullets, as follows:

If at least one of the operands is a pointer, pointer conversions (7.3.12 [conv.ptr]) and qualification conversions (7.3.6 [conv.qual]) are performed on both operands to bring them to their composite pointer type (Clause 7 [expr]). Comparing pointers is defined as follows: Two pointers compare equal




1858. Comparing pointers to union members

Section: 7.6.10  [expr.eq]     Status: CD4     Submitter: Richard Smith     Date: 2014-02-12

[Moved to DR at the November, 2014 meeting.]

Comparison of pointers to members is currently specified in 7.6.10 [expr.eq] paragraph 3 as,

two pointers to members compare equal if they would refer to the same member of the same most derived object (6.7.2 [intro.object]) or the same subobject if indirection with a hypothetical object of the associated class type were performed, otherwise they compare unequal.

The “same member” requirement could be interpreted as incorrect for union members. The wording should be clarified in this regard.

Proposed Resolution (July, 2014):

Insert the following before bullet 5 of 7.6.10 [expr.eq] paragraph 3:




1805. Conversions of array operands in conditional-expressions

Section: 7.6.16  [expr.cond]     Status: CD4     Submitter: Richard Smith     Date: 2013-11-02

[Moved to DR at the November, 2014 meeting.]

The final bullet of 7.6.16 [expr.cond] paragraph 3, describing the attempt to convert the operands of the conditional operator to the other operand's type as part of determining the type of the result, says,

The phrase “if E2 were converted to a prvalue” is problematic if E2 has an array type. For example,

  struct S {
    S(const char *s);
    operator const char *();
  };

  S s;
  const char *f(bool b) {
    return b ? s : "";   // #1
  }

One might expect that the expression in #1 would be ambiguous, since S can be converted both to and from const char*. However, the target type for the conversion of s is const char[1], not const char*, so that conversion fails and the result of the conditional-expression has type S.

It might be better to specify the target type for this trial conversion to be the type after the usual lvalue-to-rvalue, array-to-pointer, and function-to-pointer conversions instead of simply the result of converting “to a prvalue.”

Proposed resolution (February, 2014):

Change the final subbullet of 7.6.16 [expr.cond] paragraph 3 as follows:

...The process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows:

[Editorial note: this wording was approved by CWG, but I'd suggest an editorial change to “...or if both have class types but the underlying classes are not the same and neither is a base class of the other.” —wmm]


1843. Bit-field in conditional operator with throw operand

Section: 7.6.16  [expr.cond]     Status: CD4     Submitter: Richard Smith     Date: 2014-01-25

[Moved to DR at the November, 2014 meeting.]

Presumably the result of something like

    b ? x : throw y

is a bit-field if x is, but the current wording does not say that.

Proposed resolution (February, 2014):

Change 7.6.16 [expr.cond] paragraph 2 as follows (this assumes the revised wording of the resolution of issue 1299 as the base text):

If either the second or the third operand has type void, one of the following shall hold:




1895. Deleted conversions in conditional operator operands

Section: 7.6.16  [expr.cond]     Status: CD4     Submitter: Richard Smith     Date: 2014-03-17

[Adopted at the February, 2016 meeting.]

In an example like,

  struct B;
  struct A { A(); A(B&) = delete; operator B&(); };
  struct B : A {} b;
  B &c = true ? A() : b;

the rules of 7.6.16 [expr.cond] paragraph 3 make this ambiguous: A() can be implicitly converted to the type “lvalue reference to B,” and b satisfies the constraints to be converted to an A prvalue (it's of a type derived from A and the cv-qualifiers are okay). Bullet 3 bullet 1 is clear that we do not actually try to create an A temporary from b, so we don't notice that it invokes a deleted constructor and rule out that conversion.

If the deleted conversion is in the other sense, the result is unambiguous:

  struct B;
  struct A { A(); A(B&); operator B&() = delete; };
  struct B : A {} b;
  B &c = true ? A() : b;

A() can no longer be implicitly converted to the type “lvalue reference to B”: since the declaration B &t = A(); is not well formed (it invokes a deleted function), there is no implicit conversion. So we unambiguously convert the third operand to an A prvalue.

These should presumably either both be valid or both invalid. EDG and gcc call both ambiguous.

Notes from the June, 2014 meeting:

The wording should be changed to handle the convertibility test more like overload resolution: the conversion "exists" if the conversion function is declared, but is ill-formed if it would actually be used.

Proposed resolution (October, 2015):

  1. Add the following as a new paragraph following 7.6.16 [expr.cond] paragraph 2:

  2. Otherwise, if the second and third operand are glvalue bit-fields of the same value category and of types cv1 T and cv2 T, respectively, the operands are considered to be of type cv T for the remainder of this section, where cv is the union of cv1 and cv2.
  3. Change 7.6.16 [expr.cond] paragraph 3 as follows:

  4. Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, or if both are glvalues of the same value category and the same type except for cv-qualification, an attempt is made to convert form an implicit conversion sequence (12.2.4.2 [over.best.ics]) from each of those operands to the type of the other. [Note: Properties such as access, whether an operand is a bit-field, or whether a conversion function is deleted are ignored for that determination. —end note] The process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows: Attempts are made to form an implicit conversion sequence from an operand expression E1 of type T1 to a target type related to the type T2 of the operand expression E2 as follows:

    Using this process, it is determined whether an implicit conversion sequence can be formed from the second operand can be converted to match to the target type determined for the third operand, and whether the third operand can be converted to match the second operand vice versa. If both can be converted sequences can be formed, or one can be converted but the conversion is formed, but it is the ambiguous conversion sequence, the program is ill-formed. If neither can be converted no conversion sequence can be formed, the operands are left unchanged and further checking is performed as described below. If exactly one conversion is possible, Otherwise, if exactly one conversion sequence can be formed, that conversion is applied to the chosen operand and the converted operand is used in place of the original operand for the remainder of this section. [Note: The conversion might be ill-formed even if an implicit conversion sequence could be formed. —end note]

This resolution also resolves issue 1932.




1932. Bit-field results of conditional operators

Section: 7.6.16  [expr.cond]     Status: CD4     Submitter: Richard Smith     Date: 2014-02-21

[Adopted at the February, 2016 meeting.]

According to 7.6.16 [expr.cond] paragraph 3,

if the second and third operand have different types and either has (possibly cv-qualified) class type, or if both are glvalues of the same value category and the same type except for cv-qualification, an attempt is made to convert each of those operands to the type of the other. The process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows:

If two bit-field glvalues have exactly the same scalar type, paragraph 3 does not apply (two non-class operands must differ in at least cv-qualification). For an example like

  struct S {
    int i:3;
    const int j:4;
  } s;
  int k = true ? s.i : s.j;

the condition is satisfied. The intent is that S::i can be converted to const int but S::j cannot be converted to int, so the result should be a bit-field lvalue of type const int. However, the test for convertibility is phrased in terms of direct reference binding, which is inapplicable to bit-fields, resulting in neither conversion succeeding, leading to categorizing the expression as ambiguous.

Proposed resolution (October, 2015):

This issue is resolved by the resolution of issue 1895.




1925. Bit-field prvalues

Section: 7.6.20  [expr.comma]     Status: CD4     Submitter: Richard Smith     Date: 2014-05-12

[Moved to DR at the May, 2015 meeting.]

According to 7.6.20 [expr.comma] paragraph 1,

The type and value of the result are the type and value of the right operand; the result is of the same value category as its right operand, and is a bit-field if its right operand is a glvalue and a bit-field.

The description of a bit-field result seems to indicate that the operand might not be a glvalue but could still be a bit-field. There doesn't appear to be a normative prohibition against prvalue bit-fields, so one should presumably be added, and this wording should be adjusted to remove the suggestion that such a thing might exist.

Proposed resolution (November, 2014):

  1. Change 7.2.1 [basic.lval] paragraph 2 as follows:

  2. Whenever a glvalue appears in a context where a prvalue is expected, the glvalue is converted to a prvalue; see 7.3.2 [conv.lval], 7.3.3 [conv.array], and 7.3.4 [conv.func]. [Note: An attempt to bind an rvalue reference to an lvalue is not such a context; see 9.4.4 [dcl.init.ref]. —end note] [Note: There are no prvalue bit-fields; if a bit-field is converted to a prvalue (7.3.2 [conv.lval]), a prvalue of the type of the bit-field is created, which might then be promoted (7.3.7 [conv.prom]). —end note]
  3. Change 7.6.20 [expr.comma] paragraph 1 as follows:

  4. ...The type and value of the result are the type and value of the right operand; the result is of the same value category as its right operand, and is a bit-field if its right operand is a glvalue and a bit-field. If the value...



1683. Incorrect example after constexpr changes

Section: 7.7  [expr.const]     Status: CD4     Submitter: Daniel Krügler     Date: 2013-05-15

The example in 7.7 [expr.const] paragraph 6,

  struct A {
    constexpr A(int i) : val(i) { }
    constexpr operator int() { return val; }
    constexpr operator long() { return 43; }
  private:
    int val;
  };
  template<int> struct X { };
  constexpr A a = 42;
  X<a> x;               // OK: unique conversion to int
  int ary[a];           // error: ambiguous conversion

is no longer correct now that constexpr does not imply const for member functions, since the conversion functions cannot be invoked for the constant a.

Notes from the September, 2013 meeting:

This issue is being handled editorially and is being placed in "review" status to ensure that the change has been made.




1694. Restriction on reference to temporary as a constant expression

Section: 7.7  [expr.const]     Status: CD4     Submitter: Richard Smith     Date: 2013-05-30

[Moved to DR at the November, 2014 meeting.]

We're missing a restriction on the value of a temporary which is bound to a static storage duration reference:

  void f(int n) {
    static constexpr int *&&r = &n;
  }

This is currently valid, because &n is a core constant expression, and it is a constant expression because the reference binds to a temporary (of type int*) that has static storage duration (because it's lifetime-extended by the reference binding).

The value of r is constant here (it's a constant reference to a temporary with a non-constant initializer), but I don't think we should accept this. Generally, I think a temporary which is lifetime-extended by a constexpr variable should also be treated as if it were declared to be a constexpr object.

Proposed resolution (September, 2013) [SUPERSEDED]:

Change 7.7 [expr.const] paragraph 4 as follows:

A constant expression is either a glvalue core constant expression whose value refers to an object with static storage duration or to a function entity that is a permitted result of a constant expression, or a prvalue core constant expression whose value is an object where, for that object and its subobjects:

An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary or is a temporary whose value satisfies the above constraints, or it is a function.

Proposed resolution (February, 2014):

Change 7.7 [expr.const] paragraph 4 as follows:

A constant expression is either a glvalue core constant expression whose value refers to an object with static storage duration or to a function entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value is an object where, for that object and its subobjects:

An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary object or is a temporary object whose value satisfies the above constraints, or it is a function.




1757. Const integral subobjects

Section: 7.7  [expr.const]     Status: CD4     Submitter: Richard Smith     Date: 2013-09-20

[Moved to DR at the November, 2014 meeting.]

The requirements for a constant expression in 7.7 [expr.const] permit an lvalue-to-rvalue conversion on

a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression

This does not exclude subobjects of objects that are not compile-time constants, for example:

  int f();
  struct S {
    S() : a(f()), b(5) {}
    int a, b;
  };
  const S s;
  constexpr int k = s.b;

This rule is intended to provide backward compatibility with pre-constexpr C++, but it should be restricted to complete objects. Care should be taken in resolving this issue not to break the handling of string literals, since use of their elements in constant expressions depends on the current form of this rule.

Proposed resolution (February, 2014):

Change 7.7 [expr.const] bullet 2.7 as follows:

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.9.1 [intro.execution]), would evaluate one of the following expressions:




1952. Constant expressions and library undefined behavior

Section: 7.7  [expr.const]     Status: CD4     Submitter: Daniel Krügler     Date: 2014-06-22

[Moved to DR at the May, 2015 meeting.]

According to bullet 2.5 of 7.7 [expr.const],

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.9.1 [intro.execution]), would evaluate one of the following expressions:

The definition of “operation” is unclear. In particular, is it intended to include use of library components that are specified to produce undefined behavior, such as use of the offsetof macro when applied to a non-standard-layout class?

Proposed resolution (April, 2015):

  1. Change 7.7 [expr.const] bullet 2.5 as follows:

  2. Add the following at the end of 7.7 [expr.const] paragraph 2:

  3. If e satisfies the constraints of a core constant expression, but evaluation of e would evaluate an operation that has undefined behavior as specified in Clauses Clause 16 [library] through Clause 33 [thread] of this International Standard, it is unspecified whether e is a core constant expression.



2004. Unions with mutable members in constant expressions

Section: 7.7  [expr.const]     Status: CD4     Submitter: Richard Smith     Date: 2014-09-16

[Moved to DR at the October, 2015 meeting.]

In an example like

  union U { int a; mutable int b; };
  constexpr U u1 = {1};
  int k = (u1.b = 2);
  constexpr U u2 = u1; // ok!!

The initialization of u2 is not disqualified by the current wording of the Standard because the copy is done via the object representation, not formally involving an lvalue-to-rvalue conversion. A restriction should be added to 7.7 [expr.const] forbidding the evaluation of a defaulted copy/move construction/assignment on a class type that has any variant mutable subobjects.

Proposed resolution (May, 2015):

  1. Add the following bullet after bullet 3.1 of 9.2.6 [dcl.constexpr]:

  2. The definition of a constexpr function shall satisfy the following constraints:

  3. Add the following bullet after bullet 4.1 of 9.2.6 [dcl.constexpr]

  4. The definition of a constexpr constructor shall satisfy the following constraints:




2022. Copy elision in constant expressions

Section: 7.7  [expr.const]     Status: CD4     Submitter: Jason Merrill     Date: 2014-10-16

[Adopted at the June, 2016 meeting.]

Consider the following example:

  struct A {
    void *p;
    constexpr A(): p(this) {}
  };

  constexpr A a;		// well-formed
  constexpr A b = A();          // ?

The declaration of a seems well-formed because the address of a is constant.

The declaration of b, however, seems to depend on whether copy elision is performed. If it is, the declaration is the equivalent of a; if not, however, this creates a temporary and initializes p to the address of that temporary, making the initialization non-constant and the declaration ill-formed.

It does not seem desirable for the well-formedness of the program to depend on whether the implementation performs an optional copy elision.

Notes from the November, 2014 meeting:

CWG decided to leave it unspecified whether copy elision is performed in cases like this and to add a note to 11.4.5.3 [class.copy.ctor] to make clear that that outcome is intentional.

Notes from the May, 2015 meeting:

CWG agreed that copy elision should be mandatory in constant expressions.

Proposed resolution (April, 2016):

  1. Change 7.7 [expr.const] paragraph 1 as follows:

  2. ...Expressions that satisfy these requirements, assuming that copy elision is performed, are called constant expressions. [Note:...
  3. Change 9.2.6 [dcl.constexpr] paragraph 7 as follows, breaking the existing running text into a bulleted list:

  4. A call to a constexpr function produces the same result as a call to an equivalent non-constexpr function in all respects except that

  5. Change 11.4.5.3 [class.copy.ctor] paragraph 31 as follows:

  6. ...This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

    Copy elision is required where an expression is evaluated in a context requiring a constant expression (7.7 [expr.const]) and in constant initialization (6.9.3.2 [basic.start.static]). [Note: Copy elision might not be performed if the same expression is evaluated in another context. —end note] [Example:

      class Thing {
      public:
        Thing();
        ~Thing();
        Thing(const Thing&);
      };
    
      Thing f() {
        Thing t;
        return t;
      }
    
      Thing t2 = f();
    
      struct A {
        void *p;
        constexpr A(): p(this) {}
      };
    
      constexpr A a;        // well-formed, a.p points to a
      constexpr A b = A();  // well-formed, b.p points to b
    
      void g() {
        A c = A();          // well-formed, c.p may point to c or to an ephemeral temporary
      }
    

    Here the criteria for elision can be combined...




2129. Non-object prvalues and constant expressions

Section: 7.7  [expr.const]     Status: CD4     Submitter: Faisal Vali     Date: 2015-05-20

[Adopted at the February, 2016 meeting.]

According to 7.7 [expr.const] paragraph 5,

A constant expression is either a glvalue core constant expression whose value... or a prvalue core constant expression whose value is an object where...

Since an integer literal is prvalue that is not an object, this definition does not allow it to be a constant expression.

Proposed resolution (February, 2016):

Change 7.7 [expr.const] paragraph 5 as follows:

A constant expression is either a glvalue core constant expression whose value refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value is an object where, for that object and its subobjects satisfies the following constraints:

An entity is a permitted result of a constant expression if...




2167. Non-member references with lifetimes within the current evaluation

Section: 7.7  [expr.const]     Status: CD4     Submitter: Richard Smith     Date: 2015-08-11

[Adopted at the February, 2016 meeting.]

The current wording of 7.7 [expr.const] bullet 2.9 says:

This incorrectly excludes non-member references whose lifetime began within the current evaluation.

Proposed resolution (February, 2016):

Change 7.7 [expr.const] bullet 2.9.2 as follows:

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.9.1 [intro.execution]), would evaluate one of the following expressions:




1274. Common nonterminal for expression and braced-init-list

Section: 8.6.5  [stmt.ranged]     Status: CD4     Submitter: Daniel Krügler     Date: 2011-03-25

[Moved to DR at the October, 2015 meeting.]

It would be helpful to have a single grammar term for expression and braced-init-list, which often occur together in the text. In particular, 8.6.5 [stmt.ranged] paragraph 1 allows both, but the description of __RangeT refers only to the expression case; such errors would be less likely if the common term were available.

Proposed resolution (May, 2015):

  1. Add a new production to the grammar in 9.4 [dcl.init] paragraph 1:

  2. Change the grammar in 7.6.1 [expr.post] paragraph 1 as follows:

  3. Change the grammar in 8.6 [stmt.iter] paragraph 1 as follows:

  4. Change 8.6.5 [stmt.ranged] paragraph 1 as follows:

  5. For a range-based for statement of the form

    let range-init be equivalent to the expression surrounded by parentheses90

    and for a range-based for statement of the form

    let range-init be equivalent to the braced-init-list. In each case, a A range-based for statement is equivalent to

      {
      auto && __range = range-init for-range-initializer;
        for ( auto __begin = begin-expr,
                   __end = end-expr;
              __begin != __end;
              ++__begin ) {
          for-range-declaration = *__begin;
          statement
        }
      }
    

    where

  6. Change the grammar of 8.7 [stmt.jump] paragraph 1 as follows:

  7. Change 8.7.4 [stmt.return] paragraph 2 as follows:

  8. The expression or braced-init-list expr-or-braced-init-list of a return statement is called its operand. A return statement...
  9. Change 12.4.5 [over.sub] paragraph 1 as follows:

  10. operator[] shall be a non-static member function with exactly one parameter. It implements the subscripting syntax

    or

    Thus, a subscripting expression...




2017. Flowing off end is not equivalent to no-expression return

Section: 8.7.4  [stmt.return]     Status: CD4     Submitter: Richard Smith     Date: 2014-10-06

[Adopted at the February, 2016 meeting.]

According to 8.7.4 [stmt.return] paragraph 2,

Flowing off the end of a function is equivalent to a return with no value...

This is not correct, since a return with no value is ill-formed in a value-returning function but flowing off the end results in undefined behavior.

Proposed resolution (May, 2015): [SUPERSEDED]

Change 8.7.4 [stmt.return] paragraph 2 as follows:

...Flowing off the end of a value-returning function is undefined behavior. Flowing off the end of any other function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.

Additional notes, October, 2015:

There is similar wording in 14.4 [except.handle] paragraph 14. Also, it might be better to avoid the use of the word “value”, since it is currently not clearly defined.

Proposed resolution (October, 2015):

  1. Change 6.9.3.1 [basic.start.main] paragraph 5 as follows:

  2. A return statement in main has the effect of leaving the main function (destroying any objects with automatic storage duration) and calling std::exit with the return value as the argument. If control reaches flows off the end of the compound-statement of main without encountering a return statement, the effect is that of executing equivalent to a return with operand 0 (see also 14.4 [except.handle]).

      return 0;
    
  3. Change 8.7.4 [stmt.return] paragraph 2 as follows:

  4. ...Flowing off the end of a function with a void return type is equivalent to a return with no value; this results in undefined behavior in a value-returning function operand. Otherwise, flowing off the end of a function other than main (6.9.3.1 [basic.start.main] results in undefined behavior.
  5. Change 14.4 [except.handle] paragraph 14 as follows:

  6. The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor. Otherwise, a function returns when control reaches the end of a handler for the function-try-block (8.7.4 [stmt.return]). Flowing off the end of a function-try-block is equivalent to a return with no value; this results in undefined behavior in a value-returning function (8.7.4 [stmt.return]) flowing off the end of the compound-statement of a handler of a function-try-block is equivalent to flowing off the end of the compound-statement of that function (see 8.7.4 [stmt.return]).



1830. Repeated specifiers

Section: 9.1  [dcl.pre]     Status: CD4     Submitter: Ville Voutilainen     Date: 2014-01-10

[Moved to DR at the November, 2014 meeting.]

Although repeated type-specifiers such as const are forbidden, there is no such prohibition against repeated non-type specifiers like constexpr and virtual. Should there be?

On the “con” side, it's not clear that such a prohibition actually helps anyone; it could happen via macros, and a warning about non-macro use could be a QoI issue. Also, C99 moved in the opposite direction, removing the prohibition against repeated cv-qualifiers.

Proposed resolution (February, 2014):

Add the following as a new paragraph before 9.2 [dcl.spec] paragraph 2:

Each decl-specifier shall appear at most once in the complete decl-specifier-seq of a declaration, except that long may appear twice.

If a type-name is encountered...




1990. Ambiguity due to optional decl-specifier-seq

Section: 9.1  [dcl.pre]     Status: CD4     Submitter: Hubert Tong     Date: 2014-08-27

[Moved to DR at the October, 2015 meeting.]

In an example like

  void f() {
    f();  // #1
  }

The statement at #1 is ambiguous and can be parsed as either an expression or as a declaration. The problem is the fact that the decl-specifier-seq in a simple-declaration is optional.

Proposed resolution (May, 2015):

  1. Change the grammar in 9.1 [dcl.pre] paragraph 1 as follows:




  2. Change 9.1 [dcl.pre] paragraph 2 as follows:

  3. The A simple-declaration or nodeclspec-function-declaration of the form

    is divided into three parts. Attributes are described in 9.12 [dcl.attr]. decl-specifiers, the principal components of a decl-specifier-seq, are described in 9.2 [dcl.spec]. declarators, the components of an init-declarator-list, are described in 9.3 [dcl.decl]. The attribute-specifier-seq in a simple-declaration appertains to each of the entities declared by the declarators of the init-declarator-list. [Note:...

  4. Change 9.1 [dcl.pre] paragraph 11 as follows:

  5. Only in function declarations for A nodeclspec-function-declaration shall declare a constructors, destructors, and type or conversions function can the decl-specifier-seq be omitted.93 [Note: a nodeclspec-function-declaration can only be used in a template-declaration (Clause 13 [temp]), explicit-instantiation (13.9.3 [temp.explicit]), or explicit-specialization (13.9.4 [temp.expl.spec]). —end note]
  6. Change 9.3.4 [dcl.meaning] paragraph 1 as follows:

  7. A list of declarators appears after an optional ( 9.1 [dcl.pre]) decl-specifier-seq (9.2 [dcl.spec]). Each A declarator contains exactly one declarator-id; it names the identifier...
  8. Change 11.4.5 [class.ctor] paragraph 1 as follows:

  9. ...In a constructor declaration, each Each decl-specifier in the optional decl-specifier-seq of a constructor declaration (if any) shall be friend, inline, explicit, or constexpr. [Example:...
  10. Change 11.4.8.3 [class.conv.fct] paragraph 1 as follows:

  11. ...Such functions are called conversion functions. No return type can be specified. A decl-specifier in the decl-specifier-seq of a conversion function (if any) shall be neither a type-specifier nor static. If a conversion function is a member function, the The type of the conversion function (9.3.4.6 [dcl.fct]) is...
  12. Delete 11.4.8.3 [class.conv.fct] paragraph 6:

  13. Conversion functions cannot be declared static.
  14. Change 11.4.7 [class.dtor] paragraph 1 as follows:

  15. ...In a destructor declaration, each Each decl-specifier of the optional decl-specifier-seq of a destructor declaration (if any) shall be friend, inline, or virtual.

This resolution also resolves issue 2016.




1793. thread_local in explicit specializations

Section: 9.2.2  [dcl.stc]     Status: CD4     Submitter: Mike Herrick     Date: 2013-10-02

[Moved to DR at the November, 2014 meeting.]

According to 9.2.2 [dcl.stc] paragraph 1,

...If thread_local appears in any declaration of a variable it shall be present in all declarations of that entity... A storage-class-specifier shall not be specified in an explicit specialization (13.9.4 [temp.expl.spec]) or an explicit instantiation (13.9.3 [temp.explicit]) directive.

These two requirements appear to be in conflict when an explicit instantiation or explicit specialization names a thread_local variable. For example,

  template <class T> struct S {
    thread_local static int tlm;
  };
  template <> int S<int>::tlm = 0;
  template <> thread_local int S<float>::tlm = 0;

which of the two explicit specializations is correct?

Proposed resolution (February, 2014):

Change 9.2.2 [dcl.stc] paragraph 1 as follows:

...A storage-class-specifier other than thread_local shall not be specified in an explicit specialization (13.9.4 [temp.expl.spec]) or an explicit instantiation (13.9.3 [temp.explicit]) directive.



1799. mutable and non-explicit const qualification

Section: 9.2.2  [dcl.stc]     Status: CD4     Submitter: Richard Smith     Date: 2013-10-21

[Moved to DR at the November, 2014 meeting.]

According to 9.2.2 [dcl.stc] paragraph 9,

The mutable specifier can be applied only to names of class data members (11.4 [class.mem]) and cannot be applied to names declared const or static, and cannot be applied to reference members.

This is similar to issue 1686 in that the restriction appears to apply only to declarations in which the const keyword appears directly. It should instead apply to members with const-qualified types, regardless of how the qualification was achieved.

Proposed resolution (January, 2014) [SUPERSEDED]:

Change 9.2.2 [dcl.stc] paragraph 9 as follows:

The mutable specifier can be applied only to names of non-static class data members (11.4 [class.mem]) and cannot be applied to names declared const or static, and cannot be applied to reference members whose type is neither const-qualified nor a reference type. [Example:...

Proposed resolution (February, 2014):

Change 9.2.2 [dcl.stc] paragraph 9 as follows:

The mutable specifier can be applied shall appear only to names in the declaration of class a non-static data members member (11.4 [class.mem]) and cannot be applied to names declared const or static, and cannot be applied to reference members whose type is neither const-qualified nor a reference type. [Example:...



1930. init-declarator-list vs member-declarator-list

Section: 9.2.2  [dcl.stc]     Status: CD4     Submitter: Richard Smith     Date: 2014-05-19

[Adopted at the February, 2016 meeting.]

According to 9.2.2 [dcl.stc] paragraph 1,

If a storage-class-specifier appears in a decl-specifier-seq, there can be no typedef specifier in the same decl-specifier-seq and the init-declarator-list of the declaration shall not be empty...

This obviously should apply to mutable but does not because mutable applies to member-declarator-lists, not init-declarator-lists. Similarly, in 9.2.9.2 [dcl.type.cv] paragraph 1,

If a cv-qualifier appears in a decl-specifier-seq, the init-declarator-list of the declaration shall not be empty.

this should apply to member declarations as well.

Proposed resolution (October, 2015):

  1. Change 9.2.2 [dcl.stc] paragraph 1 as follows:

  2. ...If a storage-class-specifier appears in a decl-specifier-seq, there can be no typedef specifier in the same decl-specifier-seq and the init-declarator-list or member-declarator-list of the declaration shall not be empty (except for an anonymous union declared in a named namespace or in the global namespace, which shall be declared static (11.5 [class.union])). The storage-class-specifier applies...
  3. Change 9.2.9.2 [dcl.type.cv] paragraph 1 as follows:

  4. ...If a cv-qualifier appears in a decl-specifier-seq, the init-declarator-list or member-declarator-list of the declaration shall not be empty. [Note:...

Additional note, November, 2014:

The preceding resolution, which was advanced to "tentatively ready" status during the review session following the November, 2014 (Urbana) meeting, introduces an apparently unintentional conflict with 11.5 [class.union] paragraph 6 regarding the requirements for anonymous unions in unnamed namespaces and has been returned to "review" status to allow further discussion.

Notes from the October, 2015 meeting:

The proposed resolution was changed to address the preceding concern.




1823. String literal uniqueness in inline functions

Section: 9.2.3  [dcl.fct.spec]     Status: CD4     Submitter: Richard Smith     Date: 2013-12-17

[Moved to DR at the November, 2014 meeting.]

According to 9.2.3 [dcl.fct.spec] paragraph 4,

A string literal in the body of an extern inline function is the same object in different translation units.

The Standard does not otherwise specify when string literals are required to be the same object, and this requirement is not widely implemented. Should it be removed?

Proposed resolution (February, 2014):

  1. Change 5.13.5 [lex.string] paragraph 1 as follows:

  2. A string literal string-literal is a sequence of characters...
  3. Change 5.13.5 [lex.string] paragraph 2 as follows:

  4. A string literal string-literal that has an R in the prefix...
  5. Change 5.13.5 [lex.string] paragraph 6 as follows:

  6. After translation phase 6, a string literal string-literal that does not begin...
  7. Change 5.13.5 [lex.string] paragraph 7 as follows:

  8. A string literal string-literal that begins with u8...
  9. Change 5.13.5 [lex.string] paragraph 10 as follows:

  10. A string literal string-literal that begins with u, such as u"asdf", is a char16_t string literal. A char16_t string literal has type “array of n const char16_t”, where n is the size of the string as defined below; it has static storage duration and is initialized with the given characters. A single c-char may produce more than one char16_t character in the form of surrogate pairs.
  11. Change 5.13.5 [lex.string] paragraph 11 as follows:

  12. A string literal string-literal that begins with U, such as U"asdf", is a char32_t string literal. A char32_t string literal has type “array of n const char32_t”, where n is the size of the string as defined below; it has static storage duration and is initialized with the given characters.
  13. Change 5.13.5 [lex.string] paragraph 12 as follows:

  14. A string literal string-literal that begins with L, such as L"asdf", is a wide string literal. A wide string literal has type “array of n const wchar_t”, where n is the size of the string as defined below; it has static storage duration and is initialized with the given characters.
  15. Delete 5.13.5 [lex.string] paragraph 13:

  16. Whether all string literals are distinct (that is, are stored in nonoverlapping objects) is implementation-defined. The effect of attempting to modify a string literal is undefined.
  17. Change 5.13.5 [lex.string] paragraph 14 as follows:

  18. In translation phase 6 (5.2 [lex.phases]), adjacent string literals string-literals are concatenated. If both string literals string-literals have the same encoding-prefix, the resulting concatenated string literal has that encoding-prefix. If one string literal string-literal has no encoding-prefix, it is treated as a string literal string-literal of the same encoding-prefix as the other operand. If a UTF-8 string literal token is adjacent to a wide string literal token, the program is ill-formed. Any other concatenations are conditionally-supported with implementation-defined behavior. [Note: This concatenation is an interpretation, not a conversion. Because the interpretation happens in translation phase 6 (after each character from a literal has been translated into a value from the appropriate character set), a string literal string-literal's initial rawness has no effect on the interpretation or well-formedness of the concatenation. —end note] Table 8...
  19. Add the following as a new paragraph at the end of 5.13.5 [lex.string]:

  20. Evaluating a string-literal results in a string literal object with static storage duration, initialized from the given characters as specified above. Whether all string literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified. [Note: The effect of attempting to modify a string literal is undefined. —end note]
  21. Change 9.2.3 [dcl.fct.spec] paragraph 4 as follows:

  22. ...A static local variable in an extern inline function always refers to the same object. A string literal in the body of an extern inline function is the same object in different translation units. [Note: A string literal appearing in a default argument is not in the body of an inline function merely because the expression is used in a function call from that inline function. —end note] A type defined within the body of an extern inline function is the same type in every translation unit.

Additional note, February, 2014:

Two editorial changes have been made since CWG approved the proposed resolution:

  1. The proposed change to 5.13.5 [lex.string] paragraph 15 has not been made. The term string-literal refers to the syntactic construct appearing in the source, but the addition of the terminating null character is made to the concatenated string literal, which is (appropriately) referred to in the preceding paragraph (as in the original text of paragraph 15) using the English term, not the non-terminal.
  2. The deletion of the requirement in 9.2.3 [dcl.fct.spec] paragraph 4 that string literals in inline functions be the same made the note following that requirement irrelevant, so the deletion has been extended to include the note as well.

The issue has been returned to "review" status to allow possible reconsideration of these editorial changes.




1247. Restriction on alias name appearing in type-id

Section: 9.2.4  [dcl.typedef]     Status: CD4     Submitter: James Widman     Date: 2011-02-26

[Moved to DR at the May, 2015 meeting.]

With the resolution of issue 1044, there is no need to say that the name of the alias cannot appear in the type-id of the declaration.

Proposed resolution (April, 2015):

Change 9.2.4 [dcl.typedef] paragraph 2 as follows:

A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics as if it were introduced by the typedef specifier. In particular, it does not define a new type and it shall not appear in the type-id. [Example:...



2071. typedef with no declarator

Section: 9.2.4  [dcl.typedef]     Status: CD4     Submitter: Richard Smith     Date: 2014-01-16

[Adopted at the February, 2016 meeting.]

There should be a rule to prohibit the almost certainly erroneous declaration

  typedef struct X { };    // Missing declarator

Proposed resolution (September, 2015):

Change 9.2.4 [dcl.typedef] paragraph 1 as follows:

Declarations containing the decl-specifier typedef declare identifiers that can be used later for naming fundamental (6.8.2 [basic.fundamental]) or compound (6.8.4 [basic.compound]) types. The typedef specifier shall not be combined in a decl-specifier-seq with any other kind of specifier except a type-specifier, and it shall not be used in the decl-specifier-seq of a parameter-declaration (9.3.4.6 [dcl.fct]) nor in the decl-specifier-seq of a function-definition (9.5 [dcl.fct.def]). If a typedef specifier appears in a declaration without a declarator, the program is ill-formed.



1712. constexpr variable template declarations

Section: 9.2.6  [dcl.constexpr]     Status: CD4     Submitter: Richard Smith     Date: 2013-07-08

[Moved to DR at the November, 2014 meeting.]

According to 9.2.6 [dcl.constexpr] paragraph 1,

If any declaration of a function, function template, or variable template has a constexpr specifier, then all its declarations shall contain the constexpr specifier.

This requirement does not make sense applied to variable templates. The constexpr specifier requires that there be an initializer, and a variable template declaration with an initializer is a definition, so there cannot be more than one declaration of a variable template with the constexpr specifier.

Proposed resolution (February, 2014):

Change 9.2.6 [dcl.constexpr] paragraph 1 as follows:

...If any declaration of a function, or function template, or variable template has a constexpr specifier, then all its declarations shall contain the constexpr specifier. [Note:...



1872. Instantiations of constexpr templates that cannot appear in constant expressions

Section: 9.2.6  [dcl.constexpr]     Status: CD4     Submitter: Richard Smith     Date: 2014-02-17

[Adopted at the February, 2016 meeting.]

According to 9.2.6 [dcl.constexpr] paragraph 6,

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is still a constexpr function or constexpr constructor, even though a call to such a function cannot appear in a constant expression.

The restriction on appearing in a constant expression assumes the previous wording that made such a specialization non-constexpr, and a call to a non-constexpr function cannot appear in a constant expression. With the current wording, however, there is no normative restriction against calls to such specializations. 7.7 [expr.const] should be updated to include such a prohibition.

Proposed resolution (January, 2016):

Add the following bullet following 7.7 [expr.const] bulllet 2.3:

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.9.1 [intro.execution]), would evaluate one of the following expressions:




1911. constexpr constructor with non-literal base class

Section: 9.2.6  [dcl.constexpr]     Status: CD4     Submitter: Richard Smith     Date: 2014-04-13

[Moved to DR at the November, 2014 meeting.]

An example like

  struct X {
    std::unique_ptr<int> p;
    constexpr X() { }
  };

is ill-formed because the X constructor cannot be used in a constant expression, because a constant expression cannot construct an object of a non-literal type like unique_ptr. This prevents use of something like

  X x;

to guarantee constant-initialization.

Proposed resolution (June, 2014):

Change 9.2.6 [dcl.constexpr] paragraph 5 as follows:

For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (7.7 [expr.const]), or, for a constructor, a constant initializer for some object (6.9.3.2 [basic.start.static]), the program is ill-formed; no diagnostic required.



2163. Labels in constexpr functions

Section: 9.2.6  [dcl.constexpr]     Status: CD4     Submitter: Richard Smith     Date: 2015-07-24

[Adopted at the February, 2016 meeting.]

The requirements for constexpr functions do not, but presumably should, forbid the appearance of a label in the function body (gotos are prohibited).

Proposed resolution (January, 2016):

Add the following as an additional bullet following 9.2.6 [dcl.constexpr] bullet 3.5.2:

The definition of a constexpr function shall satisfy the following requirements:




609. What is a “top-level” cv-qualifier?

Section: 9.2.9.2  [dcl.type.cv]     Status: CD4     Submitter: Dawn Perchik     Date: 5 November 2006

[Moved to DR at the November, 2014 meeting.]

The phrase “top-level cv-qualifier” is used numerous times in the Standard, but it is not defined. The phrase could be misunderstood to indicate that the const in something like const T& is at the “top level,” because where it appears is the highest level at which it is permitted: T& const is ill-formed.

Proposed resolution (February, 2014):

Change 6.8.5 [basic.type.qualifier] paragraph 5 as follows, splitting it into two paragraphs:

In this International Standard, the notation cv (or cv1, cv2, etc.), used in the description of types, represents an arbitrary set of cv-qualifiers, i.e., one of {const}, {volatile}, {const, volatile}, or the empty set. For a type cv T, the top-level cv-qualifiers of that type are those denoted by cv. [Example: The type corresponding to the type-idconst int&” has no top-level cv-qualifiers. The type corresponding to the type-idvolatile int * const” has the top-level cv-qualifier const. For a class type C, the type corresponding to the type-idvoid (C::* volatile)(int) const” has the top-level cv-qualifier volatile. —end example]

Cv-qualifiers applied to an array type attach...




1600. Erroneous reference initialization in example

Section: 9.2.9.3  [dcl.type.simple]     Status: CD4     Submitter: Niels Dekker     Date: 2012-12-30

[Moved to DR at the November, 2014 meeting.]

The example in 9.2.9.3 [dcl.type.simple] paragraph 4 reads, in part,

  const int&& foo();
  int i;
  decltype(foo()) x1 = i; // type is const int&&

The initialization is an ill-formed attempt to bind an rvalue reference to an lvalue.

Proposed resolution (April, 2013):

Change the example in 9.2.9.3 [dcl.type.simple] paragraph 4 as follows:

  const int&& foo();
  int i;
  struct A { double x; };
  const A* a = new A();
  decltype(foo()) x1 = i 17;     // type is const int&&
  decltype(i) x2;                // type is int
  decltype(a->x) x3;             // type is double
  decltype((a->x)) x4 = x3;      // type is const double&



1852. Wording issues regarding decltype(auto)

Section: 9.2.9.3  [dcl.type.simple]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2014-02-04

[Moved to DR at the November, 2014 meeting.]

According to 9.2.9.3 [dcl.type.simple] paragraph 2,

The auto specifier is a placeholder for a type to be deduced (9.2.9.7 [dcl.spec.auto]).

This is not true when auto appears in the decltype(auto) construct.

On a slightly related wording issue, 9.2.9.7 [dcl.spec.auto] paragraph 2 says,

The auto and decltype(auto) type-specifiers designate a placeholder type that will be replaced later, either by deduction from an initializer or by explicit specification with a trailing-return-type.

This could be read as implying that decltype(auto) can be used to introduce a function with a trailing-return-type, contradicting 9.3.4.6 [dcl.fct] paragraph 2, which requires that a function declarator with a trailing-return-type must have auto as the sole type specifier.

Proposed resolution (February, 2014):

  1. Change 9.2.9.3 [dcl.type.simple] paragraph 2 as follows:

  2. The simple-type-specifier auto specifier is a placeholder for a type to be deduced (9.2.9.7 [dcl.spec.auto]). The other simple-type-specifiers...
  3. Change 9.2.9.7 [dcl.spec.auto] paragraph 1 as follows:

  4. The auto and decltype(auto) type-specifiers are used to designate a placeholder type that will be replaced later, either by deduction from an initializer or by explicit specification with a trailing-return-type. The auto type-specifier is also used to introduce a function type having a trailing-return-type or to signify that a lambda is a generic lambda.




2157. Further disambiguation of enumeration elaborated-type-specifier

Section: 9.2.9.5  [dcl.type.elab]     Status: CD4     Submitter: Richard Smith     Date: 2015-07-06

[Adopted at the February, 2016 meeting.]

The resolution of issue 1966 does not apply to the case where the enumeration name is qualified, e.g.,

  enum E : int;
  struct X {
    enum ::E : int();
  };

Proposed resolution (October, 2015):

Change 9.7.1 [dcl.enum] paragraph 1 as follows:

...A : following “enum nested-name-specifieropt identifier” within the decl-specifier-seq of a member-declaration is parsed as part of an enum-base. [Note: This resolves a potential ambiguity...



1877. Return type deduction from return with no operand

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2014-02-20

[Moved to DR at the November, 2014 meeting.]

Return type deduction from a return statement with no expression is described in 9.2.9.7 [dcl.spec.auto] paragraph 7 as follows:

When a variable declared using a placeholder type is initialized, or a return statement occurs in a function declared with a return type that contains a placeholder type, the deduced return type or variable type is determined from the type of its initializer. In the case of a return with no operand, the initializer is considered to be void(). Let T be the declared type of the variable or return type of the function. If the placeholder is the auto type-specifier, the deduced type is determined using the rules for template argument deduction. If the deduction is for a return statement and the initializer is a braced-init-list (9.4.5 [dcl.init.list]), the program is ill-formed. Otherwise, obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list, with std::initializer_list<U>. Deduce a value for U using the rules of template argument deduction from a function call (13.10.3.2 [temp.deduct.call]), where P is a function template parameter type and the initializer is the corresponding argument.

However, this does not work: the deduction for an argument of void() would give a parameter type of void and be ill-formed. It would be better simply to say that the deduced type in this case is void.

In a related example, consider

  decltype(auto) f(void *p) {
    return *p;
  }

This is presumably an error because decltype(*p) would be void&, which is ill-formed. Perhaps this case should be mentioned explicitly.

Notes from the June, 2014 meeting:

The last part of the issue is not a defect, because the unary * operator requires its operand to be a pointer to an object or function type, and void is neither, so the expression is ill-formed and deduction does not occur for that case.

It was also observed during the discussion that the same deduction problem occurs when returning an expression of type void as when the expression is omitted, so the resolution should cover both cases.

Proposed resolution (June, 2014):

Change 9.2.9.7 [dcl.spec.auto] paragraph 7 as follows:

When a variable declared using a placeholder type is initialized, or a return statement occurs in a function declared with a return type that contains a placeholder type, the deduced return type or variable type is determined from the type of its initializer. In the case of a return with no operand or with an operand of type void, the initializer declared return type shall be auto and the deduced return type is void considered to be void(). Let Otherwise, let T be the declared type...



1878. operator auto template

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2014-02-20

[Moved to DR at the November, 2014 meeting.]

The Standard currently appears to allow something like

  struct S {
    template<class T> operator auto() { return 42; }
  };

This is of very limited utility and presents difficulties for some implementations. It might be good to prohibit such constructs.

Proposed resolution (October, 2014):

Add the following as the last paragraph of 11.4.8.3 [class.conv.fct]:

A conversion function template shall not have a deduced return type (9.2.9.7 [dcl.spec.auto]).



1892. Use of auto in function type

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD4     Submitter: Richard Smith     Date: 2014-03-12

[Moved to DR at the November, 2014 meeting.]

There appear to be no restrictions against using the auto specifier in examples like the following:

  template<typename T> using X = T;
  X<auto()> f_with_deduced_return_type; // ok
  std::vector<auto(*)()> v; // ok?!
  void f(auto (*)()); // ok?!

Proposed resolution (June, 2014):

Change 9.2.9.7 [dcl.spec.auto] paragraph 2 as follows:

The placeholder type can appear with a function declarator in the decl-specifier-seq, type-specifier-seq, conversion-function-id, or trailing-return-type, in any context where such a declarator is valid. If the function declarator includes a trailing-return-type (9.3.4.6 [dcl.fct]), that specifies the declared return type of the function. Otherwise, the function declarator shall declare a function. If the declared return type of the function contains a placeholder type, the return type of the function is deduced from return statements in the body of the function, if any.



1958. decltype(auto) with parenthesized initializer

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD4     Submitter: Vinny Romano     Date: 2014-06-30

[Moved to DR at the May, 2015 meeting.]

According to 9.2.9.7 [dcl.spec.auto] paragraph 7,

If the placeholder is the decltype(auto) type-specifier, the declared type of the variable or return type of the function shall be the placeholder alone. The type deduced for the variable or return type is determined as described in 9.2.9.3 [dcl.type.simple], as though the initializer had been the operand of the decltype.

This is problematic when the parenthesized form of initializer is used, e.g.,

  int i;
  decltype(auto) x(i);

the specification would deduce the type as decltype((i)), or int&. The wording should be clarified that the expression and not the entire initializer is considered to be the operand of decltype.

Proposed resolution (April, 2015):

Change 9.2.9.7 [dcl.spec.auto] paragraph 7 as follows:

...If the placeholder is the decltype(auto) type-specifier, the declared type of the variable or return type of the function shall be the placeholder alone. The type deduced for the variable or return type is determined as described in 9.2.9.3 [dcl.type.simple], as though the initializer initializer-clause or expression-list of the initializer or the expression of the return statement had been the operand of the decltype. [Example:
  int i;
  int&& f();
  auto           x2a(i);    // decltype(x2a) is int
  decltype(auto) x2d(i);    // decltype(x2d) is int
  auto           x3a = i;   // decltype(x3a) is int
  decltype(auto) x3d = i;   // decltype(x3d) is int
  ...



2044. decltype(auto) and void

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD4     Submitter: Richard Smith     Date: 2014-11-14

[Adopted at the February, 2016 meeting.]

The resolution of issue 1877 does not correctly handle decltype(auto) return types with void return expressions:

  T f();
  decltype(auto) g() { return f(); }

fails when T is void.

Suggested resolution:

Change 9.2.9.7 [dcl.spec.auto] paragraph 7 as follows:

...In the case of a return with no operand or with an operand of type void, the declared return type shall be auto or decltype(auto) and the deduced return type is void. Otherwise...

Proposed resolution (September, 2015):

Change 9.2.9.7 [dcl.spec.auto] paragraph 7 as follows:

When a variable declared using a placeholder type is initialized, or a return statement occurs in a function declared with a return type that contains a placeholder type, the deduced return type or variable type is determined from the type of its initializer. In the case of a return with no operand or with an operand of type void,:

Otherwise, let T be...




2040. trailing-return-type no longer ambiguous

Section: 9.3  [dcl.decl]     Status: CD4     Submitter: Jens Maurer     Date: 2014-11-09

[Adopted at the February, 2016 meeting.]

According to 9.3 [dcl.decl] paragraph 5,

The type-id in a trailing-return-type includes the longest possible sequence of abstract-declarators. [Note: This resolves the ambiguous binding of array and function declarators. [Example:

  auto f()->int(*)[4]; // function returning a pointer to array[4] of int
                       // not function returning array[4] of pointer to int

end example] —end note]

However, the grammar has changed since that rule and example were added; because trailing-return-type can only appear at the top level, there is no longer any potential ambiguity.

Proposed resolution (September, 2015):

Delete 9.3 [dcl.decl] paragraph 5:

The optional attribute-specifier-seq in a trailing-return-type appertains to the indicated return type. The type-id in a trailing-return-type includes the longest possible sequence of abstract-declarators. [Note: This resolves the ambiguous binding of array and function declarators. [Example:

  auto f()->int(*)[4]; // function returning a pointer to array[4] of int
                       // not function returning array[4] of pointer to int

end example] —end note]




2175. Ambiguity with attribute in conversion operator declaration

Section: 9.3.3  [dcl.ambig.res]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2015-09-17

[Adopted at the February, 2016 meeting.]

The declaration

  operator int [[noreturn]] ();

is ambiguous with respect to the binding of the attribute. It could either be parsed (as apparently intended by the user) as part of the noptr-declarator (9.3 [dcl.decl] paragraph 4) or as part of the type-specifier-seq (9.2.9 [dcl.type] paragraph 1) of the conversion-type-id (11.4.8.3 [class.conv.fct] paragraph 1) . Current implementations disambiguate this declaration in favor of the latter interpretation, issuing an error for the declaration because the noreturn attribute cannot apply to a type.

Proposed resolution (February, 2016):

Change 11.4.8.3 [class.conv.fct] paragraph 3 as follows:

The conversion-type-id shall not represent a function type nor an array type. The conversion-type-id in a conversion-function-id is the longest possible sequence of conversion-declarators tokens that could possibly form a conversion-type-id. [Note: This prevents ambiguities between the declarator operator * and its expression counterparts. [Example:

  &ac.operator int*i;  // syntax error:
                       // parsed as: &(ac.operator int *)i
                       // not as: &(ac.operator int)*i

The * is the pointer declarator and not the multiplication operator. —end example] This rule also prevents ambiguities for attributes. [Example:

  operator int [[noreturn]] (); // error: noreturn attribute applied to a type

end example]end note]




2113. Incompete specification of types for declarators

Section: 9.3.4  [dcl.meaning]     Status: CD4     Submitter: Richard Smith     Date: 2015-04-08

[Adopted at the February, 2016 meeting.]

According to 9.3.4 [dcl.meaning]

A static, thread_local, extern, register, mutable, friend, inline, virtual, or typedef specifier applies directly to each declarator-id in an init-declarator-list; the type specified for each declarator-id depends on both the decl-specifier-seq and its declarator.

This list is missing constexpr and explicit. Also, this should apply, but doesn't, to member-declarator-lists.

Proposed resolution (September, 2015):

Change 9.3.4 [dcl.meaning] paragraph 2 as follows:

A static, thread_local, extern, register, mutable, friend, inline, virtual, constexpr, explicit, or typedef specifier applies directly to each declarator-id in an init-declarator-list or member-declarator-list; the type specified for each declarator-id depends on both the decl-specifier-seq and its declarator.



2099. Inferring the bound of an array static data member

Section: 9.3.4.5  [dcl.array]     Status: CD4     Submitter: Richard Smith     Date: 2015-03-15

[Adopted at the February, 2016 meeting.]

According to 9.3.4.5 [dcl.array] paragraph 3,

An array bound may also be omitted when the declarator is followed by an initializer (9.4 [dcl.init]). In this case the bound is calculated from the number of initial elements...

However, the grammar for member-declarator uses brace-or-equal-initializer, not initializer, so the following is ill-formed:

  struct X {
    static constexpr int arr[] = { 1, 2, 3 };
  };

Proposed resolution (October, 2015):

Change 9.3.4.5 [dcl.array] paragraph 3 as follows:

...An array bound may also be omitted when the declarator is followed by an initializer (9.4 [dcl.init]) or when a declarator for a static data member is followed by a brace-or-equal-initializer (11.4 [class.mem]). In this case both cases the bound is calculated from the number of initial elements...



393. Pointer to array of unknown bound in template argument list in parameter

Section: 9.3.4.6  [dcl.fct]     Status: CD4     Submitter: Mark Mitchell     Date: 12 Dec 2002

[Moved to DR at the November, 2014 meeting.]

EDG rejects this code:

  template <typename T>
  struct S {};

  void f (S<int (*)[]>);
G++ accepts it.

This is another case where the standard isn't very clear:

The language from 9.3.4.6 [dcl.fct] is:

If the type of a parameter includes a type of the form "pointer to array of unknown bound of T" or "reference to array of unknown bound of T," the program is ill-formed.
Since "includes a type" is not a term defined in the standard, we're left to guess what this means. (It would be better if this were a recursive definition, the way a type theoretician would do it: )

Notes from April 2003 meeting:

We agreed that the example should be allowed.

Additional note (January, 2013):

Additional discussion of this issue has arisen . For example, the following is permissible:

  T (*p) [] = (U(*)[])0;

but the following is not:

  template<class T>
  void sp_assert_convertible( T* ) {}

  sp_assert_convertible<T[]>( (U(*)[])0 );

Proposed resolution (February, 2014):

Change 9.3.4.6 [dcl.fct] paragraph 8 as follows, including deleting the footnote:

If the type of a parameter includes a type of the form “pointer to array of unknown bound of T” or “reference to array of unknown bound of T,” the program is ill-formed.101 Functions shall not have a return type of type array or function, although...



1824. Completeness of return type vs point of instantiation

Section: 9.3.4.6  [dcl.fct]     Status: CD4     Submitter: Steve Clamage     Date: 2013-12-19

[Moved to DR at the November, 2014 meeting.]

Consider the following example:

  template<typename T> struct A {
    T t;
  };
  struct S {
    A<S> f() { return A<S>(); }
  };

According to 9.3.4.6 [dcl.fct] paragraph 9,

The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function is deleted (9.5.3 [dcl.fct.def.delete]) or the definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).

Thus type A<S> must be a complete type. The requirement for a complete type triggers the instantiation of the template, which requires that its template argument be complete in order to use it as the type of a non-static data member.

According to 13.8.4.1 [temp.point] paragraph 4, the point of instantiation of A<S> is “immediately preced[ing] the namespace scope declaration or definition that refers to the specialization.” Thus the point of instantiation precedes the definition of S, making this example ill-formed. Most or all current implementations accept the example, however.

Perhaps the specification in 9.3.4.6 [dcl.fct] ought to say that the completeness of the type is checked from the context of the function body (at which S is a complete type)?

Proposed resolution (February, 2014):

Change 9.3.4.6 [dcl.fct] paragraph 9 as follows:

Types shall not be defined in return or parameter types. The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) in the context of the function definition unless the function is deleted (9.5.3 [dcl.fct.def.delete]) or the definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).



1814. Default arguments in lambda-expressions

Section: 9.3.4.7  [dcl.fct.default]     Status: CD4     Submitter: Jonathan Caves     Date: 2013-11-21

[Moved to DR at the November, 2014 meeting.]

The resolution for issue 974 permitting default arguments in lambda-expressions overlooked 9.3.4.7 [dcl.fct.default] paragraph 3:

A default argument shall be specified only in the parameter-declaration-clause of a function declaration or in a template-parameter (13.2 [temp.param])...

Proposed resolution (February, 2014):

Change 9.3.4.7 [dcl.fct.default] paragraph 3 as follows:

A default argument shall be specified only in the parameter-declaration-clause of a function declaration or lambda-declarator or in a template-parameter (13.2 [temp.param]); in the latter case, the initializer-clause shall be an assignment-expression. A default argument shall not be specified for a parameter pack. If it is specified in a parameter-declaration-clause, it shall not occur within a declarator or abstract-declarator of a parameter-declaration.103



2082. Referring to parameters in unevaluated operands of default arguments

Section: 9.3.4.7  [dcl.fct.default]     Status: CD4     Submitter: Faisal Vali     Date: 2015-02-09

[Adopted at the February, 2016 meeting.]

According to 9.3.4.7 [dcl.fct.default] paragraph 9,

A default argument is evaluated each time the function is called with no argument for the corresponding parameter. The order of evaluation of function arguments is unspecified. Consequently, parameters of a function shall not be used in a default argument, even if they are not evaluated.

This prohibits use of parameters in unevaluated operands, e.g.,

  void foo(int a = decltype(a){});

This wording predates the concept of “unevaluated operands” (the phrase “not evaluated” refers to calls to the function where an actual argument is supplied and thus the default argument is not used, not to unevaluated operands) and should not apply to such cases.

Proposed resolution (October, 2015):

  1. Change 9.3.4.7 [dcl.fct.default] paragraph 7 as follows:

  2. Local variables A local variable shall not be used appear as a potentially-evaluated expression in a default argument. [Example:

      void f() {
        int i;
        extern void g(int x = i);           // error
        extern void h(int x = sizeof(i));   // OK
        // ...
      }
    

    end example]

  3. Change 9.3.4.7 [dcl.fct.default] paragraph 8 as follows:

  4. [Note: The keyword this shall may not be used appear in a default argument of a member function; see _N4567_.5.1.1 [expr.prim.general]. [Example:

      class A {
        void f(A* p = this) { } // error
      };
    

    end example] end note]

  5. Change 9.3.4.7 [dcl.fct.default] paragraph 9 as follows:

  6. A default argument is evaluated each time the function is called with no argument for the corresponding parameter. The order of evaluation of function arguments is unspecified. Consequently, parameters of a function shall not be used in a default argument, even if they are not evaluated. A parameter shall not appear as a potentially-evaluated expression in a default argument. Parameters of a function declared before a default argument are in scope and can hide namespace and class member names. [Example:

      int a;
      int f(int a, int b = a);         // error: parameter a
                                       // used as default argument
      typedef int I;
      int g(float I, int b = I(2));    // error: parameter I found
      int h(int a, int b = sizeof(a)); // error, parameter a used OK, unevaluated operand
                                         // in default argument
    

    end example] Similarly, a A non-static member shall not be used appear in a default argument, even if it is not evaluated, unless it appears as the id-expression of a class member access expression (7.6.1.5 [expr.ref]) or unless it is used to form a pointer to member (7.6.2.2 [expr.unary.op]). [Example:...




670. Copy initialization via derived-to-base conversion in the second step

Section: 9.4  [dcl.init]     Status: CD4     Submitter: Jason Merrill     Date: 20 December 2007

[Accepted at the June, 2016 meeting as part of paper P0135R1.]

In this example:

    struct A {};

    struct B: A {
       B(int);
       B(B&);
       B(A);
    };

    void foo(B);

    void bar() {
       foo(0);
    }

we are copy-initializing a B from 0. So by 12.2.2.5 [over.match.copy] we consider all the converting constructors of B, and choose B(int) to create a B. Then, by 9.4 [dcl.init] paragraph 15, we direct-initialize the parameter from that temporary B. By 12.2.2.4 [over.match.ctor] we consider all constructors. The copy constructor cannot be called with a temporary, but B(A) is callable.

As far as I can tell, the Standard says that this example is well-formed, and calls B(A). EDG and G++ have rejected this example with a message about the copy constructor not being callable, but I have been unsuccessful in finding anything in the Standard that says that we only consider the copy constructor in the second step of copy-initialization. I wouldn't mind such a rule, but it doesn't seem to be there. And implementing issue 391 causes G++ to start accepting the example.

This question came up before in a GCC bug report; in the discussion of that bug Nathan Sidwell said that some EDG folks explained to him why the testcase is ill-formed, but unfortunately didn't provide that explanation in the bug report.

I think the resolution of issue 391 makes this example well-formed; it was previously ill-formed because in order to bind the temporary B(0) to the argument of A(const A&) we needed to make another temporary B, and that's what made the example ill-formed. If we want this example to stay ill-formed, we need to change something else.

Steve Adamczyk:

I tracked down my response to Nathan at the time, and it related to my paper N1232 (on the auto_ptr problem). The change that came out of that paper is in 12.2.4.2 [over.best.ics] paragraph 4:

However, when considering the argument of a user-defined conversion function that is a candidate by 12.2.2.4 [over.match.ctor] when invoked for the copying of the temporary in the second step of a class copy-initialization, or by 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv], or 12.2.2.7 [over.match.ref] in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed.

This is intended to prevent use of more than one implicit user- defined conversion in an initialization.

I told Nathan B(A) can't be called because its argument would require yet another user-defined conversion, but I was wrong. I saw the conversion from B to A and immediately thought “user-defined,” but in fact because B is a derived class of A the conversion according to 12.2.4.2 [over.best.ics] paragraph 6 is a derived-to-base Conversion (even though it will be implemented by calling a copy constructor).

So I agree with you: with the analysis above and the change for issue 391 this example is well-formed. We should discuss whether we want to make a change to keep it ill-formed.




1630. Multiple default constructor templates

Section: 9.4  [dcl.init]     Status: CD4     Submitter: Nikolay Ivchenkov     Date: 2013-03-01

[Moved to DR at the November, 2014 meeting.]

It is unclear whether code like the following is supposed to be supported or not:

  #include <iostream>
  #include <type_traits>

  #define ENABLE_IF(...) \
    typename std::enable_if<__VA_ARGS__, int>::type = 0
  #define PRINT_VALUE(...) \
    std::cout << #__VA_ARGS__ " = " << __VA_ARGS__ << std::endl

  struct undefined {};

  template <class T>
    undefined special_default_value(T *);

  template <class T>
    struct has_special_default_value :
      std::integral_constant
      <
        bool,
        !std::is_same
          <
            decltype(special_default_value((T *)0)),
            undefined
          >{}
      > {};

  template <class T> struct X {
    template <class U = T, ENABLE_IF(!has_special_default_value<U>{})>
      X() : value() {}
    template <class U = T, ENABLE_IF(has_special_default_value<U>{})>
      X() : value(special_default_value((T *)0)) {}
    T value;
  };

  enum E {
    e1 = 1,
    e2 = 2
  };

  E special_default_value(E *) { return e1; }

  int main() {
    X<int> x_int;
    X<E> x_E;
    PRINT_VALUE(x_int.value);
    PRINT_VALUE(x_E.value);

    PRINT_VALUE(X<int>().value);
    PRINT_VALUE(X<E>().value);
  }

The intent is that X<int> should call the first default constructor and X<E> should call the second.

If this is intended to work, the rules for making it do so are not clear; current wording reads as if a class can have only a single default constructor, and there appears to be no mechanism for using overload resolution to choose between variants.

Proposed resolution (June, 2014):

  1. Change 6.3 [basic.def.odr] paragraph 3 as follows:

  2. ...An assignment operator function in a class is odr-used by an implicitly-defined copy-assignment or move-assignment function for another class as specified in 11.4.5.3 [class.copy.ctor]. A default constructor for a class is odr-used by default initialization or value initialization as specified in 9.4 [dcl.init]. A constructor for a class is odr-used as specified in 9.4 [dcl.init]. A destructor for a class is odr-used if it is potentially invoked (11.4.7 [class.dtor]).
  3. Change 9.4 [dcl.init] paragraph 7 as follows:

  4. To default-initialize an object of type T means:

  5. Change 11.4.5 [class.ctor] paragraph 4 as follows:

  6. A default constructor for a class X is a constructor of class X that can be called without an argument either has no parameters or else each parameter that is not a function parameter pack has a default argument. If there is no user-declared constructor...
  7. Change 12.2 [over.match] bullet 2.4 as follows:

  8. Overload resolution selects the function to call in seven distinct contexts within the language:

  9. Change 12.2.2.4 [over.match.ctor] paragraph 1 as follows:

  10. When objects of class type are direct-initialized (9.4 [dcl.init]), or copy-initialized from an expression of the same or a derived class type (9.4 [dcl.init]), or default-initialized, overload resolution selects the constructor. For direct-initialization or default-initialization, the candidate functions are all the constructors of the class of the object being initialized. For copy-initialization, the candidate functions are all the converting constructors (11.4.8.2 [class.conv.ctor]) of that class. The argument list is the expression-list or assignment-expression of the initializer.



1633. Copy-initialization in member initialization

Section: 9.4  [dcl.init]     Status: CD4     Submitter: Vinny Romano     Date: 2013-03-04

[Moved to DR at the November, 2014 meeting.]

According to 9.4 [dcl.init] paragraph 16,

The initialization that occurs in the forms

  T x(a);
  T x{a};

as well as in new expressions (7.6.2.8 [expr.new]), static_cast expressions (7.6.1.9 [expr.static.cast]), functional notation type conversions (7.6.1.4 [expr.type.conv]), and base and member initializers (11.9.3 [class.base.init]) is called direct-initialization.

This wording was overlooked when brace-or-equal-initializers were added to the language, permitting copy-initialization of members by use of the = form.

Proposed resolution (April, 2013):

Change 9.4 [dcl.init] paragraphs 15-16 as follows, removing the example in paragraph 15 and making it a single running sentence:

The initialization that occurs in the = form of a brace-or-equal-initializer or condition (8.5 [stmt.select]),

  T x = a;

as well as in argument passing, function return, throwing an exception (14.2 [except.throw]), handling an exception (14.4 [except.handle]), and aggregate member initialization (9.4.2 [dcl.init.aggr]), is called copy-initialization. [Note: Copy-initialization may invoke a move (12.8). —end note]

The initialization that occurs in the forms

  T x(a);
  T x{a};

as well as in new expressions (7.6.2.8 [expr.new]), static_cast expressions (5.2.9), functional notation type conversions (7.6.1.4 [expr.type.conv]), and base and member initializers mem-initializers (11.9.3 [class.base.init]), and the braced-init-list form of a condition is called direct-initialization.




1782. Form of initialization for nullptr_t to bool conversion

Section: 9.4  [dcl.init]     Status: CD4     Submitter: Hubert Tong     Date: 2013-02-26

[Moved to DR at the November, 2014 meeting.]

According to 9.4 [dcl.init] paragraph 14,

The form of initialization (using parentheses or =) is generally insignificant, but does matter when the initializer or the entity being initialized has a class type; see below.

This does not consider conversions from std::nullptr_t to bool, which are permitted only for direct-initialization (7.3.14 [conv.fctptr]).

Proposed resolution (February, 2014):

Change 9.4 [dcl.init] paragraph 14 as follows:

The form of initialization (using parentheses or =) is generally insignificant, but does matter when the initializer or the entity being initialized has a class type; see below. If the entity being initialized...



1561. Aggregates with empty base classes

Section: 9.4.2  [dcl.init.aggr]     Status: CD4     Submitter: Gabriel Dos Reis     Date: 2012-09-29

[Accepted at the February, 2016 meeting as part of paper P0017R1.]

The definition of an aggregate class 9.4.2 [dcl.init.aggr] was originally intended to include only C-like classes because proper C++ classes were expected to encapsulate data members and use constructors for initialization. Consequently, classes with bases were excluded from being aggregates.

With the inclusion of aggregate initialization in list-initialization, the consequence of this decision could be surprising, so it should be reexamined. For example,

  struct A {
    int& val;
  };

  struct B { };

  struct C : B {
    int& val;
  };

  int main() {
    int i = 0;
    A a { i } ;         // #1
    C c { i } ;         // #2
    return 0;
  }

it is not clear that there is a good rationale for #1 being well-formed but #2 being ill-formed.

Rationale (October, 2012):

CWG felt that this language design question would be better considered by EWG.




1815. Lifetime extension in aggregate initialization

Section: 9.4.2  [dcl.init.aggr]     Status: CD4     Submitter: Dinka Ranns     Date: 2013-11-22

[Moved to DR at the November, 2014 meeting.]

With the recent addition of brace-or-equal-initializers to aggregates and the presumed resolution for issue 1696, it is not clear how lifetime extension of temporaries should work in aggregate initialization. For example:

  struct A { };
  struct B { A&& a { A{} } };
  B b;         // #1
  B b{ A{} };  // #2
  B b{};       // #3

#1 is default initialization, so (presumably) the lifetime of the temporary persists only until B's default constructor exits. #2 is aggregate initialization, which binds B::a to the temporary in the initializer for b and thus extends its lifetime to that of b. #3 is aggregate initialization, but it is not clear whether the lifetime of the temporary in the non-static data member initializer for B::a should be lifetime-extended like #2 or not, like #1.

One possibility might be to extend the lifetime in #3 but to give B a deleted default constructor since it would extend the lifetime of a temporary.

See also issue 1696.

Notes from the February, 2014 meeting:

CWG agreed with the suggested direction, which would treat #3 in the example like #2 and make the default constructor deleted, resulting in #1 being ill-formed.

Proposed resolution (June, 2014):

This issue is resolved by the resolution of issue 1696.




1571. cv-qualification for indirect reference binding via conversion function

Section: 9.4.4  [dcl.init.ref]     Status: CD4     Submitter: Michael Wong     Date: 2012-02-06

[Moved to DR at the November, 2014 meeting.]

In the case of indirect reference binding, 9.4.4 [dcl.init.ref] paragraph 5 only requires that the cv-qualification of the referred-to type be the same or greater than that of the initializer expression when the types are reference-related. This leads to the following anomaly:

  class A {
  public:
    operator volatile int &();
  };
  A a;

  const int & ir1a = a.operator volatile int&(); // error!
  const int & ir2a = a; // allowed! ir = a.operator volatile int&();

Is this intended?

Notes from the April, 2013 meeting:

CWG felt that the declaration of ir2a should also be an error.

Proposed resolution (February, 2014):

Change 9.4.4 [dcl.init.ref] paragraph 5 as follows:

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

This resolution also resolves issue 1572.




1572. Incorrect example for rvalue reference binding via conversion function

Section: 9.4.4  [dcl.init.ref]     Status: CD4     Submitter: Michael Wong     Date: 2012-10-15

[Moved to DR at the November, 2014 meeting.]

The example just before the final bullet of 9.4.5 [dcl.init.list] paragraph 5 is incorrect. It reads, in part,

  struct X {
    operator int&();
  } x;
  int&& rri2 = X(); // error: lvalue-to-rvalue conversion applied to the
                    // result of operator int&

In fact, according to 12.2.2.7 [over.match.ref] (as clarified by the proposed resolution of issue 1328, although the intent was arguably the same for the previous wording), X::operator int&() is not a candidate for the initialization of rri2, so the case falls into the last bullet, creating an int temporary.

It is not clear whether the lvalue-to-rvalue conversion whose prohibition is intended to be illustrated by that example could actually occur, given the specification of candidate functions in 12.2.2.7 [over.match.ref].

Proposed resolution (February, 2014):

This issue is resolved by the resolution of issue 1571.




1467. List-initialization of aggregate from same-type object

Section: 9.4.5  [dcl.init.list]     Status: CD4     Submitter: Jason Merrill     Date: 2012-02-06

[Moved to DR at the November, 2014 meeting.]

The current list-initialization rules do not provide for list-initialization of an aggregate from an object of the same type:

  struct X {
    X() = default;
    X(const X&) = default;
  #ifdef OK
    X(int) { }
  #endif
  };

  X x;
  X x2{x}; // error, {x} is not a valid aggregate initializer for X

Suggested resolution:

Change 9.4.5 [dcl.init.list] paragraph 3 as follows:

List-initialization of an object or reference of type T is defined as follows:

Additional notes (September, 2012):

(See messages 22368, 22371 through 22373, 22388, and 22494.)

It appears that 12.2.4.2.6 [over.ics.list] will also need to be updated in parallel with this change. Alternatively, it may be better to change 9.4.2 [dcl.init.aggr] instead of 9.4.5 [dcl.init.list] and 12.2.4.2.6 [over.ics.list].

In a related note, given

  struct NonAggregate {
    NonAggregate() {}
  };

  struct WantsIt {
    WantsIt(NonAggregate);
  };

  void f(NonAggregate n);
  void f(WantsIt);

  int main() {
    NonAggregate n;
    // ambiguous!
    f({n});
  }

12.2.4.2.6 [over.ics.list] paragraph 3 says that the call to f(NonAggregate) is a user-defined conversion, the same as the call to f(WantsIt) and thus ambiguous. Also,

    NonAggregate n;
    // #1 (n -> NonAggregate = Identity conversion)
    NonAggregate m{n};
    // #2 ({n} -> NonAggregate = User-defined conversion}
    // (copy-ctor not considered according to 12.2.4.2 [over.best.ics] paragraph 4)
    NonAggregate m{{n}};

Finally, the suggested resolution simply says “initialized from,” without specifying whether that means direct initialization or copy initialization. It should be explicit about which is intended, e.g., if it reflects the kind of list-initialization being done.

Proposed resolution (February, 2014) [SUPERSEDED]:

  1. Change 9.4.5 [dcl.init.list] paragraph 3 as follows:

  2. List-initialization of an object or reference of type T is defined as follows:

  3. Delete the final bullet of 12.2.4.2 [over.best.ics] paragraph 4, as follows:

  4. However, if the target is

    and the constructor or user-defined conversion function is a candidate by

    user-defined conversion sequences are not considered. [Note:...

  5. Insert the following two paragraphs between 12.2.4.2.6 [over.ics.list] paragraphs 1 and 2, moving the footnote from the current paragraph 3 to the second inserted paragraph:

  6. When an argument is an initializer list (9.4.5 [dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type.

    If the parameter type is a class C and the initializer list has a single element of type cv U, where U is C or a class derived from C, the implicit conversion sequence is the one required to convert the element to the parameter type.

    Otherwise, if the parameter type is a character array [Footnote: Since there are no parameters of array type, this will only occur as the underlying type of a reference parameter. —end footnote] and the initializer list has a single element that is an appropriately typed string literal (9.4.3 [dcl.init.string]), the implicit conversion is the identity conversion.

    If Otherwise, if the parameter type is std::initializer_list<X> and...

    Otherwise, if the parameter type is “array of N X[Footnote: ... —end footnote], if the initializer list has...

  7. Change 12.2.4.2.6 [over.ics.list] paragraph 7 as follows:

  8. Otherwise, if the parameter type is not a class:

  9. Change 12.2.4.3 [over.ics.rank] paragraph 3 as follows:

  10. Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of the following rules applies:

This resolution also resolves issues 1490, 1589, and 1631.

Notes from the February, 2014 meeting:

The resolution above does not adequately address the related issue 1758. It appears that conversion functions and constructors must be handled separately.

Proposed resolution (June, 2014):

  1. Change 9.4.5 [dcl.init.list] paragraph 3 as follows:

  2. List-initialization of an object or reference of type T is defined as follows:

  3. Change 12.2.2.8 [over.match.list] paragraph 1 as follows:

  4. When objects of non-aggregate class type T are list-initialized (9.4.5 [dcl.init.list]) such that 9.4.5 [dcl.init.list] specifies that overload resolution is performed according to the rules in this section, overload resolution selects the constructor...
  5. Change 12.2.4.2 [over.best.ics] paragraph 4 as follows:

  6. ...and the constructor or user-defined conversion function is a candidate by

    user-defined conversion sequences are not considered.

  7. Change 12.2.4.2.6 [over.ics.list] paragraphs 1-2 as follows, moving the footnote from paragraph 3:

  8. When an argument is an initializer list (9.4.5 [dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type.

    If the parameter type is a class X and the initializer list has a single element of type cv U, where U is X or a class derived from X, the implicit conversion sequence is the one required to convert the element to the parameter type.

    Otherwise, if the parameter type is a character array [Footnote: Since there are no parameters of array type, this will only occur as the underlying type of a reference parameter. —end footnote] and the initializer list has a single element that is an appropriately-typed string literal (9.4.3 [dcl.init.string]), the implicit conversion sequence is the identity conversion.

    If Otherwise, if the parameter type is std::initializer_list<X> and...

  9. Change 12.2.4.2.6 [over.ics.list] paragraph 7 as follows:

  10. Otherwise, if the parameter type is not a class:

  11. Move the final bullet of 12.2.4.3 [over.ics.rank] paragraph 3 to the beginning of the list and change it as follows:

This resolution also resolves issues 1490, 1589, 1631, 1756, and 1758.




1490. List-initialization from a string literal

Section: 9.4.5  [dcl.init.list]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2012-03-29

[Moved to DR at the November, 2014 meeting.]

Initialization of an array of characters from a string literal is handled by the third bullet of 9.4 [dcl.init] paragraph 16, branching off to 9.4.3 [dcl.init.string]. However, list initialization is handled by the first bullet, branching off to 9.4.5 [dcl.init.list], and there is no corresponding special case in 9.4.5 [dcl.init.list] paragraph 3 for an array of characters initialized by a brace-enclosed string literal. That is, an initialization like

  char s[4]{"abc"};

is ill-formed, which could be surprising. Similarly,

  std::initializer_list<char>{"abc"};

is plausible but also not permitted.

Notes from the October, 2012 meeting:

CWG agreed that the first example should be permitted, but not the second.

Proposed resolution (June, 2014):

This issue is resolved by the resolution of issue 1467.




1518. Explicit default constructors and copy-list-initialization

Section: 9.4.5  [dcl.init.list]     Status: CD4     Submitter: Richard Smith     Date: 2012-07-08

[Adopted at the June, 2016 meeting as paper P0398R0.]

Consider the following example:

  struct A {
   explicit A() = default;
  };

  struct B : A {
   explicit B() = default;
  };

  struct C {
   explicit C();
  };

  struct D : A {
   C c;
   explicit D() = default;
  };

  template<typename T> void f() {
   T t = {};
  }
  template<typename T> void g() {
   void x(T t);
   x({});
  }

The question is whether f<B>, f<C>, f<D>, g<B>, g<C>, and g<D> are well-formed or ill-formed.

The crux here is whether 12.2.2.8 [over.match.list] is the governing law in each of these cases. If it is, the initialization is ill-formed, because copy-list-initialization has selected an explicit constructor. The standard seems clear that f<A> and g<A> are valid (because A is an aggregate, so 12.2.2.8 [over.match.list] is not reached nor applicable), f<B> is valid (because value-initialization does not call the default constructor, so 12.2.2.8 [over.match.list] is not reached), and that g<B>, g<C>, and g<D> are ill-formed (because 12.2.2.8 [over.match.list] is reached from 12.2.4.2.6 [over.ics.list] and selects an explicit constructor). The difference between f<B> and g<B> is troubling.

For f<C> and f<D>, it's not clear whether the default constructor call within value-initialization within list-initialization uses 12.2.2.8 [over.match.list] — but some form of overload resolution seems to be implied, since presumably we want to apply SFINAE to variadic constructor templates, diagnose classes which have multiple default constructors through the addition of default arguments, and the like.

It has been suggested that perhaps we are supposed to reach 12.2.2.8 [over.match.list] for an empty initializer list for a non-aggregate class with a default constructor only when we're coming from 12.2.4.2.6 [over.ics.list], and not when 9.4.5 [dcl.init.list] delegates to value-initialization. That would make all the fs valid, but g<B>, g<C>, and g<D> ill-formed.

11.4.8.2 [class.conv.ctor] paragraph 2 says explicit constructors are only used for direct-initialization or casts, which argues for at least f<C>, f<D>, g<C> and g<D> being ill-formed.

See also issue 2116.

Proposed resolution (May, 2015): [SUPERSEDED]

This issue is resolved by the resolution of issue 1630: default initialization now uses 12.2.2.4 [over.match.ctor], which now permits explicit constructors for default-initialization.

Additional note, October, 2015:

It has been suggested that the resolution of issue 1630 went too far in allowing use of explicit constructors for default initialization, and that default initialization should be considered to model copy initialization instead. The resolution of this issue would provide an opportunity to adjust that.

Proposed resolution (October, 2015):

Change 12.2.2.4 [over.match.ctor] paragraph 1 as follows:

When objects of class type are direct-initialized (9.4 [dcl.init]), copy-initialized from an expression of the same or a derived class type (9.4 [dcl.init]), or default-initialized (9.4 [dcl.init]), overload resolution selects the constructor. For direct-initialization or default-initialization, the candidate functions are all the constructors of the class of the object being initialized. For copy-initialization, the candidate functions are all the converting constructors (11.4.8.2 [class.conv.ctor]) of that class. The argument list is the expression-list or assignment-expression of the initializer.



1599. Lifetime of initializer_list underlying array

Section: 9.4.5  [dcl.init.list]     Status: CD4     Submitter: Johannes Schaub     Date: 2012-02-10

[Adopted as paper P0135R1 at the June, 2016 meeting.]

The normative wording of 9.4.5 [dcl.init.list] regarding the lifetime of the array underlying an initializer_list object does not match the intent as specified in the example in paragraph 6 of that section, even after application of the resolution of issue 1290. That example contains the lines:

  void f() {
    std::initializer_list<int> i3 = { 1, 2, 3 };
  }

The commentary indicates that the lifetime of the array created for the initialization of i3 “persists for the lifetime of the variable.” However, that is not the effect of the normative wording. According to paragraph 3,

if T is a specialization of std::initializer_list<E>, an initializer_list object is constructed as described below and used to initialize the object according to the rules for initialization of an object from a class of the same type (9.4 [dcl.init]).

In other words, the underlying array for {1,2,3} in the example is associated with the temporary and shares its lifetime; its lifetime is not extended to that of the variable.

(See also issue 1565.)

Notes from the February, 2014 meeting:

The resolution of issue 1299, clarifying the relationship between temporary expressions and temporary objects, is relevant to this issue.




1756. Direct-list-initialization of a non-class object

Section: 9.4.5  [dcl.init.list]     Status: CD4     Submitter: Richard Smith     Date: 2013-09-20

[Moved to DR at the November, 2014 meeting.]

The wording of 9.4.5 [dcl.init.list] paragraph 3,

if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element

does not specify whether the initialization is direct-initialization, copy-initialization, or the same kind of initialization that applied to the list-initialization. This matters when E is a class type with an explicit conversion function. (Note that aggregate initialization performs copy-initialization on its subobjects, but it's not clear whether that should be the pattern followed for this case.)

Proposed resolution (June, 2014):

This issue is resolved by the resolution of issue 1467.




2137. List-initialization from object of same type

Section: 9.4.5  [dcl.init.list]     Status: CD4     Submitter: Richard Smith     Date: 2015-06-10

[Adopted at the June, 2016 meeting.]

It is not clear in code like the following that selecting a copy/move constructor is the correct choice when an initializer list contains a single element of the type being initialized, as required by issue 1467:

  #include <initializer_list>
  #include <iostream>

  struct Q {
    Q() { std::cout << "default\n"; }
    Q(Q const&) { std::cout << "copy\n"; }
    Q(Q&&) { std::cout << "move\n"; }
    Q(std::initializer_list<Q>) { std::cout << "initializer list\n"; }
  };

  int main() {
    Q x = Q { Q() };
  }

Here the intent is that Q objects can contain other Q objects, but this is broken by the resolution of issue 1467.

Perhaps the presence of an initializer-list constructor should change the outcome?

Proposed resolution (April, 2016):

  1. Change 9.4.5 [dcl.init.list] bullet 3.1 as follows:

  2. List-initialization of an object or reference of type T is defined as follows:
  3. Change 12.2.4.2.6 [over.ics.list] paragraph 2 as follows:

  4. If the parameter type is a an aggregate class X and the initializer list has a single element of type cv U, where U is X or a class derived from X, the implicit conversion sequence is the one required to convert the element to the parameter type.
  5. Change 12.2.4.2.6 [over.ics.list] paragraph 6 as follows, breaking the existing running text into a bulleted list:

  6. Otherwise, if the parameter is a non-aggregate class X and overload resolution per 12.2.2.8 [over.match.list] chooses a single best constructor C of X to perform the initialization of an object of type X from the argument initializer list, list:

    If multiple constructors are viable...




1791. Incorrect restrictions on cv-qualifier-seq and ref-qualifier

Section: 9.5.1  [dcl.fct.def.general]     Status: CD4     Submitter: David Krauss     Date: 2013-10-01

[Moved to DR at the November, 2014 meeting.]

Paragraph 5 of 9.5.1 [dcl.fct.def.general] says,

A cv-qualifier-seq or a ref-qualifier (or both) can be part of a non-static member function declaration, non-static member function definition, or pointer to member function only (9.3.4.6 [dcl.fct]); see _N4868_.11.4.3.2 [class.this].

This is redundant with the specification in 9.3.4.6 [dcl.fct] paragraph 6 and is factually incorrect, since the list there contains other permissible constructs. It should be at most a note or possibly removed altogether.

Proposed resolution (February, 2014):

Change 9.5.1 [dcl.fct.def.general] paragraph 5 as follows:

A cv-qualifier-seq or a ref-qualifier (or both) can be part of a non-static member function declaration, non-static member function definition, or pointer to member function only (9.3.4.6 [dcl.fct]); see _N4868_.11.4.3.2 [class.this]. [Note: a cv-qualifier-seq affects the type of this in the body of a member function; see 9.3.4.3 [dcl.ref]. —end note]



2145. Parenthesized declarator in function definition

Section: 9.5.1  [dcl.fct.def.general]     Status: CD4     Submitter: Richard Smith     Date: 2015-06-19

[Adopted at the June, 2016 meeting.]

According to 9.5.1 [dcl.fct.def.general] paragraph 2,

The declarator in a function-definition shall have the form

However, in practice implementations accept a parenthesized declarator in a function definition.

Proposed resolution (April, 2016):

Change 9.5.1 [dcl.fct.def.general] paragraph 2 as follows:

The declarator in In a function-definition shall have the form,

either void declarator ; or declarator ; shall be a well-formed function declarator as described in 9.3.4.6 [dcl.fct]. A function shall be defined only in namespace or class scope.




1552. exception-specifications and defaulted special member functions

Section: 9.5.2  [dcl.fct.def.default]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2012-09-07

[Moved to DR at the November, 2014 meeting.]

The current wording of 9.5.2 [dcl.fct.def.default] paragraph 2 has some surprising implications:

An explicitly-defaulted function may be declared constexpr only if it would have been implicitly declared as constexpr, and may have an explicit exception-specification only if it is compatible (14.5 [except.spec]) with the exception-specification on the implicit declaration.

In an example like

  struct A {
    A& operator=(A&);
  };
  A& A::operator=(A&) = default;

presumably the exception-specification of A::operator=(A&) is noexcept(false). However, attempting to make that exception-specification explicit,

  A& A::operator=(A&) noexcept(false) = default;

is an error. Is this intentional?

Proposed resolution (February, 2014):

Change 14.5 [except.spec] paragraph 4 as follows:

...If any declaration of a pointer to function, reference to function, or pointer to member function has an exception-specification, all occurrences of that declaration shall have a compatible exception-specification. If a declaration of a function has an implicit exception-specification, other declarations of the function shall not specify an exception-specification. In an explicit instantiation...

(This resolution also resolves issue 1492.)

Additional note (January, 2013):

The resolution conflicts with the current specification of operator delete: in 6.7.5.5 [basic.stc.dynamic] paragraph 2, the two operator delete overloads are declared with an implicit exception specification, while in 17.6 [support.dynamic] paragraph 1, they are declared as noexcept.

Additional note (February, 2014):

The overloads cited in the preceding note have been independently changed in N3936 to include a noexcept specification, making the proposed resolution correct as it stands.




1846. Declaring explicitly-defaulted implicitly-deleted functions

Section: 9.5.2  [dcl.fct.def.default]     Status: CD4     Submitter: Richard Smith     Date: 2014-01-30

[Moved to DR at the November, 2014 meeting.]

According to 9.5.2 [dcl.fct.def.default] paragraph 2,

An explicitly-defaulted function may be declared constexpr only if it would have been implicitly declared as constexpr.

However, the rules for determining whether a function is constexpr and its exception-specification depend on the definition of function and do not apply to deleted functions. Can an explicitly-defaulted implicitly-deleted function be declared constexpr or have an exception-specification, and if so, how is its correctness to be determined?

Proposed resolution (February, 2014):

Change 9.5.2 [dcl.fct.def.default] paragraph 2 as follows:

An explicitly-defaulted function that is not defined as deleted may be declared constexpr only if it would have been implicitly declared as constexpr.



2015. odr-use of deleted virtual functions

Section: 9.5.3  [dcl.fct.def.delete]     Status: CD4     Submitter: David Majnemer     Date: 2014-10-05

[Moved to DR at the October, 2015 meeting.]

It is not clear that the odr-use of a virtual function described in 6.3 [basic.def.odr] paragraph 3 is exempt from the prohibition against referring to a deleted function (9.5.3 [dcl.fct.def.delete] paragraph 2) .

Proposed resolution (May, 2015):

Change 9.5.3 [dcl.fct.def.delete] paragraph 2 as follows:

A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed. [Note: This includes calling the function implicitly or explicitly and forming a pointer or pointer-to-member to the function. It applies even for references in expressions that are not potentially-evaluated. If a function is overloaded, it is referenced only if the function is selected by overload resolution. The implicit odr-use (6.3 [basic.def.odr]) of a virtual function does not, by itself, constitute a reference.end note]



1638. Declaring an explicit specialization of a scoped enumeration

Section: 9.7.1  [dcl.enum]     Status: CD4     Submitter: Richard Smith     Date: 2013-03-12

[Adopted at the February, 2016 meeting.]

There is no syntax currently for declaring an explicit specialization of a member scoped enumeration. A declaration (not a definition) of such an explicit specialization most resembles an opaque-enum-declaration, but the grammar for that requires that the name be a simple identifier, which will not be the case for an explicit specialization of a member enumeration. This could be remedied by adding a nested-name-specifier to the grammar with a restriction that a nested-name-specifier only appear in an explicit specialization.

Proposed resolution (October, 2015):

  1. Change the grammar in 9.7.1 [dcl.enum] paragraph 1 as follows:

  2. Add the following at the end of 9.7.1 [dcl.enum] paragraph 1:

  3. If an opaque-enum-declaration contains a nested-name-specifier, the declaration shall be an explicit specialization (13.9.4 [temp.expl.spec]).



1766. Values outside the range of the values of an enumeration

Section: 9.7.1  [dcl.enum]     Status: CD4     Submitter: CWG     Date: 2013-09-23

[Moved to DR at the November, 2014 meeting.]

Although issue 1094 clarified that the value of an expression of enumeration type might not be within the range of the values of the enumeration after a conversion to the enumeration type (see 7.6.1.9 [expr.static.cast] paragraph 10), the result is simply an unspecified value. This should probably be strengthened to produce undefined behavior, in light of the fact that undefined behavior makes an expression non-constant. See also 11.4.10 [class.bit] paragraph 4.

Proposed resolution (February, 2014):

Change 7.6.1.9 [expr.static.cast] paragraph 10 as follows:

A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (9.7.1 [dcl.enum]). Otherwise, the resulting value is unspecified (and might not be in that range) behavior is undefined. A value of floating-point type...



1966. Colon following enumeration elaborated-type-specifier

Section: 9.7.1  [dcl.enum]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2014-07-08

[Moved to DR at the May, 2015 meeting.]

The resolution of issue 1514 was intended to apply whenever enum identifier appears; however, the fact that the disambiguation rule appears in the description of the enum-specifier makes it unclear that it is intended to apply in other contexts such as:

  enum E { e1 };
  void f() {
    false ? new enum E : int();
  }

Proposed resolution (April, 2015):

Change 9.7.1 [dcl.enum] paragraph 1 as follows:

A : following “enum identifierwithin the decl-specifier-seq of a member-declaration is parsed as part of an enum-base. [Note: This resolves a potential ambiguity between the declaration of an enumeration with an enum-base and the declaration of an unnamed bit-field of enumeration type...



2156. Definition of enumeration declared by using-declaration

Section: 9.7.1  [dcl.enum]     Status: CD4     Submitter: Richard Smith     Date: 2015-07-06

[Adopted at the February, 2016 meeting.]

The description of enumeration declarations in 9.7.1 [dcl.enum] does not, but should, contain similar wording to that preventing a class definition from defining a class type named by a using-declaration:

If a class-head-name contains a nested-name-specifier, the class-specifier shall refer to a class that was previously declared directly in the class or namespace to which the nested-name-specifier refers, or in an element of the inline namespace set (9.8.2 [namespace.def]) of that namespace (i.e., not merely inherited or introduced by a using-declaration), and the class-specifier shall appear in a namespace enclosing the previous declaration. In such cases, the nested-name-specifier of the class-head-name of the definition shall not begin with a decltype-specifier.

Proposed resolution (February, 2016):

Add the following as a new paragraph at the end of 9.7.1 [dcl.enum]:

If an enum-head contains a nested-name-specifier, the enum-specifier shall refer to an enumeration that was previously declared directly in the class or namespace to which the nested-name-specifier refers, or in an element of the inline namespace set (9.8.2 [namespace.def]) of that namespace (i.e., not merely inherited or introduced by a using-declaration), and the enum-specifier shall appear in a namespace enclosing the previous declaration. In such cases, the nested-name-specifier of the enum-head of the definition shall not begin with a decltype-specifier.



987. Which declarations introduce namespace members?

Section: 9.8  [basic.namespace]     Status: CD4     Submitter: Michael Wong     Date: 19 October, 2009

[Moved to DR at the November, 2014 meeting.]

According to 9.8 [basic.namespace] paragraph 1,

The name of a namespace can be used to access entities declared in that namespace; that is, the members of the namespace.

implying that all declarations in a namespace, including definitions of members of nested namespaces, explicit instantiations, and explicit specializations, introduce members of the containing namespace. _N4868_.9.8.2.3 [namespace.memdef] paragraph 3 clarifies the intent somewhat:

Every name first declared in a namespace is a member of that namespace.

However, current changes to clarify the behavior of deleted functions (which must be deleted on their “first declaration”) state that an explicit specialization of a function template is its first declaration.

Proposed resolution (November, 2014):

This issue is resolved by the resolution of issue 1838.




1657. Attributes for namespaces and enumerators

Section: 9.8.2  [namespace.def]     Status: CD4     Submitter: Richard Smith     Date: 2013-08-26

[Addressed by the adoption of paper N4266 at the November, 2104 meeting.]

During the discussion of paper N3394, it was observed that the grammar does not currently, but perhaps should, permit attributes to be specified for namespaces and enumerators.




1795. Disambiguating original-namespace-definition and extension-namespace-definition

Section: 9.8.2  [namespace.def]     Status: CD4     Submitter: Richard Smith     Date: 2013-10-04

[Moved to DR at the November, 2014 meeting.]

According to 9.8.2 [namespace.def] paragraph 2,

The identifier in an original-namespace-definition shall not have been previously defined in the declarative region in which the original-namespace-definition appears.

Apparently the intent of this requirement is to say that, given the declarations

  namespace N { }
  namespace N { }

the second declaration is to be taken as an extension-namespace-definition and not an original-namespace-definition, since the general rules in _N4868_.6.4.1 [basic.scope.declarative] cover the case in which the identifier has been previously declared as something other than a namespace.

This use of “shall” for disambiguation is novel, however, and it would be better to replace it with a specific statement addressing disambiguation in paragraphs 2 and 3.

Proposed Resolution (July, 2014):

  1. Change 6.4.6 [basic.scope.namespace] paragraph 1 as follows:

  2. The declarative region of a namespace-definition is its namespace-body. The potential scope denoted by an original-namespace-name is the concatenation of the declarative regions established by each of the namespace-definitions in the same declarative region with that original-namespace-name. Entities declared in a namespace-body...
  3. Change 9.8.2 [namespace.def] paragraphs 1-4 as follows:

  4. The grammar for a namespace-definition is

    The identifier in an original-namespace-definition shall not have been previously defined in the declarative region in which the original-namespace-definition appears. The identifier in an original-namespace-definition is the name of the namespace. Subsequently in that declarative region, it is treated as an original-namespace-name.

    The original-namespace-name in an extension-namespace-definition shall have previously been defined in an original-namespace-definition in the same declarative region.

    Every namespace-definition shall appear in the global scope or in a namespace scope (6.4.6 [basic.scope.namespace]).

    In a named-namespace-definition, the identifier is the name of the namespace. If the identifier, when looked up (6.5.3 [basic.lookup.unqual]), refers to a namespace-name (but not a namespace-alias) introduced in the declarative region in which the named-namespace-definition appears, the namespace-definition extends the previously-declared namespace. Otherwise, the identifier is introduced as a namespace-name into the declarative region in which the named-namespace-definition appears.

  5. Change 9.8.2 [namespace.def] paragraph 7 as follows:

  6. If the optional initial inline keyword appears in a namespace-definition for a particular namespace, that namespace is declared to be an inline namespace. The inline keyword may be used on an extension-namespace-definition a namespace-definition that extends a namespace only if it was previously used on the original-namespace-definition namespace-definition that initially declared the namespace-name for that namespace.
  7. Delete 9.8.3 [namespace.alias] paragraph 4:

  8. A namespace-name or namespace-alias shall not be declared as the name of any other entity in the same declarative region. A namespace-name defined at global scope shall not be declared as the name of any other entity in any global scope of the program. No diagnostic is required for a violation of this rule by declarations in different translation units.
  9. Change 9.8.4 [namespace.udir] paragraph 5 as follows:

  10. If a namespace is extended by an extension-namespace-definition after a using-directive for that namespace is given, the additional members of the extended namespace and the members of namespaces nominated by using-directives in the extension-namespace-definition extending namespace-definition can be used after the extension-namespace-definition extending namespace-definition.



2061. Inline namespace after simplifications

Section: 9.8.2  [namespace.def]     Status: CD4     Submitter: Richard Smith     Date: 2014-12-18

[Adopted at the February, 2016 meeting.]

After the resolution of issue 1795, 9.8.2 [namespace.def] paragraph 3 now says:

In a named-namespace-definition, the identifier is the name of the namespace. If the identifier, when looked up (6.5.3 [basic.lookup.unqual]), refers to a namespace-name (but not a namespace-alias) introduced in the declarative region in which the named-namespace-definition appears, the namespace-definition extends the previously-declared namespace. Otherwise, the identifier is introduced as a namespace-name into the declarative region in which the named-namespace-definition appears.

This appears to break code like the following:

  namespace A {
    inline namespace b {
      namespace C {
        template<typename T> void f();
      }
    }
  }

  namespace A {
    namespace C {
      template<> void f<int>() { }
    }
  }

because (by definition of “declarative region”) C cannot be used as an unqualified name to refer to A::b::C within A if its declarative region is A::b.

Proposed resolution (September, 2015):

Change 9.8.2 [namespace.def] paragraph 3 as follows:

In a named-namespace-definition, the identifier is the name of the namespace. If the identifier, when looked up (6.5.3 [basic.lookup.unqual]), refers to a namespace-name (but not a namespace-alias) that was introduced in the declarative region namespace in which the named-namespace-definition appears or that was introduced in a member of the inline namespace set of that namespace, the namespace-definition extends the previously-declared namespace. Otherwise, the identifier is introduced as a namespace-name into the declarative region in which the named-namespace-definition appears.



1887. Problems with :: as nested-name-specifier

Section: 9.9  [namespace.udecl]     Status: CD4     Submitter: Jeff Snyder     Date: 2014-03-04

[Moved to DR at the November, 2014 meeting.]

Issue 1411 added :: as a production for nested-name-specifier. However, the grammar for using-declarations should have been updated but was overlooked:

In addition, there is some verbiage in 6.5.5.3 [namespace.qual] paragraph 1 and 9.9 [namespace.udecl] paragraph 9 that should probably be revised.

Proposed resolution (October, 2014):

  1. Change the grammar in 9.9 [namespace.udecl] paragraph 1 as follows:

  2. Change 6.5.5.3 [namespace.qual] paragraph 1 as follows:

  3. If the nested-name-specifier of a qualified-id nominates a namespace (including the case where the nested-name-specifier is ::, i.e., nominating the global namespace), the name specified after the nested-name-specifier is looked up in the scope of the namespace. If a qualified-id starts with ::, the name after the :: is looked up in the global namespace. In either case, the The names in a template-argument of a template-id are looked up in the context in which the entire postfix-expression occurs.
  4. Change _N4567_.5.1.1 [expr.prim.general] paragraph 10 as follows:

  5. A ::, or a The nested-name-specifier :: names the global namespace. A nested-name-specifier that names a namespace (9.8 [basic.namespace]), in either case followed by the name of a member of that namespace (or the name of a member of a namespace made visible by a using-directive), is a qualified-id; 6.5.5.3 [namespace.qual] describes name lookup for namespace members that appear in qualified-ids. The result is...
  6. Change 9.9 [namespace.udecl] paragraph 9 as follows:

  7. Members declared by a using-declaration can be referred to by explicit qualification just like other member names (6.5.5.3 [namespace.qual]). In a using-declaration, a prefix :: refers to the global namespace. [Example:



1903. What declarations are introduced by a non-member using-declaration?

Section: 9.9  [namespace.udecl]     Status: CD4     Submitter: Richard Smith     Date: 2014-03-26

[Adopted at the October, 2015 meeting as P0136R1.]

The set of declarations introduced by a using-declaration that is a member-declaration is specified by 9.9 [namespace.udecl] paragraph 3. However, there is no corresponding specification for a non-member using-declaration.




1708. overly-strict requirements for names with C language linkage

Section: 9.11  [dcl.link]     Status: CD4     Submitter: Richard Smith     Date: 2013-06-29

[Moved to DR at the November, 2014 meeting.]

According to 9.11 [dcl.link] paragraph 6,

An entity with C language linkage shall not be declared with the same name as an entity in global scope, unless both declarations denote the same entity; no diagnostic is required if the declarations appear in different translation units.

This restriction is too broad; it does not allow for the so-called stat hack, where a C-linkage function and a class are both declared in global scope, and it does not allow for function overloading, either. It should be revised to apply only to variables.

Additional note (February, 2014):

See also issue 1838 for an interaction with using-declarations.

Proposed resolution (February, 2014):

Change 9.11 [dcl.link] paragraph 6 as follows:

...An entity with C language linkage shall not be declared with the same name as an entity a variable in global scope, unless both declarations denote the same entity; no diagnostic is required if the declarations appear in different translation units...

Additional note, May, 2014:

It was observed that this resolution would allow a definition of main as a C-linkage variable in a namespace. The issue is being returned to "review" status for further discussion.




2079. [[ appearing in a balanced-token-seq

Section: 9.12.1  [dcl.attr.grammar]     Status: CD4     Submitter: Jonathan Caves     Date: 2015-02-03

[Adopted at the February, 2016 meeting.]

According to 9.12.1 [dcl.attr.grammar] paragraph 6,

Two consecutive left square bracket tokens shall appear only when introducing an attribute-specifier. [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]

In order to allow program fragments to appeae within attributes, this restriction should not apply within the balanced-token-seq of an attribute.

Proposed resolution (September, 2015):

Change 9.12.1 [dcl.attr.grammar] paragraph 6 as follows:

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]




1615. Alignment of types, variables, and members

Section: 9.12.2  [dcl.align]     Status: CD4     Submitter: Richard Smith     Date: 2013-02-01

[Moved to DR at the November, 2014 meeting.]

According to 9.12.2 [dcl.align] paragraph 5,

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 were omitted (including those in other declarations).

Presumably the intent was “other declarations of the same entity,” but the wording as written could be read to make the following example well-formed (assuming alignof(int) is 4):

  struct alignas(4) A {
    alignas(8) int n;
  };
  struct alignas(8) B {
    char c;
  };
  alignas(1) B b;
  struct alignas(1) C : B {};
  enum alignas(8) E : int { k };
  alignas(4) E e = k;

Proposed resolution (February, 2014):

Change 9.12.2 [dcl.align] paragraph 5 as follows:

...if all alignment-specifiers appertaining to that entity were omitted (including those in other declarations). [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]




2027. Unclear requirements for multiple alignas specifiers

Section: 9.12.2  [dcl.align]     Status: CD4     Submitter: Steve Clamage     Date: 2014-10-20

[Moved to DR at the October, 2015 meeting.]

The description of alignment-specifiers is unclear. For example, 9.12.2 [dcl.align] bullet 2.2 says,

if the constant expression evaluates to a fundamental alignment, the alignment requirement of the declared entity shall be the specified fundamental alignment

However, paragraph 4 says,

When multiple alignment-specifiers are specified for an entity, the alignment requirement shall be set to the strictest specified alignment.

meaning that a less-strict alignment will be ignored, rather than being the alignment of the entity, and presumably meaning that no diagnostic is required for an insufficiently-strict alignment if a more stringent requirement is also supplied.

Proposed resolution (May, 2015):

  1. Change 9.12.2 [dcl.align] paragraph 2 as follows:

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

  3. Change 9.12.2 [dcl.align] paragraph 4 as follows:

  4. When multiple alignment-specifiers are specified for an entity, the The alignment requirement shall be set to of an entity is the strictest specified non-zero alignment specified by its alignment-specifiers, if any; otherwise, the alignment-specifiers have no effect.



1813. Direct vs indirect bases in standard-layout classes

Section: Clause 11  [class]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2013-11-20

[Moved to DR at the November, 2014 meeting.]

One of the criteria for a standard-layout class in Clause 11 [class] paragraph 7 is:

In an example like

  struct B { int i; };
  struct C : B { };
  struct D : C { };

this could be read as indicating that D is not a standard-layout class, since it has two base classes, one direct and one indirect, that each have a non-static data member. The intent should be clarified.

See also issue 1881 for a related question about standard-layout classes.

Proposed resolution (June, 2014):

Change Clause 11 [class] paragraph 7 as follows:

A standard-layout class is a class that:

[Example:

   struct B { int i; };         // standard-layout class
   struct C : B { };            // standard-layout class
   struct D : C { };            // standard-layout class
   struct E : D { char : 4; };  // not a standard-layout class

   struct Q {};
   struct S : Q { };
   struct T : Q { };
   struct U : S, T { };         // not a standard-layout class

end example]

This resolution also resolves issue 1881.

(See also the related changes in the resolution of issue 1672.)




1881. Standard-layout classes and unnamed bit-fields

Section: Clause 11  [class]     Status: CD4     Submitter: Dinka Ranns     Date: 2014-02-25

[Moved to DR at the November, 2014 meeting.]

According to 11.4.10 [class.bit] paragraph 2,

Unnamed bit-fields are not members and cannot be initialized.

However, the rules defining standard-layout classes in Clause 11 [class] paragraph 7 do not account for the fact that a class containing an unnamed bit-field has associated storage.

See also issue 1813 for a related question about standard-layout classes.

Proposed resolution (June, 2014):

This issue is resolved by the resolution of issue 1813.




2120. Array as first non-static data member in standard-layout class

Section: Clause 11  [class]     Status: CD4     Submitter: Hubert Tong     Date: 2015-05-06

[Moved to DR at the October, 2015 meeting.]

The specification for determining whether a derived class is a standard-layout class, considering the types of its bases and those associated with its initial member, overlooked the case of a class whose first member is an array.

Proposed resolution (May, 2015):

Change Clause 11 [class] paragraph 7 as follows:

...M(X) is defined as follows:

[Note: M(X) is the set of the types of all non-base-class subobjects that are guaranteed in a standard-layout class to be at a zero offset in X. —end note]




1496. Triviality with deleted and missing default constructors

Section: 11.3  [class.name]     Status: CD4     Submitter: Daniel Krügler     Date: 2012-04-25

[Adopted at the February, 2016 meeting.]

A default constructor that is defined as deleted is trivial, according to 11.4.5 [class.ctor] paragraph 5. This means that, according to Clause 11 [class] paragraph 6, such a class can be trivial. If, however, the class has no default constructor because it has a user-declared constructor, the class is not trivial. Since both cases prevent default construction of the class, it is not clear why there is a difference in triviality between the cases.

(See also issue 1928.)

Notes from the October, 2012 meeting:

It was observed that this issue was related to issue 1344, as the current specification allows adding a default constructor by adding default arguments to the definition of a constructor. The resolution of that issue should also resolve this one.

Notes from the September, 2013 meeting:

It was decided to resolve issue 1344 separately from this issue, so this issue now requires its own resolution.

Proposed resolution (October, 2015):

Change Clause 11 [class] paragraph 6 as follows:

A trivial class is a class that has a default constructor (11.4.5 [class.ctor]), has no non-trivial default constructors, and is trivially copyable and has one or more default constructors (11.4.5 [class.ctor]), all of which are either trivial or deleted and at least one of which is not deleted. [Note: In particular, a trivially copyable or trivial class does not have virtual functions or virtual base classes. —end note]



1397. Class completeness in non-static data member initializers

Section: 11.4  [class.mem]     Status: CD4     Submitter: Jason Merrill     Date: 2011-09-23

[Moved to DR at the November, 2014 meeting.]

In Bloomington there was general agreement that given a class that uses non-static data member initializers, the exception-specification for the default constructor depends on whether those initializers are noexcept. However, according to 11.4 [class.mem] paragraph 2, the class is regarded as complete within the brace-or-equal-initializers for non-static data members.

This suggests that we need to finish processing the class before parsing the NSDMI, but our direction on issue 1351 suggests that we need to parse the NSDMI in order to finish processing the class. Can't have both...

Additional note (March, 2013):

A specific example:

  struct A {
    void *p = A{};
    operator void*() const { return nullptr; }
  };

Perhaps the best way of addressing this would be to make it ill-formed for a non-static data member initializer to use a defaulted constructor of its class.

See also issue 1360.

Notes from the September, 2013 meeting:

One approach that might be considered would be to parse deferred portions lazily, on demand, and then issue an error if this results in a cycle.

Proposed resolution (February, 2014):

Change 11.4 [class.mem] paragraph 4 as follows:

A brace-or-equal-initializer shall appear only in the declaration of a data member. (For static data members, see 11.4.9.3 [class.static.data]; for non-static data members, see 11.9.3 [class.base.init]). A brace-or-equal-initializer for a non-static data member shall not directly or indirectly cause the implicit definition of a defaulted default constructor for the enclosing class or the exception-specification of that constructor.



1672. Layout compatibility with multiple empty bases

Section: 11.4  [class.mem]     Status: CD4     Submitter: Richard Smith     Date: 2013-04-27

[Moved to DR at the November, 2014 meeting.]

The layout compatibility rules of 11.4 [class.mem] paragraph 16 are phrased only in terms of non-static data members, ignoring the existence of base classes:

Two standard-layout struct (Clause 11 [class]) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout-compatible types (6.8 [basic.types]).

However, this means that in an example like

  struct empty {};
  struct A { char a; };
  struct also_empty : empty {};
  struct C : empty, also_empty { char c; };
  union U {
    struct X { A a1, a2; } x;
    struct Y { C c1, c2; } y;
  } u;

u.x.a2.a and u.y.c2.c must have the same address, even though sizeof(A) would typically be 1 and sizeof(B) would need to be at least 2 to give the empty subobjects different addresses.

Proposed resolution (October, 2014):

Change Clause 11 [class] paragraph 7 as indicated and add the following as a new paragraph:

A class S is a standard-layout class is a class that if it:

M(X) is defined as follows:

[Note: M(X) is the set of the types of all non-base-class subobjects that are guaranteed in a standard-layout class to be at a zero offset in X. —end note]

(See also the related changes in the resolution for issue 1813.)




1719. Layout compatibility and cv-qualification revisited

Section: 11.4  [class.mem]     Status: CD4     Submitter: Jeffrey Yasskin     Date: 2013-07-24

[Moved to DR at the November, 2014 meeting.]

When the effect of cv-qualification on layout compatibility was previously discussed (see issue 1334), the question was resolved by reference to the historical origin of layout compatibility: it was a weakening of type correctness that was added for C compatibility, mimicking exactly the corresponding C specification of compatible types in this context and going no further. Because cv-qualified and cv-unqualified types are not compatible in C, they were not made layout-compatible in C++.

Because of specific use-cases involving std::pair and the like, however, and in consideration of the fact that cv-qualified and cv-unqualified versions of types are aliasable by the rules of 7.2.1 [basic.lval], the outcome of that question is worthy of reconsideration.

Proposed resolution (June, 2014):

  1. Change 6.1 [basic.pre] paragraph 3 as follows:

  2. An entity is a value, object, reference, function, enumerator, type, class member, bit-field, template, template specialization, namespace, parameter pack, or this.
  3. Change 6.8 [basic.types] paragraph 11 as follows:

  4. If two types T1 and T2 are the same type, then T1 and T2 Two types cv1 T1 and cv2 T2 are layout-compatible types if T1 and T2 are the same type, layout-compatible enumerations (9.7.1 [dcl.enum]), or layout-compatible standard-layout class types (11.4 [class.mem]). [Note: Layout-compatible enumerations are described in 9.7.1 [dcl.enum]. Layout-compatible standard-layout structs and standard-layout unions are described in 11.4 [class.mem]. —end note]
  5. Change 6.8.4 [basic.compound] paragraph 3 as follows:

  6. ...The value representation of pointer types is implementation-defined. Pointers to cv-qualified and cv-unqualified versions (6.8.5 [basic.type.qualifier]) of layout-compatible types shall have the same value representation and alignment requirements (6.7.6 [basic.align]). [Note:...
  7. Insert the following as a new paragraph before 11.4 [class.mem] paragraph 16 and change paragraphs 16 through 18 as follows:

  8. The common initial sequence of two standard-layout struct (Clause 11 [class]) types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that corresponding entities have layout-compatible types and either neither entity is a bit-field or both are bit-fields with the same width. [Example:

      struct A { int a; char b; };
      struct B { const int b1; volatile char b2; };
      struct C { int c; unsigned : 0; char b; };
      struct D { int d; char b : 4; };
      struct E { unsigned int e; char b; };
    

    The common initial sequence of A and B comprises all members of either class. The common initial sequence of A and C and of A and D comprises the first member in each case. The common initial sequence of A and E is empty. —end example]

    Two standard-layout struct (Clause 11 [class]) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout-compatible types their common initial sequence comprises all members and bit-fields of both classes (6.8 [basic.types]).

    Two standard-layout union (Clause 11 [class]) types unions are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in any order) have layout-compatible types (6.8 [basic.types]).

    If a standard-layout union contains two or more standard-layout structs that share a common initial sequence, and if the standard-layout union object currently contains one of these standard-layout structs, it is permitted to inspect the common initial part of any of them. Two standard-layout structs share a common initial sequence if corresponding members have layout-compatible types and either neither member is a bit-field or both are bit-fields with the same width for a sequence of one or more initial members. In a standard-layout union with an active member (11.5 [class.union]) of struct type T1, it is permitted to read a non-static data member m of another union member of struct type T2 provided m is part of the common initial sequence of T1 and T2. [Note: Reading a volatile object through a non-volatile glvalue has undefined behavior (9.2.9.2 [dcl.type.cv]). —end note]




1909. Member class template with the same name as the class

Section: 11.4  [class.mem]     Status: CD4     Submitter: Richard Smith     Date: 2014-04-01

[Moved to DR at the November, 2014 meeting.]

The list in 11.4 [class.mem] paragraph 14 of the kinds of class members whose names must differ from that of the class does not include an entry for a member class template. Presumably it should.

Proposed resolution (October, 2014):

Change 11.4 [class.mem] paragraph 14 as follows:

If T is the name of a class, then each of the following shall have a name different from T:




2153. pure-specifier in friend declaration

Section: 11.4  [class.mem]     Status: CD4     Submitter: Richard Smith     Date: 2015-06-30

[Adopted at the February, 2016 meeting.]

The current wording does not appear to ban a pure-specifier from a friend declaration.

Proposed resolution (February, 2016):

Change 11.4 [class.mem] paragraph 6 as follows:

...A pure-specifier shall be used only in the declaration of a virtual function (11.7.3 [class.virtual]) that is not a friend declaration.



2154. Ambiguity of pure-specifier

Section: 11.4  [class.mem]     Status: CD4     Submitter: Richard Smith     Date: 2015-06-30

[Adopted at the February, 2016 meeting.]

There does not appear to be a rule to disambiguate a pure-specifier and a brace-or-equal-initializer in a member declarator.

Proposed resolution (February, 2016):

Add the following as a new paragraph following 11.4 [class.mem] paragraph 3:

[Note: A single name can denote several function members provided their types are sufficiently different (Clause 12 [over]). —end note]

In a member-declarator, an = immediately following the declarator is interpreted as introducing a pure-specifier if the declarator-id has function type, otherwise it is interpreted as introducing a brace-or-equal-initializer. [Example:

  struct S {
    using T = void();
    T * p = 0;        // OK: brace-or-equal-initializer
    virtual T f = 0;  // OK: pure-specifier
  };

end example]




1888. Implicitly-declared default constructors and explicit

Section: 11.4.5  [class.ctor]     Status: CD4     Submitter: Daniel Krügler     Date: 2014-03-04

[Moved to DR at the May, 2015 meeting.]

It used to be clear that an implicitly-declared default constructor is not explicit. That has been inadvertently lost due to other changes, so this specification should be added to 11.4.5 [class.ctor] in parallel with the similar statement in 11.4.5.3 [class.copy.ctor] paragraph 3.

Proposed resolution (November, 2014):

  1. Change 11.4.5 [class.ctor] paragraph 4 as follows:

  2. A default constructor for a class X is a constructor of class X that can be called without an argument. If there is no user-declared constructor for class X, a non-explicit constructor having no parameters is implicitly declared as defaulted (9.5 [dcl.fct.def]). An implicitly-declared default constructor...
  3. Change 11.4.8.2 [class.conv.ctor] paragraph 3 as follows:

  4. A non-explicit copy/move constructor (11.4.5.3 [class.copy.ctor]) is a converting constructor. [Note: An implicitly-declared copy/move constructor is not an explicit constructor; it may be called for implicit type conversions. end note]
  5. Change 11.4.5.3 [class.copy.ctor] paragraph 7 as follows:

  6. If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly. If the class...
  7. Change 11.4.5.3 [class.copy.ctor] paragraph 9 as follows:

  8. If the definition of a class X does not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if...



2084. NSDMIs and deleted union default constructors

Section: 11.4.5  [class.ctor]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2015-02-12

[Adopted at the February, 2016 meeting.]

According to 11.4.5 [class.ctor] paragraph 4 says,

A defaulted default constructor for class X is defined as deleted if:

This should make the following example ill-formed:

  struct S {
    S();
  };
  union U {
    S s{};
  } u;

because the default constructor of U is deleted. However, both clang and g++ accept this without error. Should the rule be relaxed for a union with an NSDMI?

Notes from the May, 2015 meeting:

An NSDMI is basically syntactic sugar for a mem-initializer, so the presence of one should be treated as if a user-declared default constructor were present.

Proposed resolution (October, 2015):

Change 11.4.5 [class.ctor] paragraph 4 as follows:

...A defaulted default constructor for class X is defined as deleted if:




1590. Bypassing non-copy/move constructor copying

Section: 11.4.5.3  [class.copy.ctor]     Status: CD4     Submitter: Richard Smith     Date: 2012-11-26

[Adopted as paper P0135R1 at the June, 2016 meeting.]

Copy initialization in some cases uses constructors that are not copy/move constructors (e.g., a specialization of a constructor template might be selected by overload resolution, or in copy-list-initialization, any constructor could be selected). Some ABIs require that an object of certain class types be passed in a register (effectively using the trivial copy/move constructor), even if the class has a non-trivial constructor that would be selected to do the copy. The Standard should be changed to permit this usage.

Proposed resolution (April, 2013):

Add the following as a new paragraph following 11.4.5.3 [class.copy.ctor] paragraph 1:

When an object of class type X is passed to or returned from a function, if X has a trivial, accessible copy or move constructor that is not deleted, and X has no non-trivial copy constructors, move constructors, or destructors, implementations are permitted to perform an additional copy or move of the object using the trivial constructor (even if it would not be selected by overload resolution to perform a copy or move of the object). [Note: This latitude is granted to allow objects of class type to be passed to or returned from functions in registers. —end note]

See also issue 1928.

Additional note, May, 2014:

Questions have been raised regarding this resolution. In particular, the interaction of the “extra copy” with copy elision, lifetime, and access checking context are not specified. In addition, some concern has also been expressed regarding the requirement that the trivial copy/move constructor be accessible. The issue is being returned to "review" status for discussion of these points.

Notes from the June, 2014 meeting:

CWG felt that the requirements for accessibility should be removed, in line with the idea making all access public in a program should not change its semantics. Similarly, the prohibition of non-trivial functions was not desirable. The approach should be to recognize the extra copy as a temporary object and deal explicitly with its lifetime.




1734. Nontrivial deleted copy functions

Section: 11.4.5.3  [class.copy.ctor]     Status: CD4     Submitter: James Widman     Date: 2013-08-09

[Adopted at the February, 2016 meeting.]

The intent was for PODs in C++11 to be a superset of C++03 PODs. Consequently, in the following example, C should be a POD but isn't:

  struct A {
    const int m;
    A& operator=(A const&) = default; // deleted and trivial, so A is a
                                      // POD, as it would be in 2003
                                      // without this explicit op= decl
  };
  static_assert(__is_trivially_copyable(A), "");

  struct B {
    int i;
    B& operator=(B &) & = default;      // non-trivial
    B& operator=(B const&) & = default; // trivial
  };

  struct C {
    const B m;
    C& operator=(C const& r) = default; // deleted (apparently), but non-trivial (apparently)
    /* Notionally:
      C& operator=(C const& r) {
        (*this).m.operator=(r.m);
        return *this;
      }
    */
  };
  static_assert(!__is_trivially_copyable(C), "");

This is because of the following text from 11.4.5.3 [class.copy.ctor] paragraph 25:

for each non-static data member of X that is of class type (or array thereof), the assignment operator selected to copy/move that member is trivial;

In this case, overload resolution fails, so no assignment operator is selected, so C::operator=(const C&) is non-trivial.

(See also issue 1928.)

Additional note, November, 2014:

See paper N4148.

Additional note, October, 2015:

Moved from "extension" to "open" status, along with issue 1928, to allow reconsideration by CWG. It has been suggested that the triviality of a deleted function should be irrelevant, since it cannot be used in any event. A possible change to implement that, more conservative than the one proposed in N4148, would be:

A trivially copyable class is a class that:

Proposed resolution (October, 2015):

Change Clause 11 [class] paragraph 6 as follows:

A trivially copyable class is a class that:




1916. “Same cv-unqualified type”

Section: 11.4.5.3  [class.copy.ctor]     Status: CD4     Submitter: Richard Smith     Date: 2014-04-18

[Moved to DR at the May, 2015 meeting.]

11.4.5.3 [class.copy.ctor] paragraph 31 uses the phrase, “same cv-unqualified type,” twice. This is ambiguous, potentially either requiring that the types not be cv-qualified or meaning that cv-qualification should be ignored. The latter meaning is intended and the phrase should be replaced accordingly.

Proposed resolution (November, 2014):

Change 11.4.5.3 [class.copy.ctor] paragraph 31 as follows:

...This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

  1. in a return statement in a function with a class return type, when the expression expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter or a variable introduced by the exception-declaration of a handler (14.4 [except.handle])) with the same cv-unqualified type (ignoring cv-qualification) as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value

  2. in a throw-expression...

  3. when a temporary class object that has not been bound to a reference (6.7.7 [class.temporary]) would be copied/moved to a class object with the same cv-unqualified type (ignoring cv-qualification), the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move

  4. when the exception-declaration...




2171. Triviality of copy constructor with less-qualified parameter

Section: 11.4.5.3  [class.copy.ctor]     Status: CD4     Submitter: Jason Merrill     Date: 2015-09-14

[Adopted at the June, 2016 meeting.]

Issue 1333 says that a defaulted copy constructor with a less-const-qualified parameter type than the implicit declaration is non-trivial. This is inconsistent with the usual pattern that whether a special member function is callable is separate from whether it is trivial; the different declaration only affects whether you can call it with a const argument, it doesn't affect the operations involved. Should this outcome be reconsidered?

Proposed resolution (April, 2016):

  1. Change 11.4.5.3 [class.copy.ctor] paragraph 12 as follows:

  2. A copy/move constructor for class X is trivial if it is not user-provided, its parameter-type-list is equivalent to the parameter-type-list of an implicit declaration, and if...
  3. Change 11.4.5.3 [class.copy.ctor] paragraph 25 as follows:

  4. A copy/move assignment operator for class X is trivial if it is not user-provided, its parameter-type-list is equivalent to the parameter-type-list of an implicit declaration, and if...



1806. Virtual bases and move-assignment

Section: 11.4.6  [class.copy.assign]     Status: CD4     Submitter: Richard Smith     Date: 2013-11-04

[Moved to DR at the November, 2014 meeting.]

The proposed resolution for issue 1402 overlooked some needed changes in 11.4.5.3 [class.copy.ctor] paragraph 28.

Proposed resolution (February, 2014):

Change 11.4.5.3 [class.copy.ctor] paragraph 28 as follows:

...It is unspecified whether subobjects representing virtual base classes are assigned more than once by the implicitly-defined copy/move assignment operator. [Example:

  struct V { };
  struct A : virtual V { };
  struct B : virtual V { };
  struct C : B, A { };

It is unspecified whether the virtual base class subobject V is assigned twice by the implicitly-defined copy/move assignment operator for C. —end example] [Note: This does not apply to move assignment, as a defaulted move assignment operator is deleted if the class has virtual bases. —end note]




2180. Virtual bases in destructors and defaulted assignment operators

Section: 11.4.6  [class.copy.assign]     Status: CD4     Submitter: Vinny Romano     Date: 2015-10-13

[Adopted at the February, 2016 meeting.]

According to 11.4.5.3 [class.copy.ctor] paragraph 28,

The implicitly-defined copy/move assignment operator for a non-union class X performs memberwise copy/move assignment of its subobjects. The direct base classes of X are assigned first, in the order of their declaration in the base-specifier-list, and then the immediate non-static data members of X are assigned, in the order in which they were declared in the class definition... It is unspecified whether subobjects representing virtual base classes are assigned more than once by the implicitly-defined copy/move assignment operator.

However, the determination of whether a defaulted copy/move assignment operator is defined as deleted (paragraph 23) considers only the “potentially constructed” subobjects:

A defaulted copy/move assignment operator for class X is defined as deleted if X has:

where “potentially constructed” is defined (in 11.4.4 [special] paragraph 5) as:

For a class, its non-static data members, its non-virtual direct base classes, and, if the class is not abstract (11.7.4 [class.abstract]), its virtual base classes are called its potentially constructed subobjects.

i.e., excluding direct virtual base classes of abstract classes. This seems contradictory, since an implementation is expressly permitted to assign such base classes and thus presumably is permitted to fail if no such assignment is possible.

Similarly, 11.4.7 [class.dtor] paragraph 8 says,

After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X's direct non-variant non-static data members, the destructors for X's direct base classes and, if X is the type of the most derived class (11.9.3 [class.base.init]), its destructor calls the destructors for X's virtual base classes.

This appears to allow a virtual base's destructor to be called more than once, once for each class naming it as a direct virtual base and once for the most-derived class.

Proposed resolution (February, 2016):

  1. Change 11.4.7 [class.dtor] paragraph 8 as follows:

  2. After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X's direct non-variant non-static data members, the destructors for X's non-virtual direct base classes and, if X is the type of the most derived class (11.9.3 [class.base.init]), its destructor calls the destructors for X's virtual base classes. All destructors are called as if...
  3. Change 11.4.5.3 [class.copy.ctor] bullet 23.4 as follows:

  4. A defaulted copy/move assignment operator for class X is defined as deleted if X has:




1492. Exception specifications on template destructors

Section: 11.4.7  [class.dtor]     Status: CD4     Submitter: Jason Merrill     Date: 2012-04-01

[Moved to DR at the November, 2014 meeting.]

According to 11.4.7 [class.dtor] paragraph 3,

A declaration of a destructor that does not have an exception-specification is implicitly considered to have the same exception-specification as an implicit declaration (14.5 [except.spec]).

The implications of this are not clear for the destructor of a class template. For example,

  template <class T> struct B: T {
    ~B();
  };
  template <class T> B<T>::~B() noexcept {}

The implicit exception-specification of the in-class declaration of the destructor depends on the characteristics of the template argument. Does this mean that the out-of-class definition of the destructor is ill-formed, or will it be ill-formed only in specializations where the template argument causes the implicit exception-specification to be other than noexcept?

Proposed resolution (February, 2014):

This issue is resolved by the resolution of issue 1552.

Notes from the April, 2013 meeting:

This issue was approved as a DR at the April, 2013 (Bristol) meeting, but it was not noticed that issue 1552 was not being moved at that time. It is being returned to "drafting" status pending the resolution of that issue.




1811. Lookup of deallocation function in a virtual destructor definition

Section: 11.4.7  [class.dtor]     Status: CD4     Submitter: Richard Smith     Date: 2013-11-18

[Moved to DR at the November, 2014 meeting.]

According to 11.4.7 [class.dtor] paragraph 12,

At the point of definition of a virtual destructor (including an implicit definition (11.4.5.3 [class.copy.ctor])), the non-array deallocation function is looked up in the scope of the destructor's class (6.5.2 [class.member.lookup]), and, if no declaration is found, the function is looked up in the global scope. If the result of this lookup is ambiguous or inaccessible, or if the lookup selects a placement deallocation function or a function with a deleted definition (9.5 [dcl.fct.def]), the program is ill-formed. [Note: This assures that a deallocation function corresponding to the dynamic type of an object is available for the delete-expression (11.4.11 [class.free]). —end note]

This specification is not sufficiently clear regarding the nature of the lookup. Presumably the intent is that the processing be parallel to that described in 7.6.2.9 [expr.delete], but that should be made explicit.

Proposed resolution (February, 2014):

Change 11.4.7 [class.dtor] paragraph 12 as follows:

At the point of definition of a virtual destructor (including an implicit definition (11.4.5.3 [class.copy.ctor])), the non-array deallocation function is looked up in the scope of the destructor's class (6.5.2 [class.member.lookup]), and, if no declaration is found, the function is looked up in the global scope determined as if for the expression delete this appearing in a non-virtual destructor of the destructor's class (see 7.6.2.9 [expr.delete]). If the result of this lookup is ambiguous or inaccessible, lookup fails or if the lookup selects a placement deallocation function or a function with deallocation function has a deleted definition (9.5 [dcl.fct.def]), the program is ill-formed. [Note: This assures that a deallocation function corresponding to the dynamic type of an object is available for the delete-expression (11.4.11 [class.free]). —end note]



1848. Parenthesized constructor and destructor declarators

Section: 11.4.7  [class.dtor]     Status: CD4     Submitter: Richard Smith     Date: 2014-02-03

[Moved to DR at the November, 2014 meeting.]

The description of the required syntax for declaring a destructor in 11.4.7 [class.dtor] paragraph 1 says,

A declaration of a destructor uses a function declarator (9.3.4.6 [dcl.fct]) of the form

where the ptr-declarator...

A declaration such as

    (~S())

arguably “uses” a declarator of the required form, since the cited wording does not forbid placing a declarator of that form inside parentheses. (Similar considerations apply to the syntax of constructors in 11.4.5 [class.ctor] paragraph 1.) There is implementation divergence on this point. The wording should be clarified as to whether parentheses surrounding a declarator of the required form are permitted or not.

Proposed Resolution (July, 2014):

  1. Change 11.4.5 [class.ctor] paragraph 1 as follows:

  2. Constructors do not have names. A In a declaration of a constructor, uses the declarator is a function declarator (9.3.4.6 [dcl.fct]) of the form...
  3. Change 11.4.7 [class.dtor] paragraph 1 as follows:

  4. A In a declaration of a destructor, uses the declarator is a function declarator (9.3.4.6 [dcl.fct]) of the form...



2068. When can/must a defaulted virtual destructor be defined?

Section: 11.4.7  [class.dtor]     Status: CD4     Submitter: Richard Smith     Date: 2015-01-12

[Adopted at the February, 2016 meeting.]

The rules in 6.3 [basic.def.odr] and 11.4.7 [class.dtor] do not specify when the destructor for B can/must be defined:

   struct A { virtual ~A(); };
   struct B : A {};
   int main() {
     A *p = new B;
     delete p;
   }

An implementation should be allowed, but not required, to implicitly define a virtual special member function at any point where it has been desclared, as well as being required to define it as described in 11.4.7 [class.dtor] paragraph 6, etc.

Proposed resolution (September, 2015):

Change 11.4.7 [class.dtor] paragraph 6 as follows:

A destructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (6.3 [basic.def.odr]) to destroy an object of its class type (6.7.5 [basic.stc]) or when it is explicitly defaulted after its first declaration.



2069. Do destructors have names?

Section: 11.4.7  [class.dtor]     Status: CD4     Submitter: Richard Smith     Date: 2015-01-13

[Adopted at the February, 2016 meeting.]

According to 9.9 [namespace.udecl] paragraph 4,

[Note: Since destructors do not have names, a using-declaration cannot refer to a destructor for a base class....

However, 11.4.7 [class.dtor] paragraph 13 says,

In an explicit destructor call, the destructor name appears as a ~ followed by a type-name or decltype-specifier that denotes the destructor's class type...

See also 6.5.5.2 [class.qual] bullet 1.1:

a destructor name is looked up as specified in 6.5.5 [basic.lookup.qual];

Proposed resolution (September, 2015):

  1. Change 6.5.5.2 [class.qual] bullet 1.1 as follows:

  2. Change 11.4.7 [class.dtor] paragraph 13 as follows:

  3. In an explicit destructor call, the destructor name appears as is specified by a ~ followed by a type-name or decltype-specifier that denotes the destructor's class type. The invocation...



1978. Redundant description of explicit constructor use

Section: 11.4.8.2  [class.conv.ctor]     Status: CD4     Submitter: Geoffrey Romer     Date: 2014-07-22

[Moved to DR at the May, 2015 meeting.]

The conditions under which an explicit constructor will be used are given in 11.4.8.2 [class.conv.ctor] paragraph 2:

An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (9.4 [dcl.init]) or where casts (7.6.1.9 [expr.static.cast], 7.6.3 [expr.cast]) are explicitly used. A default constructor may be an explicit constructor; such a constructor will be used to perform default-initialization or value-initialization (9.4 [dcl.init]).

The existence of this wording in normative text is confusing and should probably simply say that explicit constructors are used in direct initialization, with cross-references to 9.4 [dcl.init] and 12.2.2.5 [over.match.copy], possibly as a note.

Proposed resolution (April, 2015):

Change 11.4.8.2 [class.conv.ctor] paragraph 2 as follows:

[Note: An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (9.4 [dcl.init]) or where casts (7.6.1.9 [expr.static.cast], 7.6.3 [expr.cast]) are explicitly used; see also 12.2.2.5 [over.match.copy]. A default constructor may be an explicit constructor; such a constructor will be used to perform default-initialization or value-initialization (9.4 [dcl.init]). [Example: ... —end example] end note]



2016. Confusing wording in description of conversion function

Section: 11.4.8.3  [class.conv.fct]     Status: CD4     Submitter: Daniel Krügler     Date: 2014-10-05

[Moved to DR at the October, 2015 meeting.]

The statement in 11.4.8.2 [class.conv.ctor] paragraph 1,

No return type can be specified.

is confusing, since a conversion operator has a return type. It would be more precise to phrase the restriction in terms of the permissible decl-specifiers in the function's decl-specifier-seq. The next sentence is also problematic,

If a conversion function is a member function, the type of the conversion function (9.3.4.6 [dcl.fct]) is “function taking no parameter returning conversion-type-id”.

as it implies that a conversion function might not be a member function.

Proposed resolution (May, 2015):

This issue is resolved by the resolution of issue 1990.




1861. Values of a bit-field

Section: 11.4.10  [class.bit]     Status: CD4     Submitter: Hubert Tong     Date: 2014-02-13

[Adopted at the June, 2016 meeting.]

The resolution of issue 1816 left many aspects of bit-fields unspecified, including whether a signed bit field has a sign bit and the meaning of the bit-field width. Also, the requirement in 6.8.2 [basic.fundamental] paragraph 1 that

For narrow character types, all bits of the object representation participate in the value representation.

should not apply to oversize character-typed bit-fields.

Notes from the June, 2014 meeting:

CWG decided to address only the issue of oversized bit-fields of narrow character types at this time, splitting off the more general questions regarding bit-fields to issue 1943.

Proposed resolution (April, 2016):

Change 6.8.2 [basic.fundamental] paragraph 1 as follows:

...For narrow character types, all bits of the object representation participate in the value representation. [Note: A bit-field of narrow character type whose length is larger than the number of bits in the object representation of that type has padding bits; see 11.4.10 [class.bit]. —end note] For unsigned narrow character types, each possible bit pattern of the value representation represents a distinct number...



1801. Kind of expression referring to member of anonymous union

Section: 11.5  [class.union]     Status: CD4     Submitter: David Majnemer     Date: 2013-10-24
[Adopted at the November, 2014 meeting as part of paper N4268.]

It is not clear whether naming a member of a global anonymous union should be considered an id-expression or implicitly a member access expression. For example, given

  static union {
    int i;
  };

  template <int &> struct S {};
  S<i> V;

is the last line well-formed? There is implementation variance on this question.

Notes from the February, 2014 meeting:

CWG agreed that the example should be ill-formed.




1940. static_assert in anonymous unions

Section: 11.5  [class.union]     Status: CD4     Submitter: Richard Smith     Date: 2014-06-12

[Moved to DR at the November, 2014 meeting.]

C++ allows only non-static data member declarations in an anonymous union, but C and several C++ implementations permit static_assert declarations. Should the C++ Standard be changed accordingly?

Proposed resolution (June, 2014):

Change 11.5 [class.union] paragraph 5 as follows:

A union of the form

is called an anonymous union; it defines an unnamed object of unnamed type. The member-specification of an anonymous union shall only define non-static data members Each member-declaration in the member-specification of an anonymous union shall either define a non-static data member or be a static_assert-declaration. [Note:...




1873. Protected member access from derived class friends

Section: 11.8.3  [class.access.base]     Status: CD4     Submitter: Richard Smith     Date: 2014-02-18

[Moved to DR at the May, 2015 meeting.]

According to 11.8.3 [class.access.base] paragraph 5,

A member m is accessible at the point R when named in class N if

The granting of access via class P is troubling. At the least, there should be a restriction that P be visible at R. Alternatively, this portion of the rule could be removed altogether; this provision does not appear to be widely used in existing code and such references can be easily converted to use P instead of N for naming the member.

Notes from the June, 2014 meeting:

CWG noted that removing the friend provision would introduce an undesirable asymmetry between member functions of P and its friends. Instead, the intent is to require P to be a complete type at R.

Notes from the November, 2014 meeting:

Although the asymmetry is unfortunate, the difference between a reference in a member function and a reference in a friend is that the member function concretely determines which P is in view, while the friend could be befriended by a class that is a specialization of a class template and thus would require instantiations that would not otherwise occur. CWG thus decided simply to eliminate the friend case.

Proposed resolution, November, 2014:

Change bullet 5.3 of 11.8.3 [class.access.base] as follows:

...A member m is accessible at the point R when named in class N if




1752. Right-recursion in mem-initializer-list

Section: 11.9.3  [class.base.init]     Status: CD4     Submitter: Christof Meerwald     Date: 2013-09-16

[Moved to DR at the November, 2014 meeting.]

The grammar for mem-initializer-list in 11.9.3 [class.base.init] paragraph 1 (after the resolution of issue 1649) is right-recursive:

In general, however, such lists elsewhere in the Standard are described using a left-recursive grammar, e.g., for initializer-list in 9.4 [dcl.init] paragraph 1:

It would be better to be consistent in the definition of mem-initializer-list.

Proposed resolution (February, 2014):

Change the grammar in 11.9.3 [class.base.init] paragraph 1 as follows:




1967. Temporary lifetime and move-elision

Section: 11.9.6  [class.copy.elision]     Status: CD4     Submitter: Richard Smith     Date: 2014-07-11

[Moved to DR at the May, 2015 meeting.]

When copy elision is performed, the lifetime of the merged object ends at the later of the times the two objects' lifetimes would have ended. If the copy elision is done using a move constructor, however, it might make sense to assume that the moved-from object's lifetime is no longer interesting and the lifetime should be that of the moved-to object.

Proposed resolution (April, 2015):

Change 11.4.5.3 [class.copy.ctor] paragraph 31 as follows:

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and. If the first parameter of the selected constructor is an rvalue reference to the object's type, the destruction of that object occurs when the target would have been destroyed; otherwise, the destruction occurs at the later of the times when the two objects would have been destroyed without the optimization.122 This elision of copy/move operations, called copy elision, is permitted...



1750. “Argument” vs “parameter”

Section: 12.2.2.5  [over.match.copy]     Status: CD4     Submitter: Mike Miller     Date: 2013-09-13

[Moved to DR at the November, 2014 meeting.]

The current wording of the second bullet of paragraph 1 of 12.2.2.5 [over.match.copy] contains the phrase,

When initializing a temporary to be bound to the first parameter of a constructor that takes a reference to possibly cv-qualified T as its first argument...

Presumably “argument” should be “parameter.”

Proposed resolution (February, 2014):

Change 12.2.2.5 [over.match.copy] paragraph 1 as follows:

...the candidate functions are selected as follows:




1758. Explicit conversion in copy/move list initialization

Section: 12.2.2.8  [over.match.list]     Status: CD4     Submitter: Richard Smith     Date: 2013-09-21

[Moved to DR at the November, 2014 meeting.]

Consider the following example:

  struct X { X(); };
  struct Y { explicit operator X(); } y;
  X x{y};

This appears to be ill-formed, although the corresponding case with parentheses is well-formed. There seem to be two factors that prevent this from being accepted:

First, the special provision allowing an explicit conversion function to be used when initializing the parameter of a copy/move constructor is in 12.2.2.5 [over.match.copy], and this case takes us to 12.2.2.8 [over.match.list] instead.

Second, 12.2.4.2 [over.best.ics] paragraph 4 says that in this case, because we are in 12.2.2.8 [over.match.list], and we have a single argument, and we are calling a copy/move constructor, we are not allowed to consider a user-defined conversion sequence for the argument.

Similarly, in an example like

  struct A {
   A() {}
   A(const A &) {}
  };
  struct B {
   operator A() { return A(); }
  } b;
  A a{b};

the wording in 12.2.4.2 [over.best.ics] paragraph 4 with regard to 12.2.2.8 [over.match.list] prevents considering B's conversion function when initializing the first parameter of A's copy constructor, thereby making this code ill-formed.

Notes from the February, 2014 meeting:

This issue should be addressed by the eventual resolution of issue 1467.

Proposed resolution (June, 2014):

This issue is resolved by the resolution of issue 1467.




1902. What makes a conversion “otherwise ill-formed”?

Section: 12.2.4.2  [over.best.ics]     Status: CD4     Submitter: Richard Smith     Date: 2014-03-26

[Moved to DR at the November, 2014 meeting.]

According to 12.2.4.2 [over.best.ics] paragraph 9,

If no sequence of conversions can be found to convert an argument to a parameter type or the conversion is otherwise ill-formed, an implicit conversion sequence cannot be formed.

However, compare this with 12.2.4.2 [over.best.ics] paragraph 2:

Implicit conversion sequences are concerned only with the type, cv-qualification, and value category of the argument and how these are converted to match the corresponding properties of the parameter. Other properties, such as the lifetime, storage class, alignment, or accessibility of the argument and whether or not the argument is a bit-field are ignored. So, although an implicit conversion sequence can be defined for a given argument-parameter pair, the conversion from the argument to the parameter might still be ill-formed in the final analysis.

It is not clear what cases are in view in paragraph 9.

Proposed resolution (October, 2014):

  1. Change 12.2.4.2 [over.best.ics] paragraph 2 as follows:

  2. Implicit conversion sequences are concerned only with the type, cv-qualification, and value category of the argument and how these are converted to match the corresponding properties of the parameter. Other properties, such as the lifetime, storage class, alignment, or accessibility of the argument, and whether or not the argument is a bit-field, and whether a function is deleted (9.5.3 [dcl.fct.def.delete]), are ignored. So, although an implicit conversion sequence can be defined for a given argument-parameter pair, the conversion from the argument to the parameter might still be ill-formed in the final analysis.
  3. Change 12.2.4.2 [over.best.ics] paragraph 9 as follows:

  4. If no sequence of conversions can be found to convert an argument to a parameter type or the conversion is otherwise ill-formed, an implicit conversion sequence cannot be formed.



2076. List-initialization of arguments for constructor parameters

Section: 12.2.4.2  [over.best.ics]     Status: CD4     Submitter: Richard Smith     Date: 2015-01-27

[Adopted at the June, 2016 meeting.]

The resolution of issue 1467 made some plausible constructs ill-formed. For example,

   struct A { A(int); };
   struct B { B(A); };
   B b{{0}};

This is now ambiguous, because the text disallowing user-defined conversions for B's copy and move constructors was removed from 12.2.4.2 [over.best.ics] paragraph 4. Another example:

  struct Params { int a; int b; };
  class Foo {
  public:
    Foo(Params);
  };
  Foo foo{{1, 2}};

This is now ambiguous between Foo(Params) and Foo(Foo&&).

For non-class types, we allow initialization from a single-item list to perform a copy only if the element within the list is not itself a list (12.2.4.2.6 [over.ics.list] bullet 9.1). The analogous rule for this case would be to add back the bullet in 12.2.4.2 [over.best.ics] paragraph 4, but only in the case where the initializer is itself an initializer list:

the second phase of 12.2.2.8 [over.match.list] when the initializer list has exactly one element that is itself an initializer list, where the target is the first parameter of a constructor of class X, and the conversion is to X or reference to (possibly cv-qualified) X,

Proposed resolution (March, 2016):

Change 12.2.4.2 [over.best.ics] paragraph 4 as follows:

...and the constructor or user-defined conversion function is a candidate by

user-defined conversion sequences are not considered. [Note:...




1631. Incorrect overload resolution for single-element initializer-list

Section: 12.2.4.2.6  [over.ics.list]     Status: CD4     Submitter: Johannes Schaub     Date: 2013-03-03

[Moved to DR at the November, 2014 meeting.]

According to bullet 1 of 12.2.4.2.6 [over.ics.list] paragraph 6,

Otherwise, if the parameter type is not a class:

This wording ignores the possibility that the element might be an initializer list (as opposed to an expression with a type, as illustrated in the example). This oversight affects an example like:

  struct A { int a[1]; };
  struct B { B(int); };
  void f(B, int);
  void f(int, A);

  int main() {
    f({0}, {{1}});
  }

Proposed resolution (June, 2014):

This issue is resolved by the resolution of issue 1467.




2075. Passing short initializer lists to array reference parameters

Section: 12.2.4.2.6  [over.ics.list]     Status: CD4     Submitter: Richard Smith     Date: 2015-01-27

[Moved to DR at the October, 2015 meeting.]

According to 12.2.4.2.6 [over.ics.list] paragraph 5 says,

Otherwise, if the parameter type is “array of N X”, if the initializer list has exactly N elements or if it has fewer than N elements and X is default-constructible, and if all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X.

There are terminological problems with this formulation. “Default constructible” is not otherwise used in the Standard and should instead refer to an implicit conversion sequence from an empty braced-init-list. Similarly, “can be implicitly converted” should instead refer to the existence of an implicit conversion sequence.

Proposed resolution, May, 2015:

Replace the indicated wording with:

Otherwise, if the parameter type is “array of N X,” if there exists an implicit conversion sequence for each element of the array from the corresponding element of the initializer list (or from {} if there is no such element), the implicit conversion sequence is the worst such implicit conversion sequence.



1589. Ambiguous ranking of list-initialization sequences

Section: 12.2.4.3  [over.ics.rank]     Status: CD4     Submitter: Johannes Schaub     Date: 2012-11-21

[Moved to DR at the November, 2014 meeting.]

The interpretation of the following example is unclear in the current wording:

   void f(long);
   void f(initializer_list<int>);
   int main() { f({1L});

The problem is that a list-initialization sequence can also be a standard conversion sequence, depending on the types of the elements and the type of the parameter, so more than one bullet in the list in 12.2.4.3 [over.ics.rank] paragraph 3 applies:

Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of the following rules applies:

These bullets give opposite results for the example above, and there is implementation variance in which is selected.

Notes from the April, 2013 meeting:

CWG determined that the latter bullet should apply only if the first one does not.

Proposed resolution (June, 2014):

This issue is resolved by the resolution of issue 1467.




2052. Template argument deduction vs overloaded operators

Section: 12.4  [over.oper]     Status: CD4     Submitter: Richard Smith     Date: 2014-12-03

[Moved to DR at the October, 2015 meeting.]

In an example like

  struct A { operator int(); };
  template<typename T> T operator<<(T, int);
  void f(A a) { 1 << a; }

Template argument deduction succeeds for the operator template, producing the signature operator<<(int,int). The resulting declaration is synthesized and added to the overload set, per 13.10.4 [temp.over] paragraph 1. However, this violates the requirement of 12.4 [over.oper] paragraph 6,

An operator function shall either be a non-static member function or be a non-member function that has at least one parameter whose type is a class, a reference to a class, an enumeration, or a reference to an enumeration.

This is not a SFINAE context, so the program is ill-formed, rather than selecting the built-in operator.

Proposed resolution (May, 2015):

Change 13.10.4 [temp.over] paragraph 1 as follows,

...If, for a given function template, argument deduction fails or the synthesized function template specialization would be ill-formed, no such function is added to the set of candidate functions for that template.



1874. Type vs non-type template parameters with class keyword

Section: 13.2  [temp.param]     Status: CD4     Submitter: Richard Smith     Date: 2014-02-18

[Moved to DR at the November, 2014 meeting.]

The Standard is not clear enough that a template parameter like class T is to be interpreted as a type parameter and not an ill-formed non-type parameter of class type T.

Proposed resolution (October, 2014):

  1. Change 13.2 [temp.param] paragraph 2 as follows, moving the example from paragraph 3 to paragraph 2:

  2. There is no semantic difference between class and typename in a template-parameter. typename followed by an unqualified-id names a template type parameter. typename followed by a qualified-id denotes the type in a non-type137 parameter-declaration. A template-parameter of the form class identifier is a type-parameter. [Example:

      class T { /* ... */ };
      int i;
    
      template<class T, T i> void f(T t) {
        T t1 = i;      // template-parameters T and i
        ::T t2 = ::i;  // global namespace members T and i
      }
    

    Here, the template f has a type-parameter called T, rather than an unnamed non-type template-parameter of class T. —end example]. A storage class shall not be specified in a template-parameter declaration. Types shall not be defined in a template-parameter declaration. [Note: A template parameter may be a class template. For example... —end note]

  3. Change 13.2 [temp.param] paragraph 3 as follows, moving the example from paragraph 2 to paragraph 3:

  4. A type-parameter whose identifier does not follow an ellipsis defines its identifier to be a typedef-name (if declared with class or typename) or template-name (if declared with template) in the scope of the template declaration. [Note: Because of the name lookup rules, a template-parameter that could be interpreted as either a non-type template-parameter or a type-parameter (because its identifier is the name of an already existing class) is taken as a type-parameter. For example... —end note] [Note: A template parameter may be a class template. For example,

      template<class T> class myarray { /* ... */ };
    
      template<class K, class V, template<class T> class C = myarray>
      class Map {
        C<K> key;
        C<V> value;
      };
    

    end note]




2032. Default template-arguments of variable templates

Section: 13.2  [temp.param]     Status: CD4     Submitter: CWG     Date: 2014-11-03

[Adopted at the February, 2016 meeting.]

According to 13.2 [temp.param] paragraph11,

If a template-parameter of a class template or alias template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. If a template-parameter of a primary class template or alias template is a template parameter pack, it shall be the last template-parameter.

These requirements should apply to variable templates as well.

Proposed resolution (September, 2015):

Change 13.2 [temp.param] paragraph 11 as follows:

If a template-parameter of a class template, variable template, or alias template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. If a template-parameter of a primary class template, primary variable template, or alias template is a template parameter pack, it shall be the last template-parameter. A template parameter pack...



2008. Default template-arguments underspecified

Section: 13.4  [temp.arg]     Status: CD4     Submitter: Andrew Sutton     Date: 2014-09-23

[Adopted at the February, 2016 meeting.]

Proposed resolution, October, 2015:

Add the following as a new paragraph after 13.4 [temp.arg] paragraph 7:

When the template in a template-id is an overloaded function template...

When a simple-template-id does not name a function, a default template-argument is implicitly instantiated (13.9.2 [temp.inst]) when the value of that default argument is needed. [Example:

  template<typename T, typename U = int> struct S { };
  S<bool>* p; // the type of p is S<bool, int>*

The default argument for U is instantiated to form the type S<bool, int>*. —end example]

Notes from the November, 2014 meeting:

The preceding was extracted from the wording of the Concepts Lite draft Technical Specification.




2106. Unclear restrictions on use of function-type template arguments

Section: 13.4.2  [temp.arg.type]     Status: CD4     Submitter: David Krauss     Date: 2015-03-17

[Adopted at the February, 2016 meeting.]

According to 13.4.2 [temp.arg.type] paragraph 3,

If a declaration acquires a function type through a type dependent on a template-parameter and this causes a declaration that does not use the syntactic form of a function declarator to have function type, the program is ill-formed.

This is not clear enough regarding which declarations are in view. For example, does it apply to a typedef declaration? Does it apply to a parameter declaration, where normal function-to-pointer decay would apply? There is implementation variance at block scope.

Also, since this applies a restriction to the usage of dependent types, not template type arguments per se, the paragraph presumably should appear in 13.8.3.2 [temp.dep.type] and not its current location.

Proposed resolution (September, 2015):

  1. Delete 13.4.2 [temp.arg.type] paragraph 3:

  2. If a declaration acquires a function type through a type dependent on a template-parameter and this causes a declaration that does not use the syntactic form of a function declarator to have function type, the program is ill-formed. [Example:

      template<class T> struct A {
        static T t;
      };
      typedef int function();
      A<function> a; // ill-formed: would declare A<function>::t
                     // as a static member function
    

    end example]

  3. Add the following as a new paragraph following 13.9 [temp.spec] paragraph 6:
  4. ...X<int> has a static member s of type int and X<char*> has a static member s of type char*. —end example]

    If a function declaration acquired its function type through a dependent type (13.8.3.2 [temp.dep.type]) without using the syntactic form of a function declaator, the program is ill-formed. [Example:

       template<class T> struct A {
         static T t;
       };
       typedef int function();
       A<function> a;   // ill-formed: would declare A<function>::t
                        // as a static member function
    

    end example]




1451. Objects with no linkage in non-type template arguments

Section: 13.4.3  [temp.arg.nontype]     Status: CD4     Submitter: Daniel Krügler     Date: 2012-02-01

[Adopted at the November, 2014 meeting as part of paper N4268.]

According to 13.4.3 [temp.arg.nontype] bullet 1.3, only objects with linkage can be used to form non-type template arguments. Is this restriction still needed? It would be convenient to use block-scope objects as template arguments.

Rationale (February, 2012):

This is a request for an extension to the language and thus more appropriately addressed by EWG.




2064. Conflicting specifications for dependent decltype-specifiers

Section: 13.6  [temp.type]     Status: CD4     Submitter: Richard Smith     Date: 2014-12-27

[Adopted at the February, 2016 meeting.]

According to 13.8.3.2 [temp.dep.type] paragraph 9, a type is dependent if it is

denoted by decltype(expression), where expression is type-dependent (13.8.3.3 [temp.dep.expr]).

However, 13.6 [temp.type] paragraph 2 says,

If an expression e involves a template parameter, decltype(e) denotes a unique dependent type. Two such decltype-specifiers refer to the same type only if their expressions are equivalent (13.7.7.2 [temp.over.link]). [Note: however, it may be aliased, e.g., by a typedef-name. —end note]

These seem to be in need of reconciliation.

Proposed resolution (January, 2016):

Change 13.6 [temp.type] paragraph 2 as follows:

If an expression e involves a template parameter is type-dependent (13.8.3.3 [temp.dep.expr]), decltype(e) denotes a unique dependent type. Two such decltype-specifiers refer to the same type only if their expressions are equivalent (13.7.7.2 [temp.over.link]). [Note: however, it such a type may be aliased, e.g., by a typedef-name. —end note]



1804. Partial specialization and friendship

Section: 13.7.5  [temp.friend]     Status: CD4     Submitter: Steve Clamage     Date: 2013-11-01

[Moved to DR at the November, 2014 meeting.]

According to 13.7.5 [temp.friend] paragraph 5,

A member of a class template may be declared to be a friend of a non-template class. In this case, the corresponding member of every specialization of the class template is a friend of the class granting friendship. For explicit specializations the corresponding member is the member (if any) that has the same name, kind (type, function, class template, or function template), template parameters, and signature as the member of the class template instantiation that would otherwise have been generated.

Should this treatment of members of explicit specializations also apply to members of partial specializations?

Proposed resolution (February, 2014):

Change 13.7.5 [temp.friend] paragraph 5 as follows:

A member of a class template may be declared to be a friend of a non-template class. In this case, the corresponding member of every specialization of the primary class template and class template partial specializations thereof is a friend of the class granting friendship. For explicit specializations and specializations of partial specializations, the corresponding member is the member (if any) that has the same name, kind (type, function, class template, or function template), template parameters, and signature as the member of the class template instantiation that would otherwise have been generated. [Example:...



1315. Restrictions on non-type template arguments in partial specializations

Section: 13.7.6.1  [temp.spec.partial.general]     Status: CD4     Submitter: Johannes Schaub     Date: 2011-05-12

[Adopted at the February, 2016 meeting.]

The rationale for the restriction in 13.7.6.1 [temp.spec.partial.general] bullet 9.1 is not clear:

In the example, it's clear that I is non-deducible, but this rule prevents plausible uses like:

  template <int I, int J> struct A {};
  template <int I> struct A<I, I*2> {};

(See also issues 1647, 2033, and 2127.)

Proposed resolution (September, 2015):

Change 13.7.6.1 [temp.spec.partial.general] bullet 9.1 as follows:




1819. Acceptable scopes for definition of partial specialization

Section: 13.7.6.1  [temp.spec.partial.general]     Status: CD4     Submitter: Richard Smith     Date: 2013-12-05

[Moved to DR at the November, 2014 meeting.]

According to 13.7.6 [temp.spec.partial] paragraph 6,

A class template partial specialization may be declared or redeclared in any namespace scope in which its definition may be defined (13.7.2 [temp.class] and 13.7.3 [temp.mem]).

However, there is nothing in those referenced sections specifying where the definition may appear. Should this have referred to the definition of the primary template?

Also, the cross-reference to 13.7.2 [temp.class] is suspect; the actual rules for where non-member class templates may be defined are found in _N4868_.9.8.2.3 [namespace.memdef] paragraphs 1-2, 9.3.4 [dcl.meaning] paragraph 1, and 9.8.2 [namespace.def] paragraph 8.

(Apropos of 9.8.2 [namespace.def], the description in paragraph 8 mentions explicit instantiation and explicit specialization, but presumably inadvertently omits partial specializations.)

Proposed resolution (February, 2014) [SUPERSEDED]:

Change 13.7.6 [temp.spec.partial] paragraph 6 as follows:

A class template partial specialization may be declared or redeclared in any namespace scope in which its definition the corresponding primary template may be defined (13.7.2 [temp.class] and 13.7.3 [temp.mem]). [Example:...

Additional note, February, 2014:

The proposed resolution approved by CWG at the February, 2014 meeting does not address the additional points raised in the issue, specifically the cross-reference to 13.7.2 [temp.class] and the omission of partial specializations from 9.8.2 [namespace.def]. The issue has been returned to "review" status to consider amending the resolution to include these items.

Proposed Resolution (July, 2014):

  1. Change 9.8.2 [namespace.def] paragraph 8 as follows:

  2. ...Furthermore, each member of the inline namespace can subsequently be partially specialized (13.7.6 [temp.spec.partial]), explicitly instantiated (13.9.3 [temp.explicit]), or explicitly specialized (13.9.4 [temp.expl.spec]) as though it were a member of the enclosing namespace. Finally, looking up a name...
  3. Change 13.7.6 [temp.spec.partial] paragraph 6 as follows:

  4. A class template partial specialization may be declared or redeclared in any namespace scope in which its definition the corresponding primary template may be defined (13.7.2 [temp.class] _N4868_.9.8.2.3 [namespace.memdef] and 13.7.3 [temp.mem]). [Example:...



2033. Redundant restriction on partial specialization argument

Section: 13.7.6.1  [temp.spec.partial.general]     Status: CD4     Submitter: CWG     Date: 2014-11-04

[Adopted at the February, 2016 meeting.]

Bullets 9.3 and 9.4 of 13.7.6.1 [temp.spec.partial.general] say,

Within the argument list of a class template partial specialization, the following restrictions apply:

The former is implied by the latter and should be omitted.

(See also issues 1315, 1647, and 2127.)

Proposed resolution (September, 2015):

Delete bullet 9.3 of 13.7.6.1 [temp.spec.partial.general]:




1446. Member function with no ref-qualifier and non-member function with rvalue reference

Section: 13.7.7.3  [temp.func.order]     Status: CD4     Submitter: Jason Merrill     Date: 2012-02-07

[Moved to DR at the November, 2014 meeting.]

A member function with no ref-qualifier can be called for a class prvalue, as can a non-member function whose first parameter is an rvalue reference to that class type. However, 13.7.7.3 [temp.func.order] does not handle this case.

Proposed resolution (February, 2014):

Change 13.7.7.3 [temp.func.order] paragraph 3 as follows:

...If only one of the function templates M is a non-static member of some class A, that function template M is considered to have a new first parameter inserted in its function parameter list. Given cv as the cv-qualifiers of the function template M (if any), the new parameter is of type “rvalue reference to cv A” if the optional ref-qualifier of the function template M is &&, or if M has no ref-qualifier and the first parameter of the other template has rvalue reference type. Otherwise, the new parameter is of type “lvalue reference to cv Aotherwise. [Note: This allows a non-static member to be ordered with respect to a nonmember function and for the results to be equivalent to the ordering of two equivalent nonmembers. —end note] [Example:...



1558. Unused arguments in alias template specializations

Section: 13.7.8  [temp.alias]     Status: CD4     Submitter: Nikolay Ivchenkov     Date: 2012-09-19

[Moved to DR at the November, 2014 meeting.]

The treatment of unused arguments in an alias template specialization is not specified by the current wording of 13.7.8 [temp.alias]. For example:

  #include <iostream>

  template <class T, class...>
    using first_of = T;

  template <class T>
    first_of<void, typename T::type> f(int)
      { std::cout << "1\n"; }

  template <class T>
    void f(...)
      { std::cout << "2\n"; }

  struct X { typedef void type; };

  int main() {
    f<X>(0);
    f<int>(0);
  }

Is the reference to first_of<void, T::type> with T being int equivalent to simply void, or is it a substitution failure?

(See also issues 1430, 1520, and 1554.)

Notes from the October, 2012 meeting:

The consensus of CWG was to treat this case as substitution failure.

Proposed resolution (February, 2014):

Add the following as a new paragraph before 13.7.8 [temp.alias] paragraph 3:

When a template-id refers to the specialization of an alias template, it is equivalent...

However, if the template-id is dependent, subsequent template argument substitution still applies to the template-id. [Example:

  template<typename...> using void_t = void;
  template<typename T> void_t<typename T::foo> f();
  f<int>(); // error, int does not have a nested type foo

end example]

The type-id in an alias template declaration shall not refer...




1850. Differences between definition context and point of instantiation

Section: 13.8  [temp.res]     Status: CD4     Submitter: Richard Smith     Date: 2014-02-04

[Moved to DR at the November, 2014 meeting.]

Various characteristics of entities referred to by a non-dependent reference in a template can change between the definition context and the point of instantiation of a specialization of that template. These include initialization (which affects whether an object can be used in a constant expression), function and template default arguments, and the completeness of types. There is implementation divergence as to whether these are checked in the definition context or at the point of instantiation. Presumably a rule is needed to make it ill-formed, no diagnostic required, if the validity of such a reference changes between the two contexts.

Proposed resolution (February, 2014):

Change 13.8 [temp.res] paragraph 8 as follows:

...If a type used in a non-dependent name is incomplete at the point at which a template is defined but is complete at the point at which an instantiation is done, and if the completeness of that type affects whether or not the program is well-formed or affects the semantics of the program, hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, the program is ill-formed; no diagnostic is required. If the interpretation of such a construct in the hypothetical instantiation is different from the interpretation of the corresponding construct in any actual instantiation of the template, the program is ill-formed; no diagnostic is required. [Note: This can happen in situations including the following:

end note] [Note: If a template is instantiated...




1922. Injected class template names and default arguments

Section: 13.8.2  [temp.local]     Status: CD4     Submitter: Hubert Tong     Date: 2014-05-05

[Moved to DR at the May, 2015 meeting.]

Use of the injected-class-name of a class template with a template-argument-list that relies on default arguments is not clearly specified in the current wording of the Standard. In particular, according to 13.2 [temp.param] paragraph 10,

The set of default template-arguments available for use with a template declaration or definition is obtained by merging the default arguments from the definition (if in scope) and all declarations in scope in the same way default function arguments are (9.3.4.7 [dcl.fct.default]).

However, the injected-class-name hides the template declarations, so it is not clear whether the default arguments are available at that point or not.

Proposed resolution (November, 2014):

Change 13.2 [temp.param] paragraph 10 as follows:

The set of default template-arguments available for use with a template declaration or definition is obtained by merging the default arguments from the definition (if in scope) and all prior declarations in scope of the template in the same way default function arguments are (9.3.4.7 [dcl.fct.default]). [Example:...



591. When a dependent base class is the current instantiation

Section: 13.8.3  [temp.dep]     Status: CD4     Submitter: James Widman     Date: 24 August 2006

[Moved to DR at the November, 2014 meeting.]

Is the following example well-formed?

    template<class T> struct A {
         typedef int M;
         struct B {
             typedef void M;
             struct C;
         };
    };

    template<class T> struct A<T>::B::C : A<T> {
         M  // A<T>::M or A<T>::B::M?
             p[2];
    };

13.8.3 [temp.dep] paragraph 3 says the use of M should refer to A<T>::B::M because the base class A<T> is not searched because it's dependent. But in this case A<T> is also the current instantiation (13.8.3.2 [temp.dep.type]) so it seems like it should be searched.

Notes from the August, 2011 meeting:

The recent changes to the handling of the current instantiation may have sufficiently addressed this issue.

Additional note (September, 2012):

See also issue 1526 for additional analysis demonstrating that this issue is still current despite the changes to the description of the current instantiation. The status has consequently been changed back to "open" for further consideration.

Proposed resolution (February, 2014):

  1. Add the following as a new paragraph before 13.8.3.2 [temp.dep.type] paragraph 4:

  2. A dependent base class is a base class that is a dependent type and is not the current instantiation. [Note: a base class can be the current instantiation in the case of a nested class naming an enclosing class as a base. —end note] [Example:

      template<class T> struct A {
        typedef int M;
        struct B {
          typedef void M;
          struct C;
        };
      };
    
      template<class T> struct A<T>::B::C : A<T> {
        M m; // OK, A<T>::M
      };
    

    end example]

    A name is a member of the current instantiation if...

  3. Change 13.8.2 [temp.local] paragraph 9 as follows:

  4. In the definition of a class template or in the definition of a member of such a template that appears outside of the template definition, for each non-dependent base class (13.8.3.2 [temp.dep.type]) which does not depend on a template-parameter (13.8.3 [temp.dep]), if the name of the base class or the name of a member of the base class is the same as the name of a template-parameter, the base class name or member name hides the template-parameter name (_N4868_.6.4.10 [basic.scope.hiding]). [Example:...
  5. Change 13.8.3 [temp.dep] paragraph 3 as follows:

  6. In the definition of a class or class template, if a base class depends on a template-parameter, the base class scope the scope of a dependent base class (13.8.3.2 [temp.dep.type]) is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member. [Example:...



1292. Dependent calls with braced-init-lists containing a pack expansion

Section: 13.8.3  [temp.dep]     Status: CD4     Submitter: James Widman     Date: 2011-04-10

[Moved to DR at the November, 2014 meeting.]

The discussion of issue 1233 revealed that the dependency of function calls involving a braced-init-list containing a pack expansion is not adequately addressed by the existing wording.

Proposed resolution (February, 2014):

  1. Change 13.8.3 [temp.dep] paragraph 1 as follows:

  2. ...In an expression of the form:

    where the postfix-expression is an unqualified-id, the unqualified-id denotes a dependent name if

  3. Add the following as a new paragraph at the end of 13.8.3.3 [temp.dep.expr]:

  4. A class member access expression (7.6.1.5 [expr.ref]) is type-dependent if...

    A braced-init-list is type-dependent if any element is type-dependent or is a pack expansion.




2101. Incorrect description of type- and value-dependence

Section: 13.8.3  [temp.dep]     Status: CD4     Submitter: Maxim Kartashev     Date: 2015-03-16

[Moved to DR at the October, 2015 meeting.]

According to 13.8.3 [temp.dep] paragraph 1,

Expressions may be type-dependent (on the type of a template parameter) or value-dependent (on the value of a non-type template parameter).

Proposed resolution (May, 2015):

Change 13.8.3 [temp.dep] paragraph 1 to read,

An expression may be type-dependent (that is, its type may depend on a template parameter) or value-dependent (that is, its value when evaluated as a constant expression (7.7 [expr.const]) may depend on a template parameter) as described in this subclause."



1309. Incorrect note regarding lookup of a member of the current instantiation

Section: 13.8.3.2  [temp.dep.type]     Status: CD4     Submitter: Johannes Schaub     Date: 2011-05-05

[Moved to DR at the May, 2015 meeting.]

The note in 13.8.3.2 [temp.dep.type] paragraph 7 reads,

[Note: the result of name lookup differs only when the member of the current instantiation was found in a non-dependent base class of the current instantiation and a member with the same name is also introduced by the substitution for a dependent base class of the current instantiation. —end note]

However, this is not correct. Consider the following example:

  struct Y { int X; };

  template<typename T>
  struct A : Y {
   enum B : int;
   void f() { A::X; } // finds Y::X here!
  };

  template<typename T>
  enum A<T>::B : int {
   X // introduces member A::X into A<T>!
  };

  void g() { A<int> a; a.f(); }

A::X is a member of the current instantiation, so paragraph 7 requires it to be looked up again when instantiating and to give a diagnostic if the lookup differs from the lookup in the definition context. The note incorrectly indicates that this can only happen if the conflicting name was introduced by a dependent base class.

Proposed resolution (August, 2011) [SUPERSEDED]:

Change 13.8.3.2 [temp.dep.type] paragraph 7 as follows:

...If the result of this lookup differs from the result of name lookup in the template definition context, name lookup is ambiguous. [Note: the result of name lookup differs only when the member of the current instantiation was found in a non-dependent base class of the current instantiation and a member with the same name is also introduced by the substitution for a dependent base class of the current instantiation. —end note] [Example:

  struct A {
    int m;
  };

  struct B {
    int m;
  };

  template<typename T>
  struct C : A, T {
    int f() { return this->m; } // finds A::m in the template definition context
  };

  int g(C<B> cb) {
    return cb.f();              // error: finds both A::m and B::m in the template instantiation context
  }

end example]

Notes from the December, 2011 teleconference:

Changes to the exposition were suggested and the issue returned to "drafting" status.

Proposed resolution (November, 2014):

Change 13.8.3.2 [temp.dep.type] paragraph 7 as follows:

...If the result of this lookup differs from the result of name lookup in the template definition context, name lookup is ambiguous. [Note: the result of name lookup differs only when the member of the current instantiation was found in a non-dependent base class of the current instantiation and a member with the same name is also introduced by the substitution for a dependent base class of the current instantiation. —end note] [Example:

  struct A {
    int m;
  };

  struct B {
    int m;
  };

  template<typename T>
  struct C : A, T {
    int f() { return this->m; }  // finds A::m in the template definition context
  };

  template int C<B>::f();        // error: finds both A::m and B::m

end example]




1988. Ambiguity between dependent and non-dependent bases in implicit member access

Section: 13.8.3.2  [temp.dep.type]     Status: CD4     Submitter: Richard Smith     Date: 2014-08-22

[Moved to DR at the May, 2015 meeting.]

According to 13.8.3.2 [temp.dep.type] paragraph 7,

If, for a given set of template arguments, a specialization of a template is instantiated that refers to a member of the current instantiation with a qualified-id or class member access expression, the name in the qualified-id or class member access expression is looked up in the template instantiation context. If the result of this lookup differs from the result of name lookup in the template definition context, name lookup is ambiguous. [Note: the result of name lookup differs only when the member of the current instantiation was found in a non-dependent base class of the current instantiation and a member with the same name is also introduced by the substitution for a dependent base class of the current instantiation. —end note]

It is not clear whether this applies to an example like,

   struct A { int n; };
   struct B { int n; };
   template<typename T> struct C : T, B {
     int f() { return n; }
   };
   int k = C<A>().f();

since the reference to n is transformed into a class member access expression, per 11.4.3 [class.mfct.non.static] paragraph 3.

Notes from the November, 2014 meeting:

The transformation to a member access expression or qualified-id should be performed only in the instantiated function, not when processing the template definition.

Proposed resolution (April, 2015):

  1. Change 11.4.3 [class.mfct.non.static] paragraph 3 as follows:

  2. When an id-expression (7.5 [expr.prim]) that is not part of a class member access syntax (7.6.1.5 [expr.ref]) and not used to form a pointer to member (7.6.2.2 [expr.unary.op]) is used in a member of class X in a context where this can be used (_N4567_.5.1.1 [expr.prim.general]), if name lookup (6.5 [basic.lookup]) resolves the name in the id-expression to a non-static non-type member of some class C, and if either the id-expression is potentially evaluated or C is X or a base class of X, the id-expression is transformed into a class member access expression (7.6.1.5 [expr.ref]) using (*this) (_N4868_.11.4.3.2 [class.this]) as the postfix-expression to the left of the . operator. [Note: If C is not X or a base class of X, the class member access expression is ill-formed. —end note] Similarly during name lookup, when an unqualified-id (7.5 [expr.prim]) used in the definition of a member function for class X resolves to a static member, an enumerator or a nested type of class X or of a base class of X, the unqualified-id is transformed into a qualified-id (7.5 [expr.prim]) in which the nested-name-specifier names the class of the member function. These transformations do not apply in the template definition context (13.8.3.2 [temp.dep.type]). [Example:...
  3. Change the example in 13.8.3.2 [temp.dep.type] paragraph 7, as modified by the resolution of issue 1309, as follows:

  4.   struct A {
        int m;
      };
    
      struct B {
        int m;
      };
    
      template<typename T>
      struct C : A, T {
        int f() { return this->m; }// finds A::m in the template definition context
        int g() { return m; }      // finds A::m in the template definition context
      };
    
      template int C<B>::f();      // error: finds both A::m and B::m
      template int C<B>::g();      // OK: transformation to class member access syntax
                                   // does not occur in the template definition context; see 11.4.3 [class.mfct.non.static]
    



2024. Dependent types and unexpanded parameter packs

Section: 13.8.3.2  [temp.dep.type]     Status: CD4     Submitter: Richard Smith     Date: 2014-10-17

[Moved to DR at the October, 2015 meeting.]

Consider an example like:

  template<typename ...Ts> struct X { X(int); };
  template<typename T> using Y = int;
  template<typename ...Ts> void f() {
    X<Y<Ts>...> x;
  }

The presence of the ellipsis should make the reference to X a dependent type, but there is no rule making it so.

Proposed resolution (May, 2015):

Change 13.8.3.2 [temp.dep.type] paragraph 9 as follows:

A type is dependent if it is




1779. Type dependency of __func__

Section: 13.8.3.3  [temp.dep.expr]     Status: CD4     Submitter: John Spicer     Date: 2013-09-25

[Moved to DR at the November, 2014 meeting.]

The length of the __func__ array is implementation-defined but potentially depends on the signature of the function in which it occurs. However, __func__ is not listed among the type-dependent id-expressions in 13.8.3.3 [temp.dep.expr] paragraph 3.

Proposed resolution (February, 2014):

Change 13.8.3.3 [temp.dep.expr] paragraph 3 as follows:

An id-expression is type-dependent if it contains




1899. Value-dependent constant expressions

Section: 13.8.3.4  [temp.dep.constexpr]     Status: CD4     Submitter: Richard Smith     Date: 2014-03-22

[Moved to DR at the May, 2015 meeting.]

13.8.3.4 [temp.dep.constexpr] paragraph 1 begins,

Except as described below, a constant expression is value-dependent if...

However, this terminology is misleading, because “constant expression” is now defined in terms of evaluation, and a value-dependent expression cannot be evaluated.

Proposed resolution (November, 2014):

  1. Change 13.8.3.2 [temp.dep.type] paragraph 8 as follows:

  2. A type is dependent if it is

  3. Change 13.8.3.4 [temp.dep.constexpr] paragraph 1 as follows:

  4. Except as described below, a constant an expression used in a context where a constant expression is required is value-dependent if any subexpression is value-dependent.



2066. Does type-dependent imply value-dependent?

Section: 13.8.3.4  [temp.dep.constexpr]     Status: CD4     Submitter: Richard Smith     Date: 2015-01-09

[Adopted at the February, 2016 meeting.]

Consider the following example:

  template<int> struct X { typedef int type; };
  template<typename T> struct Y {
    void f() { X<false ? this - this : 0>::type x; } // missing typename?
  };
  void g() { Y<void>().f(); }

This appears to be valid because the template argument expression is not value-dependent.

Until I discovered this, I had been assuming that any type-dependent expression is also value-dependent. The only exception to that appears to be the expression this, which may be type-dependent but is never value-dependent.

Now, this need not ever be value-dependent, because evaluation of it will never succeed when it appears as a subexpression of an expression that we're checking for constant-expression-ness. But if that's really what we want here, then the same applies to function parameters with dependent types, and probably a few other cases.

Proposed resolution (September, 2015) [SUPERSEDED]:

Change 13.8.3.4 [temp.dep.constexpr] paragraph 4 as follows:

Expressions of the following form are value-dependent:

Proposed resolution (March, 2016):

This issue is resolved by the resolution of issue 2109.




2109. Value dependence underspecified

Section: 13.8.3.4  [temp.dep.constexpr]     Status: CD4     Submitter: Maxim Kartashev     Date: 2015-03-26

[Adopted at the February, 2016 meeting.]

In the following example,

  struct A {};

  struct X {
     template <typename Q>
     int memfunc();
  };

  template <int (X::* P) ()> int foo(...);

  template<class T> struct B {
     static int bar() {
       A a;
       return foo<&X::memfunc<T> >(a);
     }
  };

  template <int (X::* P) ()>  int foo(A a) { return 0; }

  int main()  {
     return B<int>::bar();
  }

the call foo<&X::memfunc<T> >(a); is dependent only if the template argument is dependent, which is only true because of the use of the template parameter T. Implementations generally agree that this is dependent, but there does not appear to be wording to support this determination.

Proposed resolution (September, 2015):

Change 13.8.3.4 [temp.dep.constexpr] paragraph 2 as follows:

An id-expression is value-dependent if:

This resolution also resolves issue 2066.




212. Implicit instantiation is not described clearly enough

Section: 13.9.2  [temp.inst]     Status: CD4     Submitter: Christophe de Dinechin     Date: 7 Mar 2000

[Adopted at the February, 2016 meeting.]

Three points have been raised where the wording in 13.9.2 [temp.inst] may not be sufficiently clear.

  1. In paragraph 4, the statement is made that
    A class template specialization is implicitly instantiated... if the completeness of the class type affects the semantics of the program...

    It is not clear what it means for the "completeness... [to affect] the semantics." Consider the following example:

            template<class T> struct A;
            extern A<int> a;
    
            void *foo() { return &a; }
    
            template<class T> struct A
            {
            #ifdef OPTION
                    void *operator &() { return 0; }
            #endif
            };
    

    The question here is whether it is necessary for template class A to declare an operator & for the semantics of the program to be affected. If it does not do so, the meaning of &a will be the same whether the class is complete or not and thus arguably the semantics of the program are not affected.

    Presumably what was intended is whether the presence or absence of certain member declarations in the template class might be relevant in determining the meaning of the program. A clearer statement may be desirable.

  2. Paragraph 5 says,
    If the overload resolution process can determine the correct function to call without instantiating a class template definition, it is unspecified whether that instantiation actually takes place.

    The intent of this wording, as illustrated in the example in that paragraph, is to allow a "smart" implementation not to instantiate class templates if it can determine that such an instantiation will not affect the result of overload resolution, even though the algorithm described in Clause 12 [over] requires that all the viable functions be enumerated, including functions that might be found as members of specializations.

    Unfortunately, the looseness of the wording allowing this latitude for implementations makes it unclear what "the overload resolution process" is — is it the algorithm in Clause 12 [over] or something else? — and what "the correct function" is.

  3. According to paragraph 6,
    If an implicit instantiation of a class template specialization is required and the template is declared but not defined, the program is ill-formed.

    Here, it is not clear what conditions "require" an implicit instantiation. From the context, it would appear that the intent is to refer to the conditions in paragraph 4 that cause a specialization to be instantiated.

    This interpretation, however, leads to different treatment of template and non-template incomplete classes. For example, by this interpretation,

        class A;
        template <class T> struct TA;
        extern A a;
        extern TA<int> ta;
    
        void f(A*);
        void f(TA<int>*);
    
        int main()
        {
            f(&a);    // well-formed; undefined if A
                      // has operator &() member
            f(&ta);   // ill-formed: cannot instantiate
        }
    

    A different approach would be to understand "required" in paragraph 6 to mean that a complete type is required in the expression. In this interpretation, if an incomplete type is acceptable in the context and the class template definition is not visible, the instantiation is not attempted and the program is well-formed.

    The meaning of "required" in paragraph 6 must be clarified.

(See also issues 204 and 63.)

Notes on 10/01 meeting:

It was felt that item 1 is solved by addition of the word "might" in the resolution for issue 63; item 2 is not much of a problem; and item 3 could be solved by changing "required" to "required to be complete".

Proposed resolution (January, 2016):

  1. Change 13.9.2 [temp.inst] paragraph 1 as follows, moving the note and example from paragraph 6 and breaking it into two paragraphs:

  2. Unless a class template specialization has been explicitly instantiated (13.9.3 [temp.explicit]) or explicitly specialized (13.9.4 [temp.expl.spec]), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program. [Note: In particular, if the semantics of an expression depend on the member or base class lists of a class template specialization, the class template specialization is implicitly generated. For instance, deleting a pointer to class type depends on whether or not the class declares a destructor, and a conversion between pointers to class type depends on the inheritance relationship between the two classes involved. —end note] [Example:

      template<class T> class B { /* ... */ };
      template<class T> class D : public B<T> { /* ... */ };
    
      void f(void*);
      void f(B<int>*);
    
      void g(D<int>* p, D<char>* pp, D<double>* ppp) {
        f(p);            // instantiation of D<int> required: call f(B<int>*)
        B<char>* q = pp; // instantiation of D<char> required:
                         // convert D<char>* to B<char>*
        delete ppp;      // instantiation of D<double> required
    }
    

    end example] If a class template has been declared, but not defined, at the point of instantiation (13.8.4.1 [temp.point]), the instantiation yields an incomplete class type (6.8 [basic.types]). [Example:

      template<class T> class X;
    
      X<char> ch;      // error: incomplete type X<char>
    

    end example] [Note: Within a template declaration, a local class (11.6 [class.local]) or enumeration and the members of a local class are never considered to be entities that can be separately instantiated (this includes their default arguments, exception-specifications, and non-static data member initializers, if any). As a result, the dependent names are looked up, the semantic constraints are checked, and any templates used are instantiated as part of the instantiation of the entity within which the local class or enumeration is declared. —end note]

    The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not...

  3. Delete 13.9.2 [temp.inst] paragraph 6, moving the note and example to paragraph 1 as shown above:

  4. A class template specialization is implicitly instantiated if the class type is used in a context that requires a completely-defined object type or if the completeness of the class type might affect the semantics of the program. [Note: In particular, if the semantics of an expression depend on the member or base class lists of a class template specialization, the class template specialization is implicitly generated. For instance, deleting a pointer to class type depends on whether or not the class declares a destructor, and conversion between pointer to class types depends on the inheritance relationship between the two classes involved. —end note] [Example:

      template<class T> class B { /* ... */ };
      template<class T> class D : public B<T> { /* ... */ };
    
      void f(void*);
      void f(B<int>*);
    
      void g(D<int>* p, D<char>* pp, D<double>* ppp) {
        f(p);            // instantiation of D<int> required: call f(B<int>*)
        B<char>* q = pp; // instantiation of D<char> required:
                         // convert D<char>* to B<char>*
        delete ppp;      // instantiation of D<double> required
    }
    

    end example]

  5. Change 13.9.2 [temp.inst] paragraph 7 as follows:

  6. If the function selected by overload resolution process (12.2 [over.match]) can determine the correct function to call be determined without instantiating a class template definition, it is unspecified whether that instantiation actually takes place. [Example:...
  7. Delete 13.9.2 [temp.inst] paragraphs 8-9:

  8. If an implicit instantiation of a class template specialization is required and the template is declared but not defined, the program is ill-formed. [Example:

      template<class T> class X;
    
      X<char> ch; // error: definition of X required
    

    end example]

    The implicit instantiation of a class template does not cause any static data members of that class to be implicitly instantiated.




1484. Unused local classes of function templates

Section: 13.9.2  [temp.inst]     Status: CD4     Submitter: Johannes Schaub     Date: 2012-03-25

[Moved to DR at the November, 2014 meeting.]

Do local classes of function templates get the same treatment as member classes of class templates? In particular, is their definition only instantiated when they are required? For example,

  template<typename T> void f() {
    struct B {
      T t;
    };
  }

  int main() {
    f<void>();
  }

Implementations vary on this question.

(This question is superficially similar to the one in issue 1253. However, the entities in view in that issue can be named and defined outside the containing template and thus can be explicitly specialized, none of which is true for local classes of function templates.)

It should also be noted that the resolution of this issue should apply as well to local enumeration types.

Proposed resolution (October, 2012):

Change 13.9.2 [temp.inst] paragraph 1 as follows:

Unless a class template specialization has been explicitly instantiated (13.9.3 [temp.explicit]) or explicitly specialized (13.9.4 [temp.expl.spec]), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program. [Note: Within a template declaration, a local class or enumeration and the members of a local class are never considered to be entities that can be separately instantiated (this includes their default arguments, exception-specifications, and non-static data member initializers, if any). As a result, the dependent names are looked up, the semantic constraints are checked, and any templates used are instantiated as part of the instantiation of the entity within which the local class or enumeration is declared. —end note] The implicit instantiation of a class template specialization...

Notes from the April, 2013 meeting:

The proposed resolution interacts with N3649 (generic lambdas), adopted at this meeting, and this issue has returned to "review" status to allow any necessary changes to be made.




2041. Namespace for explicit class template specialization

Section: 13.9.4  [temp.expl.spec]     Status: CD4     Submitter: Jason Merrill     Date: 2014-11-11

[Adopted at the February, 2016 meeting.]

13.9.4 [temp.expl.spec] paragraph 2 says,

An explicit specialization shall be declared in a namespace enclosing the specialized template. An explicit specialization whose declarator-id is not qualified shall be declared in the nearest enclosing namespace of the template, or, if the namespace is inline (9.8.2 [namespace.def]), any namespace from its enclosing namespace set.

However, an explicit specialization of a class template does not have a declarator-id.

Proposed resolution (September, 2015):

Change 13.9.4 [temp.expl.spec] paragraph 2 as follows:

An explicit specialization shall be declared in a namespace enclosing the specialized template. An explicit specialization whose declarator-id or class-head-name is not qualified shall be declared in the nearest enclosing namespace of the template, or, if the namespace is inline (9.8.2 [namespace.def]), any namespace from its enclosing namespace set. Such a declaration...



1391. Conversions to parameter types with non-deduced template arguments

Section: 13.10.2  [temp.arg.explicit]     Status: CD4     Submitter: Jason Merrill     Date: 2011-09-08

[Moved to DR at the October, 2015 meeting.]

According to 13.10.2 [temp.arg.explicit] paragraph 6,

Implicit conversions (7.3 [conv]) will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter type contains no template-parameters that participate in template argument deduction. [Note: Template parameters do not participate in template argument deduction if they are explicitly specified...

But this isn't clear about when these conversions are done. Consider

    template<class T> struct A {
       typename T::N n;
    };
    template<class T> struct B { };

    template<class T, class T2>
    void foo(const A<T>& r); // #1
    template<class T>
    void foo(const B<T>& r); // #2

    void baz() {
       B<char> b;
       foo(b); // OK
       foo<char>(b); // error
    }

With the explicit template argument, the first parameter of #1 no longer participates in template argument deduction, so implicit conversions are done. If we check for the implicit conversion during the deduction process, we end up instantiating A<char>, resulting in a hard error. If we wait until later to check the conversion, we can reject #1 because T2 is not deduced and never need to consider the conversion.

But if we just accept the parameter and leave it up to normal overload resolution to reject an unsuitable candidate, that breaks this testcase:

    template<class T>
    struct A {
       typename T::N n;
    };

    template<class T>
    struct B { };

    template <class T, class... U>
    typename A<T>::value_t bar(int, T, U...);

    template <class T>
    T bar(T, T);

    void baz()
    {
       B<char> b;
       bar(b, b);
    }

Here, if deduction succeeds, we substitute in the deduced arguments of T = B<char>, U = { }, and end up instantiating A<B<char>>, which fails.

EDG and GCC currently reject the first testcase and accept the second; clang accepts both.

Notes from the October, 2012 meeting:

The position initially favored by CWG was that implicit conversions are not considered during deduction but are only applied afterwards, so the second example is ill-formed, and that the normative wording of the referenced paragraph should be moved into the note. This approach does not handle some examples currently accepted by some implementations, however; for example:

   template <class T> struct Z {
    typedef T::x xx;
   };
   template <class T> Z<T>::xx f(void *, T);
   template <class T> void f(int, T);
   struct A {} a;
   int main() {
     f(1, a); // If the implementation rules out the first overload
              // because of the invalid conversion from int to void*,
              // the error instantiating Z<A> will be avoided
   }

Additional discussion is required.

Notes from the April, 2013 meeting:

The approach needed to accept this code appears to be doing the convertibility check between deduction and substitution.

Proposed resolution (May, 2015):

  1. Change 13.10.3.2 [temp.deduct.call] paragraph 1 as follows:

  2. Template argument deduction is done by comparing each function template parameter type (call it P) that contains template-parameters that participate in template argument deduction with the type of the corresponding argument of the call (call it A) as described below. If P is a dependent type, removing references and cv-qualifiers from P gives std::initializer_list<P'> or P'[N] for some P' and N and the argument is a non-empty initializer list (9.4.5 [dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument, and in the P'[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (13.10.3.6 [temp.deduct.type]). [Example:...
  3. Delete the note in 13.10.3.2 [temp.deduct.call] paragraph 4:

  4. [Note: as specified in 13.10.2 [temp.arg.explicit], implicit conversions will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter contains no template-parameters that participate in template argument deduction. Such conversions are also allowed, in addition to the ones described in the preceding list. —end note]
  5. Add the following as a new paragraph at the end of 13.10.3.2 [temp.deduct.call]:

  6. If deduction succeeds for all parameters that contain template-parameters that participate in template argument deduction, and all template arguments are explicitly specified, deduced, or obtained from default template arguments, remaining parameters are then compared with the corresponding arguments. For each remaining parameter P with a type that was non-dependent before substitution of any explicitly-specified template arguments, if the corresponding argument A cannot be implicitly converted to P, deduction fails. [Note: Parameters with dependent types in which no template-parameters participate in template argument deduction, and parameters that became non-dependent due to substitution of explicitly-specified template arguments, will be checked during overload resolution. —end note] [Example:

      template <class T> struct Z {
        typedef typename T::x xx;
      };
      template <class T> typename Z<T>::xx f(void *, T); // #1
      template <class T> void f(int, T);                 // #2
      struct A {} a;
      int main() {
        f(1, a);                                         // OK, deduction fails for #1 because there is no conversion from int to void*
      }
    

    end example]

  7. Change 13.10.3.5 [temp.deduct.partial] paragraph 4 as follows:

  8. Each type nominated above from the parameter template and the corresponding type from the argument template are used as the types of P and A. If a particular P contains no template-parameters that participate in template argument deduction, that P is not used to determine the ordering.
  9. Change 13.10.3.6 [temp.deduct.type] paragraph 4 as follows:

  10. In most cases, the types, templates, and non-type values that are used to compose P participate in template argument deduction. That is, they may be used to determine the value of a template argument, and the value so determined must be consistent with the values determined elsewhere. In certain contexts, however, the value does not participate in type deduction, but instead uses the values of template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails. [Note: Under 13.10.3.2 [temp.deduct.call] and 13.10.3.5 [temp.deduct.partial], if P contains no template-parameters that appear in deduced contexts, no deduction is done, and so P and A need not have the same form. —end note]

This resolution also resolves issue 1847.

Additional note October, 2015:

See also issue 1939.




1809. Narrowing and template argument deduction

Section: 13.10.3  [temp.deduct]     Status: CD4     Submitter: Richard Smith     Date: 2013-11-12

[Moved to DR at the November, 2014 meeting.]

13.10.3 [temp.deduct] paragraph 9 reads,

Except as described above, the use of an invalid value shall not cause type deduction to fail. [Example: In the following example 1000 is converted to signed char and results in an implementation-defined value as specified in (7.3.9 [conv.integral]). In other words, both templates are considered even though 1000, when converted to signed char, results in an implementation-defined value.

  template <int> int f(int);
  template <signed char> int f(int);
  int i1 = f<1>(0);      // ambiguous
  int i2 = f<1000>(0);   // ambiguous

end example]

This is no longer correct, even ignoring the fact that some implementations may be able to represent the value 1000 as a signed char: integral and enumeration non-type template arguments are now converted constant expressions (13.4.3 [temp.arg.nontype] paragraph 1), and converted constant expressions disallow narrowing conversions (7.7 [expr.const] paragraph 3).

Proposed resolution (February, 2014):

Change 13.10.3 [temp.deduct] paragraph 9 as follows:

Except as described above, the use of an invalid value shall not cause type deduction to fail. [Example: In the following example, 1000 is converted to signed char and results in an implementation-defined value as specified in (7.3.9 [conv.integral]). In other words, both templates are considered even though 1000, when converted to signed char, results in an implementation-defined value assuming a signed char cannot represent the value 1000, a narrowing conversion would be required to convert the template-argument of type int to signed char, therefore substitution fails for the second template (13.4.3 [temp.arg.nontype])..

  template <int> int f(int);
  template <signed char> int f(int);
  int i1 = f<1000>(0);       // ambiguous OK
  int i2 = f<1000>(0);       // ambiguous; not narrowing

end example]




1591. Deducing array bound and element type from initializer list

Section: 13.10.3.2  [temp.deduct.call]     Status: CD4     Submitter: Peter Dimov     Date: 2012-12-01

[Moved to DR at the November, 2014 meeting.]

Currently, 13.10.3.2 [temp.deduct.call] paragraph 1 says,

Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives std::initializer_list<P'> for some P' and the argument is an initializer list (9.4.5 [dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (13.10.3.6 [temp.deduct.type]).

It would seem reasonable, however, to allow an array bound to be deduced from the number of elements in the initializer list, e.g.,

  template<int N> void g(int const (&)[N]);
  void f() {
    g( { 1, 2, 3, 4 } );
  }

Additional note (March, 2013):

The element type should also be deducible.

Proposed resolution (February, 2014):

  1. Change 13.10.3.2 [temp.deduct.call] paragraph 1 as follows:

  2. Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below. If P is a dependent type, removing references and cv-qualifiers from P gives std::initializer_list<P'> or P'[N] for some P' and N, and the argument is an a non-empty initializer list (9.4.5 [dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument, and in the P'[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (13.10.3.6 [temp.deduct.type]). [Example:

      template<class T> void f(std::initializer_list<T>);
      f({1,2,3});              // T deduced to int
      f({1,"asdf"});           // error: T deduced to both int and const char*
    
      template<class T> void g(T);
      g({1,2,3});              // error: no argument deduced for T
    
      template<class T, int N> void h(T const(&)[N]);
      h({1,2,3});              // T deduced to int, N deduced to 3
    
      template<class T> void j(T const(&)[3]);
      j({42});                 // T deduced to int, array bound not considered
    
      struct Aggr { int i; int j; };
      template<int N> void k(Aggr const(&)[N]);
      k({1,2,3});              // error: deduction fails, no conversion from int to Aggr
      k({{1},{2},{3}});        // OK, N deduced to 3
    
      template<int M, int N> void m(int const(&)[M][N]);
      m({{1,2},{3,4}});        // M and N both deduced to 2
    
      template<class T, int N> void n(T const(&)[N], T);
      n({{1},{2},{3}},Aggr()); // OK, T is Aggr, N is 3
    

    end example] For a function parameter pack...

  3. Change the penultimate bullet of 13.10.3.6 [temp.deduct.type] paragraph 5 as follows:

  4. The non-deduced contexts are:




2147. Initializer-list arguments and pack deduction

Section: 13.10.3.2  [temp.deduct.call]     Status: CD4     Submitter: Hubert Tong     Date: 2015-06-22

[Adopted at the February, 2016 meeting.]

The current wording of 13.10.3.2 [temp.deduct.call] paragraph 1 dealing with deduction for a trailing parameter pack refers to “the type” of the argument, which does not apply to an initializer list. There is implementation divergence in the handling of an example like

  #include <initializer_list>

  template <typename... T>
  void q(std::initializer_list<std::initializer_list<T>>... tt);

  void bar() { q({{0}}, {{'\0'}}); }

Proposed resolution (September, 2015):

Change 13.10.3.2 [temp.deduct.call] paragraph 1 as follows:

...For a function parameter pack that occurs at the end of the parameter-declaration-list, the type A of deduction is performed for each remaining argument of the call, is compared with taking the type P of the declarator-id of the function parameter pack as the corresponding function template parameter type. Each comparison deduction deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. When a function parameter pack appears in a non-deduced context (13.10.3.6 [temp.deduct.type]), the type of that parameter pack is never deduced. [Example:...



1705. Unclear specification of “more specialized”

Section: 13.10.3.5  [temp.deduct.partial]     Status: CD4     Submitter: Johannes Schaub     Date: 2013-06-26

[Moved to DR at the November, 2014 meeting.]

The current wording of 13.10.3.5 [temp.deduct.partial] paragraph 10 is:

If for each type being considered a given template is at least as specialized for all types and more specialized for some set of types and the other template is not more specialized for any types or is not at least as specialized for any types, then the given template is more specialized than the other template. Otherwise, neither template is more specialized than the other.

This is confusing and needs to be clarified.

Proposed resolution (September, 2013) [SUPERSEDED]:

Change 13.10.3.5 [temp.deduct.partial] paragraphs 9 and 10 as follows:

If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):

If for each type being considered a given template is at least as specialized for all types and more specialized for some set of types and the other template is not more specialized for any types or is not at least as specialized for any types, then the given template is more specialized than the other template. Otherwise, neither template is more specialized than the other. A given template is at least as specialized as another template if it is at least as specialized as the other template for all types being considered. A given template is more specialized than another template if it is at least as specialized as the other template for all types being considered, and the other template is not at least as specialized as the given template for any type being considered.

Proposed resolution (February, 2014):

Change 13.10.3.5 [temp.deduct.partial] paragraphs 9-10 as follows:

If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):

If for each type being considered a given template is at least as specialized for all types and more specialized for some set of types and the other template is not more specialized for any types or is not at least as specialized for any types, then the given template is more specialized than the other template. Otherwise, neither template is more specialized than the other. Function template F is at least as specialized as function template G if, for each pair of types used to determine the ordering, the type from F is at least as specialized as the type from G. F is more specialized than G if F is at least as specialized as G and G is not at least as specialized as F.




1847. Clarifying compatibility during partial ordering

Section: 13.10.3.6  [temp.deduct.type]     Status: CD4     Submitter: Jason Merrill     Date: 2014-01-30

[Moved to DR at the October, 2015 meeting.]

There is implementation divergence in the handling of an example like

  template<typename D> struct A { };
  template<typename T> struct Wrap1 { typedef T type; };
  template<typename T> struct Wrap2 { typedef T type; };

  template<typename T1>
  A<typename Wrap1<T1>::type>
  fn(const A<T1>& x, const A<T1>& y);

  template<typename T2, typename U>
  A<typename Wrap2<T2>::type>
  fn(const A<T2>& x, const A<U>& y);

  A<int> (*p)(const A<int>&, const A<int>&) = fn;

The implementations that accept this example do so by not comparing the return types of the two templates during partial ordering, which seems to make sense given that partial ordering would not have been performed if the candidate specializations were not indistinguishable from the perspective of overload resolution. However, the existing wording is not clear that that is how such types are be handled.

Proposed resolution (May, 2015):

This issue is resolved by the resolution of issue 1391.




2091. Deducing reference non-type template arguments

Section: 13.10.3.6  [temp.deduct.type]     Status: CD4     Submitter: Richard Smith     Date: 2015-03-05

[Adopted at the June, 2016 meeting.]

According to 13.10.3.6 [temp.deduct.type] paragraph 17,

If P has a form that contains <i>, and if the type of the corresponding value of A differs from the type of i, deduction fails.

This gives the wrong result for an example like:

  template<int &> struct X;
  template<int &N> void f(X<N>&);
  int n;
  void g(X<n> &x) { f(x); }

Here, P is X<N>, which contains <i>. The type of i is int&. The corresponding value from A is n, which is a glvalue of type int. Presumably this should be valid.

I think this rule means to say something like,

If P has a form that contains <i>, and the type of i differs from the type of the corresponding template parameter of the template named by the enclosing simple-template-id, deduction fails.

Proposed resolution (March, 2016):

Change 13.10.3.6 [temp.deduct.type] paragraph 17 as follows:

If P has a form that contains <i>, and if the type of the corresponding value of A differs from the type of i differs from the type of the corresponding template parameter of the template named by the enclosing simple-template-id, deduction fails. If P has a form that contains [i]...



1863. Requirements on thrown object type to support std::current_exception()

Section: 14.2  [except.throw]     Status: CD4     Submitter: Canada     Date: 2013-09-22

[Moved to DR at the October, 2015 meeting.]

N3690 comment CA 12

The specification of std::current_exception() in 17.9.7 [propagation] allows either referring to the exception object itself or to a copy thereof, implying that the exception object must be copyable. However, the specification of throw-expression allows throwing objects that cannot be copied, only moved. Presumably the requirements should include a non-deleted accessible copy constructor that is odr-used by a throw-expression, even if the object being thrown is moved to the exception object.

Additional note, February, 2014:

This issue was referred to CWG by EWG at the September, 2013 meeting but was overlooked at that time.

Proposed resolution (May, 2015):

  1. Change 11.4.7 [class.dtor] paragraph 11 as follows:

  2. ...A destructor is potentially invoked if it is invoked or as specified in 7.6.2.8 [expr.new] and, 11.9.3 [class.base.init], and 14.2 [except.throw]. A program is ill-formed if...
  3. Change 14.2 [except.throw] paragraph 5 as follows:

  4. When the thrown object is a class object, the constructor selected for the copy-initialization as well as the constructor selected for a copy-initialization considering the thrown object as an lvalue and the destructor shall be non-deleted and accessible, even if the copy/move operation is elided (11.4.5.3 [class.copy.ctor]). The destructor is potentially invoked (11.4.7 [class.dtor]).



1774. Discrepancy between subobject destruction and stack unwinding

Section: 14.3  [except.ctor]     Status: CD4     Submitter: Canada     Date: 2013-09-24

[Moved to DR at the November, 2014 meeting.]

N3690 comment CA 24

The current wording of 14.6.2 [except.terminate] paragraph 2 affords implementations a significant degree of freedom when exception handling results in a call to std::terminate:

In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std::terminate() is called. In the situation where the search for a handler (14.4 [except.handle]) encounters the outermost block of a function with a noexcept-specification that does not allow the exception (14.5 [except.spec]), it is implementation-defined whether the stack is unwound, unwound partially, or not unwound at all before std::terminate() is called. In all other situations, the stack shall not be unwound before std::terminate() is called.

This contrasts with the treatment of subobjects and objects constructed via delegating constructos in 14.3 [except.ctor] paragraph 2:

An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (11.9.3 [class.base.init]) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object's destructor will be invoked.

Here the destructors must be called. It would be helpful if these requirements were harmonized.

Notes from the September, 2013 meeting:

Although the Canadian NB comment principally was a request to reconsider the resolution of issue 1424, which CWG decided to retain, the comment also raised the question above, which CWG felt merited its own issue.

Proposed resolution (June, 2014):

  1. Change all of 14.3 [except.ctor], reparagraphing as follows:

  2. As control passes from the point where an exception is thrown to a handler, destructors are invoked by a process, specified in this section, called stack unwinding. If a destructor directly invoked by stack unwinding exits with an exception, std::terminate is called (14.6.2 [except.terminate]). [Note: Consequently, destructors should generally catch exceptions and not let them propagate out of the destructor. —end note]

    The destructor is invoked for all automatic objects each automatic object of class type constructed since the try block was entered. The automatic objects are destroyed in the reverse order of the completion of their construction.

    An For an object of class type of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed, the destructor is invoked for all each of its the object's fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects each subobject for which the principal constructor (11.9.3 [class.base.init]) has completed execution and the destructor has not yet begun execution. The subobjects are destroyed in the reverse order of the completion of their construction. Such destruction is sequenced before entering a handler of the function-try-block of the constructor or destructor, if any.

    Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object's destructor will be is invoked. Such destruction is sequenced before entering a handler of the function-try-block of a delegating constructor for that object, if any.

    [Note: If the object was allocated in by a new-expression (7.6.2.8 [expr.new]), the matching deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation], 7.6.2.8 [expr.new], 11.4.11 [class.free]), if any, is called to free the storage occupied by the object. end note]

    The process of calling destructors for automatic objects constructed on the path from a try block to the point where an exception is thrown is called “stack unwinding.” If a destructor called during stack unwinding exits with an exception, std::terminate is called (14.6.2 [except.terminate]). [Note: So destructors should generally catch exceptions and not let them propagate out of the destructor. —end note]

  3. Delete 14.4 [except.handle] paragraph 11:

  4. The fully constructed base classes and members of an object shall be destroyed before entering the handler of a function-try-block of a constructor for that object. Similarly, if a delegating constructor for an object exits with an exception after the non-delegating constructor for that object has completed execution, the object's destructor shall be executed before entering the handler of a function-try-block of a constructor for that object. The base classes and non-variant members of an object shall be destroyed before entering the handler of a function-try-block of a destructor for that object (11.4.7 [class.dtor]).

This resolution also resolves issue 1807.




1807. Order of destruction of array elements after an exception

Section: 14.3  [except.ctor]     Status: CD4     Submitter: Thomas Koeppe     Date: 2013-11-11

[Moved to DR at the November, 2014 meeting.]

The destruction of fully-constructed array elements when array initialization is terminated by an exception is required by 14.3 [except.ctor] paragraph 2, but the order in which they are to be destroyed is not specified. Presumably it should be in reverse order of construction.

Proposed resolution (June, 2014):

This issue is resolved by the resolution of issue 1774.




1866. Initializing variant members with non-trivial destructors

Section: 14.3  [except.ctor]     Status: CD4     Submitter: Vinny Romano     Date: 2014-02-12

[Moved to DR at the November, 2014 meeting.]

According to 14.3 [except.ctor] paragraph 2,

An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (11.9.3 [class.base.init]) has completed execution and the destructor has not yet begun execution.

This introduces a potential leak if a variant member is initialized and has a non-trivial destructor. If the assumption can't be made that such an initialized member is the active member at the time an exception occurs so that it can be destroyed, perhaps variant members of types having a non-trivial destructor should be prohibited.

Notes from the June, 2014 meeting:

CWG favored removing the exclusion of variant members from the destruction following an exception during construction, though not during destruction. If the active member of the union has changed between the initialization and destruction, the behavior is undefined.

Proposed Resolution (July, 2014):

Change 14.3 [except.ctor] paragraph 2 as follows:

An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (11.9.3 [class.base.init]) has completed execution and the destructor has not yet begun execution, except that in the case of destruction, the variant members of a union-like class are not destroyed. Similarly, if the non-delegating constructor...



2093. Qualification conversion for pointer-to-member handler matching

Section: 14.4  [except.handle]     Status: CD4     Submitter: David Majnemer     Date: 2015-03-06

[Adopted at the February, 2016 meeting.]

The criteria for matching an exception handler in 14.4 [except.handle] paragraph 3 include a qualification conversion for a handler of pointer or reference to pointer type but not for a handler of pointer-to-member type. However, current implementations permit such conversions.

Proposed resolution (October, 2015):

Change 14.4 [except.handle] bullet 3.3 as follows:

A handler is a match for an exception object of type E if

Notes from the October, 2015 meeting:

This resolution should not be adopted as a Defect Report, only a change in the working paper for future revisions of the Standard, because it could silently change the behavior of well-defined programs in implementations that conform to the existing wording.




92. Should exception-specifications be part of the type system?

Section: 14.5  [except.spec]     Status: CD4     Submitter: Jonathan Schilling     Date: 2 Feb 1999

[Adopted at the October, 2015 meeting as P0012R1.]

It was tentatively agreed at the Santa Cruz meeting that exception specifications should fully participate in the type system. This change would address gaps in the current static checking of exception specifications such as

    void (*p)() throw(int);
    void (**pp)() throw() = &p;   // not currently an error

This is such a major change that it deserves to be a separate issue.

See also issues 25, 87, and 133.

Additional note (March, 2013):

The advent of the noexcept operator makes this issue more relevant in C++11.

Notes from the April, 2013 meeting:

CWG feels that a paper on this topic is needed before any action can be taken.

Notes from the September, 2013 meeting:

CWG feels that EWG would be a better venue for this issue. Possible options might include removal of dynamic-exception-specifications and incorporation of the binary noexcept status in the type system.

Rationale (February, 2014):

EWG determined that no action should be taken on this issue.

Additional note, April, 2015:

EWG has expressed interest in further exploring this issue (see EWG issue 169), so it has been returned to "extension" status. See also issues 1946 and 2010.




1351. Problems with implicitly-declared exception-specifications

Section: 14.5  [except.spec]     Status: CD4     Submitter: Richard Smith     Date: 2011-08-16

[Moved to DR at the November, 2014 meeting.]

The determination of the exception-specification for an implicitly-declared special member function, as described in 14.5 [except.spec] paragraph 14, does not take into account the fact that nonstatic data member initializers and default arguments in default constructors can contain throw-expressions, which are not part of the exception-specification of any function that is “directly invoked” by the implicit definition. Also, the reference to “directly invoked” functions is not restricted to potentially-evaluated expressions, thus possibly including irrelevant exception-specifications.

Additional note (August, 2012):

The direction established by CWG for resolving this issue was to consider functions called from default arguments and non-static data member initializers in determining the exception-specification. This leads to a problem with ordering: because non-static data member initializers can refer to members declared later, their effect cannot be known until the end of the class. However, a non-static data member initializer could possibly refer to an implicitly-declared constructor, either its own or that of an enclosing class.

Proposed resolution (October, 2012) [SUPERSEDED]:

  1. Add the following two new paragraphs and make the indicated changes to 14.5 [except.spec] paragraph 14:

  2. A set of potential exceptions may contain types and the special value “any.” The set of potential exceptions of an expression is the union of all sets of potential exceptions of each potentially-evaluated subexpression e:

    The set of potential exceptions of a function f of some class X, where f is an inheriting constructor or an implicitly-declared special member function, is defined as follows:

    An inheriting constructor (_N4527_.12.9 [class.inhctor]) and an implicitly declared implicitly-declared special member function (11.4.4 [special]) have an are considered to have an implicit exception-specification. If f is an inheriting constructor or an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f's implicit definition; f allows all exceptions if any function it directly invokes allows all exceptions, and f has the exception-specification noexcept(true) if every function it directly invokes allows no exceptions. The implicit exception-specification is noexcept(false) if the set of potential exceptions of the function contains “any;” otherwise, if that set contains at least one type, the implicit exception-specification specifies each type T contained in the set; otherwise, the implicit exception-specification is noexcept(true). [Note: An instantiation of an inheriting constructor template has an implied exception-specification as if it were a non-template inheriting constructor. —end note] [Example:

      struct A {
        A();
        A(const A&) throw();
        A(A&&) throw();
        ~A() throw(X);
      };
      struct B {
        B() throw();
        B(const B&) throw();
        B(B&&, int = (throw Y(), 0)) throw(Y) noexcept;
        ~B() throw(Y);
      };
      struct D : public A, public B {
          // Implicit declaration of D::D();
          // Implicit declaration of D::D(const D&) noexcept(true);
          // Implicit declaration of D::D(D&&) throw(Y);
          // Implicit declaration of D::~D() throw(X, Y);
      };
    

    Furthermore, if...

  3. Change 7.6.2.7 [expr.unary.noexcept] paragraph 3 as follows:

  4. The result of the noexcept operator is false if in a potentially-evaluated context the set of potential exceptions of the expression (14.5 [except.spec]) would contain contains “any” or at least one type and true otherwise.

    Otherwise, the result is true.

(This resolution also resolves issues 1356 and 1465.)

Additional note (October, 2012):

The preceding wording has been modified from the version that was reviewed following the October, 2012 meeting and thus has been returned to "review" status.

Additional note (March, 2013):

It has been suggested that it might be more consistent with other parts of the language, and particularly in view of the deprecation of dynamic-exception-specifications, if a potentially-throwing non-static data member initializer simply made an implicit constructor noexcept(false) instead of giving it a set of potential exception types.

Additional note, April, 2013:

One problem with the approach suggested in the preceding note would be something like the following example:

  struct S {
    virtual ~S() throw(int);
  };
  struct D: S { };

This approach would make the example ill-formed, because the derived class destructor would be declared to throw types not permitted by the base class destructor's exception-specification. A further elaboration on the suggestion above that would not have this objection would be to define all dynamic-exception-specifications as simply equivalent to noexcept(false).

(See also issue 1639.)

Additional note, April, 2013:

The version of this resolution approved in Bristol assumed the underlying text of the C++11 IS; however, the wording of 14.5 [except.spec] paragraph 14 has been changed by previous resolutions, so this and the related issues are being returned to "review" status.

Proposed resolution, February, 2014 [SUPERSEDED]:

  1. Change 14.5 [except.spec] paragraph 5 as follows:

  2. If a virtual function has an exception-specification, all declarations, including the definition, of any function that overrides that virtual function in any derived class shall only allow exceptions that are allowed by the exception-specification of the base class virtual function, unless the overriding function is defined as deleted. [Example:...
  3. Add the following two new paragraphs and change 14.5 [except.spec] paragraph 14 as indicated:

  4. A set of potential exceptions may contain types and the special value “any”. The set of potential exceptions of an expression is the union of all sets of potential exceptions of each potentially-evaluated subexpression e:

    The set of potential exceptions of an implicitly-declared special member function f of some class X is defined as follows:

    An inheriting constructor (_N4527_.12.9 [class.inhctor]) and an implicitly-declared special member function (11.4.4 [special]) have are considered to have an implicit exception-specification. If f is an inheriting constructor or an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f's implicit definition; f allows all exceptions if any function it directly invokes allows all exceptions, and f has the exception-specification noexcept(true) if every function it directly invokes allows no exceptions. [Note: It follows that f has the exception-specification noexcept(true) if it invokes no other functions. —end note] [Note: An instantiation of an inheriting constructor template has an implied exception-specification as if it were a non-template inheriting constructor. —end note] The implicit exception-specification is noexcept(false) if the set of potential exceptions of the special member function contains “any”; otherwise, if that set contains at least one type, the implicit exception-specification specifies each type T contained in the set; otherwise, the implicit exception-specification is noexcept(true). [Example:

      struct A {
        A();
        A(const A&) throw();
        A(A&&) throw();
        ~A() throw(X);
      };
      struct B {
        B() throw();
        B(const B&) = default; // Declaration of B::B(const B&) noexcept(true) throw();
        B(B&&, int = (throw Y(), 0)) throw(Y) noexcept;
        ~B() throw(Y);
      };
      struct D : public A, public B {
          // Implicit declaration of D::D();
          // Implicit declaration of D::D(const D&) noexcept(true);
          // Implicit declaration of D::D(D&&) throw(Y);
          // Implicit declaration of D::~D() throw(X, Y);
      };
    

    Furthermore...

  5. Change 7.6.2.7 [expr.unary.noexcept]paragraph 3 as follows:

  6. The result of the noexcept operator is false true if in a potentially-evaluated context the set of potential exceptions of the expression (14.5 [except.spec]) would contain is empty, and false otherwise.

    Otherwise, the result is true.

    (This resolution also resolves issues 1356, 1465, and 1639.)

Additional note, May, 2014:

The current version of the proposed resolution only defines the set of potential exceptions for special member functions; since an inheriting constructor is not a special member function, the exception-specification for an inheriting constructor is no longer specified.

In addition, the structure of the specification of the set of potential exceptions of an expression is unclear. If the bulleted list is intended to be the definition of general statement (“union of all sets of potential exceptions...”), it's incomplete because it doesn't consider exceptions thrown by the evaluation of function arguments in a call, just the exceptions thrown by the function itself; if it's intended to be a list of exceptions to the general rule, the rule about core constant expressions doesn't exclude unselected subexpressions that might throw, so those exceptions are incorrect included in the union.

The issue has been returned to "review" status to allow discussion of these points.

See also the discussion in messages 25290 through 25293.

Proposed resolution (June, 2014):

  1. Change 14.5 [except.spec] paragraph 5 as follows:
  2. If a virtual function has an exception-specification, all declarations, including the definition, of any function that overrides that virtual function in any derived class shall only allow exceptions that are allowed by the exception-specification of the base class virtual function, unless the overriding function is defined as deleted. [Example:...
  3. Add the following new paragraphs following 14.5 [except.spec] paragraph 13:

  4. An exception-specification is not considered part of a function's type.

    A potential exception of a given context is either a type that might be thrown as an exception or a pseudo-type, denoted by “any”, that represents the situation where an exception of an arbitrary type might be thrown. A subexpression e1 of an expression e is an immediate subexpression if there is no subexpression e2 of e such that e1 is a subexpression of e2.

    The set of potential exceptions of a function, function pointer, or member function pointer f is defined as follows:

    The set of potential exceptions of an expression e is empty if e is a core constant expression (7.7 [expr.const]). Otherwise, it is the union of the sets of potential exceptions of the immediate subexpressions of e, including default argument expressions used in a function call, combined with a set S defined by the form of e, as follows:

    [Example: Given the following declarations

      void f() throw(int);
      void g();
      struct A { A(); };
      struct B { B() noexcept; };
      struct D() { D() throw (double); };
    

    the set of potential exceptions for some sample expressions is:

    end example]

    Given a member function f of some class X, where f is an inheriting constructor (_N4527_.12.9 [class.inhctor]) or an implicitly-declared special member function, the set of potential exceptions of the implicitly-declared member function f consists of all the members from the following sets:

  5. Change 14.5 [except.spec] paragraph 14 as follows:

  6. An inheriting constructor (_N4527_.12.9 [class.inhctor]) and an implicitly declared implicitly-declared special member function (11.4.4 [special]) are considered to have an implicit exception-specification, as follows, where f is the member function and S is the set of potential exceptions of the implicitly-declared member function f:.

    If f is an inheriting constructor or an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f's implicit definition; f allows all exceptions if any function it directly invokes allows all exceptions, and f has the exception-specification noexcept(true) if every function it directly invokes allows no exceptions. [Note: It follows that f has the exception-specification noexcept(true) if it invokes no other functions. —end note] [Note: An instantiation of an inheriting constructor template has an implied exception-specification as if it were a non-template inheriting constructor. —end note] [Example:

      struct A {
        A(int = (A(5), 0)) noexcept;
        A(const A&) throw();
        A(A&&) throw();
        ~A() throw(X);
      };
      struct B {
        B() throw();
        B(const B&) = default; // Declaration of B::B(const B&) noexcept(true)
        B(B&&, int = (throw Y(), 0)) throw(Y) noexcept;
        ~B() throw(Y);
      };
      int n = 7;
      struct D : public A, public B {
        int * p = new (std::nothrow) int[n];
        // Implicit declaration of D::D() throw(X, std::bad_array_new_length);
        // Implicit declaration of D::D();
        // Implicit declaration of D::D(const D&) noexcept(true);
        // Implicit declaration of D::D(D&&) throw(Y);
        // Implicit declaration of D::~D() throw(X, Y);
      };
    
  7. Change 7.6.2.7 [expr.unary.noexcept] paragraph 3 as follows:

  8. The result of the noexcept operator is false true if in a potentially-evaluated context the set of potential exceptions of the expression would contain is empty, and false otherwise.

    Otherwise, the result is true.

This resolution also resolves issues 1356, 1465, and 1639.




1356. Exception specifications of copy assignment operators with virtual bases

Section: 14.5  [except.spec]     Status: CD4     Submitter: Sean Hunt     Date: 2011-08-16

[Moved to DR at the November, 2014 meeting.]

It is unspecified if an implicitly-defined copy assignment operator directly invokes the copy assignment operators of virtual bases. The exception-specification of such a copy assignment operator is thus also unspecified. The specification in 14.5 [except.spec] paragraph 14 should explicitly include the exceptions from the copy assignment operators of virtual base classes, regardless of whether the implicit definition actually invokes the virtual base assignment operators or not.

Proposed resolution (June, 2014):

This issue is resolved by the resolution of issue 1351.




1639. exception-specifications and pointer/pointer-to-member expressions

Section: 14.5  [except.spec]     Status: CD4     Submitter: Steve Adamczyk     Date: 2013-02-12

[Moved to DR at the November, 2014 meeting.]

The current specification is not clear whether the exception-specification for a function is propagated to the result of taking its address. For example:

  template<class T> struct A {
    void f() noexcept(false) {}
    void g() noexcept(true) {}
  };

  int main() {
    if (noexcept((A<short>().*(&A<short>::f))()))
      return 1;

    if (!noexcept((A<long>().*(&A<long>::g))()))
      return 1;

     return 0;
  }

There is implementation variance on whether main returns 0 or 1 for this example. (It also appears that taking the address of a member function of a class template requires instantiating its exception-specification, but it is not clear whether the Standard currently specifies this or not.)

(See also issues 92 and 1351.)

Proposed resolution (June, 2013):

This issue is resolved by the proposed resolution of issue 1351.




1777. Empty pack expansion in dynamic-exception-specification

Section: 14.5  [except.spec]     Status: CD4     Submitter: Richard Smith     Date: 2013-09-24

[Moved to DR at the November, 2014 meeting.]

According to 13.7.4 [temp.variadic] paragraph 6, describing an empty pack expansion,

When N is zero, the instantiation of the expansion produces an empty list. Such an instantiation does not alter the syntactic interpretation of the enclosing construct, even in cases where omitting the list entirely would otherwise be ill-formed or would result in an ambiguity in the grammar.

This leaves open the question of whether something like

  template<typename...T> void f() throw(T...);

should be considered to have a non-throwing exception-specification when T... is empty. The definition in 14.5 [except.spec] paragraph 12 appears to be syntactic regarding dynamic-exception-specifications:

An exception-specification is non-throwing if it is of the form throw(), noexcept, or noexcept(constant-expression ) where the constant-expression yields true. A function with a non-throwing exception-specification does not allow any exceptions.

It seems evident, however, that a dynamic-exception-specification with an empty pack expansion “does not allow any exceptions.”

Proposed resolution (February, 2014):

Change 14.5 [except.spec] paragraph 12 as follows:

A function with no exception-specification or with an exception-specification of the form noexcept(constant-expression ) where the constant-expression yields false allows all exceptions. An exception-specification is non-throwing if it is of the form throw(), noexcept, or noexcept(constant-expression ) where the a dynamic-exception-specification whose set of adjusted types is empty (after any packs are expanded) or a noexcept-specification whose constant-expression is either absent or yields true. A function with a non-throwing exception-specification does not allow any exceptions.



1946. exception-specifications vs pointer dereference

Section: 14.5  [except.spec]     Status: CD4     Submitter: Mike Miller     Date: 2014-06-21

[Adopted at the October, 2015 meeting as P0012R1.]

The resolution of issue 1351 results in the following:

  void (*p)() throw(int);
  void (&r)() throw(int) = *p;  // ill-formed

The reason is that the set of potential exceptions for an indirection is “any” instead of maintaining the known potential exceptions of the operand. It would seem to be reasonable to propagate the set in such cases.

A similar issue arises with function template argument deduction:

  template<typename T> T& f(T* p);
  void (*p)() throw(int);
  void (&r)() throw(int) = f(p);  // ill-formed

See also issues 2010, 1995, 1975, and 1798.

Additional note, May, 2015:

See also issue 92 and EWG issue 169.




1975. Permissible declarations for exception-specifications

Section: 14.5  [except.spec]     Status: CD4     Submitter: Richard Smith     Date: 2014-07-18

[Moved to DR at the October, 2015 meeting.]

The declarations in which an exception-specification may appear is not completely clear from the current wording of 14.5 [except.spec] paragraph 2.

Suggested resolution:

Change 14.5 [except.spec] paragraph 2 as follows:

An exception-specification shall appear only on a function declarator for a function type, pointer to function type, reference to function type, or pointer to member function type that is the top-level type of a declaration or definition of a function, variable, or non-static data member, or on such a type appearing as a parameter or return type in such a function declarator. An exception-specification shall not appear in a typedef declaration or alias-declaration. [Example:...

See also issues 2010, 1995, 1946, and 1798.

Proposed resolution (May, 2015):

Change 14.5 [except.spec] paragraph 2 as follows:

An exception-specification shall appear only on within a lambda-declarator or within a function declarator for a function type, pointer to function type, reference to function type, or pointer to member function type that is the top-level type of a declaration or definition, or on such a type appearing as a parameter or return type in a function declarator. An exception-specification shall not appear in a typedef declaration or alias-declaration. of a function, variable, or non-static data member. It shall appear only on the top-level declarator, on the declarator of one of its parameter-declarations (if any), or on its return type. [Example:...



1995. exception-specifications and non-type template parameters

Section: 14.5  [except.spec]     Status: CD4     Submitter: Gabriel Dos Reis     Date: 2014-09-03

[Adopted at the October, 2015 meeting as P0012R1.]

In the following example,

   struct A {
     template<class T>
       T foo(T t) noexcept { return t; }
     };

     template<class T, int (T::*fptr)(int)> struct S {
       A a;
       S() noexcept(noexcept((a.*fptr)(1))) {}
     };

     int foo() {
       return noexcept(S<A,&A::foo>());
     }

what should foo return? The question hinges upon whether the noexcept property of the template argument is preserved or lost in the process of substitution.

See also issues 2010, 1975, 1946, and 1798.




2010. exception-specifications and conversion operators

Section: 14.5  [except.spec]     Status: CD4     Submitter: Richard Smith     Date: 2014-09-26

[Adopted at the October, 2015 meeting as P0012R1.]

It is not possible to provide an exception-specification for a conversion-type-id, which prevents the result from being used in assignment or initialization of a function pointer with a non-throwing exception-specification. This seems unnecessarily restrictive.

See also issues 1995, 1975, 1946, and 1798.

Additional note, May, 2015:

See also issue 92 and EWG issue 169.




2039. Constant conversions to bool

Section: 14.5  [except.spec]     Status: CD4     Submitter: Richard Smith     Date: 2014-11-09

[Adopted at the February, 2016 meeting.]

According to 14.5 [except.spec] paragraph 1,

In a noexcept-specification, the constant-expression, if supplied, shall be a constant expression (7.7 [expr.const]) that is contextually converted to bool ( 7.3 [conv]).

This allows the expression to have any type that can be converted to bool, which is too lenient; it should instead say something like “a converted constant expression of type bool (7.7 [expr.const]).” This would include the conversion to bool in the determination of whether the expression is constant or not and would also disallow narrowing conversions.

A similar consideration applies to static_assert (9.1 [dcl.pre] paragraph 6), which should probably be recast to something like, “an expression that is a constant expression (7.7 [expr.const]) after contextual conversion to bool (7.3 [conv]).”

Proposed resolution (September, 2015):

  1. Change 7.7 [expr.const] paragraph 4 as follows:

  2. ...binds directly. [Note: such expressions may be used in new expressions (7.6.2.8 [expr.new]), as case expressions (8.5.3 [stmt.switch]), as enumerator initializers if the underlying type is fixed (9.7.1 [dcl.enum]), as array bounds (9.3.4.5 [dcl.array]), and as non-type template arguments (13.4 [temp.arg]). —end note] A contextually converted constant expression of type bool is an expression, contextually converted to bool (7.3 [conv]), where the converted expression is a constant expression and the conversion sequence contains only the conversions above.
  3. Change 9.1 [dcl.pre] paragraph 6 as follows:

  4. In a static_assert-declaration, the constant-expression shall be a contextually converted constant expression of type bool (7.7 [expr.const]) that can be contextually converted to bool (7.3 [conv]). If the value of the expression when so converted is true...
  5. Change 14.5 [except.spec] paragraph 1 as follows:

  6. In a noexcept-specification, the constant-expression, if supplied, shall be a contextually converted constant expression of type bool (7.7 [expr.const]) that is contextually converted to bool ( 7.3 [conv]). A ( token that follows noexcept...



2047. Coordinating “throws anything” specifications

Section: 14.5  [except.spec]     Status: CD4     Submitter: Richard Smith     Date: 2014-11-18

[Adopted at the February, 2016 meeting.]

The resolutions of issues 330 and 1351 use different terminology for an exception specification that can throw anything: the former refers to a “(conceptual) set of all types,” while the latter uses a “pseudo-type, denoted by 'any'.” These should be unified.

Proposed resolution (October, 2015) [SUPERSEDED]:

  1. Change 14.5 [except.spec] paragraph 13 as follows:

  2. A The set of potential exceptions of a given context is either a type a set of types that might be thrown as an exception or a pseudo-type, denoted by “any”, that represents the situation where an exception of an arbitrary type might be thrown; the (conceptual) set of all types is used to denote that an exception of arbitrary type might be thrown. A subexpression e1 of an expression e is an immediate subexpression if there is no subexpression e2 of e such that e1 is a subexpression of e2.
  3. Delete 14.5 [except.spec] paragraph 14:

  4. The set of potential exceptions of a function, function pointer, or member function pointer f is defined as follows:

  5. Change 14.5 [except.spec] paragraph 15 as follows:

  6. The set of potential exceptions of an expression e is empty if e is a core constant expression (7.7 [expr.const]). Otherwise, it is the union of the sets of potential exceptions of the immediate subexpressions of e, including default argument expressions used in a function call, combined with a set S defined by the form of e, as follows:

    [Example: Given the following declarations

      void f() throw(int);
      void g();
      struct A { A(); };
      struct B { B() noexcept; };
      struct D() { D() throw (double); };
    

    the set of potential exceptions for some sample expressions is:

    end example]

  7. Change 14.5 [except.spec] paragraph 16 as follows:

  8. Given a A member function f of some class X, where f is an inheriting constructor (_N4527_.12.9 [class.inhctor]) or an implicitly-declared special member function, the set of potential exceptions of the implicitly-declared member function f is considered to have an implicit exception specification that consists of all the members from the following sets...
  9. Delete the normative portion of 14.5 [except.spec] paragraph 17 and merge the note and example into the preceding paragraph, as follows:

  10. An inheriting constructor (_N4527_.12.9 [class.inhctor]) and an implicitly-declared special member function ( 11.4.4 [special]) are considered to have an implicit exception specification, as follows, where S is the set of potential exceptions of the implicitly-declared member function:

    [Note: An instantiation of an inheriting constructor template...

Additional note (November, 2015):

The base text underlying the preceding proposed resolution was changed at the October, 2015 meeting by the adoption of paper P0136R1. As a result, this issue has been returned to "drafting" status to allow reconciliation of the two sets of changes.

Proposed resolution (January, 2016):

  1. Change 14.5 [except.spec] paragraph 12 as follows:

  2. A The set of potential exceptions of a given context is either a type a set of types that might be thrown as an exception or a pseudo-type, denoted by “any”, that represents the situation where an exception of an arbitrary type might be thrown; the (conceptual) set of all types is used to denote that an exception of arbitrary type might be thrown. A subexpression e1 of an expression e is an immediate subexpression if there is no subexpression e2 of e such that e1 is a subexpression of e2.
  3. Delete 14.5 [except.spec] paragraph 13:

  4. The set of potential exceptions of a function, function pointer, or member function pointer f is defined as follows:

  5. Change 14.5 [except.spec] paragraph 14 as follows:

  6. The set of potential exceptions of an expression e is empty if e is a core constant expression (7.7 [expr.const]). Otherwise, it is the union of the sets of potential exceptions of the immediate subexpressions of e, including default argument expressions used in a function call, combined with a set S defined by the form of e, as follows:

    [Example: Given the following declarations

      void f() throw(int);
      void g();
      struct A { A(); };
      struct B { B() noexcept; };
      struct D { D() throw (double); };
    

    the set of potential exceptions for some sample expressions is:

    end example]

  7. Change 14.5 [except.spec] paragraph 16 as follows:

  8. Given an An implicitly-declared special member function f of some class X, the set of potential exceptions of the implicitly-declared special member function f is considered to have an implicit exception specification that consists of all the members from the following sets:...
  9. Delete the normative text of 14.5 [except.spec] paragraph 17 and merge the example with the preceding paragraph:

  10. An implicitly-declared special member function ( 11.4.4 [special]) is considered to have an implicit exception specification, as follows, where S is the set of potential exceptions of the implicitly-declared special member function:

    [Example:...




2098. Is uncaught_exceptions() per-thread?

Section: 14.6.3  [except.uncaught]     Status: CD4     Submitter: Ville Voutilainen     Date: 2015-03-14

[Adopted at the February, 2016 meeting.]

The current specification of std::uncaught_exceptions() (14.6.3 [except.uncaught] paragraph 1) does not, but should, state that it is the number of uncaught exceptions in the current thread.

Proposed resolution (September, 2015):

Change 14.6.3 [except.uncaught] paragraph 1 as follows:

...The function std::uncaught_exceptions() (17.9.6 [uncaught.exceptions]) returns the number of uncaught exceptions in the current thread.



2001. non-directive is underspecified

Section: Clause 15  [cpp]     Status: CD4     Submitter: Richard Smith     Date: 2014-09-10

[Adopted at the February, 2016 meeting.]

The Standard needs to describe non-directives more fully, e.g., whether they are ill-formed, conditionally-supported, etc. Since they are, in fact, directives, a different name might also be in order.

Proposed resolution (October, 2015):

  1. Change Clause 15 [cpp] paragraph 1 as follows:

  2. Change Clause 15 [cpp] paragraph 2 as follows:

  3. A text line shall not begin with a # preprocessing token. A non-directive conditionally-supported-directive shall not begin with any of the directive names appearing in the syntax. A conditionally-supported-directive is conditionally supported with implementation-defined semantics.



1955. #elif with invalid controlling expression

Section: 15.2  [cpp.cond]     Status: CD4     Submitter: Jonathan Wakely     Date: 2014-06-25     Liaison: WG14

[Adopted at the February, 2016 meeting.]

An #elif is treated differently from an #else followed immediately by an #if: assuming the preceding #if's condition was true, the condition in the second #if need not be a valid expression, while the condition in the #elif directive, though not evaluated, still must be syntactically correct.

C DR 412 changes that for C; C++ should make the corresponding change.

Proposed resolution (November, 2014):

Change 15.2 [cpp.cond] paragraph 6 as follows:

Each directive's condition is checked in order. If it evaluates to false (zero), the group that it controls is skipped: directives are processed only through the name that determines the directive in order to keep track of the level of nested conditionals; the rest of the directives' preprocessing tokens are ignored, as are the other preprocessing tokens in the group. Only the first group whose control condition evaluates to true (nonzero) is processed; any following groups are skipped and their controlling directives are processed as if they were in a group that is skipped. If none of the conditions...



2038. Document C++14 incompatibility of new braced deduction rule

Section: C.4  [diff.cpp14]     Status: CD4     Submitter: Jonathan Caves     Date: 2014-11-08

[Adopted at the February, 2016 meeting.]

The adoption of document N3922 at the November, 2014 meeting introduces a new incompatibility that should be documented in Annex Clause Annex C [diff]:

  int x1 = 1;
  auto x2{x1};    // Is now int before was std::initializer<int>

Proposed resolution (September, 2015):

Insert the following as a new section following C.4.2 [diff.cpp14.lex]:

C.4.2 Clause 7: Declarations

[diff.cpp14.dcl]

9.2.9.7 [dcl.spec.auto]
Change: auto deduction from braced-init-list
Rationale: More intuitive deduction behavior
Effect on original feature: Valid C++14 code may fail to compile or may change meaning in this International Standard. For example:

  auto x1{1};    // Was std::initializer_list<int>, now int
  auto x2{1, 2}; // Was std::initializer_list<int>, now ill-formed



2031. Missing incompatibility for &&

Section: C.6.3  [diff.cpp03.expr]     Status: CD4     Submitter: Melissa Mears     Date: 2014-10-31

[Moved to DR at the October, 2015 meeting.]

The introduction of rvalue references in C++11 changed the interpretation of some previously well-formed examples such as the following:

  struct Struct { template <typename T> operator T(); };
  bool example_1 = new int && false;               // #1
  bool example_2 = &Struct::operator int && false; // #2

Previously the && was interpreted as an operator, while it is now part of a type-name. However, this change is not mentioned in Annex Clause Annex C [diff].

Proposed resolution (May, 2015):

Add the following as a new subsection in C.6.3 [diff.cpp03.expr]:

7.6.15 [expr.log.or]
Change: && is valid in a type-name
Rationale: Required for new features
Effect on original feature: Valid C++ 2003 code may fail to compile or produce different results in this International Standard, as the following example illustrates:

  bool b1 = new int && false;           // previously false, now ill-formed
  struct S { operator int(); };
  bool b2 = &S::operator int && false;  // previously false, now ill-formed



2184. Missing C compatibility entry for decrement of bool

Section: C.7.4  [diff.expr]     Status: CD4     Submitter: CWG     Date: 2015-10-21

C permits decrementing a _Bool operand, but C++ has never allowed that operation on bool operands. Since there is some code that maps those types together for cross-language compatibility, it might be worth mentioning the different behavior (and, with the adoption of P0002R1, for increment as well).

Notes from the June, 2016 meeting:

This issue will be handled editorially and is placed in "review" status until that point.






Issues with "CD5" Status


1836. Use of class type being defined in trailing-return-type

Section: _N4567_.5.1.1  [expr.prim.general]     Status: CD5     Submitter: Mike Miller     Date: 2014-01-17

[Voted into the WP at the July, 2017 meeting.]

According to _N4567_.5.1.1 [expr.prim.general] paragraph 3,

Unlike the object expression in other contexts, *this is not required to be of complete type for purposes of class member access (7.6.1.5 [expr.ref]) outside the member function body.

Is this special treatment of member access expressions intended to apply only to *this, or does it apply to other ways of specifying the class being defined in the object expression? For example,

  struct S {
    int i;
    auto f1() -> decltype((*this).i);      // okay
    auto f2(S& This) -> decltype(This.i);  // okay?
    auto f3() -> decltype(((S*)0)->i);     // okay?
  };

There is implementation divergence on this question.

If the intent is to allow object expressions other than *this to have the current class type, this specification should be moved from _N4567_.5.1.1 [expr.prim.general] to 7.6.1.5 [expr.ref] paragraph 2, which is where the general requirement for complete object expression types is found.

On a related point, the note immediately following the above-cited passage is not quite correct:

[Note: only class members declared prior to the declaration are visible. —end note]

This does not apply when the member is a “member of an unknown specialization,” per 13.8.3.2 [temp.dep.type] bullet 5.3 sub-bullet 1; for example,

  template<typename T> struct S : T {
    auto f() -> decltype(this->x);
  };

Here x is presumed to be a member of the dependent base T and is not “declared prior to the declaration” that refers to it.

Proposed resolution (May, 2017):

  1. Change 7.5.2 [expr.prim.this] paragraph 2 as follows:

  2. Unlike the object expression in other contexts, *this is not required to be of complete type for purposes of class member access (7.6.1.5 [expr.ref]) outside the member function body. [Note: Only class members declared prior to the declaration are visible. —end note] [Note: In a trailing-return-type, the class being defined is not required to be complete for purposes of class member access (7.6.1.5 [expr.ref]). Class members declared later are not visible. [Example:

      struct A {
        char g();
        template<class T> auto f(T t) -> decltype(t + g())
        { return t + g(); }
      };
      template auto A::f(int t) -> decltype(t + g());
    

    end example] end note]

  3. Change 7.6.1.5 [expr.ref] paragraph 2 as follows, splitting the paragraph into two paragraphs as indicated:

  4. For the first option (dot) the first expression shall be a glvalue having complete class type. For the second option (arrow) the first expression shall be a prvalue having pointer to complete class type. In both cases, the class type shall be complete unless the class member access appears in the definition of that class. [Note: If the class is incomplete, lookup in the complete class type is required to refer to the same declaration (6.4.7 [basic.scope.class]). —end note]

    The expression E1->E2 is converted to the equivalent form (*(E1)).E2; the remainder...




2289. Uniqueness of structured binding names

Section: _N4868_.6.4.1  [basic.scope.declarative]     Status: CD5     Submitter: Richard Smith     Date: 2016-07-20

[Accepted as a DR at the February, 2019 meeting.]

The current wording is not clear regarding examples like the following:

  struct A { int x; } a;
  struct B {} b; template<int> int &get(const B&);
  struct C {}; int c[1];
  auto [A] = a; // ok? 
  auto [B] = b; // ok? 
  auto [C] = c; // ok? 

Notes from the April, 2017 teleconference:

Structured bindings have no C compatibility implications, so the tag/nontag treatment need not apply.

Proposed resolution (February, 2019):

Change _N4868_.6.4.1 [basic.scope.declarative] paragraph 4 as follows:

Given a set of declarations in a single declarative region, each of which specifies the same unqualified name,




2164. Name hiding and using-directives

Section: _N4868_.6.4.10  [basic.scope.hiding]     Status: CD5     Submitter: Richard Smith     Date: 2015-07-26

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

Consider the following example:

   const int i = -1;
   namespace T {
     namespace N { const int i = 1; }
     namespace M {
       using namespace N;
       int a[i];
     }
   }

According to 6.5.3 [basic.lookup.unqual], lookup for i finds T::N::i and stops. However, according to _N4868_.6.4.10 [basic.scope.hiding] paragraph 1, the appearance of T::N::i in namespace T does not hide ::i, so both declarations of i are visible in the declaration of a.

It seems strange that we specify this name hiding rule in two different ways in two different places, but they should at least be consistent.

On a related note, the wording in 6.5.3 [basic.lookup.unqual] paragraph 2, “as if they were declared in the nearest enclosing namespace...” could be confusing with regard to the “declared in the same scope” provisions of _N4868_.6.4.10 [basic.scope.hiding].

Proposed resolution (November, 2017)

Change _N4868_.6.4.10 [basic.scope.hiding] paragraphs 1 and 2 as follows:

A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class (6.5.2 [class.member.lookup]) A declaration of a name in a nested declarative region hides a declaration of the same name in an enclosing declarative region; see _N4868_.6.4.1 [basic.scope.declarative] and 6.5.3 [basic.lookup.unqual].

A class name (11.3 [class.name]) or enumeration name (9.7.1 [dcl.enum]) can be hidden by the name of a variable, data member, function, or enumerator declared in the same scope. If a class name (11.3 [class.name]) or enumeration name (9.7.1 [dcl.enum]) and a variable, data member, function, or enumerator are declared in the same scope declarative region (in any order) with the same name (excluding declarations made visible via using-directives (6.5.3 [basic.lookup.unqual])), the class or enumeration name is hidden wherever the variable, data member, function, or enumerator name is visible.




682. Missing description of lookup of template aliases

Section: _N4868_.6.5.6  [basic.lookup.classref]     Status: CD5     Submitter: Daveed Vandevoorde     Date: 1 March, 2008

[Accepted as a DR at the July, 2019 meeting.]

_N4868_.6.5.6 [basic.lookup.classref] does not mention template aliases as the possible result of the lookup but should do so.

Proposed resolution, June, 2019:

Change _N4868_.6.5.6 [basic.lookup.classref] paragraph 1 as follows:

In a class member access expression (7.6.1.5 [expr.ref]), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (13.3 [temp.names]) or a less-than operator. The identifier is first looked up in the class of the object expression (6.5.2 [class.member.lookup]). If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template whose specializations are types.



1938. Should hosted/freestanding be implementation-defined?

Section: 4.1  [intro.compliance]     Status: CD5     Submitter: Richard Smith     Date: 2014-06-09

[Accepted as a DR at the February, 2019 meeting.]

Whether an implementation is hosted or freestanding is only required to be documented by the value of the __STDC_HOSTED__ macro (15.11 [cpp.predefined]). Should this characteristic be classified as implementation-defined, thus requiring documentation?

Proposed resolution (January, 2019):

Change 16.4.2.5 [compliance] paragraph 1 as follows:

Two kinds of implementations are defined: hosted and freestanding (4.1 [intro.compliance]) the kind of the implementation is implementation-defined. For a hosted implementation, this document describes the set of available headers.



1332. Handling of invalid universal-character-names

Section: 5.3  [lex.charset]     Status: CD5     Submitter: Mike Miller     Date: 2011-06-20

According to 5.3 [lex.charset] paragraph 2,

The character designated by the universal-character-name \UNNNNNNNN is that character whose character short name in ISO/IEC 10646 is NNNNNNNN; the character designated by the universal-character-name \uNNNN is that character whose character short name in ISO/IEC 10646 is 0000NNNN. If the hexadecimal value for a universal-character-name corresponds to a surrogate code point (in the range 0xD800-0xDFFF, inclusive), the program is ill-formed. Additionally, if the hexadecimal value for a universal-character-name outside the c-char-sequence, s-char-sequence, or r-char-sequence of a character or string literal corresponds to a control character (in either of the ranges 0x00-0x1F or 0x7F-0x9F, both inclusive) or to a character in the basic source character set, the program is ill-formed.

It is not specified what should happen if the hexadecimal value does not designate a Unicode code point: is that undefined behavior or does it make the program ill-formed?

As an aside, a note should be added explaining why these requirements apply to to an r-char-sequence when, as the footnote at the end of the paragraph explains,

A sequence of characters resembling a universal-character-name in an r-char-sequence (5.13.5 [lex.string]) does not form a universal-character-name.

Additional note, February, 2021:

This issue was resolved editorially in N4842.




1859. UTF-16 in char16_t string literals

Section: 5.13.5  [lex.string]     Status: CD5     Submitter: Richard Smith     Date: 2014-02-12

[Adopted at the February, 2019 meeting as part of paper P1041R4.]

The resolution of issue 1802 clarified that char16_t string literals can contain surrogate pairs, in contrast to char16_t character literals. However, there is no explicit requirement that char16_t literals be encoded as UTF-16, although that is explicitly stated for char16_t character literals, so it's not clear what the value is required to be in the surrogate-pair case.

Rationale (February, 2019)

The Standard is now clear on this point.




2371. Use of the English term “attributes” is confusing

Section: 6.2  [basic.def]     Status: CD5     Submitter: Gabriel Dos Reis     Date: 2017-12-05

[Adopted at the February, 2019 meeting as part of paper P1103R3.]

The English word “attributes” is used occasionally in the Standard to refer to general characteristics rather than to the constructs described by the attribute syntactic nonterminal. For example, 6.2 [basic.def] paragraph 1 says,

A declaration (9.1 [dcl.pre]) may introduce one or more names into a translation unit or redeclare names introduced by previous declarations. If so, the declaration specifies the interpretation and attributes of these names.

A similar use occurs in 6.5 [basic.lookup] paragraph 1:

Only after name lookup, function overload resolution (if applicable) and access checking have succeeded are the attributes introduced by the name's declaration used further in expression processing (Clause 7 [expr]).

These nonspecific uses of the term are potentially confusing and should be replaced, possibly with a specification of which “attributes” are intended.

Perhaps less confusing, although in a similar vein, is 11.4.10 [class.bit] paragraph 1:

The bit-field attribute is not part of the type of the class member.

The use of the term here should also be reconsidered.

Notes from the October, 2018 teleconference:

These points are addressed by further work on the modules TS.




1581. When are constexpr member functions defined?

Section: 6.3  [basic.def.odr]     Status: CD5     Submitter: Richard Smith     Date: 2012-10-29

[Accepted as a DR as paper P0859R0 at the October, 2017 meeting.]

11.4.4 [special] is perfectly clear that special member functions are only implicitly defined when they are odr-used. This creates a problem for constant expressions in unevaluated contexts:

   struct duration {
     constexpr duration() {}
     constexpr operator int() const { return 0; }
   };
   // duration d = duration(); // #1
   int n = sizeof(short{duration(duration())});

The issue here is that we are not permitted to implicitly define constexpr duration::duration(duration&&) in this program, so the expression in the initializer list is not a constant expression (because it invokes a constexpr function which has not been defined), so the braced initializer contains a narrowing conversion, so the program is ill-formed.

If we uncomment line #1, the move constructor is implicitly defined and the program is valid. This spooky action at a distance is extremely unfortunate. Implementations diverge on this point.

There are also similar problems with implicit instantiation of constexpr functions. It is not clear which contexts require their instantiation. For example:

  template<int N> struct U {};
  int g(int);
  template<typename T> constexpr int h(T) { return T::error; }
  template<typename T> auto f(T t) -> U<g(T()) + h(T())> {}
  int f(...);
  int k = f(0);

There are at least two different ways of modeling the current rules:

These two approaches can be distinguished by code like this:

  int k = sizeof(U<0 && h(0)>);

Under the first approach, this code is valid; under the second, it is ill-formed.

A possible approach to resolving this issue would be to change the definition of “potentially-evaluated” such that template arguments, array bounds, and braced-init-lists (and any other expressions which are constant evaluated) are always potentially-evaluated, even if they appear within an unevaluated context, and to change 13.9.2 [temp.inst] paragraph 3 to say simply that function template specializations are implicitly instantiated when they are odr-used.

A related question is whether putatively constexpr constructors must be instantiated in order to determine whether their class is a literal type or not. See issue 1358.

Jason Merrill:

I'm concerned about unintended side-effects of such a large change to “potentially-evaluated;” I would prefer something that only affects constexpr.

It occurs to me that this is parallel to issue 1330: just like we want to instantiate exception specifiers for calls in unevaluated context, we also want to instantiate constexpr functions. I think we should define some other term to say when there's a reference to a declaration, and then say that the declaration is odr-used when that happens in potentially-evaluated context.

Notes from the April, 2013 meeting:

An additional question was raised about whether constexpr functions should be instantiated as a result of appearing within unevaluated subexpressions of constant expressions. For example:

  #include <type_traits>

  template <class T> constexpr T f(T t) { return +t; }

  struct A { };

  template <class T>
  decltype(std::is_scalar<T>::value ? T::fail : f(T()))
    g() { }

  template <class T>
  void g(...);

  int main()
  {
    g<A>();
  }

If constexpr instantiation happens during constant expression evaluation, f<A> is never instantiated and the program is well-formed. If constexpr instantiation happens during parsing, f<A> is instantiated and the program is ill-formed.




2020. Inadequate description of odr-use of implicitly-invoked functions

Section: 6.3  [basic.def.odr]     Status: CD5     Submitter: Richard Smith     Date: 2014-10-08

[Accepted as a DR at the February, 2019 meeting.]

According to 6.3 [basic.def.odr] paragraph 3,

A function whose name appears as a potentially-evaluated expression is odr-used if it is the unique lookup result or the selected member of a set of overloaded functions (6.5 [basic.lookup], 12.2 [over.match], 12.3 [over.over]), unless it is a pure virtual function and its name is not explicitly qualified. [Note: This covers calls to named functions (7.6.1.3 [expr.call]), operator overloading (Clause 12 [over]), user-defined conversions (11.4.8.3 [class.conv.fct]), allocation function for placement new (7.6.2.8 [expr.new]), as well as non-default initialization (9.4 [dcl.init]). A constructor selected to copy or move an object of class type is odr-used even if the call is actually elided by the implementation (11.4.5.3 [class.copy.ctor]). —end note] An allocation or deallocation function for a class is odr-used by a new expression appearing in a potentially-evaluated expression as specified in 7.6.2.8 [expr.new] and 11.4.11 [class.free]. A deallocation function for a class is odr-used by a delete expression appearing in a potentially-evaluated expression as specified in 7.6.2.9 [expr.delete] and 11.4.11 [class.free].

There are a couple of problems with this specification. First, contrary to the note, the names of overloaded operators, conversion functions, etc., do not appear in potentially-evaluated expressions, so the normative text does not make the note true. Also, the “as specified in” references do not cover odr-use explicitly, only the invocation of the functions.

One possible way of addressing these deficiencies would be a blanket rule like,

A function is odr-used if it is invoked by a potentially-evaluated expression.

(The existing wording about appearing in a potentially-evaluated expression would still be needed for non-call references.)

Proposed resolution (January, 2019):

  1. Change 6.3 [basic.def.odr] paragraph 2 as follows:

  2. An expression or conversion is potentially evaluated unless it is an unevaluated operand (7.2 [expr.prop]), or a subexpression thereof, or a conversion in an initialization or conversion sequence in such a context. The set of potential results of an expression e is defined as follows:...
  3. Cbange 6.3 [basic.def.odr] paragraph 3 as follows:

  4. A function is named by an expression an expression or conversion as follows:

  5. Change 6.3 [basic.def.odr] paragraph 7 as follows:

  6. A virtual member function is odr-used if it is not pure. A function is odr-used if it is named by a potentially-evaluated expression or conversion. A non-placement...



2083. Incorrect cases of odr-use

Section: 6.3  [basic.def.odr]     Status: CD5     Submitter: Hubert Tong     Date: 2015-02-11

[Accepted as a DR at the February, 2019 meeting.]

The resolution of issue 1741 was not intended to cause odr-use to occur in cases where it did not do so previously. However, in an example like

  extern int globx;
  int main() {
    const int &x = globx;
    struct A {
     const int *foo() {
       return &x;
     }
    } a;
    return *a.foo();
  }

x satisfies the requirements for appearing in a constant expression, but applying the lvalue-to-rvalue converstion to x does not yield a constant expression. Similarly,

  struct A {
    int q;
    constexpr A(int q) : q(q) { }
    constexpr A(const A &a) : q(a.q * 2) { }
  };

  int main(void) {
    constexpr A a(42);
    constexpr int aq = a.q;
    struct Q {
     int foo() { return a.q; }
    } q;
    return q.foo();
  }

a satisfies the requirements for appearing in a constant expression, but applying the lvalue-to-rvalue conversion to a invokes a non-trivial function.

Proposed resolution (January, 2019):

  1. Change 6.3 [basic.def.odr] bullet 2.4 as follows:

  2. An expression is potentially evaluated unless it is an unevaluated operand (7.2 [expr.prop]) or a subexpression thereof. The set of potential results of an expression e is defined as follows:

  3. Change 6.3 [basic.def.odr] paragraph 4, converting the running text into bullets, as follows:

  4. A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) to x yields a constant expression (7.7 [expr.const]) that does not invoke a function other than a trivial special member function (11.4.4 [special]) and, if x is an object,

    This resolution also resolves issues 2103 and 2170.




2103. Lvalue-to-rvalue conversion is irrelevant in odr-use of a reference

Section: 6.3  [basic.def.odr]     Status: CD5     Submitter: Richard Smith     Date: 2015-03-17

[Accepted as a DR at the February, 2019 meeting.]

Issue 1741 accidentally caused 6.3 [basic.def.odr] to indicate that an lvalue-to-rvalue conversion is necessary for the odr-use of a reference. This is incorrect; any appearance of the reference name in a potentially-evaluated expression should require the reference to be defined.

See also issue 2083.

Proposed resolution (January, 2019):

This issue is resolved by the resolution of issue 2083.




2170. Unclear definition of odr-use for arrays

Section: 6.3  [basic.def.odr]     Status: CD5     Submitter: Hubert Tong     Date: 2015-09-02

[Accepted as a DR at the February, 2019 meeting.]

The current definition of odr-use of a variable is problematic when applied to an array:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) to x yields a constant expression (7.7 [expr.const]) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is applied to e, or e is a discarded-value expression (Clause 7 [expr]).

Consider an example like

  struct S {
    constexpr static const int arr[3] = { 0, 1, 2 };
  };
  int i = S::arr[1];  // Should not require S::arr to be defined

Although the “set of potential results” test correctly handles the subscripting operation (since the resolution of issue 1926), it requires applying the lvalue-to-rvalue conversion to S::arr itself and not just to the result of the subscripting operation. Class objects exhibit a similar problem.

Proposed resolution (January, 2019):

This issue is resolved by the resolution of issue 2083.




2300. Lambdas in multiple definitions

Section: 6.3  [basic.def.odr]     Status: CD5     Submitter: Robert Haberlach     Date: 2016-04-11

[Accepted as a DR at the July, 2019 meeting.]

A lambda expression in two translation units has distinct closure types, because each such expression's type is unique within the program. This results in an issue with the ODR, which requires that the definitions of an entity are identical. For example, if

  template <int> void f() {std::function<void()> f = []{};}

appears in two translation units, different specializations of function's constructor template are called, which violates 6.3 [basic.def.odr] bullet 6.4.

Issue 765 dealt with a similar problem for inline functions, but the issue still remains for templates.

Proposed resolution, April, 2019:

Change 6.3 [basic.def.odr] paragraph 12 as follows:

...Given such an entity named D defined in more than one translation unit, then

If D is a template and is defined in more than one translation unit, then the preceding requirements shall apply both to names from the template's enclosing scope used in the template definition (_N4868_.13.8.4 [temp.nondep]), and also to dependent names at the point of instantiation (13.8.3 [temp.dep]). If the definitions of D satisfy all these requirements, then the behavior is as if there were a single definition of D. These requirements also apply to corresponding entities defined within each definition of D (including the closure types of lambda-expressions, but excluding entities defined within default arguments or defualt template arguments of either D or an entity not defined within D). For each such entity and for D itself, the behavior is as if there is a single entity with a single definition, including in the application of these requirements to other entities. [Note: The entity is still declared in multiple translation units, and 6.6 [basic.link] still applies to these declarations. In particular, lambda-expressions (7.5.5 [expr.prim.lambda]) appearing in the type of D may result in the different declarations having distinct types, and lambda-expressions appearng in a default argument of D may still denote different types in different translation units. —end note] If the definitions of D do not satisfy these requirements, then the behavior is undefined. program is ill-formed, no diagnostic required. [Example:

  inline void f(bool cond, void (*p)()) {
    if (cond) f(false, []{});
  }
  inline void g(bool cond, void (*p)() = []{}) {
    if (cond) g(false);
  }
  struct X {
    void h(bool cond, void (*p)() = []{}) {
      if (cond) h(false);
    }
  };

If the definition of f appears in multiple translation units, the behavior of the program is as if there is only one definition of f. If the definition of g appears in multiple translation units, the program is ill-formed (no diagnostic required) because each such definition uses a default argument that refers to a distinct lambda-expression closure type. The definition of X can sppear in multiple translation units of a valid program; the lambda-expressions defined within the default argumeht of X::h within the definition of X denote the same closure type in each translation unit. —end example]




2353. Potential results of a member access expression for a static data member

Section: 6.3  [basic.def.odr]     Status: CD5     Submitter: Richard Smith     Date: 2017-08-18

[Accepted as a DR at the February, 2019 meeting.]

According to 6.3 [basic.def.odr] bullet 2.3, the potential results of a member access expression are simply the object expression. This rule incorrectly handles an example like:

  struct X {
    static const int n = 0;
  };
  X x = {};

  int b = x.n;

Because X::n is not one of the potential results, the expression x.n odr-uses X::n, requiring it to be defined.

Notes from the April, 2018 teleconference:

CWG agreed with the suggested direction to make the member a potential result in cases like the example.

Proposed resolution (February, 2019):

Change 6.3 [basic.def.odr] paragraph 2 as follows:

An expression is potentially evaluated unless it is an unevaluated operand (7.2 [expr.prop]) or a subexpression thereof. The set of potential results of an expression e is defined as follows:




2380. capture-default makes too many references odr-usable

Section: 6.3  [basic.def.odr]     Status: CD5     Submitter: Richard Smith     Date: 2018-06-14

[Accepted as a DR at the February, 2019 meeting.]

The current rule for determining when a local entity is odr-usable because of a capture-default is too broad. For example:

  void f() {
    int n;
    void g(int k = n);                  // ill-formed
    [](int k = n) {};                   // ill-formed
    [=](int k = n) {};                  // valid!
    [=](int k = [=]{ return n; }()) {}; // valid!
  }

Proposed resolution (January, 2019):

Change 6.3 [basic.def.odr] bullet 9.2.2 and add to the example as follows:

A local entity (6.3 [basic.def.odr]) is odr-usable in a declarative region (_N4868_.6.4.1 [basic.scope.declarative]) if:

If a local entity is odr-used in a declarative region in which it is not odr-usable, the program is ill-formed. [Example:

  void f(int n) {
    [] { n = 1; };        // error, n is not odr-usable due to intervening lambda-expression
    struct A {
      void f() { n = 2; } // error, n is not odr-usable due to intervening function definition scope
    };
    void g(int = n);      // error, n is not odr-usable due to intervening function parameter scope
    [=](int k = n) {};    // error, n is not odr-usable due to being outside the block scope of the lambda-expression
    [&] { [n]{ return n; }; }; // OK
  }



555. Pseudo-destructor name lookup

Section: 6.5  [basic.lookup]     Status: CD5     Submitter: Krzysztof Zelechowski     Date: 26 January 2006

[Adopted at the November, 2018 meeting as part of paper P1131R2.]

The Standard does not completely specify how to look up the type-name(s) in a pseudo-destructor-name (7.6.1 [expr.post] paragraph 1, _N4778_.7.6.1.4 [expr.pseudo]), and what information it does have is incorrect and/or in the wrong place. Consider, for instance, _N4868_.6.5.6 [basic.lookup.classref] paragraphs 2-3:

If the id-expression in a class member access (7.6.1.5 [expr.ref]) is an unqualified-id, and the type of the object expression is of a class type C (or of pointer to a class type C), the unqualified-id is looked up in the scope of class C. If the type of the object expression is of pointer to scalar type, the unqualified-id is looked up in the context of the complete postfix-expression.

If the unqualified-id is ~type-name, and the type of the object expression is of a class type C (or of pointer to a class type C), the type-name is looked up in the context of the entire postfix-expression and in the scope of class C. The type-name shall refer to a class-name. If type-name is found in both contexts, the name shall refer to the same class type. If the type of the object expression is of scalar type, the type-name is looked up in the scope of the complete postfix-expression.

There are at least three things wrong with this passage with respect to pseudo-destructors:

  1. A pseudo-destructor call (_N4778_.7.6.1.4 [expr.pseudo]) is not a “class member access”, so the statements about scalar types in the object expressions are vacuous: the object expression in a class member access is required to be a class type or pointer to class type (7.6.1.5 [expr.ref] paragraph 2).

  2. On a related note, the lookup for the type-name(s) in a pseudo-destructor name should not be described in a section entitled “Class member access.”

  3. Although the class member access object expressions are carefully allowed to be either a class type or a pointer to a class type, paragraph 2 mentions only a “pointer to scalar type” (disallowing references) and paragraph 3 deals only with a “scalar type,” presumably disallowing pointers (although it could possibly be a very subtle way of referring to both non-class pointers and references to scalar types at once).

The other point at which lookup of pseudo-destructors is mentioned is 6.5.5 [basic.lookup.qual] paragraph 5:

If a pseudo-destructor-name (_N4778_.7.6.1.4 [expr.pseudo]) contains a nested-name-specifier, the type-names are looked up as types in the scope designated by the nested-name-specifier.

Again, this specification is in the wrong location (a pseudo-destructor-name is not a qualified-id and thus should not be treated in the “Qualified name lookup” section).

Finally, there is no place in the Standard that describes the lookup for pseudo-destructor calls of the form p->T::~T() and r.T::~T(), where p and r are a pointer and reference to scalar, respectively. To the extent that it gives any guidance at all, _N4868_.6.5.6 [basic.lookup.classref] deals only with the case where the ~ immediately follows the . or ->, and 6.5.5 [basic.lookup.qual] deals only with the case where the pseudo-destructor-name contains a nested-name-specifier that designates a scope in which names can be looked up.

See document J16/06-0008 = WG21 N1938 for further discussion of this and related issues, including 244, 305, 399, and 414.

Proposed resolution (June, 2008):

  1. Add a new paragraph following 7.6.1 [expr.post] paragraph 2 as follows:

  2. When a postfix-expression is followed by a dot . or arrow -> operator, the interpretation depends on the type T of the expression preceding the operator. If the operator is ., T shall be a scalar type or a complete class type; otherwise, T shall be a pointer to a scalar type or a pointer to a complete class type. When T is a (pointer to) a scalar type, the postfix-expression to which the operator belongs shall be a pseudo-destructor call (_N4778_.7.6.1.4 [expr.pseudo]); otherwise, it shall be a class member access (7.6.1.5 [expr.ref]).
  3. Change _N4778_.7.6.1.4 [expr.pseudo] paragraph 2 as follows:

  4. The left-hand side of the dot operator shall be of scalar type. The left-hand side of the arrow operator shall be of pointer to scalar type. This scalar type The type of the expression preceding the dot operator, or the type to which the expression preceding the arrow operator points, is the object type...
  5. Change 7.6.1.5 [expr.ref] paragraph 2 as follows:

  6. For the first option (dot) the type of the first expression (the object expression) shall be “class object” (of a complete type) is a class type. For the second option (arrow) the type of the first expression (the pointer expression) shall be “pointer to class object” (of a complete type) is a pointer to a class type. In these cases, the id-expression shall name a member of the class or of one of its base classes.
  7. Add a new paragraph following 6.5 [basic.lookup] paragraph 2 as follows:

  8. In a pseudo-destructor-name that does not include a nested-name-specifier, the type-names are looked up as types in the context of the complete expression.
  9. Delete the last sentence of _N4868_.6.5.6 [basic.lookup.classref] paragraph 2:

  10. If the id-expression in a class member access (7.6.1.5 [expr.ref]) is an unqualified-id, and the type of the object expression is of a class type C, the unqualified-id is looked up in the scope of class C. If the type of the object expression is of pointer to scalar type, the unqualified-id is looked up in the context of the complete postfix-expression.

Notes from the August, 2011 meeting:

The proposed resolution must be updated with respect to the current wording of the WP.




2372. Incorrect matching rules for block-scope extern declarations

Section: 6.6  [basic.link]     Status: CD5     Submitter: Chen Fuxingi     Date: 2017-12-17

[Accepted as a DR at the February, 2019 meeting.]

According to 6.6 [basic.link] paragraph 6,

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block scope declaration declares that same entity and receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed. Otherwise, if no matching entity is found, the block scope entity receives external linkage.

The requirement that the entities have the same type does not cover all cases that it should. Consider an example like:

  static int a[3];
  void g() {
    printf("%p\n", (void*)a);
    extern int a[];
    printf("%p\n", (void*)a);
  }

According to the cited wording, the block-scope declaration of a does not match the namespace scope declaration because int[] and int[3] are different types, thus the first reference to a refers to the static variable while the second one refers to a variable a defined in some other translation unit. This is clearly not intended, and current implementations treat both references as referring to the static variable.

Notes from the October, 2018 teleconference:

CWG agreed with the direction and noted that a similar situation occurs with extern "C" functions.

Proposed resolution (November, 2018):

Change 6.6 [basic.link] paragraph 6 as follows:

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, such that the block scope declaration would be a (possibly ill-formed) redeclaration if the two declarations appeared in the same declarative region, the block scope declaration declares that same entity and receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed. Otherwise, if no matching entity is found, the block scope entity receives external linkage. If, within a translation unit, the same entity is declared with both internal and external linkage, the program is ill-formed. [Example:

  static void f();
  extern "C" void h();
  static int i = 0;      // #1
  void g() {
    extern void f();     // internal linkage
    extern void h();     // C language linkage

    int i;               // #2: i has no linkage
    {
      extern void f();   // internal linkage
      extern int i;      // #3: external linkage, ill-formed
    }
  }

Without the declaration at line #2, the declaration at line #3 would link with the declaration at line #1. Because the declaration with internal linkage is hidden, however, #3 is given external linkage, making the program ill-formed. —end example]




2387. Linkage of const-qualified variable template

Section: 6.6  [basic.link]     Status: CD5     Submitter: John Spicer     Date: 2018-09-28

[Accepted as a DR at the February, 2019 meeting.]

The list in 6.6 [basic.link] paragraph 3 specifying which entities receive internal linkage does not mention variable templates, so presumably a variable template has external linkage. Clause 13 [temp] paragraph 6 gives the impression that a specialization of a template with external linkage also has external linkage. However, current implementations appear to give internal linkage to specializations of const-qualified variable templates. Should const-qualified variable templates have internal linkage?

Notes from the December, 2018 teleconference:

CWG felt that a const type should not affect the linkage of a variable template or its instances.

Proposed resolution (February, 2019):

Change 6.6 [basic.link] paragraph 3 as follows:

A name having namespace scope (6.4.6 [basic.scope.namespace]) has internal linkage if it is the name of

[Note: An instantiated variable template that has const-qualified type can have external linkage, even if not declared extern. —end note]




2256. Lifetime of trivially-destructible objects

Section: 6.7.3  [basic.life]     Status: CD5     Submitter: Richard Smith     Date: 2016-03-30

[Accepted as a DR at the February, 2019 meeting.]

According to 6.5 [basic.lookup] bullet 1.4, the following example has defined behavior because the lifetime of n extends until its storage is released, which is after a's destructor runs:

  void f() {
    struct A { int *p; ~A() { *p = 0; } } a;
    int n;
    a.p = &n;
  }

It would be more consistent if the end of the lifetime of all objects, regardless of whether they have a non-trivial destructor, were treated the same.

Notes from the March, 2018 meeting:

CWG agreed with the suggested direction.

Proposed resolution (November, 2018):

  1. Change 6.7.3 [basic.life] paragraph 1 as follows:

  2. The lifetime of an object or reference is a runtime property of the object or reference. An object is said to have non-vacuous initialization if it is of a class or array type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor. [Note: : Initialization by a trivial copy/move constructor is non-vacuous initialization. —end note] A variable is said to have vacuous initialization if it is default-initialized and, if it is of class type or (possibly multi-dimensional) array thereof, that class type has a trivial default constructor. The lifetime of an object of type T begins when:

    except that if the object is a union member or subobject thereof, its lifetime only begins if that union member is the initialized member in the union (9.4.2 [dcl.init.aggr], 11.9.3 [class.base.init]), or as described in 11.5 [class.union]. The lifetime of an object o of type T ends when:

  3. Change 6.9.3.4 [basic.start.term] paragraphs 1 and 2 as follows:

  4. Destructors (11.4.7 [class.dtor]) for initialized Constructed objects (that is, objects whose lifetime (6.7.3 [basic.life]) has begun 9.4 [dcl.init]) with static storage duration, are destroyed and functions registered with std::atexit, are called as part of a call to std::exit (17.5 [support.start.term]). The call to std::exit is sequenced before the invocations of the destructors destructions and the registered functions. [Note: Returning from main invokes std::exit (6.9.3.1 [basic.start.main]). —end note]

    Destructors for initialized Constructed objects with thread storage duration within a given thread are called destroyed as a result of returning from the initial function of that thread and as a result of that thread calling std::exit. The completions of the destructors for destruction of all initialized constructed objects with thread storage duration within that thread strongly happens before the initiation of the destructors of destroying any object with static storage duration.

  5. Change 8.7 [stmt.jump] paragraph 2 as follows:

  6. ...[Note: However, the program can be terminated (by calling std::exit() or std::abort() (17.5 [support.start.term]), for example) without destroying class objects with automatic storage duration. —end note]
  7. Change 8.8 [stmt.dcl] paragraph 2 as follows:

  8. It is possible to transfer into a block, but not in a way that bypasses declarations with initialization (including ones in conditions and init-statements). A program that jumps92 from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer vacuous initialization (9.4 [dcl.init]). In such a case, the variables with vacuous initialization are constructed in the order of their declaration. [Example:...
  9. Change 8.8 [stmt.dcl] paragraph 5 as follows:

  10. The destructor for a A block-scope object with static or thread storage duration will be executed destroyed if and only if it was constructed. [Note: 6.9.3.4 [basic.start.term] describes the order in which block-scope objects with static and thread storage duration are destroyed. —end note]
  11. Change 9.4 [dcl.init] paragraph 21 as follows:

  12. An object whose initialization has completed is deemed to be constructed, even if the object is of non-class type or no constructor of the object's class is invoked for the initialization. [Note: Such an object might have been value-initialized or initialized by aggregate initialization (9.4.2 [dcl.init.aggr]) or by an inherited constructor (11.9.4 [class.inhctor.init]). —end note] Destroying an object of class type invokes the destructor of the class. Destroying a scalar type has no effect other than ending the lifetime of the object (6.7.3 [basic.life]). Destroying an array destroys each element in reverse subscript order.
  13. Change 11.4.7 [class.dtor] paragraph 2 as follows:

  14. A destructor is used to destroy objects of its class type. The address of a destructor...
  15. Change 14.3 [except.ctor] paragraphs 1 and 2 as follows:

  16. As control passes from the point where an exception is thrown to a handler, destructors are invoked objects with automatic storage duration are destroyed by a process, specified in this subclause, called stack unwinding.

    The destructor is invoked for each automatic object of class type Each object with automatic storage duration is destroyed if it has been constructed, but not yet destroyed, since the try block was entered. If an exception is thrown during the destruction of temporaries or local variables for a return statement (8.7.4 [stmt.return]), the destructor for the returned object (if any) is also invoked. The objects are destroyed in the reverse order of the completion of their construction. [Example:...




1910. “Shall” requirement applied to runtime behavior

Section: 6.7.5.5.2  [basic.stc.dynamic.allocation]     Status: CD5     Submitter: Richard Smith     Date: 2014-04-12

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

According to 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 3,

If an allocation function declared with a non-throwing exception-specification (14.5 [except.spec]) fails to allocate storage, it shall return a null pointer. Any other allocation function that fails to allocate storage shall indicate failure only by throwing an exception (14.2 [except.throw]) of a type that would match a handler (14.4 [except.handle]) of type std::bad_alloc (17.6.4.1 [bad.alloc]).

The use of the word “shall” to constrain runtime behavior is inappropriate, as it normally identifies cases requiring a compile-time diagnostic.

Proposed resolution (November, 2017)

  1. Change 6.7.5.5 [basic.stc.dynamic] paragraph 3 as follows:

  2. Any allocation and/or deallocation functions defined in a C ++ program, including the default versions in the library, shall conform to the semantics If the behavior of an allocation or deallocation function does not satisfy the semantic constraints specified in 6.7.5.5.2 [basic.stc.dynamic.allocation] and 6.7.5.5.3 [basic.stc.dynamic.deallocation], the behavior is undefined.
  3. Change 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 1 as follows:

  4. ...The value of the first parameter shall be is interpreted as the requested size of the allocation...
  5. Change 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 2 as follows:

  6. The An allocation function attempts to allocate the requested amount of storage. If it is successful, it shall return returns the address of the start of a block of storage whose length in bytes shall be is at least as large as the requested size. There are no constraints on the contents of the allocated storage on return from the allocation function. The order, contiguity, and initial value of storage allocated by successive calls to an allocation function are unspecified. The For an allocation function other than a reserved placement allocation function (17.6.3.4 [new.delete.placement], the pointer returned shall be is suitably aligned so that it can be converted to a pointer to any suitable complete object type (17.6.3.2 [new.delete.single]) and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function). Even if the size of the space requested is zero, the request can fail. If the request succeeds, the value returned shall be by a replaceable allocation function is a non-null pointer value (7.3.12 [conv.ptr]) p0 different from any previously returned value p1, unless that value p1 was subsequently passed to an operator delete a replaceable deallocation function. Furthermore, for the library allocation functions in 17.6.3.2 [new.delete.single] and 17.6.3.3 [new.delete.array], p0 shall represent represents the address of a block of storage disjoint from the storage for any other object accessible to the caller. The effect of indirecting through a pointer returned as from a request for zero size is undefined.38
  7. Change 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 3 as follows:

  8. An allocation function that fails to allocate storage can invoke the currently installed new-handler function (17.6.4.3 [new.handler]), if any. [Note: A program-supplied allocation function can obtain the address of the currently installed new_handler using the std::get_new_handler function (17.6.4.4 [set.new.handler]). —end note] If an An allocation function that has a non-throwing exception specification (14.5 [except.spec]) fails to allocate storage, it shall return indicates failure by returning a null pointer value. Any other allocation function that fails to allocate storage shall indicate never returns a null pointer value and indicates failure only by throwing an exception (14.2 [except.throw]) of a type that would match a handler (14.4 [except.handle]) of type std::bad_alloc (17.6.4.1 [bad.alloc]).



2207. Alignment of allocation function return value

Section: 6.7.5.5.2  [basic.stc.dynamic.allocation]     Status: CD5     Submitter: Richard Smith     Date: 2015-12-02

[Accepted as a DR at the July, 2019 meeting.]

According to 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 2,

For an allocation function other than a reserved placement allocation function (17.6.3.4 [new.delete.placement]), the pointer returned is suitably aligned so that it can be converted to a pointer to any suitable complete object type (17.6.3.2 [new.delete.single]) and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function).

This requirement seems excessive, as it appears to require the strictest fundamental alignment even for objects that are too small to require such an alignment.

Proposed resolution (March, 2019):

  1. Change 6.7.5.5.2 [basic.stc.dynamic.allocation] paragraph 2 as follows:

  2. ...The order, contiguity, and initial value of storage allocated by successive calls to an allocation function are unspecified. For an allocation function other than a reserved placement allocation function (17.6.3.4 [new.delete.placement]), the pointer returned is suitably aligned so that it can be converted to a pointer to any suitable complete object type (17.6.3.2 [new.delete.single]) and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function). Even if the size of the space requested...
  3. Add the following as a new paragraph to 6.7.5.5.2 [basic.stc.dynamic.allocation] after the existing paragraph 2:

  4. For an allocation function other than a reserved placement allocation function (17.6.3.4 [new.delete.placement]), the pointer returned on a successful call shall represent the address of storage that is aligned as follows:

  5. Change 17.6.3.2 [new.delete.single] paragraph 1 as follows:

  6. Effects: The allocation functions (6.7.5.5.2 [basic.stc.dynamic.allocation]) called by a new-expression (7.6.2.8 [expr.new]) to allocate size bytes of storage. The second form is called for a type with new-extended alignment, and allocates storage with the specified alignment. The the first form is called otherwise, and allocates storage suitably aligned to represent any object of that size provided the object's type does not have new-extended alignment.
  7. Change 17.6.3.3 [new.delete.array] paragraph 1 as follows:

  8. Effects: The allocation functions (6.7.5.5.2 [basic.stc.dynamic.allocation]) called by the array form of a new-expression (7.6.2.8 [expr.new]) to allocate size bytes of storage. The second form is called for a type with new-extended alignment, and allocates storage with the specified alignment. The the first form is called otherwise, and allocates storage suitably aligned to represent any array object of that size or smaller, provided the object's type does not have new-extended alignment.218



2354. Extended alignment and object representation

Section: 6.7.6  [basic.align]     Status: CD5     Submitter: Mike Miller     Date: 2017-09-05

[Accepted as a DR at the February, 2019 meeting.]

There is implementation variance in the treatment of an example like:

  enum struct alignas(64) A {};
  A a[10];

The Standard does not appear to indicate whether padding bits are added to the object representation for an enumeration or not, affecting the result of sizeof and the alignment of the array elements.

If the extended alignment does not affect the object representation of the type, it would be useful to specify that the array declaration is ill-formed because it requires violating the alignment requirement (and similarly for an array new-expression where the bound is known at compile time).

Notes from the April, 2018 teleconference:

It appears that the same question exists for class types that are directly given extended alignment, as opposed to receiving it from a subobject.

Notes from the November, 2018 meeting:

CWG agreed to remove permission for alignas to be applied to enumerations. The class case is already handled by the wording in 7.6.2.5 [expr.sizeof] paragraph 2 that the size of a class “includes any padding required for placing objects of that type in an array.”

Proposed resolution (December, 2018):

Change 9.12.2 [dcl.align] paragraph 1 as follows:

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 (14.4 [except.handle]). An alignment-specifier may also be applied to the declaration of a class (in an elaborated-type-specifier (9.2.9.5 [dcl.type.elab]) or class-head ( Clause 11 [class]), respectively) and to the declaration of an enumeration (in an opaque-enum-declaration or enum-head, respectively (9.7.1 [dcl.enum])). An alignment-specifier with an ellipsis...



1299. “Temporary objects” vs “temporary expressions”

Section: 6.7.7  [class.temporary]     Status: CD5     Submitter: Johannes Schaub     Date: 2011-04-16

[Voted into the WP at the July, 2017 meeting as document P0727R0.]

The Standard is insufficiently precise in dealing with temporaries. It is not always clear when the term “temporary” is referring to an expression whose result is a prvalue and when it is referring to a temporary object.

(See also issue 1568.)

Proposed resolution (February, 2014) [SUPERSEDED]:

The resolution is contained in document N3918.

This resolution also resolves issues 1651 and 1893.

Additional note, November, 2014:

Concerns have been raised that the meaning of “corresponding temporary object” is not clear enough in the proposed wording. In addition, N3918 says that it resolves issue 1300, but that issue is 1) marked as a duplicate of issue 914 and 2) the subject of continuing deliberations in EWG. This issue is being returned to "review" status to allow CWG to address these concerns.

Proposed resolution (March, 2017):

  1. Change Clause 7 [expr] paragraph 12 as follows:

  2. ...is applied. [Note: If the expression is an lvalue of class type, it must have a volatile copy constructor to initialize the temporary object that is the result object of the lvalue-to-rvalue conversion. —end note] The glvalue expression...
  3. Change 6.7.7 [class.temporary] paragraph 6 as follows:

  4. The third context is when a reference is bound to a temporary object.116 The temporary object to which the reference is bound or the temporary object that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference if the glvalue referring to the temporary object was obtained through one of the following:

    [Example:

      template<typename T> using id = T;
    
      int&& a = id<int[3]>{1, 2, 3}[i];          // temporary array has same lifetime as a
      const int& b = static_cast<const int&>(0); // temporary int has same lifetime as b
      int&& c = cond ? id<int[3]>{1, 2, 3}[i] : static_cast<int&&>(0);
                                                 // exactly one of the two temporaries is lifetime-extended
    

    end example] [Note: If a temporary object has a reference member initialized by another temporary object, lifetime extension applies recursively to such a member's initializer. [Example:

      struct S {
        const int& m;
      };
      const S& s = S{1};  // both S and int temporaries have lifetime of s
    

    end example] —end note]

    except The exceptions to this lifetime rule are:

  5. Change 12.2.2.5 [over.match.copy] bullet 1.2 as follows:

  6. Change 16.4.5.9 [res.on.arguments] bullet 1.3 as follows:

  7. Change 20.3.2.3.4 [util.smartptr.weak.assign] paragraph 2 as follows:

  8. Remarks: The implementation may meet the effects (and the implied guarantees) via different means, without creating a temporary object.
  9. Change the footnote in 28.6.2.1 [template.valarray.overview] paragraph 1 as follows:

  10. ...generalized subscript operators. [Footnote: The intent is to specify an array template that hass the minimum functionality necessary to address aliasing ambiguities and the proliferation of temporaries temporary objects. Thus... —end footnote]
  11. Change the last bullet of C.6.16 [diff.cpp03.input.output] as follows:

This resolution also resolves issues 943 and 1076.




2257. Lifetime extension of references vs exceptions

Section: 6.7.7  [class.temporary]     Status: CD5     Submitter: Hubert Tong     Date: 2016-04-07

[Accepted as a DR at the February, 2019 meeting.]

There is implementation divergence on the following example:

  #include <stdio.h>
  struct A {
    bool live;
    A() : live(true) {
      static int cnt;
      if (cnt++ == 1) throw 0;
    }
    ~A() {
      fprintf(stderr, "live: %d\n", live);
      live = false;
    }
  };
  struct AA { A &&a0, &&a1; };
  void doit() {
    static AA aa = { A(), A() };
  }
  int main(void) {
    try {
      doit();
    }
    catch (...) {
      fprintf(stderr, "in catch\n");
      doit();
    }
  }

Some implementations produce

  in catch
  live: 1
  live: 1
  live: 0

While others produce

  live: 1
  in catch
  live: 1
  live: 1

With regard to the reference to which the first-constructed object of type A is bound, at what point should its lifetime end? Perhaps it should be specified that the lifetime of the temporary is only extended if said initialization does not exit via an exception (which means that calling ::std::exit(0) as opposed to throwing would not result in a call to ~A()).

See also issue 1634.

Notes from the December, 2016 teleconference:

The consensus was that the temporaries should be destroyed immediately if an exception occurs. 14.3 [except.ctor] paragraph 3 should be extended to apply to static initialization, so that even if a temporary is lifetime-extended (because it has static storage duration), it will be destroyed in case of an exception.

Proposed resolution (January, 2019):

Change 14.3 [except.ctor] paragraph 3 as follows:

If the initialization or destruction of an object other than by delegating constructor is terminated by an exception, the destructor is invoked for each of the object's direct subobjects and, for a complete object, virtual base class subobjects, whose initialization has completed (9.4 [dcl.init]) and whose destructor has not yet begun execution, except that in the case of destruction, the variant members of a union-like class are not destroyed. [Note: If such an object has a reference member that extends the lifetime of a temporary object, this ends the lifetime of the reference member, so the lifetime of the temporary object is effectively not extended.—end note] The subobjects are destroyed in the reverse order of the completion of their construction. Such destruction is sequenced before entering a handler of the function-try-block of the constructor or destructor, if any.



689. Maximum values of signed and unsigned integers

Section: 6.8.2  [basic.fundamental]     Status: CD5     Submitter: James Kanze     Date: 30 March, 2008

[Resolved by paper P1236R1, adopted at the November, 2018 meeting.]

The relationship between the values representable by corresponding signed and unsigned integer types is not completely described, but 6.8 [basic.types] paragraph 4 says,

The value representation of an object is the set of bits that hold the value of type T.

and 6.8.2 [basic.fundamental] paragraph 3 says,

The range of nonnegative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the value representation of each corresponding signed/unsigned type shall be the same.

I.e., the maximum value of each unsigned type must be larger than the maximum value of the corresponding signed type.

C90 doesn't have this restriction, and C99 explicitly says (6.2.6.2, paragraph 2),

For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; there shall be exactly one sign bit. Each bit that is a value bit shall have the same value as the same bit in the object representation of the corresponding unsigned type (if there are M value bits in the signed type and N in the unsigned type, then M <= N).

Unlike C++, the sign bit is not part of the value, and on an architecture that does not have native support of unsigned types, an implementation can emulate unsigned integers by simply ignoring what would be the sign bit in the signed type and be conforming.

The question is whether we intend to make a conforming implementation on such an architecture impossible. More generally, what range of architectures do we intend to support? And to what degree do we want to follow C99 in its evolution since C89?

(See paper J16/08-0141 = WG21 N2631.)




2287. Pointer-interconvertibility in non-standard-layout unions

Section: 6.8.4  [basic.compound]     Status: CD5     Submitter: Richard Smith     Date: 2016-07-06

[Voted into the WP at the July, 2017 meeting.]

According to 11.5 [class.union] paragraph 2,

[Note: A union object and its non-static data members are pointer-interconvertible (6.8.4 [basic.compound], 7.6.1.9 [expr.static.cast]). As a consequence, all non-static data members of a union object have the same address. —end note]

However, the normative wording now only requires this for standard-layout unions.

Proposed resolution (April, 2017):

Change 6.8.4 [basic.compound] bullet 4.2 as follows:

Two objects a and b are pointer-interconvertible if:




2366. Can default initialization be constant initialization?

Section: 6.9.3.2  [basic.start.static]     Status: CD5     Submitter: Geoffrey Romer     Date: 2017-11-01

[Accepted as a DR at the July, 2019 meeting.]

According to 6.9.3.2 [basic.start.static] paragraph 2,

Constant initialization is performed if a variable or temporary object with static or thread storage duration is initialized by a constant initializer for the entity. If constant initialization is not performed, a variable with static storage duration (6.7.5.2 [basic.stc.static]) or thread storage duration (6.7.5.3 [basic.stc.thread]) is zero-initialized (9.4 [dcl.init]). Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization.

This appears to require an explicit initializer for constant initialization and would preclude examples like:

  struct S {
    int i = 1;
  };
  static constexpr S s;

where there is no initializer.

Notes from the October, 2018 teleconference:

CWG agreed that the example should be well-formed.

Proposed resolution, June, 2019:

  1. Change 6.9.3.2 [basic.start.static] paragraph 2 as follows:

  2. Constant initialization is performed if a variable or temporary object with static or thread storage duration is constant-initialized by a constant initializer (7.7 [expr.const]) for the entity. If constant initialization is not performed, a variable with static storage duration (6.7.5.2 [basic.stc.static]) or thread storage duration (6.7.5.3 [basic.stc.thread]) is zero-initialized (9.4 [dcl.init]). Together, zero-initialization and constant initialization...
  3. Change 7.7 [expr.const] paragraph 2 as follows:

  4. A constant initializer for a variable or temporary object o is an initializer for which interpreting its full-expression as a constant-expression results in a constant expression constant-initialized if

  5. Change 7.7 [expr.const] paragraph 3 as follows:

  6. A variable is usable in constant expressions after its initializing declaration is encountered if it is a constexpr variable, or it is a constant-initialized variable of reference type or of const-qualified integral or enumeration type, and its initializer is a constant initializer.
  7. Change 9.2.6 [dcl.constexpr] paragraph 5 as follows:

  8. For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (7.7 [expr.const]), or, for a constructor, a constant initializer for an evaluated subexpression of the initialization full-expression of some constant-initialized object (6.9.3.2 [basic.start.static]), the program is ill-formed, no diagnostic required. [Example:...



1076. Value categories and lvalue temporaries

Section: 7.2.1  [basic.lval]     Status: CD5     Submitter: Daniel Krügler     Date: 2010-06-10

[Voted into the WP at the July, 2017 meeting as document P0727R0.]

The taxonomy of value categories in 7.2.1 [basic.lval] classifies temporaries as prvalues. However, some temporaries are explicitly referred to as lvalues (cf 14.2 [except.throw] paragraph 3) .

Proposed resolution (March, 2017):

This issue is resolved by the resolution of issue 1299.




2051. Simplifying alias rules

Section: 7.2.1  [basic.lval]     Status: CD5     Submitter: Richard Smith     Date: 2014-12-03

[Accepted as a DR at the February, 2019 meeting.]

The aliasing rules of 7.2.1 [basic.lval] paragraph 10 were adapted from C with additions for C++. However, a number of the points either do not apply or are subsumed by other points. For example, the provision for aggregate and union types is needed in C for struct assignment, which in C++ is done via constructors and assignment operators in C++, not by accessing the complete object.

Suggested resolution:

Replace 7.2.1 [basic.lval] paragraph 10 as follows:

If a program attempts to access the stored value of an object through a glvalue whose type is not similar (7.3.6 [conv.qual]) to one of the following types the behavior is undefined: [Footnote:... —end footnote]

Additional note, October, 2015:

It has been suggested that the aliasing rules should be extended to permit an object of an enumeration with a fixed underlying type to alias an object with that underlying type.

Proposed resolution (January, 2019):

  1. Change 7.2.1 [basic.lval] paragraph 11 as follows:

  2. If a program attempts to access the stored value of an object through a glvalue of other than whose type is not similar (7.3.6 [conv.qual]) to one of the following types the behavior is undefined:58

    If a program invokes a defaulted copy/move constructor or copy/move assignment operator for a union of type U with a glvalue argument that does not denote an object of type cv U within its lifetime, the behavior is undefined. [Note: Unlike in C, C++ has no accesses of class type. —end note]

  3. Change 7.3.6 [conv.qual] paragraph 1 as follows:

  4. A cv-decomposition of a type T is a sequence of cvi and Pi such that T is

    where...




2381. Composite pointer type of pointers to plain and noexcept member functions

Section: 7.2.2  [expr.type]     Status: CD5     Submitter: Mike Miller     Date: 2018-06-19

[Accepted as a DR at the February, 2019 meeting.]

According to 7.3.14 [conv.fctptr] paragraph 1,

A prvalue of type “pointer to member of type noexcept function” can be converted to a prvalue of type “pointer to member of type function”. The result designates the member function.

However, 7.2.2 [expr.type] paragraph 4 does not allow for this conversion in determining the composite pointer type of two pointers to member functions. The corresponding conversion is considered for pointers to non-member functions, but only qualification conversions are considered for pointers to members:

The composite pointer type of two operands p1 and p2 having types T1 and T2, respectively, where at least one is a pointer or pointer-to-member type or std::nullptr_t, is:

Note also that the places that refer to “composite pointer type” only refer to 7.3.13 [conv.mem] for conversions involving pointers to members, omitting any reference to 7.3.14 [conv.fctptr] for pointers to member functions. This affects 7.6.9 [expr.rel], 7.6.10 [expr.eq], and 7.6.16 [expr.cond].

Proposed resolution (February, 2019):

  1. Change 7.2.2 [expr.type] paragraph 4 as follows:

  2. The composite pointer type of two operands p1 and p2 having types T1 and T2, respectively, where at least one is a pointer or pointer-to-member type or std::nullptr_t, is:

  3. Change 7.6.10 [expr.eq] paragraph 4 as follows:

  4. If at least one of the operands is a pointer to member, pointer-to-member conversions (7.3.13 [conv.mem]), function pointer conversions (7.3.14 [conv.fctptr]), and qualification conversions (7.3.6 [conv.qual]) are performed on both operands to bring them to their composite pointer type (7.2 [expr.prop]). Comparing...
  5. Change 7.6.16 [expr.cond] bullet 7.4 as follows:

  6. Lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are performed on the second and third operands. After those conversions, one of the following shall hold:




2310. Type completeness and derived-to-base pointer conversions

Section: 7.3.12  [conv.ptr]     Status: CD5     Submitter: Richard Smith     Date: 2016-08-08

[Accepted as a DR at the February, 2019 meeting.]

The specification of derived-to-base pointer conversions in 7.3.12 [conv.ptr] paragraph 3 does not require that the derived class be complete at the point of the conversion. This leaves unclear the status of an example like the following, on which there is implementation divergence:

  template<typename A, typename B> struct check_derived_from {
    static A a;
    static constexpr B *p = &a;
  };
  struct W {};
  struct X {};
  struct Y {};
  struct Z : W,
    X, check_derived_from<Z, X>,  // #1 
    check_derived_from<Z, Y>, Y { // #2 
    check_derived_from<Z, W> cdf; // #3 
  };

Notes from the March, 2018 meeting:

The consensus of CWG was that the derived class must be complete at the point of the conversion, and thus all three attempted conversions in the example are ill-formed.

Proposed resolution (November, 2018):

  1. Change 7.3.12 [conv.ptr] paragraph 3 as follows:

  2. A prvalue of type “pointer to cv D”, where D is a complete class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class (11.7 [class.derived]) of D. If B is an inaccessible...
  3. Change 7.3.13 [conv.mem] paragraph 2 as follows:

  4. A prvalue of type “pointer to member of B of type cv T”, where B is a class type, can be converted to a prvalue of type “pointer to member of D of type cv T”, where D is a complete class derived class (11.7 [class.derived]) of from B. If B is an inaccessible...
  5. Change 7.6.1.9 [expr.static.cast] paragraphs 11 and 12 as followed:

  6. A prvalue of type “pointer to cv1 B”, where B is a complete class type, can be converted to a prvalue of type “pointer to cv2 D”, where D is a class derived (11.7 [class.derived]) from B , if cv2 is the same...

    A prvalue of type “pointer to member of D of type cv1 T” can be converted to a prvalue of type “pointer to member of B of type cv2 T”, where B is a base class (11.7 [class.derived]) of a complete class D, if cv2 is the same...




2133. Converting std::nullptr_t to bool

Section: 7.3.14  [conv.fctptr]     Status: CD5     Submitter: Richard Smith     Date: 2015-05-28

[Accepted as a DR at the November, 2018 (San Diego) meeting.]

According to 7.3.14 [conv.fctptr] paragraph 1,

For direct-initialization (9.4 [dcl.init]), a prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.

The mention of direct-initialization in this context (added by issue 1423) seems odd; standard conversions are on a level below initialization. Should this wording be moved to 9.4 [dcl.init], perhaps as a bullet in paragraph 1?

(See also issue 1781.)

Proposed resolution (June, 2018):

This issue is resolved by the resolution of issue 1781.




2249. identifiers and id-expressions

Section: 7.5.4.2  [expr.prim.id.unqual]     Status: CD5     Submitter: Robert Haberlach     Date: 2016-03-17

[Accepted as a DR at the June, 2018 (Rapperswil) meeting.]

According to 7.5.4.2 [expr.prim.id.unqual] paragraph 1,

An identifier is an id-expression provided it has been suitably declared (Clause 9 [dcl.dcl]).

Not only is an identifier an id-expression by (grammatical) definition, declarator-id is defined in terms of id-expression, which makes this circular. If the intention was to disallow use of undeclared identifiers as primary expressions, this should be altered accordingly.

Proposed resolution, February, 2018:

Change 7.5.4.2 [expr.prim.id.unqual] paragraph 1 as follows:

An identifier is only an id-expression provided if it has been suitably declared ( Clause 9 [dcl.dcl]) or if it appears as part of a declarator-id (9.3 [dcl.decl]). [Note: For operator-function-ids, see...



2385. Lookup for conversion-function-ids

Section: 7.5.4.3  [expr.prim.id.qual]     Status: CD5     Submitter: John Spicer     Date: 2018-07-31

[Accepted as a DR at the February, 2019 meeting.]

According to 7.5.4.3 [expr.prim.id.qual] paragraph 5,

In a qualified-id, if the unqualified-id is a conversion-function-id, its conversion-type-id shall denote the same type in both the context in which the entire qualified-id occurs and in the context of the class denoted by the nested-name-specifier.

However, _N4868_.6.5.6 [basic.lookup.classref] paragraph 7 says,

If the id-expression is a conversion-function-id, its conversion-type-id is first looked up in the class of the object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire postfix-expression. In each of these lookups, only names that denote types or templates whose specializations are types are considered.

The latter was changed by issue 1111. It seems the former may have been overlooked in that change.

Proposed resolution (February, 2019):

Change 7.5.4.3 [expr.prim.id.qual] paragraph 4 as follows:

In a qualified-id, if the unqualified-id is a conversion-function-id, its conversion-type-id shall denote the same type in both the context in which the entire qualified-id occurs and in the context of the class denoted by the nested-name-specifier is first looked up in the class denoted by the nested-name-specifier of the qualified-id and the name, if found, is used. Otherwise, it is looked up in the context in which the entire qualified-id occurs. In each of these lookups, only names that denote types or templates whose specializations are types are considered.



1913. decltype((x)) in lambda-expressions

Section: 7.5.5  [expr.prim.lambda]     Status: CD5     Submitter: Dinka Ranns     Date: 2014-04-15

[Accepted as a DR as part of paper P0588R1 at the October, 2017 meeting.]

According to 7.5.5 [expr.prim.lambda] paragraph 19,

Every occurrence of decltype((x)) where x is a possibly parenthesized id-expression that names an entity of automatic storage duration is treated as if x were transformed into an access to a corresponding data member of the closure type that would have been declared if x were an odr-use of the denoted entity.

This formulation is problematic because it assumes that x can be captured and, if captured, would result in a member of the closure class. The former is not true if the lambda has no capture-default, and the latter is not guaranteed if the capture-default is &.




1931. Default-constructible and copy-assignable closure types

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD5     Submitter: Ryou Ezoe     Date: 2014-05-24

[Accepted at the November, 2017 meeting as part of paper P0624R2.]

It would be more consistent if the closure type of a lambda expression with no lambda-capture were default-constructible and copy-assignable, since such lambdas are already convertible to a function pointer, which has those characteristics.

Rationale (June, 2014):

CWG was sympathetic to this request, although it felt that the conversion to a function pointer was irrelevant to this question. However, EWG should examine this request for a language change before any action is taken.




1937. Incomplete specification of function pointer from lambda

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: CD5     Submitter: Richard Smith     Date: 2014-06-05

[Accepted as a DR at the February, 2019 meeting.]

According to 7.5.5 [expr.prim.lambda] paragraph 6,

The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function with C ++ language linkage (9.11 [dcl.link]) having the same parameter and return types as the closure type's function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type's function call operator.

This does not mention the object for which the function call operator would be invoked (although since there is no capture, presumably the function call operator makes no use of the object pointer). This could be addressed by relating the behavior of the function call operator to a notional temporary, or the function call operator for such closure classes could be made static.

Proposed resolution (January, 2019):

  1. Change 7.5.5.2 [expr.prim.lambda.closure] paragraph 7 as follows, splitting it into two paragraphs:

  2. ...The value returned by this conversion function is the address of a function F that, when invoked, has the same effect as invoking the closure type's function call operator on a default-constructed instance of the closure type. F is a constexpr function if the function call operator is a constexpr function.

    For a generic lambda with no lambda-capture, the closure type has...

  3. Change 7.5.5.2 [expr.prim.lambda.closure] paragraph 9 as follows:

  4. The value returned by any given specialization of this conversion function template is the address of a function F that, when invoked, has the same effect as invoking the generic lambda's corresponding function call operator template specialization on a default-constructed instance of the closure type. F is a constexpr function if the corresponding specialization is a constexpr function and is an immediate function if the function call operator template specialization is an immediate function. [Note: This will result...



1468. typeid, overload resolution, and implicit lambda capture

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: CD5     Submitter: Doug Gregor     Date: 2012-02-08

[Resolved by the adoption of P0588R1 at the November, 2017 meeting.]

According to 7.5.5 [expr.prim.lambda] paragraph 11, a variable is implicitly captured if it is odr-used. In the following example,

  struct P {
   virtual ~P();
  };

  P &f(int&);
  int f(const int&);

  void g(int x) {
   [=] {
    typeid(f(x));
   };
  }

x is only odr-used if the operand of typeid is a polymorphic lvalue; otherwise, the operand is unevaluated (7.6.1.8 [expr.typeid] paragraphs 2-3). Whether the operand is a polymorphic lvalue depends on overload resolution in this case, which depends on whether x is captured or not: if x is captured, since the lambda is not mutable, the type of x in the body of the lambda is const int, while if it is not captured, it is just int. However, the const int version of f returns int and the int version of f returns a polymorphic lvalue, leading to a conundrum: x is only captured if it is not captured, and vice versa.

Notes from the October, 2012 meeting:

The approach favored by CWG was to specify that the operand of typeid is considered to be odr-used for the purpose of determining capture.




1632. Lambda capture in member initializers

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: CD5     Submitter: Vinny Romano     Date: 2013-03-04

[Accepted as a DR as part of paper P0588R1 at the October, 2017 meeting.]

According to 7.5.5 [expr.prim.lambda] paragraph 9,

A lambda-expression whose smallest enclosing scope is a block scope (6.4.3 [basic.scope.block]) is a local lambda expression; any other lambda-expression shall not have a capture-list in its lambda-introducer. The reaching scope of a local lambda expression is the set of enclosing scopes up to and including the innermost enclosing function and its parameters.

Consequently, lambdas appearing in mem-initializers and brace-or-equal-initializers cannot have a capture-list. However, these expressions are evaluated in the context of the constructor and are permitted to access this and non-static data members.

Should the definition of a local lambda be modified to permit capturing lambdas within these contexts?

Notes from the April, 2013 meeting:

CWG agreed with the intent of this issue.




2358. Explicit capture of value

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: CD5     Submitter: Daveed Vandevoorde     Date: 2017-09-20

[Accepted as a DR at the February, 2019 meeting.]

The status of an example like the following is unclear:

  int foo(int a = ([loc=1] { return loc; })()) { return a; }

because of 7.5.5.3 [expr.prim.lambda.capture] paragraph 9:

A lambda-expression appearing in a default argument shall not implicitly or explicitly capture any entity.

However, there doesn't appear to be a good reason for prohibiting such a capture.

Notes from the April, 2018 teleconference:

CWG felt that the rule for capturing should be something like the prohibition for local classes odr-using a variable with automatic storage duration in 11.6 [class.local] paragraph 1.

Proposed resolution (November, 2018):

Change 7.5.5.3 [expr.prim.lambda.capture] paragraph 9 as follows:

A lambda-expression appearing in a default argument shall not implicitly or explicitly capture any entity, except for an init-capture for which any full-expression in its initializer satisfies the constraints of an expression appearing in a default argument (9.3.4.7 [dcl.fct.default]). [Example:

  void f2() {
    int i = 1;
    void g1(int = ([i]{ return i; })());       // ill-formed
    void g2(int = ([i]{ return 0; })());       // ill-formed
    void g3(int = ([=]{ return i; })());       // ill-formed
    void g4(int = ([=]{ return 0; })());       // OK
    void g5(int = ([]{ return sizeof i; })()); // OK
    void g6(int = ([x=1] { return x; }))();    // OK
    void g7(int = ([x=i] { return x; }))();    // ill-formed
  }

end example]




1646. decltype-specifiers, abstract classes, and deduction failure

Section: 7.6.1.3  [expr.call]     Status: CD5     Submitter: Jason Merrill     Date: 2013-03-28

[Adopted at the June, 2018 meeting as part of paper P0929R2.]

According to 7.6.1.3 [expr.call] paragraph 11,

If a function call is a prvalue of object type:

Thus, an example like

  template <class T> struct A: T { };
  template <class T> A<T> f(T) { return A<T>(); };
  decltype(f(42)) *p;

is well-formed. However, a function template specialization in which the return type is an abstract class should be a deduction failure, per 13.10.3 [temp.deduct] paragraph 8, last bullet:

The requirement that the return type in a function call in a decltype-specifier not be instantiated prevents the detection of this deduction failure in an example like:

  template <class T> struct A { virtual void f() = 0; };
  template <class T> A<T> f(T) { return A<T>(); };
  decltype(f(42)) *p;

It is not clear how this should be resolved.

(See also issue 1640.)




2241. Overload resolution is not invoked with a single function

Section: 7.6.1.3  [expr.call]     Status: CD5     Submitter: CWG     Date: 2016-03-04

Various aspects of overload resolution, such as determining viable functions, specification of conversion sequences, etc., should apply when there is only one function in the overload set, but currently overload resolution is only invoked to choose among multiple functions.

Proposed resolution (March, 2018):

This issue is resolved by the resolution of issue 2092.




943. Is T() a temporary?

Section: 7.6.1.4  [expr.type.conv]     Status: CD5     Submitter: Miller     Date: 14 July, 2009

[Voted into the WP at the July, 2017 meeting as document P0727R0.]

According to 7.6.1.4 [expr.type.conv] paragraphs 1 and 3 (stated directly or by reference to another section of the Standard), all the following expressions create temporaries:

    T(1)
    T(1, 2)
    T{1}
    T{}

However, paragraph 2 says,

The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete effective object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, which is value-initialized (9.4 [dcl.init]; no initialization is done for the void() case).

This does not say that the result is a temporary, which means that the lifetime of the result is not specified by 6.7.7 [class.temporary]. Presumably this is just an oversight.

Notes from the October, 2009 meeting:

The specification in 7.6.1.4 [expr.type.conv] is in error, not because it fails to state that T() is a temporary but because it requires a temporary for the other cases with fewer than two operands. The case where T is a class type is covered by 6.7.7 [class.temporary] paragraph 1 (“a conversion that creates an rvalue”), and a temporary should not be created when T is not a class type.

Proposed resolution (March, 2017):

This issue is resolved by the resolution of issue 1299.




1893. Function-style cast with braced-init-lists and empty pack expansions

Section: 7.6.1.4  [expr.type.conv]     Status: CD5     Submitter: Richard Smith     Date: 2014-03-13

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

According to 7.6.1.4 [expr.type.conv] paragraph 1,

A simple-type-specifier (9.2.9.3 [dcl.type.simple]) or typename-specifier (13.8 [temp.res]) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (7.6.3 [expr.cast]). If the type specified is a class type, the class type shall be complete. If the expression list specifies more than a single value, the type shall be a class with a suitably declared constructor (9.4 [dcl.init], 11.4.5 [class.ctor]), and the expression T(x1, x2, ...) is equivalent in effect to the declaration T t(x1, x2, ...); for some invented temporary variable t, with the result being the value of t as a prvalue.

This does not cover the cases when the expression-list contains a single braced-init-list (which is neither an expression nor more than a single value) or if it contains no expressions as the result of an empty pack expansion.

Proposed resolution (June, 2014): [SUPERSEDED]

This issue is resolved by the resolution of issue 1299.

Proposed resolution (November, 2017)

Change 7.6.1.4 [expr.type.conv] paragraph 2 as follows:

If the initializer is a parenthesized single expression, the type conversion expression is equivalent to the corresponding cast expression (7.6.3 [expr.cast]). Otherwise, if the type is cv void and the initializer is () (after pack expansion, if any), the expression is a prvalue of the specified type that performs no initialization. Otherwise, the expression is a prvalue of the specified type whose result object is direct-initialized (9.4 [dcl.init]) with the initializer. For an expression of the form T(), T If the initializer is a parenthesized optional expression-list, the specified type shall not be an array type.



2351. void{}

Section: 7.6.1.4  [expr.type.conv]     Status: CD5     Submitter: Peter Dimov     Date: 2017-06-26

[Accepted as a DR at the June, 2018 (Rapperswil) meeting.]

According to 7.6.1.4 [expr.type.conv] paragraph 2,

If the initializer is a parenthesized single expression, the type conversion expression is equivalent to the corresponding cast expression (7.6.3 [expr.cast]). Otherwise, if the type is cv void and the initializer is () , the expression is a prvalue of the specified type that performs no initialization. Otherwise, the expression is a prvalue of the specified type whose result object is direct-initialized (9.4 [dcl.init]) with the initializer. For an expression of the form T(), T shall not be an array type.

It seems an oversight that void{} is not treated like void().

Proposed resolution, April, 2018:

Change 7.6.1.4 [expr.type.conv] paragraph 2 as follows:

...Otherwise, if the type is cv void and the initializer is () or {}, the expression is a prvalue of the specified type that performs no initialization. Otherwise...



2365. Confusing specification for dynamic_cast

Section: 7.6.1.7  [expr.dynamic.cast]     Status: CD5     Submitter: Shiyao Ma     Date: 2017-02-08

[Accepted as a DR at the February, 2019 meeting.]

From editorial issue 1453.

According to 7.6.1.7 [expr.dynamic.cast] paragraph 4,

If the value of v is a null pointer value in the pointer case, the result is the null pointer value of type T.

Paragraph 5 deals with the case of a simple up-cast, where no runtime type identification is required. Paragraph 6 says,

Otherwise, v shall be a pointer to or a glvalue of a polymorphic type (11.7.3 [class.virtual]).

This organization of the material makes it sound as if the requirement for polymorphic class types does not apply if the argument is a null pointer value, which, of course, cannot be determined at compile time. The intent is that a null pointer value argument produces a null pointer value result, regardless of whether the relationship between the classes requires runtime type identification or not, but that the requirement for polymorphic classes applies for all casts that are not simple up-casts. The wording should be clarified.

Proposed resolution (November, 2018):

Change 7.6.1.7 [expr.dynamic.cast] paragraphs 4-6 as follows:

If the value of v is a null pointer value in the pointer case, the result is the null pointer value of type T.

If T is “pointer to cv1 B” and v has type “pointer to cv2 D” such that B is a base class of D, the result is a pointer to the unique B subobject of the D object pointed to by v, or a null pointer value if v is a null pointer value. Similarly, if T is...

Otherwise, v shall be a pointer to or a glvalue of a polymorphic type (11.7.3 [class.virtual]).

If v is a null pointer value, the result is a null pointer value.




2338. Undefined behavior converting to short enums with fixed underlying types

Section: 7.6.1.9  [expr.static.cast]     Status: CD5     Submitter: Thomas Köppe     Date: 2017-03-24

[Accepted as a DR at the November, 2017 meeting.]

The specifications of std::byte (17.2.5 [support.types.byteops]) and bitmask (16.3.3.3.3 [bitmask.types]) have revealed a problem with the integral conversion rules, according to which both those specifications have, in the general case. undefined behavior. The problem is that a conversion to an enumeration type has undefined behavior unless the value to be converted is in the range of the enumeration.

For enumerations with an unsigned fixed underlying type, this requirement is overly restrictive, since converting a large value to an unsigned integer type is well-defined.

Proposed resolution (August, 2017):

Change 7.6.1.9 [expr.static.cast] paragraph 10 as follows:

A value of integral or enumeration type can be explicitly converted to a complete enumeration type. The If the enumeration type has a fixed underlying type, the value is first converted to that type by integral conversion, if necessary, and then to the enumeration type. If the enumeration type does not have a fixed underlying type, the value is unchanged if the original value is within the range of the enumeration values (9.7.1 [dcl.enum]). Otherwise, and otherwise, the behavior is undefined.



2342. Reference reinterpret_cast and pointer-interconvertibility

Section: 7.6.1.10  [expr.reinterpret.cast]     Status: CD5     Submitter: Richard Smith     Date: 2017-04-07

[Accepted as a DR at the November, 2017 meeting.]

The changes from document P0137 make it clear that this is valid:

  struct A { int n; } a;
  int *p = reinterpret_cast<int*>(&a); // ok, a and a.n are pointer-interconvertible
  int m = *p;                          // ok, p points to a.n

but the handling for that case does not extend to this one:

  int &r = reinterpret_cast<int&>(a);
  int n = r;

The relevant rule is 7.6.1.10 [expr.reinterpret.cast] paragraph 11:

A glvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result refers to the same object as the source glvalue, but with the specified type. [Note: That is, for lvalues, a reference cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with the built-in & and * operators (and similarly for reinterpret_cast<T&&>(x)). —end note]

Note that the normative rule and the note specify different rules: under the rule described in the note, the result would be a reference to the object a.n. According to the normative rule, however, we get a reference to the object a with type n.

Proposed resolution (July, 2017):

Change 7.6.1.10 [expr.reinterpret.cast] paragraph 11 as follows:

A glvalue expression of type T1, designating an object x, can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result refers to the same object as the source glvalue, but with the specified type is that of *reinterpret_cast<T2 *>(p) where p is a pointer to x of type “pointer to T1. [Note: That is, for lvalues, a reference cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with the built-in & and * operators (and similarly for reinterpret_cast<T&&>(x)). —end note] No temporary is created, no copy is made, and constructors (11.4.5 [class.ctor]) or conversion functions (11.4.8 [class.conv]) are not called. [Footnote: This is sometimes referred to as a type pun when the result refers to the same object as the source glvalue. —end footnote]



476. Determining the buffer size for placement new

Section: 7.6.2.8  [expr.new]     Status: CD5     Submitter: Ben Hutchings     Date: 14 Sep 2004

[Resolved by issue 2382, which was accepted as a DR at the November, 2019 meeting.]

(See also issue 256.)

An implementation may have an unspecified amount of array allocation overhead (7.6.2.8 [expr.new] paragraph 10), so that evaluation of a new-expression in which the new-type-id is T[n] involves a request for more than n * sizeof(T) bytes of storage through the relevant operator new[] function.

The placement operator new[] function does not and cannot check whether the requested size is less than or equal to the size of the provided region of memory (17.6.3.4 [new.delete.placement] paragraphs 5-6). A program using placement array new must calculate what the requested size will be in advance.

Therefore any program using placement array new must take into account the implementation's array allocation overhead, which cannot be obtained or calculated by any portable means.

Notes from the April, 2005 meeting:

While the CWG agreed that there is no portable means to accomplish this task in the current language, they felt that a paper is needed to analyze the numerous mechanisms that might address the problem and advance a specific proposal. There is no volunteer to write such a paper at this time.

Note, January, 2012:

It has been suggested that this issue is more appropriate for EWG and should thus be closed with "extension" status.

Rationale (February, 2012):

The CWG agreed that EWG is the appropriate venue for dealing with this issue.

Additional note (November, 2019):

The resolution of issue 2382 effectively addresses the request in this issue by eliminating the array overhead in the case of the standard placement allocator.

EWG (January, 2021):

Resolved by issue 2382. See vote.




1469. Omitted bound in array new-expression

Section: 7.6.2.8  [expr.new]     Status: CD5     Submitter: Johannes Schaub     Date: 2012-02-12

[Adopted at the February, 2019 meeting as part of paper P1009R2.]

The syntax for noptr-new-declarator in 7.6.2.8 [expr.new] paragraph 1 requires an expression, even though the bound could be inferred from a braced-init-list initializer. It is not clear whether 9.4.2 [dcl.init.aggr] paragraph 4,

An array of unknown size initialized with a brace-enclosed initializer-list containing n initializer-clauses, where n shall be greater than zero, is defined as having n elements (9.3.4.5 [dcl.array]).

should be considered to apply to the new-type-id variant, e.g.,

  new (int[]){1, 2, 3}

Additional note (August, 2012):

The consensus during the 2012-08-13 drafting review teleconference was that this issue should be referred to the Evolution Working Group and not handled by the Core Working Group.




1935. Reuse of placement arguments in deallocation

Section: 7.6.2.8  [expr.new]     Status: CD5     Submitter: Hubert Tong     Date: 2014-06-04

[Resolved by CWG1880 (November, 2014) and CWG2177 (November, 2017).]

The description in 7.6.2.8 [expr.new] paragraph 23 regarding calling a deallocation function following an exception during the initialization of an object resulting from a placement new-expression says,

If a placement deallocation function is called, it is passed the same additional arguments as were passed to the placement allocation function, that is, the same arguments as those specified with the new-placement syntax. If the implementation is allowed to make a copy of any argument as part of the call to the allocation function, it is allowed to make a copy (of the same original value) as part of the call to the deallocation function or to reuse the copy made as part of the call to the allocation function. If the copy is elided in one place, it need not be elided in the other.

This seems curious, as it allows reuse of a parameter object that presumably is destroyed immediately upon the return of the allocation function (but see issue 1880 for a question about the timing of such destructions).

Notes from the November, 2014 meeting:

The resolution for issue 1880 should mostly resolve this issue. The resolution should handle the case in which an object can only be constructed into the parameter object and neither copied nor moved.

Additional notes (July, 2022):

Issue 1880 permitted the destruction of parameter objects at the end of the full-expression enclosing the function call. Issue 2177 clarified the exceptions to the parameter object reuse.

Additional notes (CWG teleconference 2022-08-12):

This issue is resolved by issues 1880 and 2177. Reusing the parameter objects of the placement-new call for the placement-delete invocation is intentional.




2112. new auto{x}

Section: 7.6.2.8  [expr.new]     Status: CD5     Submitter: Richard Smith     Date: 2015-04-03

[Adopted at the February, 2017 meeting as part of paper P0620R0.]

According to 7.6.2.8 [expr.new] paragraph 2,

If a placeholder type (9.2.9.7 [dcl.spec.auto]) appears in the type-specifier-seq of a new-type-id or type-id of a new-expression, the new-expression shall contain a new-initializer of the form

Now that auto v{x}; is permitted, this restriction on new-expressions should be revised to allow a single-element braced-init-list as well.




2177. Placement operator delete and parameter copies

Section: 7.6.2.8  [expr.new]     Status: CD5     Submitter: Richard Smith     Date: 2015-09-30

[Accepted as a DR at the November, 2017 meeting.]

Consider the following example:

  void *operator new(size_t n, std::string s) {
    std::string t = std::move(s);
    std::cout << "new " << t << std::endl;
    return operator new(n);
  }
  void operator delete(void*, std::string s) {
    std::cout << "delete " << s << std::endl;
  }
  struct X { X() { throw 0; } };
  int main() {
    try {
      new ("longer than the small string buffer") X();
    } catch (...) {}
  }

Current implementations print

  new longer than the small string buffer
  delete

because the same std::string object is used for both the new and delete calls. We should introduce additional copies to separate out the parameters in this case or make non-trivially-copyable parameter types ill-formed here.

Notes from the October, 2015 meeting:

CWG favored limiting the parameters of an overloaded deallocation function to trivially-copyable types.

Proposed resolution (October, 2017):

Change 7.6.2.8 [expr.new] paragraph 24 as follows:

If a new-expression calls a deallocation function, it passes the value returned from the allocation function call as the first argument of type void*. If a placement deallocation function is called, it is passed the same additional arguments as were passed to the placement allocation function, that is, the same arguments as those specified with the new-placement syntax. If the implementation is allowed to introduce a temporary object or make a copy of any argument as part of the call to the allocation function, it is allowed to make a copy (of the same original value) as part of the call to the deallocation function or to reuse the copy made as part of the call to the allocation function. If the copy is elided in one place, it need not be elided in the other unspecified whether the same object is used in the call to both the allocation and deallocation functions,



2382. Array allocation overhead for non-allocating placement new

Section: 7.6.2.8  [expr.new]     Status: CD5     Submitter: Paul Sanders     Date: 2018-07-16

[Accepted as a DR at the November, 2019 meeting.]

According to 7.6.2.8 [expr.new] paragraph 12,

When a new-expression calls an allocation function and that allocation has not been extended, the new-expression passes the amount of space requested to the allocation function as the first argument of type std::size_t . That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array. For arrays of char, unsigned char, and std::byte, the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the strictest fundamental alignment requirement (6.7.6 [basic.align]) of any object type whose size is no greater than the size of the array being created.

There is no exemption for the non-allocating (void*,size_t) placement-new allocation function, so programs must allow for the possibility that the provided buffer may need to be larger (by an indeterminate amount) than the size of an array placed into existing storage.

Should the non-allocating placement-new allocation function be exempt from the array allocation overhead? (This question was explicitly referred to CWG by the EWG chair.)

Proposed resolution (February, 2019):

  1. Change 7.6.2.8 [expr.new] paragraph 12 as follows:

  2. When a new-expression calls an allocation function and that allocation has not been extended, the new-expression passes the amount of space requested to the allocation function as the first argument of type std::size_t . That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array and the allocation function is not a non-allocating form (17.6.3.4 [new.delete.placement]). For arrays of...
  3. Change 7.6.2.8 [expr.new] paragraph 16 as follows:

  4. ...Here, each instance of x is a non-negative unspecified value representing array allocation overhead; the result of the new-expression will be offset by this amount from the value returned by operator new[]. This overhead may be applied in all array new-expressions, including those referencing a placement allocation function, but not when referencing the library function operator new[](std::size_t, void*) and other placement allocation functions. The amount of overhead may vary from one invocation of new to another. —end example]

(This resolution effectively resolves issue 476, which was closed for EWG input.)




1857. Additional questions about bits

Section: 7.6.7  [expr.shift]     Status: CD5     Submitter: Tony Van Eerd     Date: 2014-02-12

[Adopted at the November, 2018 meeting as part of paper P1236R1.]

The resolution of issue 1796 addressed only the relationship of “bits” with the null character value. The values and arrangements of bits within an object are also mentioned in other contexts; these should also be considered for revision. For example, 7.6.7 [expr.shift] paragraph 2 says,

The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled.

This appears to place constraints on the bit representation, which (as noted in issue 1796) is not accessible to the program. A similar statement appears in paragraph 3 for >>.

The specification of the bitwise operations in 7.6.11 [expr.bit.and], 7.6.12 [expr.xor], and 7.6.13 [expr.or] uses the undefined term “bitwise” in describing the operations, without specifying whether it is the value or object representation that is in view.

Part of the resolution of this might be to define “bit” (which is otherwise currently undefined in C++) as a value of a given power of 2.

Notes from the June, 2014 meeting:

CWG decided to reformulate the description of the operations themselves to avoid references to bits, splitting off the larger questions of defining “bit” and the like to issue 1943 for further consideration.




2226. Xvalues vs lvalues in conditional expressions

Section: 7.6.16  [expr.cond]     Status: CD5     Submitter: Richard Smith     Date: 2016-02-01

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

In the following example,

   const T a;
   T b;
   false ? a : std::move(b);

the most appropriate result would seem to be that the expression is an lvalue of type const T that refers to either a or b. However, because 7.6.16 [expr.cond] bullet 4.1 requires that the conversion bind directly to an lvalue, while std::move(b) is an xvalue, the result is a const T temporary copy-initialized from std::move(b).

Proposed resolution (November, 2017)

Change 7.6.16 [expr.cond] bullet 4.1 as follows:

Attempts are made to form an implicit conversion sequence from an operand expression E1 of type T1 to a target type related to the type T2 of the operand expression E2 as follows:




2321. Conditional operator and cv-qualified class prvalues

Section: 7.6.16  [expr.cond]     Status: CD5     Submitter: Richard Smith     Date: 2016-09-06

[Accepted as a DR at the June, 2018 (Rapperswil) meeting.]

The following example is ill-formed:

  struct A {};
  struct B : A {};
  using T = const B;
  A a = true ? A() : T();

We don't convert from A to T because we can't form an implicit conversion sequence. We don't convert from T to A because T is more cv-qualified (even though we could form an implicit conversion sequence). It would seem reasonable to accept this case; it seems that we should only be using cv-qualifiers as a tie-breaker if the class types are otherwise the same.

Proposed resolution (March, 2018):

Change 7.6.16 [expr.cond] bullet 4.3 as follows:

...Attempts are made to form an implicit conversion sequence from an operand expression E1 of type T1 to a target type related to the type T2 of the operand expression E2 as follows:




2399. Unclear referent of “expression” in assignment-expression

Section: 7.6.19  [expr.ass]     Status: CD5     Submitter: Lisa Lippincott     Date: 2019-02-13

[Adopted as a DR at the November, 2019 meeting.]

According to 7.6.19 [expr.ass] paragraph 3,

If the left operand is not of class type, the expression is implicitly converted (7.3 [conv]) to the cv-unqualified type of the left operand.

Since the second operand of an assignment operator can now be an initializer-clause, the referent of “expression” is unclear.

See also issue 1542.

Proposed resolution (May, 2019): [SUPERSEDED]

Change 7.6.19 [expr.ass] paragraph 3 as follows:

If the left operand is not of class type and the right operand is an assignment-expression, the expression assignment-expression is implicitly converted (7.3 [conv]) to the cv-unqualified type of the left operand.

Proposed resolution (October, 2019):

Change 7.6.19 [expr.ass] paragraph 3 as follows:

The expression If the right operand is an expression, it is implicitly converted (7.3 [conv]) to the cv-unqualified type of the left operand.



2278. Copy elision in constant expressions reconsidered

Section: 7.7  [expr.const]     Status: CD5     Submitter: Richard Smith     Date: 2016-06-27

[Accepted as a DR at the February, 2019 meeting.]

The resolution of issue 2022 does not work, as it is mathematically impossible to guarantee the named return value optimization in all cases. For example:

  struct B { B *self = this; };
  extern const B b;
  constexpr B f() {
    B b;
    if (&b == &::b) return B();
    else return b;
  }
  constexpr B b = f(); // is b.self == &b?

Here an implementation is required to perform the optimization if and only if it does not perform the optimization.

The resolution would appear to be to reverse the resolution of issue 2022 and guarantee that named return value optimization is not performed in constant expression evaluation.

Notes from the March, 2018 meeting:

CWG concurred with the suggested direction.

Proposed resolution (November, 2018)

  1. Change 7.7 [expr.const] paragraph 1 as follows:

  2. Expressions that satisfy these requirements, assuming that copy elision (11.9.6 [class.copy.elision]) is not performed, are called constant expressions. [Note: Constant expressions can be evaluated during translation. —end note]
  3. Change 9.2.6 [dcl.constexpr] paragraph 7 as follows:

  4. A call to a constexpr function produces the same result as a call to an equivalent non-constexpr function in all respects except that

  5. Change 11.9.6 [class.copy.elision] paragraph 1 as follows:

  6. ...Copy elision is required not permitted where an expression is evaluated in a context requiring a constant expression (7.7 [expr.const]) and in constant initialization (6.9.3.2 [basic.start.static]). [Note: Copy elision might not be performed if the same expression is evaluated in another context. —end note]



2368. Differences in relational and three-way constant comparisons

Section: 7.7  [expr.const]     Status: CD5     Submitter: Richard Smith     Date: 2017-11-11

[Accepted as a DR at the February, 2019 meeting.]

According to 7.7 [expr.const] bullets 2.21 and 2.22, the characteristics of three-way and relational comparisons that disqualify them as constant expressions are different:

These are not equivalent, with odd results:

  struct A {
    int a;
  private:
    int b;
    constexpr auto f() { return &a < &b; }   // not constant 
    constexpr auto g() { return &a <=> &b; } // returns unspecified value 
  };

Similarly,

  struct B { int n; };
  struct C : B { int m; } c;
  constexpr auto x = &c.n < &c.m;   // not constant 
  constexpr auto y = &c.n <=> &c.m; // returns unspecified value 

The three-way rule seems to be the correct one, but additional wording is needed in 7.6.9 [expr.rel] to specify the relational ordering within a single object: addresses of subobjects of the same complete object should be weakly ordered, and when restricted to subobjects that are not permitted to have the same address, should be totally ordered.

Notes from the October, 2018 teleconference:

The consensus of CWG was to make the 3-way operator cases non-constant, as the relational cases are.

Proposed resolution (November, 2018):

Change 7.7 [expr.const] bullets 2.22 and 2.23 as follows, merging tbe bullets:

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.9.1 [intro.execution]), would evaluate one of the following expressions:




2400. Constexpr virtual functions and temporary objects

Section: 7.7  [expr.const]     Status: CD5     Submitter: Daveed Vandevoorde     Date: 2019-02-08

[Accepted as a DR at the July, 2019 meeting.]

Consider an example like the following:

  struct A {
    constexpr virtual int f() const {
      return 1;
    }
  };

  struct B : A {
    constexpr virtual int f() const {
      return 2;
    }
  };

  constexpr B b{};
  constexpr A&& ref = (B)b;

  static_assert(ref.f() == 2, "");

Since the temporary bound to ref is non-const, it can be re-newed to something else, which would make the invocation ref.f() undefined behavior, which the interpreter is required to catch.

Presumably, ref.f() should not be a constant expression, and 7.7 [expr.const] paragraph 2 should have a bullet for invoking a virtual function of a non-const object unless its lifetime began within the evaluation of the constant expression.

Proposed resolution (March, 2019):

Add the following as a new bullet following 7.7 [expr.const] bullet 4.4:

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.9.1 [intro.execution]), would evaluate one of the following expressions:




2418. Missing cases in definition of “usable in constant expressions”

Section: 7.7  [expr.const]     Status: CD5     Submitter: Richard Smith     Date: 2018-11-25

[Accepted as a DR at the July, 2019 meeting.]

The term “usable in constant expressions” (7.7 [expr.const] paragraph 3) is only defined for variables:

A variable is usable in constant expressions after its initializing declaration is encountered if it is a constexpr variable, or it is of reference type or of const-qualified integral or enumeration type, and its initializer is a constant initializer.

However, uses of the term assume that it applies more widely. For example, 7.7 [expr.const] bullet 4.7.1 mentions “a non-volatile glvalue that refers to an object that is usable in constant expressions” (not all objects are variables), and bullet 4.10.1 speaks of a “data member of reference type” (also not a variable) that is usable in constant expressions.

Proposed resolution, June, 2019:

Change 7.7 [expr.const] paragraph 3 as follows:

A variable is usable in constant expressions after its initializing declaration is encountered if it is a constexpr variable, or it is of reference type or of const-qualified integral or enumeration type, and its initializer is a constant initializer. An object or reference is usable in constant expressions if it is




2345. Jumping across initializers in init-statements and conditions

Section: 8.5.2  [stmt.if]     Status: CD5     Submitter: John Spicer     Date: 2017-04-25

According to 8.5.2 [stmt.if] paragraph 1,

If the condition (8.5 [stmt.select]) yields true the first substatement is executed. If the else part of the selection statement is present and the condition yields false, the second substatement is executed. If the first substatement is reached via a label, the condition is not evaluated and the second substatement is not executed.

Although 8.8 [stmt.dcl] paragraph 3 forbids bypassing a declaration with initialization, a condition is not syntactically a declaration, and the permission to jump into a then clause and the statement that the condition “is not evaluated” could be read to indicate that a jump across a condition with initialization is permitted. Presumably the prohibition in 8.8 [stmt.dcl] would apply to an init-statement, since it can be a declaration syntactically, but one would expect the same restrictions to apply to both.

Notes from the April, 2018 teleconference:

This issue will be handled editorially (see editorial issue 1949) and will be left in "review" status until CWG verifies that the necessary changes have been made.




1523. Point of declaration in range-based for

Section: 8.6.5  [stmt.ranged]     Status: CD5     Submitter: John Spicer     Date: 2012-07-16

[Voted into the WP at the July, 2017 meeting.]

According to the general rule for declarations in 6.4.2 [basic.scope.pdecl] paragraph 1,

The point of declaration for a name is immediately after its complete declarator (9.3 [dcl.decl]) and before its initializer (if any), except as noted below.

However, the rewritten expansion of the range-based for statement in 8.6.5 [stmt.ranged] paragraph 1 contradicts this general rule, so that the index variable is not visible in the range-init:

  for (int i : {i}) ;   // error: i not in scope

(See also issue 1498 for another question regarding the rewritten form of the range-based for.)

Notes from the October, 2012 meeting:

EWG is discussing issue 900 and the outcome of that discussion should be taken into consideration in addressing this issue.

Notes from the April, 2013 meeting:

The approach favored by CWG for resolving this issue is to change the point of declaration of the variable in the for-range-declaration to be after the ).

Proposed resolution (May, 2017):

Add the following as a new pareagraph following 6.4.2 [basic.scope.pdecl] paragraph 9:

The point of declaration for a function-local predefined variable (9.5 [dcl.fct.def]) is immediately before the function-body of a function definition.

The point of declaration for the variable or the structured bindings declared in the for-range-declaration of a range-based for statement (8.6.5 [stmt.ranged]) is immediately after the for-range-initializer.

The point of declaration for a template parameter...




2341. Structured bindings with static storage duration

Section: 9.1  [dcl.pre]     Status: CD5     Submitter: John Spicer     Date: 2017-04-04

[Accepted at the February, 2019 meeting as part of paper P1091R3.]

According to 9.1 [dcl.pre] paragraph 8,

A simple-declaration with an identifier-list is called a structured binding declaration (9.6 [dcl.struct.bind]). The decl-specifier-seq shall contain only the type-specifier auto (9.2.9.7 [dcl.spec.auto]) and cv-qualifiers.

This precludes block-scope structured bindings of static storage duration. However, namespace-scope structured bindings are permitted, and since those have static storage duration, it seems inconsistent to prohibit them at block scope. This restriction also prohibits inline structured bindings, which could be useful.

On the other hand, allowing storage class specifiers raises the question of to what extent they apply to the bindings as opposed to the container variable. That's subtle, because in two out of three cases, the bindings are not variables.

A related issue is that the linkage of structured bindings at namespace scope is not specified.




2212. Typedef changing linkage after use

Section: 9.2.4  [dcl.typedef]     Status: CD5     Submitter: Richard Smith     Date: 2015-12-09

[Accepted at the July, 2019 meeting as part of paper P1766R1 (Mitigating minor modules maladies)].

Consider:

  typedef struct { void f() { extern decltype(*this) x; void *p = &x; } } X;

This forms a complete anonymous type, builds a variable of that type, which is declared, but not defined, then the variable is odr-used, then the class is given a typedef name for linkage purposes, which changes the linkage of the class and the variable.

More insidious examples can be constructed where the use of the class's linkage happens while parsing the body of the class, not one of its methods, so we can't entirely fix this by delaying the delayed parts of the class a bit longer.

It might be reasonable to expect an implementation to look ahead past the close brace for a typedef name for linkage when it sees typedef struct {, but the possibility that the typedef keyword might be after the close brace means even that would not be entirely correct.

One approach would be to make this ill-formed by fixating the linkage of the type at the point where it is used in a way that requires linkage, and giving an error when the linkage would change. Another approach would be to limit an anonymous type to the feature subset of C (no this, no member functions, no static members).




2299. constexpr vararg functions

Section: 9.2.6  [dcl.constexpr]     Status: CD5     Submitter: Daveed Vandevoorde     Date: 2016-04-11

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

It is not clear whether a constexpr function can be a vararg function or not. In particular, it is unclear if va_list is a literal type and whether va_start, va_arg, and va_end produce constant expressions.

Proposed resolution (November, 2017)

  1. Add a new bullet to the list in 7.7 [expr.const] paragraph 2, and update the text as follows:

  2. An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.9.1 [intro.execution]), would evaluate one of the following expressions:

    If e satisfies the constraints of a core constant expression, but evaluation of e would evaluate an operation that has undefined behavior as specified in Clause 16 [library] through Clause 33 [thread] of this document, or an invocation of the va_start macro (17.13.2 [cstdarg.syn]), it is unspecified whether e is a core constant expression.




2309. Restrictions on nested statements within constexpr functions

Section: 9.2.6  [dcl.constexpr]     Status: CD5     Submitter: Faisal Vali     Date: 2016-07-30

[Accepted as a DR at the February, 2019 meeting.]

Section 9.2.6 [dcl.constexpr] bullet 3.4 specifies a list of constructs that that the body of a constexpr function shall not contain. However, the meaning of the word “contain” is not clear. For example, are things appearing in the body of a nested constexpr lambda “contained” in the body of the constexpr function?

Proposed resolution (November, 2018):

  1. Add the following two paragraphs after Clause 8 [stmt.stmt] paragraph 1:

  2. ...The optional attribute-specifier-seq appertains to the respective statement.

    A substatement of a statement is one of the following:

    [Note: The compound-statement of a lambda-expression is not a substatement of the statement (if any) in which the lambda-expression lexically appears. —end note]

    A statement S1 encloses a statement S2 if

  3. Delete the following sentence from 8.5 [stmt.select] paragraph 1:

  4. In Clause 8 [stmt.stmt], the term substatement refers to the contained statement or statements that appear in the syntax notation.
  5. Change 9.2.6 [dcl.constexpr] bullet 3.3 as follows:

  6. The definition of a constexpr function shall satisfy the following requirements:




2332. template-name as simple-type-name vs injected-class-name

Section: 9.2.9.3  [dcl.type.simple]     Status: CD5     Submitter: Daveed Vandevoorde     Date: 2016-12-10

[Accepted as a DR at the February, 2019 meeting.]

Paper P0091R3 has the following example:

  template<typename T> struct X {
    template<typename Iter>
    X(Iter b, Iter e) { /* ... */ }

    template<typename Iter>
    auto foo(Iter b, Iter e) {
      return X(b, e); // X<U> to avoid breaking change
    }

    template<typename Iter>
    auto bar(Iter b, Iter e) {
      return X<Iter::value_type>(b, e); // Must specify what we want
    }
  };

The intent was presumably to avoid breaking existing code, but the new wording in 9.2.9.3 [dcl.type.simple] paragraph 2 appears to make the expression X(b, e) ill-formed:

A type-specifier of the form typenameopt nested-name-specifieropt template-name is a placeholder for a deduced class type (9.2.9.8 [dcl.type.class.deduct]). The template-name shall name a class template that is not an injected-class-name.

Suggested resolution:

Deleting the wording in question and replacing it with a cross-reference to 13.8.2 [temp.local], which makes it clear that the injected-class-name is a type-name and not a template-name in this context, would seem to address the problem adequately.

Proposed resolution (November, 2018):

Change 9.2.9.3 [dcl.type.simple] paragraph 2 as follows:

The simple-type-specifier auto is a placeholder for a type to be deduced (9.2.9.7 [dcl.spec.auto]). A type-specifier of the form typenameopt nested-name-specifieropt template-name is a placeholder for a deduced class type (9.2.9.8 [dcl.type.class.deduct]). The template-name shall name a class template that is not an injected-class-name. [Note: An injected-class-name is never interpreted as a template-name in contexts where a type-specifier may appear (13.8.2 [temp.local]). —end note] The other simple-type-specifiers specify...



2059. Linkage and deduced return types

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD5     Submitter: Richard Smith     Date: 2014-12-15

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

Use of function return type deduction makes it possible to define functions whose return type is a type without linkage. Although 6.6 [basic.link] paragraph 8 permits such a usage if the function is defined in the same translation unit as it is used, it may be helpful to consider changing the overall rules regarding the use of types with internal or no linkage. As an example, the following example permits access to a local static variable that has not been initialized:

  auto f() {
    static int n = 123;
    struct X { int &f() { return n; } };
    return X();
  }
  int &r = decltype(f())().f();

Notes from the February, 2016 meeting:

CWG agreed that the current rule in 6.6 [basic.link] paragraph 8 is unneeded; the ODR already prohibits use of an entity that is not defined in the current translation unit and cannot be defined in a different translation unit.

Proposed resolution (November, 2017)

Change 6.6 [basic.link] paragraph 8 as follows:

...A type without linkage shall not be used as the type of a variable or function with external linkage unless

[Note: In other words, a type without linkage contains a class or enumeration that cannot be named outside its translation unit. An entity with external linkage declared using such a type could not correspond to any other entity in another translation unit of the program and thus must be defined in the translation unit if it is odr-used. Also note that classes Classes with linkage may contain members whose types do not have linkage, and that typedef. Typedef names are ignored in the determination of whether a type has linkage. —end note] [Example:

  template <class T> struct B {
    void g(T) { }
    void h(T);
    friend void i(B, T) { }
  };

  void f() {
    struct A { int x; };  // no linkage
    A a = { 1 };
    B<A> ba;              // declares B<A>::g(A) and B<A>::h(A)
    ba.g(a);              // OK
    ba.h(a);              // error: B<A>::h(A) not defined; A cannot be named in the another translation unit
    i(ba, a);             // OK
  }

end example]




2081. Deduced return type in redeclaration or specialization of function template

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD5     Submitter: John Spicer     Date: 2015-02-05

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

9.2.9.7 [dcl.spec.auto] paragraph 13 says,

Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type.

The inverse should also be true (a specialization cannot use a placeholder type if the template used a non-placeholder), but this is not said explicitly.

Proposed resolution (November, 2017)

Change 9.2.9.7 [dcl.spec.auto] paragraph 11 as follows:

Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type. Similarly, redeclarations or specializations of a function or function template with a declared return type that does not use a placeholder type shall not use a placeholder. [Example:



1640. Array of abstract instance of class template

Section: 9.3.4.5  [dcl.array]     Status: CD5     Submitter: Richard Smith     Date: 2013-03-14

[Adopted at the June, 2018 meeting as part of papeer P0929R2.]

According to 9.3.4.5 [dcl.array] paragraph 1, an array declarator whose element type is an abstract class is ill-formed. However, if the element type is a class template specialization, it may not be known that the class is abstract; because forming an array of an incomplete type is permitted (6.8 [basic.types] paragraphs 5-6), the class template is not required to be instantiated in order to use it as an element type. The expected handling if the class template is later instantiated is unclear; should the compiler issue an error about the earlier array array type at the point at which the class template is instantiated?

This also affects overload resolution:

  template<typename> struct Abstract {
    virtual void f() = 0;
    typedef int type;
  };
  template<typename T> char &abstract_test(T[1]);      // #1
  template<typename T> char (&abstract_test(...))[2];  // #2
  // Abstract<int>::type n;
  static_assert(sizeof(abstract_test<Abstract<int>>(nullptr)) == 2, "");

Overload resolution will select #1 and fail the assertion; if the commented line is uncommented, there is implementation variance, but presumably #2 should be selected and satisfy the assertion.

These effects of completing the type are troublesome. Would it be better to allow array types of abstract element type and simply prohibit creation of objects of such arrays?

(See also issue 1646.)




2233. Function parameter packs following default arguments

Section: 9.3.4.7  [dcl.fct.default]     Status: CD5     Submitter: Richard Smith     Date: 2016-02-25

[Accepted as a DR at the June, 2018 (Rapperswil) meeting.]

The resolution of issue 777 attempts to make this valid:

   template<typename ...T> void f(int n = 0, T ...t);

However, it fails to do so, since any parameters resulting from the expansion of the pack would be ordinary parameters without default arguments following a parameter with a default argument, which is ill-formed. Thus only an empty pack would be usable with such a declaration, which violates the restriction against such contexts in 13.8 [temp.res] bullet 8.3.

Proposed resolution, February, 2018:

  1. Change 7.6.1.3 [expr.call] paragraph 4 as follows:

  2. When a function is called, each parameter (9.3.4.6 [dcl.fct]) shall be is initialized (9.4 [dcl.init], 11.4.5.3 [class.copy.ctor], 11.4.5 [class.ctor]) with its corresponding argument. If there is no corresponding argument, the default argument for the parameter is used; the program is ill-formed if one is not present. [Example:

      template<typename ...T> int f(int n = 0, T ...t);
      int x = f<int>();   // error: no argument for second function parameter
    

    end example] If the function is a non-static member function, the this parameter of the function (_N4868_.11.4.3.2 [class.this]) shall be is initialized with a pointer to the object of the call, converted as if by an explicit type conversion (7.6.3 [expr.cast]). [Note: There is no access or ambiguity checking...

  3. Change 9.3.4.7 [dcl.fct.default] paragraph 1 as follows:

  4. If an initializer-clause is specified in a parameter-declaration this initializer-clause is used as a default argument. [Note: Default arguments will be used in calls where trailing arguments are missing (7.6.1.3 [expr.call]). end note]
  5. Change 9.3.4.7 [dcl.fct.default] paragraph 4 as follows:

  6. For non-template functions, default arguments can be added in later declarations of a function in the same scope. Declarations in different scopes have completely distinct sets of default arguments. That is, declarations in inner scopes do not acquire default arguments from declarations in outer scopes, and vice versa. In a given function declaration, each parameter subsequent to a parameter with a default argument shall have a default argument supplied in this or a previous declaration, unless the parameter was expanded from a parameter pack, or shall be a function parameter pack. A default argument shall not be redefined by a later declaration (not even to the same value). [Example:

      void g(int = 0, ...);   // OK, ellipsis is not a parameter so it can follow
                              // a parameter with a default argument
      void f(int, int);
      void f(int, int = 7);
      void h() {
        f(3);                 // OK, calls f(3, 7)
        void f(int = 1, int); // error: does not use default from surrounding scope
      }
      void m() {
        void f(int, int);     // has no defaults
        f(4);                 // error: wrong number of arguments
        void f(int, int = 5); // OK
        f(4); // OK, calls f(4, 5);
        void f(int, int = 5); // error: cannot redefine, even to same value
      }
      void n() {
        f(6);                 // OK, calls f(6, 7)
      }
      template<class ... T> struct C {
        void f(int n = 0, T...);
      };
      C<int> c;               // OK; instantiates declaration void C::f(int n = 0, int)
    



2346. Local variables in default arguments

Section: 9.3.4.7  [dcl.fct.default]     Status: CD5     Submitter: Geoffrey Romer     Date: 2017-04-26

[Adopted as a DR as part of paper P0588R1 at the October, 2018 meeting.]

According to 9.3.4.7 [dcl.fct.default] paragraph 7,

A local variable shall not appear as a potentially-evaluated expression in a default argument.

This prohibits plausible uses of constexpr and static local variables. Presumably this rule should be similar to the one in 11.6 [class.local] paragraph 1, regarding local classes, which applies to odr-use, not potential evaluation, and to variables with automatic storage duration.




2295. Aggregates with deleted defaulted constructors

Section: 9.4.2  [dcl.init.aggr]     Status: CD5     Submitter: Thomas Köppe     Date: 2016-06-24

[Adopted at the June, 2018 meeting as part of paper P1008R1.]

Should a class with a deleted non-user-provided default constructor be considered an aggregate?




2359. Unintended copy initialization with designated initializers

Section: 9.4.2  [dcl.init.aggr]     Status: CD5     Submitter: Zhihao Yuan     Date: 2017-10-06

[Accepted at the June, 2018 (Rapperswil) meeting.]

According to 9.4.2 [dcl.init.aggr] bullet 4.2,

Otherwise, the element is copy-initialized from the corresponding initializer-clause or the brace-or-equal-initializer of the corresponding designated-initializer-clause.

This sounds as if the initialization performed by a designated initializer is always copy-initialization. However, it was intended that the kind of initialization match the form of the initializer, i.e., a designated-initializer-clause of the form

  { .x{3} }

was intended to perform direct-initialization.

Proposed resolution, April, 2018:

Change 9.4.2 [dcl.init.aggr] bullet 4.2 as follows:

Otherwise, the element is copy-initialized from the corresponding initializer-clause or is initialized with the brace-or-equal-initializer of the corresponding designated-initializer-clause.



2267. Copy-initialization of temporary in reference direct-initialization

Section: 9.4.4  [dcl.init.ref]     Status: CD5     Submitter: Richard Smith     Date: 2016-05-25

[Accepted as a DR at the February, 2019 meeting.]

Consider the following example:

   struct A {} a;
   struct B { explicit B(const A&); };

   struct D { D(); };
   struct C { explicit operator D(); } c;

   B b1(a);            // #1, ok 
   const B &b2{a};     // #2. ok 
   const B &b3(a);     // #3, error 

   D d1(c);            // ok 
   const D &d2{c};     // ok 
   const D &d3(c);     // #6, ok 

The disparity between #3 and #6 is suprising, as is the difference from #1 and #2. The reason for this difference is in 9.4.5 [dcl.init.list] bullet 3.10:

Otherwise, if T is a reference type, a prvalue of the type referenced by T is generated. The prvalue initializes its result object by copy-list-initialization or direct-list-initialization, depending on the kind of initialization for the reference. The prvalue is then used to direct-initialize the reference.

(reflecting the resolution of issue 1494).

Notes from the March, 2018 meeting:

CWG felt that initialization of the temporary should always be copy initialization, regardless of whether the top-level initialization is copy or direct initialization. This would make #2, #3, #5, and #6 all ill-formed.

Proposed resolution (November, 2018):

  1. Change 9.4.5 [dcl.init.list] bullet 3.10 as follows:

  2. Otherwise, if T is a reference type, a prvalue of the type referenced by T is generated. The prvalue initializes its result object by copy-list-initialization or direct-list-initialization, depending on the kind of initialization for the reference. The prvalue is then used to direct-initialize the reference. [Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type. —end note]
  3. Add the following to the example in 9.4.5 [dcl.init.list] bullet 3.10:

  4.   struct A { } a;
      struct B { explicit B(const A&); };
      const B &b2(a);  // error: cannot copy-initialize B temporary from A
    
  5. Change 12.2.2.7 [over.match.ref] bullet 1.1 as follows:

  6. ...For direct-initialization, those explicit conversion functions that are not hidden within S and yield type “lvalue reference to cv2 T2(when initializing an lvalue reference or an rvalue reference to function) or “cv2 T2 or “rvalue reference to cv2 T2(when initializing an rvalue reference or an lvalue reference to function), respectively, where T2 is the same type as T or can be converted to type T with a qualification conversion (7.3.6 [conv.qual]), are also candidate functions.



2352. Similar types and reference binding

Section: 9.4.4  [dcl.init.ref]     Status: CD5     Submitter: Richard Smith     Date: 2017-07-14

[Accepted as a DR at the February, 2019 meeting.]

In an example like

  int *ptr;
  const int *const &f() {
    return ptr;
  }

What is returned is a reference to a temporary instead of binding directly to ptr. The rules for reference-related types should say that T is reference-related to U if U* can be converted to T* by a qualification conversion.

Notes from the April, 2018 teleconference:

CWG agreed with the proposed direction.

Proposed resolution (November, 2018):

Change 9.4.4 [dcl.init.ref] paragraph 4 as follows:

Given types “cv1 T1” and “cv2 T2”, “cv1 T1” is reference-related to “cv2 T2” if T1 is the same type as similar (7.3.6 [conv.qual]) to T2, or T1 is a base class of T2. “cv1 T1” is reference-compatible with “cv2 T2” if

and cv1 is the same cv-qualification as, or greater cv-qualification than, cv2 a prvalue of type “pointer to cv2 T2” can be converted to the type “pointer to cv1 T1” via a standard conversion sequence (7.3 [conv]). In all cases where the reference-related or reference-compatible relationship of two types is used to establish the validity of a reference binding, and T1 is a base class of T2 and the standard conversion sequence would be ill-formed, a program that necessitates such a binding is ill-formed if T1 is an inaccessible (11.8 [class.access]) or ambiguous (6.5.2 [class.member.lookup]) base class of T2.

Additional notes (September, 2023)

Issue 2018 is a duplicate of this issue.




1331. const mismatch with defaulted copy constructor

Section: 9.5.2  [dcl.fct.def.default]     Status: CD5     Submitter: Daniel Krügler     Date: 2011-06-15

[Adopted at the November, 2017 meeting as part of paper P0641R2.]

The current requirements of 9.5.2 [dcl.fct.def.default] paragraph 1 state that a defaulted copy constructor or copy assignment operator can have a reference to const parameter only if all its subobjects have corresponding functions with a reference to const parameter, even if that function is never called. This prevents some useful template classes.

(See also library issue 2068.)

See also issue 1426.

Rationale (August, 2011):

Possible resolutions to this issue need to be considered in a wider context, so it is more appropriate for the Evolution Working Group.




1426. Allowing additional parameter types in defaulted functions

Section: 9.5.2  [dcl.fct.def.default]     Status: CD5     Submitter: Nikolay Ivchenkov     Date: 2011-12-08

[Adopted at the November, 2017 meeting as part of paper P0641R2.]

The current wording of 9.5.2 [dcl.fct.def.default] paragraph 1 allows copy constructors and copy assignment operators to have a reference to non-const parameter, even if the implicitly-declared function would have had a reference to const parameter. This is safe because the sub-object copy functions that take a reference to const parameter can be invoked with a non-const lvalue.

It would also be possible to allow the inverse situation — permitting the defaulted function to be defined with a reference to const parameter, even though the sub-object functions have a reference to non-const parameter — by defining the defaulted function as deleted.

See also issue 1331.

Rationale (February, 2012):

This is a request to extend the language and is thus more appropriately considered by EWG.




1912. exception-specification of defaulted function

Section: 9.5.2  [dcl.fct.def.default]     Status: CD5     Submitter: Ville Voutilainen     Date: 2014-04-10

[Accepted at the February, 2019 meeting as part of paper P1286R2.]

The current rules requiring a defaulted member function to have an exception-specification compatible with that of the implicitly-declared function are overly constraining. It should be possible, for example, to specify that a defaulted move constructor will be non-throwing, based on knowledge available to the programmer, even if the implicitly-declared constructor would be throwing.

Rationale (June, 2014):

This suggested extension should be investigated by EWG before any action is taken.




2285. Issues with structured bindings

Section: 9.6  [dcl.struct.bind]     Status: CD5     Submitter: Richard Smith     Date: 2016-07-01

[Accepted as a DR at the June, 2018 (Rapperswil) meeting.]

  1. What is the point of declaration of a name introduced by a structured binding? If it's the point at which it appears, we're missing a rule to make it ill-formed to reference the name before its type is deduced (similar to what we have for 'auto'). [Suggestion: point of declaration is after the identifier-list, program is ill-formed if the name is mentioned before the end of the initializer.]

  2. Are structured bindings permitted at namespace scope? There doesn't seem to be a rule against that. If so, what's their linkage? Is it intentional that static , extern are disallowed? Should we only allow automatic storage duration? [Suggestion: only permit automatic storage duration, per the design paper.]

  3. (If the answer to 2 is yes...) is the declaration in a variable template permitted to use structured bindings? If so, how do you name the result? (The bindings themselves aren't introduced as template-names by the current wording.) If not, we're missing a restriction on that. [Suggestion: no to question 2.]

  4. Did we intend to guarantee that the object whose members are denoted by bindings is kept "together":

  5.    auto f() -> int (&)[2];
       auto [x, y] = f();
       assert(&x + 1 == &y); // ok? 
    
       struct S { int a, b, c; }; // standard-layout 
       auto [a,b,c] = S();
       assert(&((S*)&a)->b == &b); // ok? 
    

    (If yes, this means we can't synthesize independent variables for each element of an array or struct that's bound in this way, and it's harder to remove dead components of a destructured object.) Obviously we may need to keep the object together if it has a non-trivial destructor. [Suggestion: do not allow reaching the complete object from a binding.]

  6. Should the copy->move promotion be permitted for a return of a structured binding?

  7.    struct A { string s; int n; };
       string f() {
         auto [s,n] = A();
         return s; // copy required here? 
       }
    

    [Suggestion: allow promotion to move -- as if the binding were a real local variable -- if the implicit underlying variable is not a reference. Maybe also allow NRVO, depending on answer to question 8.]

Notes from the April, 2017 teleconference:

Items 1 and 3 are core issues; item 4 is NAD - the bindings are kept together, which is implied by the existing rules about copying the object into a hidden temporary. The remaining items are questions for EWG and new items in "extension" status will be opened for them.

Proposed resolution, February, 2018:

  1. Change 6.4.2 [basic.scope.pdecl] paragraph 9 as follows and add the following new paragraph thereafter:

  2. The point of declaration for a function-local predefined variable (9.5 [dcl.fct.def] 9.5.1 [dcl.fct.def.general]) is immediately before the function-body of a function definition.

    The point of declaration of a structured binding (9.6 [dcl.struct.bind]) is immediately after the identifier-list of the structured binding declaration.

  3. Add the following as a new paragraph following 9.6 [dcl.struct.bind] paragraph 1:

  4. ...taken from the corresponding structured binding declaration. The type of the id-expression e is called E. [Note: E is never a reference type (Clause 7 [expr]). —end note]

    If the initializer refers to one of the names introduced by the structured binding declaration, the program is ill-formed.

(Note: In response to item 3, the current wording of Clause 13 [temp] paragraph 1 does not allow templated structured binding declarations, and no change is being proposed.)




2313. Redeclaration of structured binding reference variables

Section: 9.6  [dcl.struct.bind]     Status: CD5     Submitter: Richard Smith     Date: 2016-08-12

[Accepted as a DR at the November, 2017 meeting.]

According to the current rules for structured binding declarations, the user-defined case declares the bindings as variables of reference type. This presumably makes an example like the following valid:

   auto [a] = std::tuple<int>(0);
   extern int &&a; // ok, redeclaration, could even be in a different TU 

This seems unreasonable, especially in light of the fact that it only works for the user-defined case and not the built-in case (where the bindings are not modeled as references).

Proposed resolution (August, 2017):

Change 9.6 [dcl.struct.bind] paragraph 3 as follows:

Otherwise, if the qualified-id std::tuple_size<E> names a complete type, the expression std::tuple_size<E>::value shall be a well-formed integral constant expression and the number of elements in the identifier-list shall be equal to the value of that expression. The unqualified-id get is looked up in the scope of E by class member access lookup (_N4868_.6.5.6 [basic.lookup.classref]), and if that finds at least one declaration, the initializer is e.get<i>(). Otherwise, the initializer is get<i>(e) where get is looked up in the associated namespaces (6.5.4 [basic.lookup.argdep]). In either case, get<i> is interpreted as a template-id. [Note: Ordinary unqualified lookup (6.5.3 [basic.lookup.unqual]) is not performed. —end note] In either case, e is an lvalue if the type of the entity e is an lvalue reference and an xvalue otherwise. Given the type Ti designated by std::tuple_element<i, E>::type , each vi is a variable variables are introduced with unique names ri of type “reference to Ti ” initialized with the initializer (9.4.4 [dcl.init.ref]), where the reference is an lvalue reference if the initializer is an lvalue and an rvalue reference otherwise. Each vi is the name of an lvalue of type Ti that refers to the object bound to ri; the referenced type is Ti.



2339. Underspecified template arguments in structured bindings

Section: 9.6  [dcl.struct.bind]     Status: CD5     Submitter: Daveed Vandevoorde     Date: 2017-03-29

[Accepted as a DR at the June, 2018 (Rapperswil) meeting.]

According to 9.6 [dcl.struct.bind] paragraph 3,

Otherwise, if the qualified-id std::tuple_size<E> names a complete type, the expression std::tuple_size<E>::value shall be a well-formed integral constant expression and the number of elements in the identifier-list shall be equal to the value of that expression. The unqualified-id get is looked up in the scope of E by class member access lookup (_N4868_.6.5.6 [basic.lookup.classref]), and if that finds at least one declaration, the initializer is e.get<i>(). Otherwise, the initializer is get<i>(e), where get is looked up in the associated namespaces (6.5.4 [basic.lookup.argdep]). In either case, get<i> is interpreted as a template-id.

It is not clear what i is in this description, and in particular, its type is not specified.

Proposed resolution, March, 2018:

Change 9.6 [dcl.struct.bind] paragraph 3 as follows:

Otherwise, if the qualified-id std::tuple_size<E> names a complete type, the expression std::tuple_size<E>::value shall be a well-formed integral constant expression and the number of elements in the identifier-list shall be equal to the value of that expression. Let i be an index prvalue of type std::size_t corresponding to vi. The unqualified-id get is looked up in the scope of E by class member access lookup (_N4868_.6.5.6 [basic.lookup.classref]), and if that finds at least one declaration, the initializer is e.get<i>(). Otherwise, the initializer is get<i>(e), where get is looked up in the associated namespaces (6.5.4 [basic.lookup.argdep]). In either case, get<i> is interpreted as a template-id. [Note: Ordinary unqualified lookup...



2386. tuple_size requirements for structured binding

Section: 9.6  [dcl.struct.bind]     Status: CD5     Submitter: Stephan T. Lavavej     Date: 2018-09-19

[Accepted as a DR at the February, 2019 meeting.]

According to 9.6 [dcl.struct.bind] paragraph 4,

Otherwise, if the qualified-id std::tuple_size<E> names a complete type, the expression std::tuple_size<E>::value shall be a well-formed integral constant expression and the number of elements in the identifier-list shall be equal to the value of that expression.

However, the common idiom in the library is that SFINAE tests the presence or absence of a value member rather than the completeness of the class; see 22.4.7 [tuple.helper] paragraph 4. The core language requirement should be changed to match the common library practice.

Proposed resolution (December, 2018):

Change 9.5 [dcl.fct.def] paragraph 4 as follows:

Otherwise, if the qualified-id std::tuple_size<E> names a complete class type with a member value, the expression std::tuple_size<E>::value shall be a well-formed integral constant expression and the number of elements in the identifier-list shall be equal to the value of that expression...



1636. Bits required for negative enumerator values

Section: 9.7.1  [dcl.enum]     Status: CD5     Submitter: Hyman Rosen     Date: 2013-03-07

[Accepted as a DR at the November, 2018 (San Diego) meeting.]

According to 9.7.1 [dcl.enum] paragraph 7,

For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type. Otherwise, for an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a two's complement representation and 0 for a one's complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin|-K,|emax|) and equal to 2M-1, where M is a non-negative integer. bmin is zero if emin is non-negative and -(bmax+K) otherwise. The size of the smallest bit-field large enough to hold all the values of the enumeration type is max(M,1) if bmin is zero and M+1 otherwise.

The result of these calculations is that the number of bits required for

  enum { N = -1, Z = 0 }

is 1, but the number required for

  enum { N = -1 }

is 2. This is surprising. This could be fixed by changing |emax| to emax.

Proposed resolution (June, 2018):

Change 9.7.1 [dcl.enum] paragraph 8 as follows:

bmax is the smallest value greater than or equal to max(|emin| - K, |emax|) , emax) and equal to 2M - 1, where M is a non-negative integer.



1742. using-declarations and scoped enumerators

Section: 9.9  [namespace.udecl]     Status: CD5     Submitter: Richard Smith     Date: 2013-08-28

[Accepted at the July, 2019 meeting as part of paper P1099R5 (Using enum).]

A using-declaration cannot name a scoped enumerator, according to 9.9 [namespace.udecl] paragraph 7. This is presumably because a scoped enumerator belongs to an enumeration scope and thus logically cannot belong to the non-enumeration scope in which the using-declaration appears. It seems inconsistent, however, to permit using-declarations to name unscoped enumerators but not scoped enumerators.

Also, 9.9 [namespace.udecl] paragraph 3 says,

In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined.

The consequence of this is that

  enum E { e0 };
  void f() {
    using E::e0;
  }

is well-formed, but

  struct B {
    enum E { e0 };
  };
  struct D : B {
    using B::E::e0;
  };

is not. Again, this seems inconsistent. Should these rules be relaxed?




2406. [[fallthrough]] attribute and iteration statements

Section: 9.12.6  [dcl.attr.fallthrough]     Status: CD5     Submitter: Mike Miller     Date: 2019-03-08

[Accepted as a DR at the July, 2019 meeting.]

According to 9.12.6 [dcl.attr.fallthrough] paragraph 1,

A fallthrough statement may only appear within an enclosing switch statement (8.5.3 [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 meaning of “next statement that would be executed” is unclear with respect to the controlled substatement of an iteration statement. There is implementation divergence on an example like:

  void f(int n) {
    switch (n) {
      case 0:
        while (true)
          [[fallthrough]]; // Well-formed?
      case 1:
        break;
    }
  }

Proposed resolution, April, 2019:

  1. Change 9.12.6 [dcl.attr.fallthrough] paragraph 1 as follows:

  2. The attribute-token fallthrough may be applied to a null statement (8.3 [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 (8.5.3 [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 and, if the fallthrough statement is contained in an iteration statement, the next statement shall be part of the same execution of the substatement of the innermost enclosing iteration statement. The program is ill-formed if there is no such statement.
  3. Change the example in 9.12.6 [dcl.attr.fallthrough] paragraph 3 as follows:

  4. [Example:

      void f(int n) {
        void g(), h(), i();
        switch (n) {
        case 1:
        case 2:
          g();
          [[fallthrough]];
        case 3:              // warning on fallthrough discouraged
          do {
            [[fallthrough]]; // error: next statement is not part of the same substatement execution
          } while (false);
        case 6:
          do {
            [[fallthrough]]; // error: next statement is not part of the same substatement execution
          } while (n--);
        case 7:
          while (false) {
            [[fallthrough]]; // error: next statement is not part of the same substatement execution
          }
        case 5:
          h();
        case 4:              // implementation may warn on fallthrough
          i();
          [[fallthrough]];   // ill-formed
        }
      }
    

    end example]




2360. [[maybe_unused]] and structured bindings

Section: 9.12.8  [dcl.attr.unused]     Status: CD5     Submitter: Michael Wong     Date: 2017-10-19

[Accepted as a DR at the February, 2019 meeting.]

According to9.12.8 [dcl.attr.unused] paragraph 2,

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.

This does not include structured bindings, although there seems to be no good reason to prohibit uses like

  [[maybe_unused]] auto [a, b] = std::make_pair(42, 0.23);

Notes from the October, 2018 teleconference:

CWG agreed that such an annotation should be permitted and apply to the underlying variable; i.e., a compiler might warn in the absence of such an attribute if none of the structured bindings were used, and the presence of the attribute would silence such warnings.

Proposed resolution (November, 2018):

Change 9.12.8 [dcl.attr.unused] paragraphs 2 and 3 as follows:

The attribute may be applied to the declaration of a class, a typedef-name, a variable (including a structured binding declaration), 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 or its structured bindings (if any) are used or unused. , or that the entity is used despite the presence of the attribute. —end note] For a structured binding declaration not marked maybe_unused, implementations should not emit such a warning unless all of its structured bindings are unused.




2234. Missing rules for simple-template-id as class-name

Section: Clause 11  [class]     Status: CD5     Submitter: Richard Smith     Date: 2016-02-25

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

There does not seem to be a rule that prohibits an example like:

  template<typename T> struct X;
  struct X<int> {
  };

Proposed resolution (November, 2017)

Change Clause 11 [class] paragraph 1 as follows:

...A class declaration where the class-name in the class-head-name is a simple-template-id shall be an explicit specialization (13.9.4 [temp.expl.spec]) or a partial specialization (13.7.6 [temp.spec.partial]). A class-specifier whose class-head omits the class-head-name defines an unnamed class. [Note: An unnamed class thus can't be final. —end note]



2293. Requirements for simple-template-id used as a class-name

Section: Clause 11  [class]     Status: CD5     Submitter: CWG     Date: 2016-06-22

[Accepted as a DR at the June, 2018 (Rapperswil) meeting.]

There is currently no requirement that a simple-template-id used as a class-name (Clause 11 [class] paragraph 1) must have template arguments for every template parameter that does not have a default template argument.

Proposed resolution (March, 2018):

  1. Change _N4868_.6.4.1 [basic.scope.declarative] paragraph 1 as follows:

  2. Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid valid, that is, in which that name may be used as an unqualified name to refer to the same entity. In general...
  3. Add the following as a new paragraph after 13.3 [temp.names] paragraph 7:

  4. A template-id that names an alias template specialization is a type-name.

    A template-id is valid if

    A simple-template-id shall be valid unless it names a function template specialization (13.10.3 [temp.deduct]). [Example:

      template<class T, T::type n = 0> class X;
      struct S {
        using type = int;
      };
      using T1 = X<S, int, int>;  // error: too many arguments
      using T2 = X<>;             // error: no default argument for first template parameter
      using T3 = X<1>;            // error: value 1 does not match type-parameter
      using T4 = X<int>;          // error: substitution failure for second template parameter
      using T5 = X<S>;            // OK
    

    end example]

  5. Change 13.10.3 [temp.deduct] paragraph 2 as follows, converting from bullets to running text:

  6. When an explicit template argument list is specified, if the template arguments are not compatible with the template parameter list or do not result in a valid function type as described below given template-id is invalid (13.3 [temp.names]), type deduction fails. Specifically, the following steps are performed when evaluating an explicitly specified template argument list with respect to a given function template:




1803. opaque-enum-declaration as member-declaration

Section: 11.4  [class.mem]     Status: CD5     Submitter: Peter Sommerlad     Date: 2013-10-31

[Resolved editorially by editorial issue 2353.]

According to 11.4 [class.mem] paragraph 1,

A member shall not be declared twice in the member-specification, except that a nested class or member class template can be declared and then later defined, and except that an enumeration can be introduced with an opaque-enum-declaration and later redeclared with an enum-specifier.

However, the grammar for member-declaration does not have a production that allows an opaque-enum-declaration.




1983. Inappropriate use of virt-specifier

Section: 11.4  [class.mem]     Status: CD5     Submitter: Richard Smith     Date: 2014-08-11

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

The restriction in 11.4 [class.mem] paragraph 8 that a virt-specifier may appear only in the declaration of a virtual function is insufficient to rule out examples like the following:

  struct A { virtual void f(); };
  struct B { friend void A::f() final; };

  template<typename T> struct C { virtual void f() {} };
  template void C<int>::f() final;
  template<> void C<char>::f() final;

One possibility might be to require that a virt-specifier appear only on the first declaration of a function.

Proposed resolution (November, 2017)

Change 11.4 [class.mem] paragraph 13 as follows:

A virt-specifier-seq shall contain at most one of each virt-specifier. A virt-specifier-seq shall appear only in the first declaration of a virtual member function (11.7.3 [class.virtual]).



2254. Standard-layout classes and bit-fields

Section: 11.4  [class.mem]     Status: CD5     Submitter: Richard Smith     Date: 2016-03-23

[Accepted as a DR at the June, 2018 (Rapperswil) meeting.]

According to 11.4 [class.mem] paragraph 25,

If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member. Otherwise, its address is the same as the address of its first base class subobject (if any). [Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. —end note] [Note: The object and its first subobject are pointer-interconvertible (6.8.4 [basic.compound], 7.6.1.9 [expr.static.cast]). —end note]

This wording does not consider the case when the first non-static data member is a bit-field, which cannot have its address taken.

Proposed resolution, February, 2018: [SUPERSEDED]

Change 11.4 [class.mem] paragraph 25 as follows:

If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member if that member is not a bit-field. Otherwise, its Its address is also the same as the address of each of its first base class subobjects (if any). [Note: There might therefore be unnamed padding within a standard-layout struct object inserted by an implementation, but not at its beginning, as necessary to achieve appropriate alignment. —end note] [Note: The object and its first subobject are pointer-interconvertible (6.8.4 [basic.compound], 7.6.1.9 [expr.static.cast]). —end note]

Notes from the March, 2018 meeting:

It was pointed out that the definition of pointer interconvertibility in 6.7.5.3 [basic.stc.thread] paragraph 4 refers to “the first base class subobject” of the object and must also be updated to reflect the above proposed resolution.

Proposed resolution (March, 2018):

  1. Change 6.8.4 [basic.compound] bullet 4.3 as follows:

  2. Two objects a and b are pointer-interconvertible if:

  3. Change 11.4 [class.mem] paragraph 25 as follows:

  4. If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member if that member is not a bit-field. Otherwise, its Its address is also the same as the address of each of its first base class subobjects (if any). [Note: There might therefore be unnamed padding within a standard-layout struct object inserted by an implementation, but not at its beginning, as necessary to achieve appropriate alignment. —end note] [Note: The object and its first subobject are pointer-interconvertible (6.8.4 [basic.compound], 7.6.1.9 [expr.static.cast]). —end note]



2404. [[no_unique_address]] and allocation order

Section: 11.4  [class.mem]     Status: CD5     Submitter: Daveed Vandevoorde     Date: 2018-12-13

[Accepted as a DR at the July, 2019 meeting.]

According to 11.4 [class.mem] paragraph 19,

Non-static data members of a (non-union) class with the same access control (11.8 [class.access]) are allocated so that later members have higher addresses within a class object.

With the advent of the [[no_unique_address]] attribute, “higher addresses” is no longer strictly accurate. According to the FAQ in P0840R2, next-to-last question:

Q: Suppose I have members a, b, c (in that order, with the same access). Today we guarantee that &a < &b < &c. What happens if b has the attribute?

Two cases:

  1. If the type of b is empty, then there is no guarantee about the address of b (other than that it is somewhere within the containing object).

  2. If the type of b is nonempty, then we still guarantee that &a < &b < &c.

Presumably the wording in 11.4 [class.mem] paragraph 19 needs to be changed to reflect that intent.

Proposed resolution, March, 2019:

Change 11.4 [class.mem] paragraph 19 as follows:

[Note: Non-static data members of a (non-union) class with the same access control (11.8 [class.access]) and non-zero size (6.7.2 [intro.object]) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (11.8 [class.access]). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (11.7.3 [class.virtual]) and virtual base classes (11.7.2 [class.mi]). end note]



2237. Can a template-id name a constructor?

Section: 11.4.5  [class.ctor]     Status: CD5     Submitter: Faisal Vali     Date: 2016-03-02

[Accepted at the March, 2018 (Jacksonville) meeting.]

Bullet 1.2 of 11.4.5 [class.ctor], describing declarator forms that are considered to declare a constructor, says:

...and the id-expression has one of the following forms:

The term class-name includes simple-template-id. It is not clear that allowing a constructor declaration of the form

  template<class T> struct X {
    X<T>(T); // constructor 
  };

is useful or helpful.

Proposed resolution (November, 2017)

  1. Change 11.4.5 [class.ctor] paragraph 1 as follows:

  2. ...and the id-expression has one of the following forms:

  3. Change 11.4.7 [class.dtor] paragraph 1 as follows:

  4. ...and the id-expression has one of the following forms:

  5. Add the following as a new paragraph in C.3 [diff.cpp17]:

  6. C.5.x Clause 15: Special member functions [diff.cpp17.special]

    Affected subclauses: 11.4.5 [class.ctor], 11.4.7 [class.dtor]
    Change: A simple-template-id is no longer valid as the declarator-id of a constructor or destructor.
    Rationale: Remove potentially error-prone option for redundancy.
    Effect on original feature: Valid C++ 2017 code may fail to compile.

      template<class T>
      struct A {
        A<T>();  // error: simple-template-id not allowed for constructor
        A(int);  // OK, injected-class-name used
        ~A<T>(); // error: simple-template-id not allowed for destructor
      };
    

(Note that this resolution is a change for C++20, NOT a defect report against C++17 and earlier versions.)




2273. Inheriting constructors vs implicit default constructor

Section: 11.4.5  [class.ctor]     Status: CD5     Submitter: Richard Smith     Date: 2016-06-17

[Voted into the WP at the July, 2017 meeting.]

In an example like

   struct A { A(int = 0); };
   struct B : A { using A::A; };
   B b0(0); // #1 
   B b;     // #2 

Is #2 valid (presumably calling the constructor inherited from A, or ill-formed due to ambiguity with 's implicit default constructor?

Proposed resolution (May, 2017):

  1. Change 9.9 [namespace.udecl] paragraph 16 as follows:

  2. For the purpose of forming a set of candidates during overload resolution, the functions that are introduced by a using-declaration into a derived class are treated as though they were members of the derived class. In particular, the implicit this parameter shall be treated as if it were a pointer to the derived class rather than to the base class. This has no effect on the type of the function, and in all other respects the function remains a member of the base class. Likewise, constructors that are introduced by a using-declaration are treated as though they were constructors of the derived class when looking up the constructors of the derived class (6.5.5.2 [class.qual]) or forming a set of overload candidates (12.2.2.4 [over.match.ctor], 12.2.2.5 [over.match.copy], 12.2.2.8 [over.match.list]). If such a constructor is selected to perform the initialization of an object of class type, all subobjects other than the base class from which the constructor originated are implicitly initialized (11.9.4 [class.inhctor.init]). [Note: A member of a derived class is sometimes preferred to a member of a base class if they would otherwise be ambiguous (12.2.4 [over.match.best]). —end note]
  3. Insert the following as a new bullet following 12.2.4 [over.match.best] bullet 1.7:

This resolution also resolves issue 2277.




2394. Const-default-constructible for members

Section: 11.4.5.2  [class.default.ctor]     Status: CD5     Submitter: Richard Smith     Date: 2018-07-10

[Accepted as a DR at the February, 2019 meeting.]

After the changes for comment RU 1 in P0490R0, a defaulted default constructor is acceptable for default initialization of a const object under certain circumstances; for example,

   struct A {};
   const A a;

is well-formed. However, default-initialization of such a class member still requires a user-provided constructor:

   struct B { const A a; };
   B b;   //error

Proposed resolution (November, 2018):

Change 11.4.5.2 [class.default.ctor] bullet 2.4 as follows:

A defaulted default constructor for class X is defined as deleted if:




2315. What is the “corresponding special member” of a variant member?

Section: 11.4.5.3  [class.copy.ctor]     Status: CD5     Submitter: Richard Smith     Date: 2016-08-15

[Accepted as a DR at the November, 2017 meeting.]

According to 11.4.5.3 [class.copy.ctor] bullet 10.1,

A defaulted copy/move constructor for a class X is defined as deleted (9.5.3 [dcl.fct.def.delete]) if X has:

However, it is not clear from this specification how to handle an example like:

  struct A {
    A();
    A(const A&);
  };
  union B {
    A a;
  };

since there is no corresponding special member in A.

Proposed resolution (August, 2017):

Change 11.4.5.3 [class.copy.ctor] paragraph 10 as follows:

An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (9.5.3 [dcl.fct.def.delete]) if X has:




1943. Unspecified meaning of “bit”

Section: 11.4.10  [class.bit]     Status: CD5     Submitter: Hubert Tong     Date: 2014-02-13

[Adopted at the November, 2018 meeting as part of paper P1236R1.]

CWG decided at the 2014-06 (Rapperswil) meeting to address only a limited subset of the questions raised by issues 1857 and 1861. This issue is a placeholder for the remaining questions, such as defining a “bit” in terms of a value of 2n, specifying whether a bit-field has a sign bit, etc.




2229. Volatile unnamed bit-fields

Section: 11.4.10  [class.bit]     Status: CD5     Submitter: David Majnemer     Date: 2016-02-08

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

According to 11.4.10 [class.bit] paragraph 2, unnamed bit-fields are not members, but there does not appear to be a prohibition against their being declared volatile. Is this intended?

Proposed resolution (November, 2017)

Change 11.4.10 [class.bit] paragraph 2 as follows:

A declaration for a bit-field that omits the identifier declares an unnamed bit-field. Unnamed bit-fields are not members and cannot be initialized. An unnamed bit-field shall not be declared with a cv-qualified type. [Note: An unnamed bit-field is useful for padding...



2253. Unnamed bit-fields and zero-initialization

Section: 11.4.10  [class.bit]     Status: CD5     Submitter: Aaron Ballman     Date: 2016-03-23

[Voted into the WP at the July, 2017 meeting.]

The current wording of the Standard does not clearly state that zero-initialization applies to unnamed bit-fields.

Notes from the December, 2016 teleconference:

The consensus was that unnamed bit-fields do constitute padding; more generally, padding should be normatively defined, along the lines suggested in 11.4.10 [class.bit] paragraphs 1-2.

Proposed resolution (March, 2017):

  1. Change 6.8 [basic.types] paragraph 4 as follows:

  2. The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T). The value representation of an object is the set of bits that hold the value of type T. Bits in the object representation that are not part of the value representation are padding bits. For trivially copyable types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values.44
  3. Change 9.4 [dcl.init] paragraph 6 as follows:

  4. To zero-initialize an object or reference of type T means:

  5. Change 11.4.10 [class.bit] paragraph 1 as follows:

  6. ...The constant-expression shall be an integral constant expression with a value greater than or equal to zero. The value of the integral constant expression may be larger than the number of bits in the object representation (6.8 [basic.types]) of the bit-field's type; in such cases the extra bits are used as padding bits (6.8 [basic.types])and do not participate in the value representation (6.8 [basic.types]) of the bit-field. Allocation of bit-fields...
  7. Change 6.8.2 [basic.fundamental] paragraph 1 as follows:

  8. ...For narrow character types, all bits of the object representation participate in the value representation. [Note: A bit-field of narrow character type whose length is larger than the number of bits in the object representation of that type has padding bits; see 11.4.10 [class.bit] 6.8 [basic.types]. —end note] For unsigned narrow character types...



2080. Example with empty anonymous union member

Section: 11.5  [class.union]     Status: CD5     Submitter: John Spicer     Date: 2015-02-03

The example in 11.5 [class.union] paragraph 8 reads,

  union U {
    int x = 0;
    union { };
    union {
      int z;
      int y = 1; // error: initialization for second variant member of U
    };
  };

The empty anonymous union appears to be a violation of the requirement in 11.4 [class.mem] paragraph 1,

Except when used to declare friends (11.8.4 [class.friend]), to declare an unnamed bit-field (11.4.10 [class.bit]), or to introduce the name of a member of a base class into a derived class (9.9 [namespace.udecl]), or when the declaration is an empty-declaration, member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class.

Additional note, February, 2021:

The issue was resolved editorially.




2227. Destructor access and default member initializers

Section: 11.9.3  [class.base.init]     Status: CD5     Submitter: Richard Smith     Date: 2016-02-01

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

There is implementation divergence on the validity of the following:

   class X { ~X(); };
   struct Y { X x = {}; };

Should X's destructor be potentially invoked by this attempt to initialize an X object? Or,

   auto *y = new Y {};

No constructor for Y is used, because this is aggregate initialization, and a destructor for X is not strictly necessary as there is no later initialization that might throw, but in the corresponding default constructor case we do require that the destructor be valid.

Perhaps the most consistent answer is that the default member initializer should not potentially invoke the destructor unless it's used (for symmetry with default arguments), but that aggregate initialization should potentially invoke the destructors of all subobjects (including the final one - exceptions could theoretically be thrown between the completion of the construction of the final aggregate element and the notional completion of the construction of the aggregate itself.

Proposed resolution (November, 2017)

  1. Add the following as a new paragraph following 9.4.2 [dcl.init.aggr] paragraph 7:

  2. An aggregate that is a class can also be initialized with a single expression not enclosed in braces, as described in 9.4 [dcl.init].

    The destructor for each element of class type is potentially invoked (11.4.7 [class.dtor]) from the context where the aggregate initialization occurs. [Note: This provision ensures that destructors can be called for fully-constructed subobjects in case an exception is thrown (14.3 [except.ctor]). —end note]

  3. Change 11.4.7 [class.dtor] paragraph 12 as follows:

    ...A destructor is potentially invoked if it is invoked or as specified in 7.6.2.8 [expr.new], 9.4.2 [dcl.init.aggr], 11.9.3 [class.base.init], and 14.2 [except.throw]. A program is ill-formed if a destructor that is potentially invoked is deleted or not accessible from the context of the invocation.



2317. Self-referential default member initializers

Section: 11.9.3  [class.base.init]     Status: CD5     Submitter: Richard Smith     Date: 2016-08-29

[Accepted as a DR at the February, 2019 meeting.]

Consider an example like:

  struct A {
    int n = A{}.n;
  };

There doesn't seem to be a good reason to support this kind of thing, and it would be simpler to say that a default member initializer can't trigger any direct or indirect use of itself in general, rather than just the two special cases that were banned by issues 1696 and 1397.

Notes from the March, 2018 meeting:

There was a suggestion that creating an object of the containing class in a default member initializer should be prohibited. That would presumably be a difference between the reference member and non-reference member cases, since the intent is to allow creation of a temporary for a reference member to bind to. The suggested approach for drafting was simply to remove the restriction to references in 9.4.2 [dcl.init.aggr] paragraph 11.

Proposed resolution (November, 2018):

Change 9.4.2 [dcl.init.aggr] paragraph 12 as follows:

If a reference member is initialized from its has a default member initializer and a potentially-evaluated subexpression thereof is an aggregate initialization that would use that default member initializer, the program is ill-formed. [Example:
  struct A;
  extern A a;
  struct A {
    const A& a1 { A{a,a} }; // OK
    const A& a2 { A{} };    // error
  };
  A a{a,a};                 // OK

  struct B {
  int n = B{}.n;            // error
  };

end example]




2290. Unclear specification for overload resolution and deleted special member functions

Section: 12.2.2  [over.match.funcs]     Status: CD5     Submitter: Howard Hinnant     Date: 2016-07-21

[Voted into the WP at the July, 2017 meeting.]

According to 12.2.2 [over.match.funcs] paragraph 8,

A defaulted move constructor or assignment operator (11.4.5.3 [class.copy.ctor]) that is defined as deleted is excluded from the set of candidate functions in all contexts.

It is unclear whether this is intended to apply to all defaulted assignment operators or only move assignment operators.

Proposed resolution (April, 2017):

Change 12.2.2 [over.match.funcs] paragraph 8 as follows:

A defaulted move constructor or assignment operator special function (11.4.5.3 [class.copy.ctor]) that is defined as deleted is excluded from the set of candidate functions in all contexts.



2356. Base class copy and move constructors should not be inherited

Section: 12.2.2  [over.match.funcs]     Status: CD5     Submitter: Richard Smith     Date: 2018-02-26

[Accepted as a DR at the June, 2018 (Rapperswil) meeting.]

Base class copy and move constructors brought into a derived class via a using-declaration should not be considered by overload resolution when constructing a derived class object.

Proposed resolution, February, 2018:

Change 12.2.2 [over.match.funcs] paragraph 8 as follows:

A defaulted move special function (11.4.5.3 [class.copy.ctor]) that is defined as deleted is excluded from the set of candidate functions in all contexts. A constructor inherited from class type C (11.9.4 [class.inhctor.init]) that has a first parameter of type “reference to cv1 P” (including such a constructor instantiated from a template) is excluded from the set of candidate functions when constructing an object of type cv2 D if the argument list has exactly one argument and C is reference-related to P and P is reference-related to D. [Example:

  struct A {
    A();
    A(A &&);                        // #1
    template<typename T> A(T &&);   // #2
  };
  struct B : A {
    using A::A;
    B(const B &);                   // #3
    B(B &&) = default;              // #4, implicitly deleted

    struct X { X(X &&) = delete; } x;
  };
  extern B b1;
  B b2 = static_cast<B&&>(b1);      // calls #3: #1, #2, and #4 are not viable
  struct C { operator B&&(); };
  B b3 = C();                       // calls #3

end example]




1781. Converting from nullptr_t to bool in overload resolution

Section: 12.2.2.6  [over.match.conv]     Status: CD5     Submitter: Hubert Tong     Date: 2013-09-26

[Accepted as a DR at the November, 2018 (San Diego) meeting.]

According to 12.2.2.6 [over.match.conv] paragraph 1, when a class type S is used as an initializer for an object of type T,

The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T via a standard conversion sequence (12.2.4.2.2 [over.ics.scs]) are candidate functions.

Because conversion from std::nullptr_t to bool is only permitted in direct-initialization (7.3.14 [conv.fctptr]), it is not clear whether there is a standard conversion sequence from std::nullptr_t to bool, considering that an implicit conversion sequence is intended to model copy-initialization. Should 12.2.2.6 [over.match.conv] be understood to refer only to conversions permitted in copy-initialization, or should the form of the initialization be considered? For example,

  struct SomeType {
    operator std::nullptr_t();
  };
  bool b{ SomeType() };    // Well-formed?

Note also 12.2.4.3 [over.ics.rank] paragraph 4, which may bear on the intent (or, alternatively, might describe a situation that cannot arise):

A conversion that does not convert a pointer, a pointer to member, or std::nullptr_t to bool is better than one that does.

See also issues 2133 and 2243.)

Proposed resolution (June, 2018):

  1. Change 7.3.15 [conv.bool] paragraph 1 as follows:

  2. A prvalue of arithmetic, unscoped enumeration, pointer, or pointer-to-member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true. For direct-initialization (9.4 [dcl.init]), a prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.
  3. Add the following bullet before 9.4 [dcl.init] bullet 17.8:

  4. The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. If the initializer is not a single (possibly parenthesized) expression, the source type is not defined.

  5. Change 12.2.4.3 [over.ics.rank] bullet 4.1 as follows:

  6. Standard conversion sequences are ordered by their ranks: an Exact Match is a better conversion than a Promotion, which is a better conversion than a Conversion. Two conversion sequences with the same rank are indistinguishable unless one of the following rules applies:

This resolution also resolves issue 2133.




2376. Class template argument deduction with array declarator

Section: 12.2.2.9  [over.match.class.deduct]     Status: CD5     Submitter: Mike Miller     Date: 2018-03-01

[Accepted as a DR at the July, 2019 meeting.]

An example like

   template <class ...T> struct A {
     A(T...) {}
   };
   A x[29]{};

Appears to be permitted by the current wording of the Standard, but existing implementations reject it. Should this usage be supported (in which case some mention of it in the wording would be useful) or prohibited?

Notes from the November, 2018 meeting:

The example is intended to be ill-formed; the intent is that declarator operators are not permitted, as with decltype(auto).

Proposed resolution, March, 2019:

Change 9.2.9.8 [dcl.type.class.deduct] paragraph 1 as follows:

If a placeholder for a deduced class type appears as a decl-specifier in the decl-specifier-seq of an initializing declaration (9.4 [dcl.init]) of a variable, the declared type of the variable shall be cv T, where T is the placeholder. [Example:

  template <class ...T> struct A {
    A(T...) {}
  };
  A x[29]{};    // error: no declarator operators allowed
  const A& y{}; // error: no declarator operators allowed

end example] The placeholder is replaced by the return type of the function selected by overload resolution for class template deduction (12.2.2.9 [over.match.class.deduct]). If the decl-specifier-seq is followed by an init-declarator-list or member-declarator-list containing more than one declarator, the type that replaces the placeholder shall be the same in each deduction.




2277. Ambiguity inheriting constructors with default arguments

Section: 12.2.4.3  [over.ics.rank]     Status: CD5     Submitter: Richard Smith     Date: 2016-06-23

[Voted into the WP at the July, 2017 meeting.]

In an example like:

  struct A {
    A(int, int = 0);
    void f(int, int = 0);
  };
  struct B : A {
    B(int); using A::A;
    void f(int); using A::f;
  }

calls to B(int) and B::f(int) are ambiguous, because they could equally call the version inherited from the base class. This doesn't match the intent in 9.9 [namespace.udecl], which usually makes derived-class functions take precedence over ones from a base class.

The above patterns are not common, although they sometimes cause breakage when refactoring a base class. However, P0136R1 brings this into sharp focus, because it causes the rejection of the following formerly-valid and very reasonable code:

  struct A {
    A(int = 0);
  };
  struct B : A {
    using B::B;
  };
  B b;

Proposed resolution (May, 2017):

This issue is resolved by the resolution of issue 2273.




2292. simple-template-id is ambiguous between class-name and type-name

Section: 13.3  [temp.names]     Status: CD5     Submitter: CWG     Date: 2016-06-22

[Accepted as a DR as paper P1131R2 at the November, 2018 (San Diego) meeting.]

The grammar term simple-template-id is used in the definition of both class-name (Clause 11 [class] paragraph 1) and type-name (9.2.9.3 [dcl.type.simple] paragraph 1). The latter case is intended to apply to alias template specializations. It would be helpful to have separate grammar terms for these uses.




1862. Determining “corresponding members” for friendship

Section: 13.7.5  [temp.friend]     Status: CD5     Submitter: Richard Smith     Date: 2014-02-13

[Accepted as a DR at the November, 2017 meeting.]

During the discussion of issue 1804, it was noted that the process of determining whether a member of an explicit or partial specialization corresponds to a member of the primary template is not well specified. In particular, it should be clarified that the primary template should not be instantiated during this process; instead, the template arguments from the specialization should simply be substituted into the member declaration.

Proposed resolution (October, 2017):

Change 13.7.5 [temp.friend] paragraph 4 as follows:

A template friend declaration may declare a member of a class template may be declared dependent type to be a friend of a non-template class. The friend declaration shall declare a function or specify a type with an elaborated-type-specifier, in either case with a nested-name-specifier ending with a simple-template-id, C, whose template-name names a class template. The template parameters of the template friend declaration shall be deducible from C (13.10.3.6 [temp.deduct.type]). In this case, the corresponding member of every specialization of the primary class template and class template partial specializations thereof a member of a specialization S of the class template is a friend of the class granting friendship. For explicit specializations and specializations of partial specializations, the corresponding member is the member (if any) that has the same name, kind (type, function, class template, or function template), template parameters, and signature as the member of the class template instantiation that would otherwise have been generated if deduction of the template parameters of C from S succeeds, and substituting the deduced template arguments into the friend declaration produces a declaration that would be a valid redeclaration of the member of the specialization. [Example:

  template<class T> struct A {
    struct B { };
    void f();
    struct D {
      void g();
    };
    T h();
    template<T U> T i();
  };
  template<> struct A<int> {
    struct B { };
    int f();
    struct D {
      void g();
    };
    template<int U> int i();
  };
  template<> struct A<float*> {
    int *h();
  };
  class C {
    template<class T> friend struct A<T>::B;      // grants friendship to A<int>::B even though
                                                  // it is not a specialization of A<T>::B
    template<class T> friend void A<T>::f();      // does not grant friendship to A<int>::f()
                                                  // because its return type does not match
    template<class T> friend void A<T>::D::g();   // does not grant friendship to A<int>::D::g()
                                                  // because A<int>::D is not a specialization of A<T>::D ill-formed, A<T>::D does not end with a simple-template-id
    template<class T> friend int *A<T*>::h();     // grants friendship to A<int*>::h() and A<float*>::h()
    template<class T> template<T U>               // grants friendship to instantiations of A<T>::i() and to A<int>::i()
      friend T A<T>::i();                         // and thereby to all specializations of those function templates
  };

end example]




2379. Missing prohibition against constexpr in friend declaration

Section: 13.7.5  [temp.friend]     Status: CD5     Submitter: Richard Smith     Date: 2018-06-14

[Accepted as a DR at the February, 2019 meeting.]

According to 13.7.5 [temp.friend] paragraph 8,

When a friend declaration refers to a specialization of a function template, the function parameter declarations shall not include default arguments, nor shall the inline specifier be used in such a declaration.

Presumably this should also include the constexpr specifier.

Notes from the December, 2018 teleconference:

This should also cover the newly-added consteval specifier.

Proposed resolution (February, 2019):

Change 13.7.5 [temp.friend] paragraph 8 as follows:

When a friend declaration refers to a specialization of a function template, the function parameter declarations shall not include default arguments, nor shall the inline specifier inline, constexpr, or consteval specifiers be used in such a declaration.



2045. “Identical” template parameter lists

Section: 13.7.7.2  [temp.over.link]     Status: CD5     Submitter: Faisal Vali     Date: 2014-11-14

[Adopted at the July, 2017 meeting as part of paper P0734R0.]

The requirement of 13.7.7.2 [temp.over.link] paragraph 6 that equivalent function templates must have “identical” template parameter lists is confusing, since the names of template parameters are not considered (13.7.7.2 [temp.over.link] paragraph 3) .




2373. Incorrect handling of static member function templates in partial ordering

Section: 13.7.7.3  [temp.func.order]     Status: CD5     Submitter: Richard Smith     Date: 2018-02-14

[Accepted as a DR at the November, 2018 (San Diego) meeting.]

According to 13.7.7.3 [temp.func.order] paragraph 3,

...If only one of the function templates M is a non-static member of some class A, M is considered to have a new first parameter inserted in its function parameter list. Given cv as the cv-qualifiers of M (if any), the new parameter is of type “rvalue reference to cv A” if the optional ref-qualifier of M is && or if M has no ref-qualifier and the first parameter of the other template has rvalue reference type. Otherwise, the new parameter is of type “lvalue reference to cv A”. [Note: This allows a non-static member to be ordered with respect to a non-member function and for the results to be equivalent to the ordering of two equivalent non-members. —end note]

This gives the wrong answer for an example like:

  struct Foo {
    template <typename T>
    static T* f(Foo*) { return nullptr; }

    template <typename T, typename A1>
    T* f(const A1&) { return nullptr; }
  };

  int main() {
    Foo x;
    x.f<int>(&x);
  }

Presumably this should say something like,

...If only one of the function templates M is a member function, and that function is a non-static member...

Proposed resolution (October, 2018):

Change 13.7.7.3 [temp.func.order] paragraph 3 as follows:

...If only one of the function templates M is a member function, and that function is a non-static member of some class A, M is considered to have a new first parameter inserted in its function parameter list. Given cv as the cv-qualifiers of M (if any), the new parameter is of type “rvalue reference to cv A” if the optional ref-qualifier of M is && or if M has no ref-qualifier and the first parameter of the other template has rvalue reference type. Otherwise, the new parameter is of type “lvalue reference to cv A”. [Note: This allows a non-static member to be ordered with respect to a non-member function and for the results to be equivalent to the ordering of two equivalent non-members. —end note]



1271. Imprecise wording regarding dependent types

Section: 13.8  [temp.res]     Status: CD5     Submitter: Daveed Vandevoorde     Date: 2011-03-24

[Accepted as a DR at the March, 2018 (Jacksonville) meeting as part of paper P0634R3.]

According to 13.8 [temp.res] paragraph 3,

When a qualified-id is intended to refer to a type that is not a member of the current instantiation (13.8.3.2 [temp.dep.type]) and its nested-name-specifier refers to a dependent type, it shall be prefixed by the keyword typename, forming a typename-specifier. If the qualified-id in a typename-specifier does not denote a type, the program is ill- formed.

The intent of the programmer cannot form the basis for a compiler determining whether to issue a diagnostic or not.

Suggested resolution:

Let N be a qualified-id with a nested-name-specifier that denotes a dependent type. If N is not prefixed by the keyword typename, N shall refer to a member of the current instantiation or it shall not refer to a type.

If the qualified-id in a typename-specifier does not denote a type, the program is ill-formed.

(See also issues 590 and 591.)

Notes from the November, 2016 meeting:

The resolution for this issue should describe the type to which a typename-specifier refers, effectively the type named by the corresponding simple-type-specifier with typename removed.

Notes from the November, 2017 meeting:

This topic is addressed in paper P0634.




2266. Has dependent type vs is type-dependent

Section: 13.8.3.2  [temp.dep.type]     Status: CD5     Submitter: Fedor Sergeev     Date: 2016-05-20

[Accepted as a DR at the February, 2019 meeting.]

According to 13.8.3.2 [temp.dep.type] bullet 6.3.2, one criterion for a name being a member of an unknown specialization is if the name is an id-expression denoting the member in a member access expression and

the type of the object expression is dependent and is not the current instantiation.

This should presumably say that the object expression is type-dependent and not that it has a dependent type; “has a dependent type” should be applied only to declarations, not expressions.

Proposed resolution (February, 2019):

Change 13.8.3.2 [temp.dep.type] bullet 6.3.2 as follows:

A name is a member of an unknown specialization if it is




2307. Unclear definition of “equivalent to a nontype template parameter”

Section: 13.8.3.2  [temp.dep.type]     Status: CD5     Submitter: Richard Smith     Date: 2016-07-21

[Accepted as a DR at the November, 2017 meeting.]

The description of whether a template argument is equivalent to a template parameter in 13.8.3.2 [temp.dep.type] paragraph 3 is unclear as it applies to non-type template parameters:

A template argument that is equivalent to a template parameter (i.e., has the same constant value or the same type as the template parameter) can be used in place of that template parameter in a reference to the current instantiation. In the case of a non-type template argument, the argument must have been given the value of the template parameter and not an expression in which the template parameter appears as a subexpression.

For example:

  template<int N> struct A {
    typedef int T[N];
    static const int AlsoN = N;
    A<AlsoN>::T s; // #0, clearly supposed to be OK 
    static const char K = N;
    A<K>::T t;     // #1, OK? 
    static const long L = N;
    A<L>::T u;     // #2, OK? 
    A<(N)>::T v;   // #3, OK? 
    static const int M = (N);
    A<M>::T w;     // #4, OK? 
  };

#1 narrows the template argument. This obviously should not be the injected-class-name, because A<257>::T may well be int[1] not int[257] . However, the wording above seems to treat it as the injected-class-name.

#2 is questionable: there is potentially a narrowing conversion here, but it doesn't actually narrow any values for the original template parameter.

#3 is hard to decipher. On the one hand, this is an expression involving the template parameter. On the other hand, a parenthesized expression is specified as being equivalent to its contained expression.

#4 should presumably go the same way that #3 does.

Proposed resolution (August, 2017):

Change 13.8.3.2 [temp.dep.type] paragraph 3 as follows:

A template argument that is equivalent to a template parameter (i.e., has the same constant value or the same type as the template parameter) can be used in place of that template parameter in a reference to the current instantiation. For a template type-parameter, a template argument is equivalent to a template parameter if it denotes the same type. For a non-type template parameter, a template argument is equivalent to a template parameter if it is an identifier that names a variable that is equivalent to the template parameter. A variable is equivalent to a template parameter if

[Note: Using a parenthesized variable name breaks the equivalence. —end note] In the case of a non-type template argument, the argument must have been given the value of the template parameter and not an expression in which the template parameter appears as a subexpression. [Example:

  template <class T> class A {
    A* p1;                   // A is the current instantiation
    A<T>* p2;                // A<T> is the current instantiation
    A<T*> p3;                // A<T*> is not the current instantiation
    ::A<T>* p4;              // ::A<T> is the current instantiation
    class B {
      B* p1;                 // B is the current instantiation
      A<T>::B* p2;           // A<T>::B is the current instantiation
      typename A<T*>::B* p3; // A<T*>::B is not the current instantiation
    };
  };

  template <class T> class A<T*> {
    A<T*>* p1;               // A<T*> is the current instantiation
    A<T>* p2;                // A<T> is not the current instantiation
  };

  template <class T1, class T2, int I> struct B {
    B<T1, T2, I>* b1;        // refers to the current instantiation
    B<T2, T1, I>* b2;        // not the current instantiation
    typedef T1 my_T1;
    static const int my_I = I;
    static const int my_I2 = I+0;
    static const int my_I3 = my_I;
    static const long my_I4 = I;
    static const int my_I5 = (I);
    B<my_T1, T2, my_I>* b3;   // refers to the current instantiation
    B<my_T1, T2, my_I2>* b4;  // not the current instantiation
    B<my_T1, T2, my_I3>* b5;  // refers to the current instantiation
    B<my_T1, T2, my_I4>* b6;  // not the current instantiation
    B<my_T1, T2, my_I5>* b7;  // not the current instantiation
  };

end example]

Additional note (November, 2017):

It was observed that the proposed resolution does not address partial specializations, which also depend on the definition of equivalence. For example:

  template <typename T, unsigned N> struct A;
  template <typename T> struct A<T, 42u> {
    typedef int Ty;
    static const unsigned num = 42u;
    static_assert(!A<T, num>::Ty(), "");
  };
  A<int, 42u> a; // GCC, MSVC, ICC accepts; Clang rejects 

The issue is being returned to "review" status in order to consider these additional questions.

Notes from the November, 2017 (Albuquerque) meeting:

CWG decided to proceed with this resolution and deal with partial specialization in a separate issue.




2294. Dependent auto static data members

Section: 13.8.3.3  [temp.dep.expr]     Status: CD5     Submitter: Richard Smith     Date: 2016-06-22

[Accepted as a DR at the June, 2018 (Rapperswil) meeting.]

The list of type-dependent id-expressions in 13.8.3.3 [temp.dep.expr] paragraph 3 should include the case when a static data member is declared with the auto type specifier and the initializer is dependent.

Proposed resolution, March, 2018:

Add the following as a new bullet after 13.8.3.3 [temp.dep.expr] bullet 3.2:




1258. “Instantiation context” differs from dependent lookup rules

Section: 13.8.4.1  [temp.point]     Status: CD5     Submitter: Nikolay Ivchenkov     Date: 2011-03-10

C++11 expanded the lookup rules for dependent function calls (13.8.4.2 [temp.dep.candidate] bullet 1.2) to include functions with internal linkage; previously only functions with external linkage were considered. However, 13.8.4.1 [temp.point] paragraph 6 still says,

The instantiation context of an expression that depends on the template arguments is the set of declarations with external linkage declared prior to the point of instantiation of the template specialization in the same translation unit.

Presumably this wording was overlooked and should be harmonized with the new specification.

Additional note (February, 2022):

The quoted paragraph was removed by P1103R3 (Merging Modules), approved 2019-02.




2255. Instantiated static data member templates

Section: 13.9  [temp.spec]     Status: CD5     Submitter: Mike Miller     Date: 2016-03-29

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

The current wording does not state that a specialization of a static data member template (Clause 13 [temp] paragraph 1) is a static data member, which leaves the status of an example like the following unclear (since 7.6.1.5 [expr.ref] bullet 4.1 is phrased in terms of static data members):

  template <class T> struct A {
    template <class U> static const U x = 1;
    static const int y = 2;
  };

  int main() {
    A<int> a;
    int y = a.y;         // OK
    int x = a.x<int>;    // ???
  }

Proposed resolution (November, 2017)

Change 13.9 [temp.spec] paragraph 2 as follows:

A function instantiated from a function template is called an instantiated function. A class instantiated from a class template is called an instantiated class. A member function, a member class, a member enumeration, or a static data member of a class template instantiated from the member definition of the class template is called, respectively, an instantiated member function, member class, member enumeration, or static data member. A member function instantiated from a member function template is called an instantiated member function. A member class instantiated from a member class template is called an instantiated member class. A variable instantiated from a variable template is called an instantiated variable. A static data member instantiated from a static data member template is called an instantiated static data member.



2330. Missing references to variable templates

Section: 13.9  [temp.spec]     Status: CD5     Submitter: Daveed Vandevoorde     Date: 2016-12-06

[Accepted as a DR at the February, 2019 meeting.]

Presumably paragraphs 1-3 of 13.9 [temp.spec] are intended to apply to variable templates, but the term does not appear in the current wording of these paragraphs.

Proposed resolution (November, 2018):

  1. Change 13.9 [temp.spec] paragraph 1 as follows:

  2. The act of instantiating a function, a variable, a class, a member of a class template or a member template is referred to as template instantiation.
  3. Change 13.9 [temp.spec] paragraphs 3 and r as follows:

  4. An explicit specialization may be declared for a function template, a variable template, a class template, a member of a class template or a member template. An explicit specialization declaration is introduced by template<> . In an explicit specialization declaration for a variable template, a class template, a member of a class template or a class member template, the name of the the variable or class that is explicitly specialized shall be a simple-template-id. In the explicit specialization declaration for a function template or a member function template, the name of the function or member function explicitly specialized may be a template-id. [Example:...

    An instantiated template specialization can be either implicitly instantiated (13.9.2 [temp.inst]) for a given argument list or be explicitly instantiated (13.9.3 [temp.explicit]). A specialization is a class, variable, function, or class member that is either instantiated or explicitly specialized (13.9.4 [temp.expl.spec]).




1378. When is an instantiation required?

Section: 13.9.2  [temp.inst]     Status: CD5     Submitter: Jason Merrill     Date: 2011-08-18

[ Resolved by P0859R0, approved in November, 2017. ]

A template instantiation can be “required” without there being a need for it at link time if it can appear in a constant expression:

    template <class T> struct A {
       static const T t;
    };
    template <class T> const T A<T>::t = 0;
    template <int I> struct B { };
    int a = sizeof(B<A<int>::t>);

    template <class T> constexpr T f(T t) { return t; }
    int b = sizeof(B<f(42)>);

It seems like it might be useful to define a term other than odr-used for this sort of use, which is like odr-used but doesn't depend on potentially evaluated context or lvalue-rvalue conversions.

Nikolay Ivchenkov:

Another possibility would be to introduce the extension described in the closed issue 1272 and then change 6.3 [basic.def.odr] paragraph 2 as follows:

An expression E is potentially evaluated unless it is an unevaluated operand ( Clause 7 [expr]) or a subexpression thereof. if and only if

An expression S is a direct subexpression of an expression E if and only if S and E are different expressions, S is a subexpression of E, and there is no expression X such that X differs from both S and E, S is a subexpression of X, and X is a subexpression of E. A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (7.7 [expr.const]) and the lvalue-to-rvalue conversion (4.1) is immediately applied...

[Example:

    template <class T> struct X {
        static int const m = 1;
        static int const n;
    };
    template <class T> int const X<T>::n = 2;

    int main() {
        // X<void>::m is odr-used,
        // X<void>::m is defined implicitly
        std::cout << X<void>::m << std::endl;

        // X<void>::n is odr-used,
        // X<void>::n is defined explicitly
        std::cout << X<void>::n << std::endl;

        // OK (issue 712 is not relevant here)
        std::cout << (1 ? X<void>::m : X<void>::n) << std::endl;
    }

(See also issues 712 and 1254.)

Additional notes (June, 2023)

This was addressed by the introduction of "needed for constant evaluation" in P0859R0 (Core Issue 1581: When are constexpr member functions defined?).




1704. Type checking in explicit instantiation of variable templates

Section: 13.9.3  [temp.explicit]     Status: CD5     Submitter: Richard Smith     Date: 2013-06-20

[Voted into the WP at the July, 2017 meeting.]

It is not clear whether the following is well-formed or not:

  template<typename T> int arr[sizeof(T)] = {};
  template int arr<int>[];

Are we supposed to instantiate the specialization and treat the explicit instantiation declaration as if it were a redeclaration (in which case the omitted array bound would presumably be OK), or is the type of the explicit instantiation declaration required to exactly match the type that the instantiated specialization has (in which case the omitted bound would presumably not be OK)? Or something else?

(See also issue 1728.)

Proposed resolution (May, 2017):

  1. Change 13.9.3 [temp.explicit] paragraph 3 as follows:

  2. If the explicit instantiation is for a class or member class, the elaborated-type-specifier in the declaration shall include a simple-template-id; otherwise, the declaration shall be a simple-declaration whose init-declarator-list comprises a single init-declarator that does not have an initializer. If the explicit instantiation is for a function or member function, the unqualified-id in the declaration declarator shall be either a template-id or, where all template arguments can be deduced, a template-name or operator-function-id. [Note: The declaration may declare a qualified-id, in which case the unqualified-id of the qualified-id must be a template-id. —end note] If the explicit instantiation is for a member function, a member class or a static data member of a class template specialization, the name of the class template specialization in the qualified-id for the member name shall be a simple-template-id. If the explicit instantiation is for a variable template specialization, the unqualified-id in the declaration declarator shall be a simple-template-id. An explicit instantiation shall appear in an enclosing namespace of its template. If the name declared in the explicit instantiation is an unqualified name, the explicit instantiation shall appear in the namespace where its template is declared or, if that namespace is inline (9.8.2 [namespace.def]), any namespace from its enclosing namespace set. [Note:...
  3. Add the following as a new paragraph following 13.9.3 [temp.explicit] paragraph 4:

  4. The declaration in an explicit-instantiation and the declaration produced by the corresponding substitution into the templated function, variable, or class are two declarations of the same entity. [Note: These declarations are required to have matching types as specified in 6.6 [basic.link], except as specified in 14.5 [except.spec]. [Example:

       template<typename T> T var = {};
       template float var<float>;   // OK, instantiated variable has type float
       template int var<int[16]>[]; // OK, absence of major array bound is permitted
       template int *var<int>;      // error: instantiated variable has type int
    
       template<typename T> auto av = T();
       template int av<int>;        // OK, variable with type int can be redeclared with type auto
    
       template<typename T> auto f() {}
       template void f<int>();      // error: function with deduced return type redeclared with non-deduced return type (9.2.9.7 [dcl.spec.auto])
    

    end example] —end note] Despite its syntactic form, the declaration in an explicit-instantiation for a variable is not itself a definition and does not conflict with the definition instantiated by an explicit instantiation definition for that variable.

  5. Change 13.9.3 [temp.explicit] paragraph 10 as follows:

    Except for inline functions and variables, declarations with types deduced from their initializer or return value (9.2.9.7 [dcl.spec.auto]), const variables of literal types, variables of reference types, and class template specializations, explicit instantiation declarations have the effect of suppressing the implicit instantiation of the definition of the entity to which they refer. [Note:...

This resolution also resolves issue 1728.




1728. Type of an explicit instantiation of a variable template

Section: 13.9.3  [temp.explicit]     Status: CD5     Submitter: Larisse Voufo     Date: 2013-08-05

[Voted into the WP at the July, 2017 meeting.]

It is not clear to what extent the type in an explicit instantiation must match that of a variable template. For example:

  template<typename T> T var = T();
  template float var<float>;   // #1.
  template int* var<int>;      // #2.
  template auto var<char>;     // #3.

(See also issue 1704.)

Proposed resolution (May, 2017):

This issue is resolved by the resolution of issue 1704.




2305. Explicit instantiation of constexpr or inline variable template

Section: 13.9.3  [temp.explicit]     Status: CD5     Submitter: John Spicer     Date: 2016-07-14

[Accepted as a DR at the November, 2017 meeting.]

According to 13.9.3 [temp.explicit] paragraph 1,

An explicit instantiation of a function template or member function of a class template shall not use the inline or constexpr specifiers.

Should this apply to explicit specializations of variable templates as well?

(See also issues 1704 and 1728).

Proposed resolution (August, 2017):

Change 13.9.3 [temp.explicit] paragraph 1 as follows:

A class, function, variable, or member template specialization can be explicitly instantiated from its template. A member function, member class or static data member of a class template can be explicitly instantiated from the member definition associated with its class template. An explicit instantiation of a function template or, member function of a class template, or variable template shall not use the inline or constexpr specifiers.



2260. Explicit specializations of deleted member functions

Section: 13.9.4  [temp.expl.spec]     Status: CD5     Submitter: Richard Smith     Date: 2016-04-17

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

Although the Standard allows for explicitly specializing a deleted function template, member function of a class template, or member function template with a non-deleted definition, this seems to be problematic for non-template member functions of class templates. For example:

  template<typename T> struct A {
    A(const A&) = delete;
    A(A&&) = default;
  };
  static_assert(is_trivially_copyable(A<int>));
  template<> struct A<int>::A(const A&) { /* ... */ }
  static_assert(is_trivially_copyable(A<int>));
  template<typename T> struct B {
    virtual void f() = delete;
  };
  struct C : B<int> { void f() override = delete; }; // ok, overriding deleted with deleted 
  template<> void B<int>::f() {} // would make C retroactively ill-formed? 

Notes from the December, 2016 teleconference:

=delete definitions of member functions should be instantiated when instantiating a class template. That would make the example an ill-formed redefinition.

Proposed resolution (November, 2017)

Change 13.9.2 [temp.inst] paragraph 2, breaking the running text into bullets, as follows:

The implicit instantiation of a class template specialization causes

The implicit instantiation of a class template specialization does not cause the implicit instantiation of default arguments or noexcept-specifiers of the class member functions. [Example:

  template<class T>
  struct C {
    void f() { T x; }
    void g() = delete;
  };
  C<void> c;                       // OK, definition of C<void>::f is not instantiated at this point
  template<> void C<int>::g() { }  // error: redefinition of C<int>::g

end example] However, for the purpose of determining whether an instantiated redeclaration is valid according to 6.3 [basic.def.odr] and 11.4 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition. [Example:




581. Can a templated constructor be explicitly instantiated or specialized?

Section: 13.10.2  [temp.arg.explicit]     Status: CD5     Submitter: Mark Mitchell     Date: 19 May 2006

[Accepted as a DR at the February, 2019 meeting.]

Although it is not possible to specify a constructor's template arguments in a constructor invocation (because the constructor has no name but is invoked by use of the constructor's class's name), it is possible to “name” the constructor in declarative contexts: per 6.5.5.2 [class.qual] paragraph 2,

In a lookup in which the constructor is an acceptable lookup result, if the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C ( Clause 11 [class]), the name is instead considered to name the constructor of class C... Such a constructor name shall be used only in the declarator-id of a declaration that names a constructor.

Should it therefore be possible to specify template-arguments for a templated constructor in an explicit instantiation or specialization? For example,

    template <int dim> struct T {};
    struct X {
      template <int dim> X (T<dim> &) {};
    };

    template X::X<> (T<2> &);

If so, that should be clarified in the text. In particular, 11.4.5 [class.ctor] paragraph 1 says,

Constructors do not have names. A special declarator syntax using an optional sequence of function-specifiers (9.2.3 [dcl.fct.spec]) followed by the constructor's class name followed by a parameter list is used to declare or define the constructor.

This certainly sounds as if the parameter list must immediately follow the class name, with no allowance for a template argument list.

It would be worthwhile in any event to revise this wording to utilize the “considered to name” approach of 6.5.5.2 [class.qual]; as it stands, this wording sounds as if the following would be acceptable:

    struct S {
        S();
    };
    S() { }    // qualified-id not required?

Notes from the October, 2006 meeting:

It was observed that explicitly specifying the template arguments in a constructor declaration is never actually necessary because the arguments are, by definition, all deducible and can thus be omitted.

Additional notes, October, 2018:

The wording in 13.10.2 [temp.arg.explicit] paragraph 1 refers to a “function name,” which constructors do not have, and so presumably the current wording does not permit an explicit specialization of a constructor template. Nevertheless, there is implementation divergence in the treatment of an example like:

  class C {
    template <typename T>
    C(const T &) {}
  };
  template C::C<double>(const double &);

with some accepting and some rejecting.

Notes from the October, 2018 teleconference:

The consensus was to allow template arguments on the constructor name but not something like C<int>::C<float>::f.

Proposed resolution (February, 2019):

  1. Cbange 13.10.2 [temp.arg.explicit] paragraph 1 as follows:

  2. Template arguments can be specified when referring to a function template specialization that is not a specialization of a constructor template by qualifying the function template name with the list of template-arguments in the same way as template-arguments are specified in uses of a class template specialization. [Example:
  3. Add the following as a new paragraph following 13.10.2 [temp.arg.explicit] paragraph 1:

  4. Template arguments shall not be specified when referring to a specialization of a constructor template (11.4.5 [class.ctor], 6.5.5.2 [class.qual]).



2322. Substitution failure and lexical order

Section: 13.10.3  [temp.deduct]     Status: CD5     Submitter: Xiang Fan     Date: 2016-09-06

[Accepted as a DR at the June, 2018 (Rapperswil) meeting.]

According to 13.10.3 [temp.deduct] paragraph 7,

The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside sizeof, decltype, and other contexts that allow non-constant expressions. The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered.

However, the same type can be represented in different lexical orders. For example, there is implementation variance on the following example, presumably because of preferring different declarations:

  template <class T> struct A { using X = typename T::X; };

  template <class T> typename T::X f(typename A<T>::X);
  template <class T> auto f(typename A<T>::X) -> typename T::X;
  template <class T> void f(...) { }

  void h() {
    f<int>(0);
  }

Proposed resolution, March, 2018:

Change 13.10.3 [temp.deduct] paragraph 7 as follows:

The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside sizeof, decltype, and other contexts that allow non-constant expressions. The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered. If substitution into different declarations of the same function template would cause template instantiations to occur in a different order or not at all, the program is ill-formed; no diagnostic required. [Note: The equivalent substitution in exception specifications is done only when the noexcept-specifier is instantiated, at which point a program is ill-formed if the substitution results in an invalid type or expression. —end note] [Example:

  template <class T> struct A { using X = typename T::X; };
  template <class T> typename T::X f(typename A<T>::X);
  template <class T> void f(...) { }
  template <class T> auto g(typename A<T>::X) -> typename T::X;
  template <class T> void g(...) { }
  template <class T> typename T::X h(typename A<T>::X);
  template <class T> auto h(typename A<T>::X) -> typename T::X; // redeclaration
  template <class T> void h(...) { }

  void h x() {
    f<int>(0);    // OK, substituting return type causes deduction to fail
    g<int>(0);    // error, substituting parameter type instantiates A<int>
    h<int>(0);    // ill-formed, no diagnostic required
  }

end example]




2303. Partial ordering and recursive variadic inheritance

Section: 13.10.3.2  [temp.deduct.call]     Status: CD5     Submitter: John Spicer     Date: 2016-05-24

[Accepted as a DR at the February, 2019 meeting.]

The status of an example like the following is not clear:

  template <typename... T>              struct A;
  template <>                           struct A<> {};
  template <typename T, typename... Ts> struct A<T, Ts...> : A<Ts...> {};
  struct B : A<int> {};

  template <typename... T>
  void f(const A<T...>&);

  void g() {
    f(B{});
  }

This seems to be ambiguous in the current wording because A<> and A<int> both succeed in deduction. It would be reasonable to prefer the more derived specialization.

Notes from the March, 2018 meeting:

The relevant specification is in 13.10.3.2 [temp.deduct.call] bullet 4.3 and paragraph 5, which specifies that if there is more than one possible deduced A, deduction fails. The consensus was to add wording similar to that of overload resolution preferring “nearer” base classes.

Proposed resolution (November, 2018):

Change 13.10.3.2 [temp.deduct.call] bullet 4.3 as follows:

In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:




2384. Conversion function templates and qualification conversions

Section: 13.10.3.4  [temp.deduct.conv]     Status: CD5     Submitter: John Spicer     Date: 2018-07-24

[Accepted as a DR at the February, 2019 meeting.]

Issue 349 resulted in the following specification in 13.10.3.4 [temp.deduct.conv] paragraph 7:

When the deduction process requires a qualification conversion for a pointer or pointer-to-member type as described above, the following process is used to determine the deduced template argument values: If A is a type

and P is a type

then the cv-unqualified T1 and T2 are used as the types of A and P respectively for type deduction. [Example:

  struct A {
    template <class T> operator T***();
  };
  A a;
  const int * const * const * p1 = a; // T is deduced as int, not const int

end example]

This rule is not widely implemented and may not be desirable. Should it be removed?

Proposed resolutions (December, 2018):

Delete 13.10.3.4 [temp.deduct.conv] paragraph 7:

When the deduction process requires a qualification conversion for a pointer or pointer-to-member type as described above, the following process is used to determine the deduced template argument values: If A is a type

and P is a type

then the cv-unqualified T1 and T2 are used as the types of A and P respectively for type deduction. [Example:

  struct A {
    template <class T> operator T***();
  };
  A a;
  const int * const * const * p1 = a; // T is deduced as int, not const int

end example]




2088. Late tiebreakers in partial ordering

Section: 13.10.3.5  [temp.deduct.partial]     Status: CD5     Submitter: Richard Smith     Date: 2015-02-19

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

The late tiebreakers for lvalue-vs-rvalue references and cv-qualification in 13.10.3.5 [temp.deduct.partial] paragraph 9 are applied

If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):

However, this is based on a false assumption. For example,

  template <typename T> struct A {
    struct typeA { };
    struct typeB { };
    using convTyA = T (*const &&)(typename A<T>::typeA);
    using convTyB = T (*const &)(typename A<T>::typeB);
    operator convTyA();
    operator convTyB();
  };

  template <typename T> void foo(T (*const &&)(typename A<T>::typeA));
  template <typename T> int foo(T (*const &)(typename A<T>::typeB));

  int main() {
    return foo<int>(A<int>());
  }

(see also issues 1847 and 1157.). We need to decide whether the rule is “deduction succeeds in both directions” or “the types are identical.” The latter seems more reasonable.

Proposed resolution (November, 2017)

Change 13.10.3.5 [temp.deduct.partial] paragraph 9 as follows:

If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):




2235. Partial ordering and non-dependent types

Section: 13.10.3.5  [temp.deduct.partial]     Status: CD5     Submitter: Richard Smith     Date: 2016-02-25

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

Paragraph 12 of 13.10.3.5 [temp.deduct.partial] contains the following example:

  template <class T> T f(int);        // #1
  template <class T, class U> T f(U); // #2
  void g() {
    f<int>(1);                        // calls #1
  }

However, paragraph 4 states,

If a particular P contains no template-parameters that participate in template argument deduction, that P is not used to determine the ordering.

Thus, we ignore the P=int, A=U case and deduction succeeds for the P=U, A=int case, so both templates are at least as specialized as each other. And consider:

  template <class... T> struct V {};
  template <class... Ts, class... Us> void Foo(V<Ts...>, V<Us&...>) {} // #3 
  template <class... Us> void Foo(V<>, V<Us&...>) {}                   // #4 
  void h() {
    Foo(V<>(), V<>());
  }

The intent is that this should call #4; that template clearly ought to be more specialized.

Proposed resolution (November, 2017)

  1. Change 13.10.3.5 [temp.deduct.partial] paragraph 4 as follows:

  2. :
    Each type nominated above from the parameter template and the corresponding type from the argument template are used as the types of P and A. If a particular P contains no template-parameters that participate in template argument deduction, that P is not used to determine the ordering.
  3. Change 13.10.3.6 [temp.deduct.type] paragraph 4 as follows:

  4. ...If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails. [Note: Under 13.10.3.2 [temp.deduct.call] and 13.10.3.5 [temp.deduct.partial], if P contains no template-parameters that appear in deduced contexts, no deduction is done, so P and A need not have the same form. —end note]



2318. Nondeduced contexts in deduction from a braced-init-list

Section: 13.10.3.6  [temp.deduct.type]     Status: CD5     Submitter: Jonathan Caves     Date: 2016-08-12

[Accepted as a DR at the February, 2019 meeting.]

The status of an example like the following is unclear:

  template<typename T, int N> void g(T (* const (&)[N])(T)) { }

  int f1(int);
  int f4(int);
  char f4(char);

  void f() {
    g({ &f1, &f4 });  // OK, T deduced to int, N deduced to 2?
  }

The problem is the interpretation of 13.10.3.6 [temp.deduct.type] paragraph 4:

In most cases, the types, templates, and non-type values that are used to compose P participate in template argument deduction. That is, they may be used to determine the value of a template argument, and the value so determined must be consistent with the values determined elsewhere. In certain contexts, however, the value does not participate in type deduction, but instead uses the values of template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

According to 13.10.3.2 [temp.deduct.call] paragraph 1, deduction is performed independently for each element of the initializer list:

Template argument deduction is done by comparing each function template parameter type (call it P) that contains template-parameters that participate in template argument deduction with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives std::initializer_list<P'> or P'[[N] for some P' and N and the argument is a non-empty initializer list (9.4.5 [dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument, and in the P'[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (13.10.3.6 [temp.deduct.type]).

Deduction fails for the second element of the list, &f4, because of ambiguity. Does this mean that deduction fails for the entire call, or does the successful deduction of T from the first element and N from the length of the list result in successful deduction for the call?

Notes from the July, 2017 meeting:

CWG determined that the call is well-formed.

Proposed resolution (November, 2018):

Change 13.10.3.2 [temp.deduct.call] paragraph 1 as follows:

Template argument deduction is done by comparing each function template parameter type (call it P) that contains template-parameters that participate in template argument deduction with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives std::initializer_list<P'> or P' [N] for some P" and N and the argument is a non-empty initializer list (9.4.5 [dcl.init.list]), then deduction is performed instead for each element of the initializer list independently, taking P' as a separate function template parameter types P'i and the i-th initializer element as its the corresponding argument. , and in In the P' [N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (13.10.3.6 [temp.deduct.type]). [Example:

  ...
  template<class T, int N> void n(T const(&)[N], T);
  n({{1},{2},{3}},Aggr()); // OK, T is Aggr, N is 3

  template<typename T, int N> void o(T (* const (&)[N])(T)) { }
  int f1(int);
  int f4(int);
  char f4(char);
  o({ &f1, &f4 }); // OK, T deduced as int from first element, nothing deduced from second element, N deduced as 2
  o({ &f1, static_cast<char(*)(char)>(&f4) }); // error: conflicting deductions for T



2092. Deduction failure and overload resolution

Section: 13.10.4  [temp.over]     Status: CD5     Submitter: Fedor Sergeev     Date: 2015-03-06

[Accepted as a DR at the March, 2018 (Jacksonville) meeting.]

Given an example like

  template <class T = int> void foo(T*);

  void test()
  {
    foo(0);   // #1 valid?
    foo<>(0);  // #2 valid?
  }

most/all implementations reject this code. However, the wording of the Standard only invokes 13.10.4 [temp.over] (“Overload resolution”) in cases where there is more than one function or function template, which is not the case here. The current wording would appear to make this well-formed because of the application of 13.10.2 [temp.arg.explicit] paragraph 2. Perhaps overload resolution should apply even when there is a single function template?

Notes from the May, 2015 meeting:

This issue is mostly a duplicate of issue 1582. However, CWG felt that it should be clarified that overload resolution applies in all cases, not just when templates are overloaded, so the issue is being left open to deal with that aspect.

Proposed resolution (November, 2017)

  1. Change 7.6.1.3 [expr.call] paragraph 1, splitting it into three paragraphs, as follows:

  2. A function call is a postfix expression followed by parentheses containing a possibly empty, comma-separated list of initializer-clauses which constitute the arguments to the function. The postfix expression shall have function type or function pointer type. For a call to a non-member function or to a static member function, the postfix expression shall be either an lvalue that refers to a function (in which case the function-to-pointer standard conversion (7.3.4 [conv.func]) is suppressed on the postfix expression), or it shall have function pointer type. Calling a function through an expression whose function type is different from the function type of the called function's definition results in undefined behavior (9.11 [dcl.link]).

    For a call to a non-static member function, the postfix expression shall be an implicit (11.4.3 [class.mfct.non.static], 11.4.9 [class.static]) or explicit class member access (7.6.1.5 [expr.ref]) whose id-expression is a function member name, or a pointer-to-member expression (7.6.4 [expr.mptr.oper]) selecting a function member; the call is as a member of the class object referred to by the object expression. In the case of an implicit class member access, the implied object is the one pointed to by this. [Note: A member function call of the form f() is interpreted as (*this).f() (see 11.4.3 [class.mfct.non.static]). —end note]

    If a function or member function name is used, the name can be overloaded (Clause 12 [over]), in which case the appropriate function shall be selected and the validity of the call are determined according to the rules in 12.2 [over.match]. If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called. Otherwise, its final overrider (11.7.3 [class.virtual]) in the dynamic type of the object expression is called; such a call is referred to as a virtual function call. [Note: The dynamic type is the type of the object referred to by the current value of the object expression. 11.9.5 [class.cdtor] describes the behavior of virtual function calls when the object expression refers to an object under construction or destruction. —end note]

  3. Add the following to 7.6.1.3 [expr.call] as a new paragraph before the existing paragraph 4:

  4. Calling a function through an expression whose function type is different from the function type of the called function's definition results in undefined behavior (9.11 [dcl.link]).

    When a function is called, each parameter (9.3.4.6 [dcl.fct]) shall be initialized...

  5. Change Clause 12 [over] paragraph 2 as follows:

  6. When an overloaded a function name is used in a call, which overloaded function declaration is being referenced is and the validity of the call are determined by comparing the types of the arguments at the point of use with the types of the parameters in the overloaded declarations that are visible at the point of use. This function selection process is called overload resolution...
  7. Change 13.10.4 [temp.over] paragraph 1 as follows:

  8. A function template can be overloaded either by (non-template) functions of its name or by (other) function templates of the same name. When a call to that the name of a function or function template is written (explicitly, or implicitly using the operator notation), template argument deduction...

This resolution also resolves issue 2241.




2336. Destructor characteristics vs potentially-constructed subobjects

Section: 14.5  [except.spec]     Status: CD5     Submitter: Nathan Sidwell     Date: 2017-02-28

[Accepted as a DR at the February, 2019 meeting.]

According to 14.5 [except.spec] paragraph 8,

The exception specification for an implicitly-declared destructor, or a destructor without a noexcept-specifier, is potentially-throwing if and only if any of the destructors for any of its potentially constructed subojects is potentially throwing.

11.4.4 [special] paragraph 5 defines “potentially constructed subobjects” as follows:

For a class, its non-static data members, its non-virtual direct base classes, and, if the class is not abstract (11.7.4 [class.abstract]), its virtual base classes are called its potentially constructed subobjects.

This leads to the following problem:

  class V {
  public:
    virtual ~V() noexcept(false);
  };

  class B : virtual V {
    virtual void foo () = 0;
    // implicitly defined virtual ~B () noexcept(true);
  };

  class D : B {
    virtual void foo ();
    // implicitly defined virtual ~D () noexcept(false);
  };

Here, D::~D() is throwing but overrides the non-throwing B::~B().

There are similar problems with the deletedness of destructors per 11.4.7 [class.dtor] paragraph 5, which also only considers potentially constructed subobjects.

Proposed resolution (November, 2018):

Change 14.5 [except.spec] paragraph 8 as follows:

The exception specification for an implicitly-declared destructor, or a destructor without a noexcept-specifier, is potentially-throwing if and only if any of the destructors for any of its potentially constructed subobjects is potentially throwing potentially-throwing or the destructor is virtual and the destructor of any virtual base class is potentially-throwing.



2390. Is the argument of __has_cpp_attribute macro-expanded?

Section: 15.2  [cpp.cond]     Status: CD5     Submitter: Richard Smith     Date: 2018-11-14

[Accepted as a DR at the July, 2019 meeting.]

The Standard does not specify whether the argument of __has_cpp_attribute is macro-expanded before being tested to see if it names an attribute or not, and there is implementation divergence.

Notes from the February, 2019 meeting:

CWG observed that a use of an attribute would be macro-expanded, so it seemed reasonable to expect to be able to specify the same macro as the argument to __has_cpp_attribute and get the corresponding result.

Proposed resolution (June, 2019):

  1. Change 15.2 [cpp.cond] paragraph 5 as follows:

  2. Each has-attribute-expression is replaced by a non-zero pp-number matching the form of an integer-literal if the implementation supports an attribute with the name specified by interpreting the pp-tokens, after macro expansion, as an attribute-token, and by 0 otherwise. The program is ill-formed if the pp-tokens do not match the form of an attribute-token.
  3. Change 15.2 [cpp.cond] paragraph 11 as follows:

  4. After all replacements due to macro expansion and evaluations of defined-macro-expressions, and has-include-expressions, and has_attribute_expressions have been performed, all remaining identifiers and keywords, except for true and false, are replaced with the pp-number 0, and then each preprocessing token is converted into a token. [Note: An alternative token (5.5 [lex.digraph]) is not an identifier, even when its spelling consists entirely of letters and underscores. Therefore it is not subject to this replacement. —end note]





Issues with "CD6" Status


536. Problems in the description of id-expressions

Section: _N4567_.5.1.1  [expr.prim.general]     Status: CD6     Submitter: Mike Miller     Date: 13 October 2005

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

There are at least a couple of problems in the description of the various id-expressions in _N4567_.5.1.1 [expr.prim.general]:

  1. Paragraph 4 embodies an incorrect assumption about the syntax of qualified-ids:

    The operator :: followed by an identifier, a qualified-id, or an operator-function-id is a primary-expression.

    The problem here is that the :: is actually part of the syntax of qualified-id; consequently, “:: followed by... a qualified-id” could be something like “:: ::i,” which is ill-formed. Presumably this should say something like, “A qualified-id with no nested-name-specifier is a primary-expression.”

  2. More importantly, some kinds of id-expressions are not described by _N4567_.5.1.1 [expr.prim.general]. The structure of this section is that the result, type, and lvalue-ness are specified for each of the cases it covers:

    This treatment leaves unspecified all the non-identifier unqualified-ids (operator-function-id, conversion-function-id, and template-id), as well as (perhaps) “:: template-id” (it's not clear whether the “:: followed by a qualified-id” case is supposed to apply to template-ids or not). Note also that the proposed resolution of issue 301 slightly exacerbates this problem by removing the form of operator-function-id that contains a tmeplate-argument-list; as a result, references like “::operator+<X>” are no longer covered in _N4567_.5.1.1 [expr.prim.general].




1837. Use of this in friend and local class declarations

Section: _N4567_.5.1.1  [expr.prim.general]     Status: CD6     Submitter: Hubert Tong     Date: 2014-01-13

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The description of the use of this found in _N4567_.5.1.1 [expr.prim.general] paragraphs 3 and 4 allow it to appear in the declaration of a non-static member function following the optional cv-qualifier-seq and in the brace-or-equal-initializer of a non-static data member; all other uses are prohibited. These restrictions appear to allow questionable uses of this in several contexts. For example:

  template <typename T>
  struct Fish { static const bool value = true; };

  struct Other {
    int p();
    auto q() -> decltype(p()) *;
  };

  class Outer {
    // The following declares a member function of class Other.
    // Is this interpreted as Other* or Outer*?
    friend auto Other::q() -> decltype(this->p()) *;
    int g();
    int f() {
     extern void f(decltype(this->g()) *);
     struct Inner {
       // The following are all within the declaration of Outer::f().
       // Is this Outer* or Inner*?
       static_assert(Fish<decltype(this->g())>::value, "");
       enum { X = Fish<decltype(this->f())>::value };
       struct Inner2 : Fish<decltype(this->g())> { };
       friend void f(decltype(this->g()) *);
       friend auto Other::q() -> decltype(this->p()) *;
     };
     return 0;
    }
  };
  struct A {
   int f();
   bool b = [] {
    struct Local {
     static_assert(sizeof this->f() == sizeof(int), ""); // A or Local?
    };
   };
  };

There is implementation divergence on the treatment of these examples.




2165. Namespaces, declarative regions, and translation units

Section: _N4868_.6.4.1  [basic.scope.declarative]     Status: CD6     Submitter: Richard Smith     Date: 2015-07-30

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The definition of “declarative region” given in _N4868_.6.4.1 [basic.scope.declarative] paragraph 1 is,

Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity.

According to 9.8 [basic.namespace] paragraph 1,

Unlike other declarative regions, the definition of a namespace can be split over several parts of one or more translation units.

This seems like a misuse of the term “declarative region”; in particular, a name x declared in namespace N in translation unit A cannot be used as an unqualified name in the part of namespace N in translation unit B unless it is also declared in B. See also issue 1884.




1291. Looking up a conversion-type-id

Section: _N4868_.6.5.6  [basic.lookup.classref]     Status: CD6     Submitter: Daveed Vandevoorde     Date: 2011-04-10

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The Standard talks about looking up a conversion-type-id as if it were an identifier (_N4868_.6.5.6 [basic.lookup.classref] paragraph 7), but that is not exactly accurate. Presumably it should talk instead about looking up names (if any) appearing in the type-specifier-seq of the conversion-type-id.




1835. Dependent member lookup before <

Section: _N4868_.6.5.6  [basic.lookup.classref]     Status: CD6     Submitter: Richard Smith     Date: 2014-01-17

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

According to _N4868_.6.5.6 [basic.lookup.classref] paragraph 1,

In a class member access expression (7.6.1.5 [expr.ref]), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (13.3 [temp.names]) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template.

Given

   template<typename T> T end(T);
   template<typename T>
   bool Foo(T it) {
     return it->end < it->end;
   }

since it is dependent and thus end cannot be looked up in the class of the object expression, it is looked up in the context of the postfix-expression. This lookup finds the function template, making the expression ill-formed.

One possibility might be to limit the lookup to the class of the object expression when the object expression is dependent.




1908. Dual destructor lookup and template-ids

Section: _N4868_.6.5.6  [basic.lookup.classref]     Status: CD6     Submitter: Hubert Tong     Date: 2014-03-31

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

According to _N4868_.6.5.6 [basic.lookup.classref] paragraph 3,

If the unqualified-id is ~type-name, the type-name is looked up in the context of the entire postfix-expression. If the type T of the object expression is of a class type C, the type-name is also looked up in the scope of class C. At least one of the lookups shall find a name that refers to (possibly cv-qualified) T.

This would apply to an example like

  namespace K {
    template <typename T, typename U = char> struct A { };
    A<short> *a;
  }

  template <typename T> using A = K::A<short, T>;

  int main() {
    K::a->~A<char>();
  }

Current implementations, however, only apply the dual lookup when the type-name is not a template-id. The specification should be changed to reflect current practice.




138. Friend declaration name lookup

Section: _N4868_.9.8.2.3  [namespace.memdef]     Status: CD6     Submitter: Martin von Loewis     Date: 14 Jul 1999

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

_N4868_.9.8.2.3 [namespace.memdef] paragraph 3 says,

If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace... When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered.
It is not clear from this passage how to determine whether an entity is "first declared" in a friend declaration. One question is whether a using-declaration influences this determination. For instance:
    void foo();
    namespace A{
      using ::foo;
      class X{
	friend void foo();
      };
    }
Is the friend declaration a reference to ::foo or a different foo?

Part of the question involves determining the meaning of the word "synonym" in 9.9 [namespace.udecl] paragraph 1:

A using-declaration introduces a name into the declarative region in which the using-declaration appears. That name is a synonym for the name of some entity declared elsewhere.
Is "using ::foo;" the declaration of a function or not?

More generally, the question is how to describe the lookup of the name in a friend declaration.

John Spicer: When a declaration specifies an unqualified name, that name is declared, not looked up. There is a mechanism in which that declaration is linked to a prior declaration, but that mechanism is not, in my opinion, via normal name lookup. So, the friend always declares a member of the nearest namespace scope regardless of how that name may or may not already be declared there.

Mike Miller: 6.5.3 [basic.lookup.unqual] paragraph 7 says:

A name used in the definition of a class X outside of a member function body or nested class definition shall be declared in one of the following ways:... [Note: when looking for a prior declaration of a class or function introduced by a friend declaration, scopes outside of the innermost enclosing namespace scope are not considered.]
The presence of this note certainly implies that this paragraph describes the lookup of names in friend declarations.

John Spicer: It most certainly does not. If that section described the friend lookup it would yield the incorrect results for the friend declarations of f and g below. I don't know why that note is there, but it can't be taken to mean that that is how the friend lookup is done.

    void f(){}
    void g(){}
    class B {
        void g();
    };
    class A : public B {
        void f();
        friend void f(); // ::f not A::f
        friend void g(); // ::g not B::g
    };

Mike Miller: If so, the lookups for friend functions and classes behave differently. Consider the example in 6.5.6 [basic.lookup.elab] paragraph 3:

    struct Base {
        struct Data;         // OK: declares nested Data
        friend class Data;   // OK: nested Data is a friend
    };

If the friend declaration is not a reference to ::foo, there is a related but separate question: does the friend declaration introduce a conflicting (albeit "invisible") declaration into namespace A, or is it simply a reference to an as-yet undeclared (and, in this instance, undeclarable) A::foo? Another part of the example in 6.5.6 [basic.lookup.elab] paragraph 3 is related:

    struct Data {
        friend struct Glob;  // OK: Refers to (as yet) undeclared Glob
                             // at global scope.
    };

John Spicer: You can't refer to something that has not yet been declared. The friend is a declaration of Glob, it just happens to declare it in a such a way that its name cannot be used until it is redeclared.

(A somewhat similar question has been raised in connection with issue 36. Consider:

    namespace N {
        struct S { };
    }
    using N::S;
    struct S;          // legal?

According to 11.3 [class.name] paragraph 2,

A declaration consisting solely of class-key identifier ; is either a redeclaration of the name in the current scope or a forward declaration of the identifier as a class name.

Should the elaborated type declaration in this example be considered a redeclaration of N::S or an invalid forward declaration of a different class?)

(See also issues 95, 136, 139, 143, 165, and 166, as well as paper J16/00-0006 = WG21 N1229.)




1252. Overloading member function templates based on dependent return type

Section: _N4868_.12.2  [over.load]     Status: CD6     Submitter: Nikolay Ivchenkov     Date: 2011-03-06

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The Standard does not allow overloading of member functions that differ only in their return type (cf enable_if).




1898. Use of “equivalent” in overload resolution

Section: _N4868_.12.3  [over.dcl]     Status: CD6     Submitter: Hubert Tong     Date: 2014-03-21

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The normative text of _N4868_.12.3 [over.dcl] relies on the term “equivalent,” for which it refers to _N4868_.12.2 [over.load], but the term appears there only in non-normative text. The resolution of this issue should be coordinated with that of issue 1668.




578. Phase 1 replacement of characters with universal-character-names

Section: 5.2  [lex.phases]     Status: CD6     Submitter: Martin Vejnár     Date: 7 May 2006

[Accepted at the October, 2021 meeting as part of paper P2314R4.]

According to 5.2 [lex.phases] paragraph 1, in translation phase 1,

Any source file character not in the basic source character set (5.3 [lex.charset]) is replaced by the universal-character-name that designates that character.

If a character that is not in the basic character set is preceded by a backslash character, for example

    "\á"

the result is equivalent to

    "\\u00e1"

that is, a backslash character followed by the spelling of the universal-character-name. This is different from the result in C99, which accepts characters from the extended source character set without replacing them with universal-character-names.

See also issue 1335.

Additional note (February, 2022):

P2314R4 Character sets and encodings (approved in October, 2021) effected changes so that extended characters are no longer translated to UCNs in phase 1.




2455. Concatenation of string literals vs translation phases 5 and 6

Section: 5.2  [lex.phases]     Status: CD6     Submitter: Tom Honermann     Date: 2020-07-02

[Addressed by paper P2314R4, adopted at the October, 2021 plenary.]

According to 5.2 [lex.phases] paragraph 1, concatenation of adjacent string literals is performed in translation phase 6, after conversion of the literal values to the execution character set. However, 5.13.5 [lex.string] paragraph 11 indicates that the interpretation of the string contents is dependent on the encoding-prefixes specified for the literals being concatenated:

In translation phase 6 (5.2 [lex.phases]), adjacent string-literals are concatenated. If both string-literals have the same encoding-prefix, the resulting concatenated string-literal has that encoding-prefix. If one string-literal has no encoding-prefix, it is treated as a string-literal of the same encoding-prefix as the other operand. If a UTF-8 string literal token is adjacent to a wide string literal token, the program is ill-formed. Any other concatenations are conditionally-supported with implementation-defined behavior. [Note: This concatenation is an interpretation, not a conversion. Because the interpretation happens in translation phase 6 (after each character from a string-literal has been translated into a value from the appropriate character set), a string-literal's initial rawness has no effect on the interpretation or well-formedness of the concatenation. —end note]

This seems to indicate that string-literals with different encoding-prefixes are separately converted and then joined, potentially resulting in strings containing code unit sequences corresponding to different character encodings. This reading would contradict the intent, expressed in adjacent table, that, e.g., u"a" "b" means the same as u"ab".

There is implementation divergence in the handling of this specification.

Phases 5 and 6 cannot simply be reversed, because interpretation of escape sequences must precede concatenation, as specified later in the same paragraph:

Characters in concatenated strings are kept distinct.

[Example:

"\xA" "B"

contains the two characters '\xA' and 'B' after concatenation (and not the single hexadecimal character '\xAB'). —end example]

Richard Smith suggested here that "we should remove phases 5 and 6 entirely, parse one or more string-literal tokens as a string literal expression, and only perform the translation from the contents of the string literal tokens into characters in the execution character set as part of specifying the semantics of a string literal expression."




1403. Universal-character-names in comments

Section: 5.7  [lex.comment]     Status: CD6     Submitter: David Krauss     Date: 2011-10-05

[ Resolved by P2314R4, adopted in October, 2021. ]

According to 5.3 [lex.charset] paragraph 2,

If the hexadecimal value for a universal-character-name corresponds to a surrogate code point (in the range 0xD800-0xDFFF, inclusive), the program is ill-formed. Additionally, if the hexadecimal value for a universal-character-name outside the c-char-sequence, s-char-sequence, or r-char-sequence of a character or string literal corresponds to a control character (in either of the ranges 0x00-0x1F or 0x7F-0x9F, both inclusive) or to a character in the basic source character set, the program is ill-formed.

These restrictions should not apply to comment text. Arguably the prohibitions of control characters and characters in the basic character set already do not apply, as they require that the preprocessing tokens for literals have already been recognized; this occurs in phase 3, which also replaces comments with single spaces. However, the prohibition of surrogate code points is not so limited and might conceivably be applied within comments.

Probably the most straightforward way of addressing this problem would be simply to state in 5.7 [lex.comment] that character sequences that resemble universal-character-names are not recognized as such within comment text.

Additional note (February, 2022):

P2314R4 Character sets and encodings (approved in October, 2021) effected changes so that extended characters are no longer translated to UCNs in phase 1.




1972. Identifier character restrictions in non-identifiers

Section: 5.10  [lex.name]     Status: CD6     Submitter: Richard Smith     Date: 2014-07-15

[Accepted at the June, 2021 meeting as part of paper P1949R7 (C++ Identifier Syntax using Unicode Standard Annex 31).]

According to 5.10 [lex.name] paragraph 1,

Each universal-character-name in an identifier shall designate a character whose encoding in ISO 10646 falls into one of the ranges specified in _N4606_.E.1 [charname.allowed].

However, identifier-nondigit is also used in the grammar for pp-number. Should this restriction also be understood to apply in that non-identifier context?




1656. Encoding of numerically-escaped characters

Section: 5.13.3  [lex.ccon]     Status: CD6     Submitter: Mike Miller     Date: 2013-04-30

[Accepted at the November, 2020 meeting as part of paper P2029R4.]

According to 5.13.3 [lex.ccon] paragraph 4,

The escape \ooo consists of the backslash followed by one, two, or three octal digits that are taken to specify the value of the desired character. The escape \xhhh consists of the backslash followed by x followed by one or more hexadecimal digits that are taken to specify the value of the desired character. There is no limit to the number of digits in a hexadecimal sequence. A sequence of octal or hexadecimal digits is terminated by the first character that is not an octal digit or a hexadecimal digit, respectively. The value of a character literal is implementation-defined if it falls outside of the implementation-defined range defined for char (for literals with no prefix), char16_t (for literals prefixed by 'u'), char32_t (for literals prefixed by 'U'), or wchar_t (for literals prefixed by 'L').

It is not clearly stated whether the “desired character” being specified reflects the source or the target encoding. This particularly affects UTF-8 string literals (5.13.5 [lex.string] paragraph 7) :

A string literal that begins with u8, such as u8"asdf", is a UTF-8 string literal and is initialized with the given characters as encoded in UTF-8.

For example, assuming the source encoding is Latin-1, is u8"\xff" supposed to specify a three-byte string whose first two bytes are 0xc3 0xbf (the UTF-8 encoding of \u00ff) or a two-byte string whose first byte has the value 0xff? (At least some current implementations assume the latter interpretation.)

Notes from the September, 2013 meeting:

The second interpretation (that the escape sequence specifies the execution-time code unit) is intended.




2333. Escape sequences in UTF-8 character literals

Section: 5.13.3  [lex.ccon]     Status: CD6     Submitter: Mike Miller     Date: 2017-01-05

[Accepted at the November, 2020 meeting as part of paper P2029R4.]

The meaning of a numeric escape appearing in a UTF-8 character literal is not clear. 5.13.3 [lex.ccon] paragraph 3 assumes that the contents of the quoted string is a character with an ISO 10646 code point value, which is not necessarily the case with a numeric escape, and paragraph 8 could be read to indicate that a numeric escape specifies the actual runtime value of the object rather than a Unicode code point. In addition, paragraph 8 only specifies the result for unprefixed and wide-character literals, not for UTF-8 literals, so that could be read as indicating that a numeric escape in a UTF-8 character literal is undefined behavior (i.e., not defined by the Standard).

Notes from the August, 2017 teleconference:

An escape sequence in a UTF-8 character literal should be ill-formed.




2402. When is the restriction to a single c-char in a Unicode literal enforced?

Section: 5.13.3  [lex.ccon]     Status: CD6     Submitter: Richard Smith     Date: 2019-01-08

[Adopted at the November, 2020 meeting as part of paper P2029R4.]

According to 5.13.3 [lex.ccon] paragraphs 3-5, a Unicode character literal “containing multiple c-chars is ill-formed.” However, it is not clear in what phase of translation that restriction applies.

One possible resolution would be to add a note saying that the pp-token is formed according to the grammar and the restriction to a single c-char is checked in phase 7.




2540. Unspecified interpretation of numeric-escape-sequence

Section: 5.13.3  [lex.ccon]     Status: CD6     Submitter: Richard Smith     Date: 2022-02-25

[Accepted at the July, 2022 meeting.]

Subclause 5.13.3 [lex.ccon] does not specify how the characters in an octal-escape-sequence or hexadecimal-escape-sequence are interpreted to obtain the integer value v that is used in bullet 3.2:

Proposed resolution (approved by CWG 2022-03-11):

  1. Change in 5.13.3 [lex.ccon] bullet 3.2 as follows:
    • A character-literal with a c-char-sequence consisting of a single numeric-escape-sequence that specifies an integer value v has a value as follows:
      • Let v be the integer value represented by the octal number comprising the sequence of octal-digits in an octal-escape-sequence or by the hexadecimal number comprising the sequence of hexadecimal-digits in a hexadecimal-escape-sequence.
      • If v does not exceed the range of representable values of the character-literal's type, then the value is v.
      • ...
  2. Change in 5.13.5 [lex.string] bullet 10.2 as follows:
    • Each numeric-escape-sequence (5.13.3 [lex.ccon]) that specifies an integer value v contributes a single code unit with a value as follows:
      • Let v be the integer value represented by the octal number comprising the sequence of octal-digits in an octal-escape-sequence or by the hexadecimal number comprising the sequence of hexadecimal-digits in a hexadecimal-escape-sequence.
      • If v does not exceed the range of representable values of the string-literal's array element type, then the value is v.
      • ...



411. Use of universal-character-name in character versus string literals

Section: 5.13.5  [lex.string]     Status: CD6     Submitter: James Kanze     Date: 23 Apr 2003

[Accepted at the November, 2020 meeting as part of paper P2029R4.]

5.13.5 [lex.string] paragraph 5 reads

Escape sequences and universal-character-names in string literals have the same meaning as in character literals, except that the single quote ' is representable either by itself or by the escape sequence \', and the double quote " shall be preceded by a \. In a narrow string literal, a universal-character-name may map to more than one char element due to multibyte encoding.

The first sentence refers us to 5.13.3 [lex.ccon], where we read in the first paragraph that "An ordinary character literal that contains a single c-char has type char [...]." Since the grammar shows that a universal-character-name is a c-char, something like '\u1234' must have type char (and thus be a single char element); in paragraph 5, we read that "A universal-character-name is translated to the encoding, in the execution character set, of the character named. If there is no such encoding, the universal-character-name is translated to an implemenation-defined encoding."

This is in obvious contradiction with the second sentence. In addition, I'm not really clear what is supposed to happen in the case where the execution (narrow-)character set is UTF-8. Consider the character \u0153 (the oe in the French word oeuvre). Should '\u0153' be a char, with an "error" value, say '?' (in conformance with the requirement that it be a single char), or an int, with the two char values 0xC5, 0x93, in an implementation defined order (in conformance with the requirement that a character representable in the execution character set be represented). Supposing the former, should "\u0153" be the equivalent of "?" (in conformance with the first sentence), or "\xC5\x93" (in conformance with the second).

Notes from October 2003 meeting:

We decided we should forward this to the C committee and let them resolve it. Sent via e-mail to John Benito on November 14, 2003.

Reply from John Benito:

I talked this over with the C project editor, we believe this was handled by the C committee before publication of the current standard.

WG14 decided there needed to be a more restrictive rule for one-to-one mappings: rather than saying "a single c-char" as C++ does, the C standard says "a single character that maps to a single-byte execution character"; WG14 fully expect some (if not many or even most) UCNs to map to multiple characters.

Because of the fundamental differences between C and C++ character types, I am not sure the C committee is qualified to answer this satisfactorily for WG21. WG14 is willing to review any decision reached for compatibility.

I hope this helps.

(See also issue 912 for a related question.)




1849. Variable templates and the ODR

Section: 6.3  [basic.def.odr]     Status: CD6     Submitter: Richard Smith     Date: 2014-02-03

[ Resolved by issue 2494, adopted in February, 2022. ]

The description in 6.3 [basic.def.odr] paragraph 6 of when entities can be multiply-declared in a program does not, but should, discuss variable templates.




2494. Multiple definitions of non-odr-used entities

Section: 6.3  [basic.def.odr]     Status: CD6     Submitter: Hubert Tong     Date: 2021-07-02

[Accepted at the February, 2022 meeting.]

According to 6.3 [basic.def.odr] paragraph 10,

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement (8.5.2 [stmt.if]); no diagnostic required.

This wording could be interpreted as allowing multiple definitions of non-inline variables and functions if they are not odr-used. That is presumably not the intent.

Notes from the August, 2021 teleconference:

CWG observed that there is a similar problem in paragraph 13. See also issue 1849.

Proposed resolution, December, 2021:

  1. Change 6.3 [basic.def.odr] paragraph 1 as follows:

  2. Each of the following is termed a definable item:

    No translation unit shall contain more than one definition of any variable, function, class type, enumeration type, template, default argument for a parameter (for a function in a given scope), or default template argument definable item.

  3. Change 6.3 [basic.def.odr] paragraph 10 as follows:

  4. Every program shall contain exactly at least one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement (8.5.2 [stmt.if]); no diagnostic required. The definition...
  5. Change 6.3 [basic.def.odr] paragraph 13 as follows:

  6. There can be more than one definition of a

    in a program provided that each definition appears in a different translation unit and the definitions satisfy the following requirements. For any definable item D with definitions in multiple translation units,

    the program is ill-formed; a diagnostic is required only if the definable item is attached to a named module and a prior definition is reachable at the point where a later definition occurs. Given such an entity D defined in more than one translation unit item, for all definitions of D, or, if D is an unnamed enumeration, for all definitions of D that are reachable at any given program point, the following requirements shall be satisfied...

  7. Delete 6.3 [basic.def.odr] paragraph 15:

  8. If these definitions do not satisfy these requirements, then the program is ill-formed; a diagnostic is required only if the entity is attached to a named module and a prior definition is reachable at the point where a later definition occurs.



554. Definition of “declarative region” and “scope”

Section: 6.4  [basic.scope]     Status: CD6     Submitter: Gabriel Dos Reis     Date: 29 December 2005

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The various uses of the term “declarative region” throughout the Standard indicate that the term is intended to refer to the entire block, class, or namespace that contains a given declaration. For example, 6.4 [basic.scope] paragraph 2 says, in part:

[Example: in

    int j = 24;
    int main()
    {
        int i = j, j;
        j = 42;
    }

The declarative region of the first j includes the entire example... The declarative region of the second declaration of j (the j immediately before the semicolon) includes all the text between { and }...

However, the actual definition given for “declarative region” in 6.4 [basic.scope] paragraph 1 does not match this usage:

Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity.

Because (except in class scope) a name cannot be used before it is declared, this definition contradicts the statement in the example and many other uses of the term throughout the Standard. As it stands, this definition is identical to that of the scope of a name.

The term “scope” is also misused. The scope of a declaration is defined in 6.4 [basic.scope] paragraph 1 as the region in which the name being declared is valid. However, there is frequent use of the phrase “the scope of a class,” not referring to the region in which the class's name is valid but to the declarative region of the class body, and similarly for namespaces, functions, exception handlers, etc. There is even a mention of looking up a name “in the scope of the complete postfix-expression” (_N4868_.6.5.6 [basic.lookup.classref] paragraph 3), which is the exact inverse of the scope of a declaration.

This terminology needs a thorough review to make it logically consistent. (Perhaps a discussion of the scope of template parameters could also be added to section 6.4 [basic.scope] at the same time, as all other kinds of scopes are described there.)

Proposed resolution (November, 2006):

  1. Change 6.4 [basic.scope] paragraph 1 as follows:

  2. Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity a statement, block, function declarator, function-definition, class, handler, template-declaration, template-parameter-list of a template template-parameter, or namespace. In general, each particular name is valid may be used as an unqualified name to refer to the entity of its declaration or to the label only within some possibly discontiguous portion of program text called its scope. To determine the scope of a declaration...
  3. Change 6.4 [basic.scope] paragraph 3 as follows:

  4. The names declared by a declaration are introduced into the scope in which the declaration occurs declarative region that directly encloses the declaration, except that declaration-statements, function parameter names in the declarator of a function-definition, exception-declarations (6.4.3 [basic.scope.block]), the presence of a friend specifier (11.8.4 [class.friend]), certain uses of the elaborated-type-specifier (9.2.9.5 [dcl.type.elab]), and using-directives (9.8.4 [namespace.udir]) alter this general behavior.
  5. Change 6.4.3 [basic.scope.block] paragraphs 1-3 and add a new paragraph 4 before the existing paragraph 4 as follows:

  6. A name declared in a block (8.4 [stmt.block]) is local to that block. Its potential scope begins at its point of declaration (6.4.2 [basic.scope.pdecl]) and ends at the end of its declarative region. The declarative region of a name declared in a declaration-statement is the directly enclosing block (8.4 [stmt.block]). Such a name is local to the block.

    The potential scope declarative region of a function parameter name (including one appearing in the declarator of a function-definition or in a lambda-parameter-declaration-clause) or of a function-local predefined variable in a function definition (9.5 [dcl.fct.def]) begins at its point of declaration. If the function has a function-try-block the potential scope of a parameter or of a function-local predefined variable ends at the end of the last associated handler, otherwise it ends at the end of the outermost block of the function definition. A parameter name is the entire function definition or lambda-expression. Such a name is local to the function definition and shall not be redeclared in the any outermost block of the function definition nor in the outermost block of any handler associated with a function-try-block function-body (including handlers of a function-try-block) or lambda-expression.

    The name in a catch exception-declaration The declarative region of a name declared in an exception-declaration is its entire handler. Such a name is local to the handler and shall not be redeclared in the outermost block of the handler.

    The potential scope of any local name begins at its point of declaration (6.4.2 [basic.scope.pdecl]) and ends at the end of its declarative region.

  7. Change _N4868_.6.4.5 [basic.funscope] as indicated:

  8. Labels (8.2 [stmt.label]) have function scope and may be used anywhere in the function in which they are declared except in members of local classes (11.6 [class.local]) of that function. Only labels have function scope.
  9. Change 8.8 [stmt.dcl] paragraph 1 as follows:

  10. A declaration statement introduces one or more new identifiers names into a block; it has the form

    [Note: If an identifier a name introduced by a declaration was previously declared in an outer block, the outer declaration is hidden for the remainder of the block, after which it resumes its force (_N4868_.6.4.10 [basic.scope.hiding]). end note]

[Drafting notes: This resolution deals almost exclusively with the unclear definition of “declarative region.” I've left the ambiguous use of “scope” alone for now. However sections 3.3.x all have headings reading “xxx scope,” but they don't mean the scope of a declaration but the different kinds of declarative regions and their effects on the scope of declarations contained therein. To me, it looks like most of 3.4 should refer to “declarative region” and not to “scope.”

The change to 6.7 fixes an “identifier” misuse (e.g., extern T operator+(T,T); at block scope introduces a name but not an identifier) and removes normative redundancy.]

Notes from the October, 2015 meeting:

This issue has been returned to "drafting" status to be reconciled with changes to the underlying existing text.




2502. Unintended declaration conflicts in nested statement scopes

Section: 6.4.3  [basic.scope.block]     Status: CD6     Submitter: Jens Maurer     Date: 2021-08-26

[Accepted at the February, 2022 meeting.]

The changes of P1787R6 inadvertently made constructs like

  if (int a = 1)
    if (int a = 1)
      ...

ill-formed.

Proposed resolution (September, 2021):

Change 6.4.3 [basic.scope.block] bullet 2.2 as follows:

If a declaration whose target scope is the block scope S of a

potentially conflicts with a declaration whose target scope is the parent scope of S, the program is ill-formed.

(See editorial issue 4843.)




2009. Unclear specification of class scope

Section: 6.4.7  [basic.scope.class]     Status: CD6     Submitter: Richard Smith     Date: 2014-09-23

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Point 2 of the rules of class scope in 6.4.7 [basic.scope.class] paragraph 1 says,

A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.

It is not clear that this provision does not apply to names appearing in function bodies, default arguments, exception-specifications, and brace-or-equal-initializers. It is also not clear what it means to “re-evaluate” a name.

One possible approach to this problem would be to say that all names declared in a class are visible throughout the class and simply make it ill-formed to refer to a name that has not been declared yet in the contexts in which that is problematic, such as types and template arguments.

In addition, the fourth point says,

A name declared within a member function hides a declaration of the same name whose scope extends to or past the end of the member function's class.

This rule is unneeded, as it simply restates the normal hiding rule in _N4868_.6.4.1 [basic.scope.declarative] paragraph 1:

The scope of a declaration is the same as its potential scope unless the potential scope contains another declaration of the same name. In that case, the potential scope of the declaration in the inner (contained) declarative region is excluded from the scope of the declaration



2331. Redundancy in description of class scope

Section: 6.4.7  [basic.scope.class]     Status: CD6     Submitter: Thomas Köppe     Date: 2016-12-07

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

[Accepted as a DR at the February, 2019 meeting.]

The first four paragraphs of 6.4.7 [basic.scope.class] are somewhat redundant. In particular:

This is editorial issue 1169.

Proposed resolution (November, 2018):

In 6.4.7 [basic.scope.class], delete paragraph 1, move paragraph 4 to the beginning, and make paragraph 3 a note:

The potential scope of a name declared in a class consists not only of the declarative region following the name's point of declaration, but also of all complete-class contexts (11.4 [class.mem]) of that class.

The potential scope of a declaration that extends to or past the end of a class definition also extends to the regions defined by its member definitions, even if the members are defined lexically outside the class (this includes static data member definitions, nested class definitions, and member function definitions, including the member function body and any portion of the declarator part of such definitions which follows the declarator-id, including a parameter-declaration-clause and any default arguments (9.3.4.7 [dcl.fct.default])).

A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.

[Note: A name declared within a member function hides a declaration of the same name whose scope extends to or past the end of the member function's class (_N4868_.6.4.10 [basic.scope.hiding]). end note]

The potential scope of a declaration that extends to or past the end of a class definition also extends to the regions defined by its member definitions, even if the members are defined lexically outside the class (this includes static data member definitions, nested class definitions, and member function definitions, including the member function body and any portion of the declarator part of such definitions which follows the declarator-id, including a parameter-declaration-clause and any default arguments (9.3.4.7 [dcl.fct.default])).

Additional note, March, 2019:

The resolution emoves the rule that a class member name can be found by unqualified lookup prior to its point of definition in complete-class contexts, at least in non-defining member declarations:

  struct X {
    void f(int n = k); // was valid, now ill-formed
    static int k;
  };

Relatedly, the "member definitions" rule (formerly p4, now p2) that was used to justify the removal of p1 is wrong (both before and after that change):

  struct A {
    void f(B b) {} // was always (incorrectly) valid
    struct B {};
  };

For these reasons, this issue has been returned to "drafting" status.




2582. Differing member lookup from nested classes

Section: 6.5.2  [class.member.lookup]     Status: CD6     Submitter: Jason Merrill     Date: 2022-05-02

[Accepted at the July, 2022 meeting.]

Consider:

  typedef int T;
  struct A {
   struct B {
    static T t;
   };
   typedef float T; // IFNDR?
  };

Subclause 6.5.2 [class.member.lookup] paragraph 6 specifies:

The result of the search is the declaration set of S(N, T). If it is an invalid set, the program is ill-formed. If it differs from the result of a search in T for N from immediately after the class-specifier of T, the program is ill-formed, no diagnostic required.

It is unclear whether the lookup of T inside A::B is subject to the "if it differs" rule, given that the class-specifier of A::B ends before introducing A::T.

Proposed resolution (approved by CWG 2022-05-06):

Change in 6.5.2 [class.member.lookup] paragraph 6 as follows:

If it differs from the result of a search in T for N from immediately after the class-specifier in a complete-class context of T, the program is ill-formed, no diagnostic required.



191. Name lookup does not handle complex nesting

Section: 6.5.3  [basic.lookup.unqual]     Status: CD6     Submitter: Alan Nash     Date: 29 Dec 1999

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The current description of unqualified name lookup in 6.5.3 [basic.lookup.unqual] paragraph 8 does not correctly handle complex cases of nesting. The Standard currently reads,

A name used in the definition of a function that is a member function (9.3) of a class X shall be declared in one of the following ways:
In particular, this formulation does not handle the following example:
    struct outer {
        static int i;
        struct inner {
            void f() {
                struct local {
                    void g() {
                        i = 5;
                    }
                };
            }
        };
    };
Here the reference to i is from a member function of a local class of a member function of a nested class. Nothing in the rules allows outer::i to be found, although intuitively it should be found.

A more comprehensive formulation is needed that allows traversal of any combination of blocks, local classes, and nested classes. Similarly, the final bullet needs to be augmented so that a function need not be a (direct) member of a namespace to allow searching that namespace when the reference is from a member function of a class local to that function. That is, the current rules do not allow the following example:

    int j;    // global namespace
    struct S {
        void f() {
            struct local2 {
                void g() {
                    j = 5;
                }
            };
        }
    };



405. Unqualified function name lookup

Section: 6.5.3  [basic.lookup.unqual]     Status: CD6     Submitter: William M. Miller     Date: 14 Apr 2003

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

There seems to be some confusion in the Standard regarding the relationship between 6.5.3 [basic.lookup.unqual] (Unqualified name lookup) and 6.5.4 [basic.lookup.argdep] (Argument-dependent lookup). For example, 6.5.3 [basic.lookup.unqual] paragraph 3 says,

The lookup for an unqualified name used as the postfix-expression of a function call is described in 6.5.4 [basic.lookup.argdep].

In other words, nothing in 6.5.3 [basic.lookup.unqual] applies to function names; the entire lookup is described in 6.5.4 [basic.lookup.argdep].

6.5.4 [basic.lookup.argdep] does not appear to share this view of its responsibility. The closest it comes is in 6.5.4 [basic.lookup.argdep] paragraph 2a:

...the set of declarations found by the lookup of the function name is the union of the set of declarations found using ordinary unqualified lookup and the set of declarations found in the namespaces and classes associated with the argument types.

Presumably, "ordinary unqualified lookup" is a reference to the processing described in 6.5.3 [basic.lookup.unqual], but, as noted above, 6.5.3 [basic.lookup.unqual] explicitly precludes applying that processing to function names. The details of "ordinary unqualified lookup" of function names are not described anywhere.

The other clauses that reference 6.5.4 [basic.lookup.argdep], clauses Clause 12 [over] and Clause 13 [temp], are split over the question of the relationship between 6.5.3 [basic.lookup.unqual] and 6.5.4 [basic.lookup.argdep]. 12.2.2.2.2 [over.call.func] paragraph 3, for instance, says

The name is looked up in the context of the function call following the normal rules for name lookup in function calls (6.5.4 [basic.lookup.argdep]).

I.e., this reference assumes that 6.5.4 [basic.lookup.argdep] is self-contained. The same is true of 12.2.2.3 [over.match.oper] paragraph 3, second bullet:

The set of non-member candidates is the result of the unqualified lookup of operator@ in the context of the expression according to the usual rules for name lookup in unqualified function calls (6.5.4 [basic.lookup.argdep]), except that all member functions are ignored.

On the other hand, however, 13.8.4.2 [temp.dep.candidate] paragraph 1 explicitly assumes that 6.5.3 [basic.lookup.unqual] and 6.5.4 [basic.lookup.argdep] are both involved in function name lookup and do different things:

For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found using the usual lookup rules (6.5.3 [basic.lookup.unqual], 6.5.4 [basic.lookup.argdep]) except that:

Suggested resolution:

Change 6.5.3 [basic.lookup.unqual] paragraph 1 from

...name lookup ends as soon as a declaration is found for the name.

to

...name lookup ends with the first scope containing one or more declarations of the name.

Change the first sentence of 6.5.3 [basic.lookup.unqual] paragraph 3 from

The lookup for an unqualified name used as the postfix-expression of a function call is described in 6.5.4 [basic.lookup.argdep].

to

An unqualified name used as the postfix-expression of a function call is looked up as described below. In addition, argument-dependent lookup (6.5.4 [basic.lookup.argdep]) is performed on this name to complete the resulting set of declarations.



1200. Lookup rules for template parameters

Section: 6.5.3  [basic.lookup.unqual]     Status: CD6     Submitter: Johannes Schaub     Date: 2010-09-18

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Although 6.4.9 [basic.scope.temp] now describes the scope of a template parameter, the description of unqualified name lookup in 6.5.3 [basic.lookup.unqual] do not cover uses of template parameter names. The note in 6.5.3 [basic.lookup.unqual] paragraph 16 says,

the rules for name lookup in template definitions are described in 13.8 [temp.res].

but the rules there cover dependent and non-dependent names, not template parameters themselves.




2370. friend declarations of namespace-scope functions

Section: 6.5.3  [basic.lookup.unqual]     Status: CD6     Submitter: Andrew Marino     Date: 2017-12-04

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Issue 1906 discussed unqualified lookup in friend declarations of class member functions, and CWG decided to reaffirm the existing specification without change. However, there is a similar issue regarding friend declarations of namespace-scope functions. According to 6.5.3 [basic.lookup.unqual] paragraph 9,

Name lookup for a name used in the definition of a friend function (11.8.4 [class.friend]) defined inline in the class granting friendship shall proceed as described for lookup in member function definitions. If the friend function is not defined in the class granting friendship, name lookup in the friend function definition shall proceed as described for lookup in namespace member function definitions.

In particular, “as described for lookup in member function definitions” does not consider names declared in the namespace of the friend function, and non-defining friend declarations of namespace-scope functions are not described at all. There is implementation divergence on these points. For example:

  namespace N {
    typedef int type;
    void f(type);
    void g(type);
    void h(type);
  }
  class C {
    typedef N::type N_type;
    friend void N::f(type) { }  // Ill-formed: cannot define namespace friend
    friend void N::g(type);     // Unclear whether type is found or not
    friend void N::h(N_type);   // Unclear whether N_type is found or not
  };

Notes from the November, 2018 meeting:

CWG agreed that the lookup for functions in namespaces should be similar to that for class member functions.




1771. Restricted lookup in nested-name-specifier

Section: 6.5.5  [basic.lookup.qual]     Status: CD6     Submitter: Canada     Date: 2013-09-24

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

N3690 comment CA 21

Consider the following example:

  template <typename T> struct B { };
  namespace N {
    namespace L {
      template <int> void A();
    }
    namespace M {
      template <int> struct A { typedef int y; };
    }
    using namespace L;
    using namespace M;
  }
  B<N::/*template */A<0>::y> (x);

Which A is referenced in the last line? According to 6.5.5 [basic.lookup.qual] paragraph 1,

If a :: scope resolution operator in a nested-name-specifier is not preceded by a decltype-specifier, lookup of the name preceding that :: considers only namespaces, types, and templates whose specializations are types.

It is not clear whether this applies to the example or not, and the interpretation of the < token depends on the result of the lookup.

Notes from the September, 2013 meeting:

The restricted lookup mentioned in 6.5.5 [basic.lookup.qual] paragraph 1 is based on a one-token lookahead; because the next token following A in the example is not ::, the restricted lookup does not apply, and the result is ambiguous. Uncommenting the template keyword in the example does not affect the lookup.




1828. nested-name-specifier ambiguity

Section: 6.5.5  [basic.lookup.qual]     Status: CD6     Submitter: Richard Smith     Date: 2014-01-08

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Issue 125 concerned an example like

  friend A::B::C();

which might be parsed as either

  friend A (::B::C)();

or

  friend A::B (::C)();

Its resolution attempted to make such constructs unambiguously ill-formed by allowing any identifier, not just namespaces and types, to appear in a nested-name-specifier, apparently on the assumption that C in this case would become part of an ill-formed nested-name-specifier instead of being taken as the unqualified-id in a qualified-id. Unfortunately, the current specification does not implement that intent, leaving both parses as valid possibilities.

A different approach might be to adjust the specification of the lookup of names appearing in nested-name-specifiers from

If a :: scope resolution operator in a nested-name-specifier is not preceded by a decltype-specifier, lookup of the name preceding that :: considers only namespaces, types, and templates whose specializations are types. If the name found does not designate a namespace or a class, enumeration, or dependent type, the program is ill-formed.

to

Lookup of an identifier followed by a :: scope resolution operator considers only namespaces, types, and templates whose specializations are types. If an identifer, template-id, or decltype-specifier is followed by a :: scope resolution operator, the name shall designate a namespace, class, enumeration, or dependent type, and shall form part of a nested-name-specifier.

This approach would also remove the need for deferred lookup for template-ids and thus resolve issue 1771.




562. qualified-ids in non-expression contexts

Section: 6.5.5.2  [class.qual]     Status: CD6     Submitter: Mike Miller     Date: 6 April 2006

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Both 6.5.5.2 [class.qual] and 6.5.5.3 [namespace.qual] specify that some lookups are to be performed “in the context of the entire postfix-expression,” ignoring the fact that qualified-ids can appear outside of expressions.

It was suggested in document J16/05-0156 = WG21 N1896 that these uses be changed to “the context in which the qualified-id occurs,” but it isn't clear that this formulation adequately covers all the places a qualified-id can occur.




2070. using-declaration with dependent nested-name-specifier

Section: 6.5.5.2  [class.qual]     Status: CD6     Submitter: Richard Smith     Date: 2015-01-15

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

It makes no sense for a user to write a class template that contains a using-declaration that is sometimes an inheriting constructor declaration and sometimes pulls in a named value from a base class; These are sufficiently different things that we're doing them a disservice by conflating them. We're also doing a disservice to all readers of the code, by allowing an inheriting constructor to be written using a syntax that does not look like one.

In an inheriting constructor using-declaration, the nested-name-specifier and the unqualified-id should be required to be the same identifier.

Notes from the May, 2015 meeting:

The consensus of CWG was that the same name should be required when the nested-name-specifier is dependent and in the using-declaration case but should be allowed to be different in all other cases. See also issues 156 and 399.




279. Correspondence of "names for linkage purposes"

Section: 6.6  [basic.link]     Status: CD6     Submitter: Daveed Vandevoorde     Date: 4 Apr 2001

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The standard says that an unnamed class or enum definition can be given a "name for linkage purposes" through a typedef. E.g.,

    typedef enum {} E;
    extern E *p;

can appear in multiple translation units.

How about the following combination?

    // Translation unit 1:
    struct S;
    extern S *q;

    // Translation unit 2:
    typedef struct {} S;
    extern S *q;

Is this valid C++?

Also, if the answer is "yes", consider the following slight variant:

    // Translation unit 1:
    struct S {};  // <<-- class has definition
    extern S *q;

    // Translation unit 2:
    typedef struct {} S;
    extern S *q;

Is this a violation of the ODR because two definitions of type S consist of differing token sequences?




338. Enumerator name with linkage used as class name in other translation unit

Section: 6.6  [basic.link]     Status: CD6     Submitter: Daveed Vandevoorde     Date: 26 Feb 2002

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The following declarations are allowed within a translation unit:

  struct S;
  enum { S };

However, 6.6 [basic.link] paragraph 9 seems to say these two declarations cannot appear in two different translation units. That also would mean that the inclusion of a header containing the above in two different translation units is not valid C++.

I suspect this is an oversight and that users should be allowed to have the declarations above appear in different translation units. (It is a fairly common thing to do, I think.)

Mike Miller: I think you meant "enum E { S };" -- enumerators only have external linkage if the enumeration does (6.6 [basic.link] paragraph 4) , and 6.6 [basic.link] paragraph 9 only applies to entities with external linkage.

I don't remember why enumerators were given linkage; I don't think it's necessary for mangling non-type template arguments. In any event, I can't think why cross-TU name collisions between enumerators and other entities would cause a problem, so I guess a change here would be okay. I can think of three changes that would have that effect:

  1. Saying that enumerators do not have linkage.
  2. Removing enumerators from the list of entities in the first sentence of 6.6 [basic.link] paragraph 9.
  3. Saying that it's okay for an enumerator in one TU to have the same name as a class type in another TU only if the enumerator hides that same class type in both TUs (the example you gave).

Daveed Vandevoorde: I don't think any of these are sufficient in the sense that the problem isn't limited to enumerators. E.g.:

  struct X;
  extern void X();
shouldn't create cross-TU collisions either.

Mike Miller: So you're saying that cross-TU collisions should only be prohibited if both names denote entities of the same kind (both functions, both objects, both types, etc.), or if they are both references (regardless of what they refer to, presumably)?

Daveed Vandevoorde: Not exactly. Instead, I'm saying that if two entities (with external linkage) can coexist when they're both declared in the same translation unit (TU), then they should also be allowed to coexist when they're declared in two different translation units.

For example:

  int i;
  void i();  // Error
This is an error within a TU, so I don't see a reason to make it valid across TUs.

However, "tag names" (class/struct/union/enum) can sometimes coexist with identically named entities (variables, functions & enumerators, but not namespaces, templates or type names).




1839. Lookup of block-scope extern declarations

Section: 6.6  [basic.link]     Status: CD6     Submitter: Hubert Tong     Date: 2014-01-18

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

According to 6.6 [basic.link] paragraph 6,

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block scope declaration declares that same entity and receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed.

It is not clear how declarations that are in the lexical scope of the block-scope declaration but not members of the nearest enclosing namespace (see 9.8.2 [namespace.def] paragraph 6) should be treated. (For example, the definition of the function in which the block extern appears might be defined in an enclosing namespace, with a visible declaration of the name in that namespace, or it might be a member function of a class containing a member function of the name being declared.) Should such declarations be produce an error or should the lexically-nearer declaration simply be ignored? There is implementation divergence on this point.

Proposed resolution, April, 2019:

  1. Change 6.6 [basic.link] paragraph 8 as follows:

  2. The name of a A function declared in block scope and the name of or a variable declared by a block scope extern declaration have is a member of the innermost enclosing namespace and its name has linkage. If such a declaration is attached to a named module, the program is ill-formed. If there is a visible prior declaration of an entity with linkage, ignoring entities declared outside the innermost enclosing namespace scope that name in that namespace, such that the block scope declaration would be a (possibly ill-formed) redeclaration if the two declarations appeared in the same declarative region, the block scope declaration declares that same entity and its name receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed. Otherwise, if no matching entity is found, the block scope entity receives external the linkage of the innermost enclosing namespace. If, within a translation unit, the same entity is declared with both internal and external linkage, the program is ill-formed. [Example:

      static void f();
      extern "C" void h();
      static int i = 0;    // #1
      void g() {
        extern void f();   // internal linkage
        extern void h();   // C language linkage
        extern void k();   // ::k, external linkage
        int i;             // #2: i has no linkage
        {
          extern void f(); // internal linkage
          extern int i;    // #3: external internal linkage, ill-formed
        }
      }
    

    Without the declaration at line #2, the declaration at line #3 would link with the declaration at line #1. Because the declaration with internal linkage is hidden, however, #3 is given external linkage, making the program ill-formed. Even though the declaration at line #2 hides the declaration at line #1, the declaration at line #3 still redeclares #1 and receives internal linkage.end example]

  3. Change 6.6 [basic.link] paragraph 9 as follows:

  4. When a A block scope declaration of an entity with linkage is not found to refer to some other declaration, then that entity is a member of the innermost enclosing namespace. However such a declaration does not introduce by itself make the member name visible to any form of name lookup in its namespace scope or eligible for declaration by qualified-id. [Example:

      namespace X {
        void p() {
          q();                   // error: q not yet declared
          extern void q();       // q is a member of namespace X
          extern void r();       // r is a member of namespace X
        }
    
        void middle() {
          q();                   // error: q not yet declared visible to name lookup
        }
    
        void q() { /* ... */ }   // definition of X::q
      }
    
      void q() { /* ... */ }     // some other, unrelated q
      void X::r() { /* ... */ }  // error: r cannot be declared by qualified-id
    

    end example]

Additional note, July, 2019:

The proposed resolution removes the sentence from the existing text reading:

If, within a translation unit, the same entity is declared with both internal and external linkage, the program is ill-formed.

Such a sitution can still arise, however:

   void f() {
     void g(); // external linkage
   }
   static void g(); // internal linkage

The remaining wording dealing with linkage agreement, 9.2.2 [dcl.stc] paragraph 6,

The linkages implied by successive declarations for a given entity shall agree. That is, within a given scope, each declaration declaring the same variable name or the same overloading of a function name shall imply the same linkage.

does not apply to this example because the declarations are not within the same scope.

The issue has been returned to "review" status to allow consideration of how best to address this problem.




1884. Unclear requirements for same-named external-linkage entities

Section: 6.6  [basic.link]     Status: CD6     Submitter: Richard Smith     Date: 2014-02-27

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

According to 6.6 [basic.link] paragraph 9,

Two names that are the same (6.1 [basic.pre]) and that are declared in different scopes shall denote the same variable, function, type, enumerator, template or namespace if

This is not as clear as it should be. The intent is that this rule prevents declaring a name with extenal linkage to be, for instance, a type in one translation unit and a namespace in a different translation unit. It has instead been read as begging the question of what it means for two entities to be the same. The wording should be tweaked to make the intention clear. Among other things, it should be clarified that "declared in" refers to the namespace of which the name is a member, not the lexical scope in which the declaration appears (which affects friend declarations, block-scope extern declarations, and elaborated-type-specifiers).

There is a similar restriction in _N4868_.6.4.1 [basic.scope.declarative] paragraph 4 dealing with declarations within a single declarative region, while 6.6 [basic.link] paragraph 9 deals with names that are associated via linkage. The relationship between these complementary requirements may need to be clarified as well.

See also issue 2165.

Additional note, March, 2019:

6.6 [basic.link] paragraph 11 concludes by saying,

A violation of this rule on type identity does not require a diagnostic.

Presumably a diagnostic should be required if the differing types appear within a single translation unit.




2058. More errors from internal-linkage namespaces

Section: 6.6  [basic.link]     Status: CD6     Submitter: Richard Smith     Date: 2014-12-15

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Issue 1603 dealt with omissions in the application of the change to give unnamed namespaces internal linkage, but its resolution overlooked a couple of items. According to 6.6 [basic.link] paragraph 6,

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block scope declaration declares that same entity and receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed. Otherwise, if no matching entity is found, the block scope entity receives external linkage.

The last sentence should say, “...receives the linkage of the innermost enclosing namespace.”

Also, 6.6 [basic.link] paragraph 8 says,

A type without linkage shall not be used as the type of a variable or function with external linkage unless

This bullet cannot occur, since a function or variable declared within an unnamed namespace cannot have external linkage.




2470. Multiple array objects providing storage for one object

Section: 6.7.2  [intro.object]     Status: CD6     Submitter: Andrey Erokhin     Date: 2021-01-29

According to 6.7.2 [intro.object] paragraph 3,

If a complete object is created (7.6.2.8 [expr.new]) in storage associated with another object e of type “array of N unsigned char” or of type “array of N std::byte” (17.2.1 [cstddef.syn]), that array provides storage for the created object if:

The intent of the third bullet is to select a unique array object among those satisfying the first two bullets. However, it is possible to have multiple array objects of the same size satisfying the first two bullets. For example:

  unsigned char buffer[8];
  struct OhNo { std::byte data[8]; };
  static_assert(sizeof(OhNo) == 8 && sizeof(int) == 4);
  OhNo *p = new (buffer) OhNo;   // buffer provides storage for OhNo
  int *q = new (p->data) int;    // who provides storage for this?
  int *r = new (buffer + 4) int; // who provides storage for this?

Suggested resolution:

Change 6.7.2 [intro.object] bullet 3.3 as follows:

Proposed resolution (February, 2021):

Change 6.7.2 [intro.object] bullet 3.3 as follows:




2185. Cv-qualified numeric types

Section: 6.8.2  [basic.fundamental]     Status: CD6     Submitter: CWG     Date: 2015-10-21

[Resolved by issue 2448, accepted as a DR at the June, 2021 meeting.]

The definitions of integral, floating, and arithmetic types in 6.8.2 [basic.fundamental] paragraphs 7-8 do not, but presumably should, include cv-qualified versions of those fundamental types.

Notes from the June, 2016 meeting:

This issue subsumes issue 251.




2448. Cv-qualification of arithmetic types and deprecation of volatile

Section: 6.8.2  [basic.fundamental]     Status: CD6     Submitter: Alisdair Meredith     Date: 2020-03-23

[Accepted as a DR at the June, 2021 meeting.]

According to the definitions in 6.8.2 [basic.fundamental], the arithmetic types include only the non-cv-qualified versions. In the taxonomy of fundamental types, the first mention of “cv-qualified versions of these types” is for scalar types (6.8 [basic.types] paragraph 9). However, 7.6.1.6 [expr.post.incr] paragraph 1 and 7.6.2.3 [expr.pre.incr] paragraph 1 both say:

The type of the operand shall be an arithmetic type other than cv bool, or...

which is a contradiction, since cv-qualified bool is not an arithmetic type. Similarly, 7.6.19 [expr.ass] paragraph 6 requires an arithmetic type for += and -=. D.4 [depr.volatile.type] deprecates the increment and decrement operators when applied to volatile-qualified arithmetic types, but the wording already made those ill-formed (since the normative wording requires an arithmetic type and not a possibly cv-qualified version thereof).

A related question is whether 12.5 [over.built], which explicitly allows for cv-qualified arithmetic types, should also note the deprecation.

See also issue 2185.

Notes from the July, 2020 teleconference:

CWG felt that no changes should be made to 12.5 [over.built].

Proposed resolution (April, 2021):

  1. Change 6.8.2 [basic.fundamental] paragraphs 11 and 12 as follows, splitting paragraph 12 as indicated:

  2. Types bool, char, wchar_t, char8_t, char16_t, char32_t, and the signed and unsigned integer types, and cv-qualified versions (6.8.5 [basic.type.qualifier]) thereof, are collectively called termed integral types. A synonym for integral type is integer type. [Note 8: Enumerations (9.7.1 [dcl.enum]) are not integral; however, unscoped enumerations can be promoted to integral types as specified in 7.3.7 [conv.prom]. —end note]

    There are three floating-point types: The three distinct types float, double, and long double can represent floating-point numbers. The type double provides at least as much precision as float, and the type long double provides at least as much precision as double. The set of values of the type float is a subset of the set of values of the type double; the set of values of the type double is a subset of the set of values of the type long double. The types float, double, and long double, and cv-qualified versions (6.8.5 [basic.type.qualifier]) thereof, are collectively termed floating-point types. The value representation of floating-point types is implementation-defined. [Note 9: This document imposes no requirements on the accuracy of floating-point operations; see also 17.3 [support.limits]. —end note]

    Integral and floating-point types are collectively called termed arithmetic types. Specializations of the standard library template std::numeric_limits (17.3 [support.limits]) shall specify the maximum and minimum values of each arithmetic type for an implementation.

  3. Change 6.8.5 [basic.type.qualifier] paragraph 1 as follows, splitting the paragraph as indicated:

  4. A type mentioned in 6.8.2 [basic.fundamental] and 6.8.4 [basic.compound] is a cv-unqualified type. Each type which is a cv-unqualified object type or is void (6.8 [basic.types]) has three corresponding cv-qualified versions of its type other than a function or reference type is part of a group of four distinct, but related, types: a cv-unqualified version, a const-qualified version, a volatile-qualified version, and a const-volatile-qualified version. The type of an object (6.7.2 [intro.object]) includes the cv-qualifiers specified in the decl-specifier-seq (9.2 [dcl.spec]), declarator (9.3 [dcl.decl]), type-id (9.3.2 [dcl.name]), or new-type-id (7.6.2.8 [expr.new]) when the object is created. The types in each such group shall have the same representation and alignment requirements (6.7.6 [basic.align]). [Footnote: The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and non-static data members of unions. —end footnote] A function or reference type is always cv-unqualified.

    The cv-qualified or cv-unqualified versions of a type are distinct types; however, they shall have the same representation and alignment requirements (6.7.6 [basic.align]).40 [Note: The type of an object (6.7.2 [intro.object]) includes the cv-qualifiers specified in the decl-specifier-seq (9.2 [dcl.spec]), declarator (9.3 [dcl.decl]), type-id (9.3.2 [dcl.name]), or new-type-id (7.6.2.8 [expr.new]) when the object is created. —end note]

  5. Change 12.5 [over.built] paragraphs 2-10 as follows:

  6. In this subclause, the term promoted integral type is used to refer to those cv-unqualified integral types which are preserved by integral promotion (7.3.7 [conv.prom]) (including e.g. int and long but excluding e.g. char ). [Note 2: In all cases where a promoted integral type is required, an operand of unscoped enumeration type will be acceptable by way of the integral promotions. —end note]

    In the remainder of this subclause, vq represents either volatile or no cv-qualifier.

    For every pair (T, vq), where T is an a cv-unqualified arithmetic type other than bool or a cv-unqualified pointer to (possibly cv-qualified) object type, there exist candidate operator functions of the form

    For every pair (T, vq), where T is an arithmetic type other than bool, there exist candidate operator functions of the form

    For every pair (T, vq), where T is a cv-qualified or cv-unqualified object type, there exist candidate operator functions of the form

    For every cv-qualified or cv-unqualified (possibly cv-qualified) object type T and for every function type T that has neither cv-qualifiers nor a ref-qualifier, there exist candidate operator functions of the form

    For every function type T that does not have cv-qualifiers or a ref-qualifier, there exist candidate operator functions of the form

    For every type T there exist candidate operator functions of the form

    For every cv-unqualified floating-point or promoted integral type T, there exist candidate operator functions of the form

[Drafting note: Clause 21 [meta] regarding type traits appropriately handles cv-qualified and cv-unqualified types and does not require revision.]




2499. Inconsistency in definition of pointer-interconvertibility

Section: 6.8.4  [basic.compound]     Status: CD6     Submitter: Jason Merrill     Date: 2021-07-29

[Accepted at the February, 2022 meeting.]

The changes for issue 2254 included the following:

Change 6.8.4 [basic.compound] bullet 4.3 as follows:

Two objects a and b are pointer-interconvertible if:

This should also have removed the phrase,

or, if the object has no non-static data members,

since the change to 11.4 [class.mem] paragraph 25 specifies that all bases of a standard-layout class have the same address, regardless of whether the derived class has non-static data members.

Proposed resolution (November, 2021):

Change 6.8.4 [basic.compound] bullet 4.3 as follows:

Two objects a and b are pointer-interconvertible if:




2479. Missing specifications for consteval and constinit

Section: 6.9.3.1  [basic.start.main]     Status: CD6     Submitter: Davis Herring     Date: 2020-10-09

[Accepted as a DR at the June, 2021 meeting.]

There are several places where the consteval and/or constinit keywords should be mentioned but are not:

6.9.3.1 [basic.start.main] paragraph 3:

A program that defines main as deleted or that declares main to be inline, static, or constexpr is ill-formed.

9.3.4.1 [dcl.meaning.general] paragraph 2:

A static, thread_local, extern, mutable, friend, inline, virtual, constexpr, or typedef specifier or an explicit-specifier applies directly to each declarator-id in an init-declarator-list or member-declarator-list...

11.4.5.1 [class.ctor.general] paragraph 1:

...In a constructor declaration, each decl-specifier in the optional decl-specifier-seq shall be friend, inline, constexpr, or an explicit-specifier.

Proposed resolution, May, 2021:

  1. Change 6.9.3.1 [basic.start.main] paragraph 3 as follows:

  2. ...A program that defines main as deleted or that declares main to be inline, static, or constexpr, or consteval is ill-formed...
  3. Change 9.3.4.1 [dcl.meaning.general] paragraph 4 as follows:

  4. A static, thread_local, extern, mutable, friend, inline, virtual, constexpr, consteval, constinit, or typedef specifier or an explicit-specifier applies directly to each declarator-id in a declaration; the type specified for each declarator-id depends on both the decl-specifier-seq and its declarator.
  5. Change 11.4.5.1 [class.ctor.general] paragraph 5 as follows:

  6. ...Constructors do not have names. In a constructor declaration, each decl-specifier in the optional decl-specifier-seq shall be friend, inline, constexpr, consteval, or an explicit-specifier.



2594. Disallowing a global function template main

Section: 6.9.3.1  [basic.start.main]     Status: CD6     Submitter: Jim X     Date: 2022-06-06

[Accepted at the July, 2022 meeting.]

Consider:

  template<class T>
  int main(T) {}

C++20 specified in 6.9.3.1 [basic.start.main] paragraph 2:

An implementation shall not predefine the main function. This function shall not be overloaded.

While it is unclear what "overloaded" means when multiple translation units are involved, it arguably disallowed function templates called main. This prohibition was removed with P1787R6 (Declarations and where to find them).

Proposed resolution (approved by CWG 2022-06-17):

Change in 6.9.3.1 [basic.start.main] paragraph 3 and add bullets as follows:

... A program that declares is ill-formed. The name main is not otherwise reserved.



2484. char8_t and char16_t in integral promotions

Section: 7.3.7  [conv.prom]     Status: CD6     Submitter: Richard Smith     Date: 2021-04-01

[Accepted as a DR at the October, 2021 meeting.]

According to 7.3.7 [conv.prom] paragraphs 1-2,

A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (6.8.6 [conv.rank]) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.

A prvalue of type char16_t, char32_t, or wchar_t (6.8.2 [basic.fundamental]) can be converted to a prvalue of the first of the following types that can represent all the values of its underlying type: int, unsigned int, long int, unsigned long int, long long int, or unsigned long long int. If none of the types in that list can represent all the values of its underlying type, a prvalue of type char16_t, char32_t, or wchar_t can be converted to a prvalue of its underlying type.

Because of its omission from the list of excluded types (perhaps as an oversight when it was added), char8_t is handled in the first paragraph. However, char16_t falls into the second paragraph, even though it is guaranteed to be convertible to int or unsigned int. This seems inconsistent, so perhaps char8_t should be moved to the second paragraph or char16_t moved to the first?

Notes from the August, 2021 teleconference:

char8_t should be handled by the second paragraph by including it in all three lists of types in the two paragraphs.

Proposed resolution (August, 2021):

Change 7.3.7 [conv.prom] paragraphs 1 and 2 as follows:

A prvalue of an integer type other than bool, char8_t, char16_t, char32_t, or wchar_t whose integer conversion rank (6.8.6 [conv.rank]) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.

A prvalue of type char8_t, char16_t, char32_t, or wchar_t (6.8.2 [basic.fundamental]) can be converted to a prvalue of the first of the following types that can represent all the values of its underlying type: int, unsigned int, long int, unsigned long int, long long int, or unsigned long long int. If none of the types in that list can represent all the values of its underlying type, a prvalue of type char8_t, char16_t, char32_t, or wchar_t can be converted to a prvalue of its underlying type.




2569. Use of decltype(capture) in a lambda's parameter-declaration-clause

Section: 7.5.4.2  [expr.prim.id.unqual]     Status: CD6     Submitter: Barry Revzin     Date: 2022-04-16     Liaison: EWG

[Accepted at the July, 2022 meeting as part of paper P2579R0 (Mitigation strategies for P2036 "Changing scope for lambda trailing-return-type").]

Paper P2036R3 disallowed using captures in the parameter-declaration-clause of a lambda, because it is not yet known at that point whether the lambda is going to be mutable, and thus the type of an expression referring to a capture may or may not be const. Such problematic uses of captures are now ill-formed. The paper was approved as a Defect Report, recommending to implementers to apply the change to all language modes back to C++11.

However, that broke legitimate uses in popular implementations of the standard library such as:

  [local_end](decltype(local_end) it) { return it != local_end; };

As specified in 9.2.9.6 [dcl.type.decltype] bullet 1.3, decltype(local_end) does not depend on whether the lambda ends up being mutable or not:

Possible approaches (not necessarily exclusive):

Suggested resolution (carves out an exception for decltype, sizeof, noexcept):

Change in 7.5.4.2 [expr.prim.id.unqual] paragraph 3 as follows:

An unqualified-id is mutable-agnostic if it is the operand of a decltype (9.2.9.6 [dcl.type.decltype]), sizeof (7.6.2.5 [expr.sizeof]), or noexcept (7.6.2.7 [expr.unary.noexcept]). If the unqualified-id appears in a lambda-expression at program point P and the entity is a local entity (6.1 [basic.pre]) or a variable declared by an init-capture (7.5.5.3 [expr.prim.lambda.capture]), then let S be the compound-statement of the innermost enclosing lambda-expression of P. If naming the entity from outside of an unevaluated operand within S would refer to an entity captured by copy in some intervening lambda-expression, then let E be the innermost such lambda-expression, and: ...

[Example:

  [=]<decltype(x) P>{};    // ok: P has type float
  [=]<decltype((x)) P>{};  // error: x refers to local entity but precedes the
                           // lambda's function parameter scope
  [=](decltype((x)) y){};  // error: x refers to local entity but is in the lambda's
                           // parameter-declaration-clause

-- end example]

Suggested resolution (carves out an exception for decltype only):

Change in 7.5.4.2 [expr.prim.id.unqual] paragraph 3 as follows:

If the unqualified-id appears in a lambda-expression at program point P and the entity is a local entity (6.1 [basic.pre]) or a variable declared by an init-capture (7.5.5.3 [expr.prim.lambda.capture]), then let S be the compound-statement of the innermost enclosing lambda-expression of P. If naming the entity from outside of an unevaluated operand within S would refer to an entity captured by copy in some intervening lambda-expression, then let E be the innermost such lambda-expression, and: ...

[Example:

  [=]<decltype(x) P>{};    // ok: P has type float
  [=]<decltype((x)) P>{};  // error: x refers to local entity but precedes the
                           // lambda's function parameter scope
  [=](decltype((x)) y){};  // error: x refers to local entity but is in the lambda's
                           // parameter-declaration-clause

-- end example]

Additional notes (April, 2022):

Forwarded to EWG with paper issue 1227, by decision of the CWG chair.

See paper P2579R0 for a more detailed discussion of the issue.




2396. Lookup of names in complex conversion-type-ids

Section: 7.5.4.3  [expr.prim.id.qual]     Status: CD6     Submitter: Richard Smith     Date: 2018-12-03

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Issue 2385 assumed a simple case where a conversion-type-id is an identifier. More complex cases need to be addressed as well. For example:

  struct A {
    struct B;
    operator B B::*();
  };
  struct B;
  void f(A a) { a.operator B B::*(); }            // first B is A::B. what is second B? 
  void g(A a) { a.operator decltype(B()) B::*();} // what about the operand of decltype? 
  void h(A a) { a.operator X<B>(); }              // what is B here? 



1822. Lookup of parameter names in lambda-expressions

Section: 7.5.5  [expr.prim.lambda]     Status: CD6     Submitter: Steve Clamage     Date: 2013-12-10

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

According to 7.5.5 [expr.prim.lambda] paragraph 7, names appearing in the compound-statement of a lambda-expression are looked up in the context of the lambda-expression, ignoring the fact that the compound-statement will be transformed into the body of the closure type's function operator. This leaves unspecified how the lambda-expression's parameters are found by name lookup. Presumably the parameters hide the corresponding names from the surrounding scope, but this needs to be specified.




2121. More flexible lambda syntax

Section: 7.5.5.1  [expr.prim.lambda.general]     Status: CD6     Submitter: EWG     Date: 2015-05-06

[Adopted at the February, 2021 meeting as paper P1102R2.]

The grammar in 7.5.5 [expr.prim.lambda] paragraph 1 allows for omitting the the parameter list but only for a non-mutable lambda, i.e., it does not permit

    auto lambda = [] mutable { };

This should be addressed, and the possibility of other abbreviated forms should be considered, such as:

    [] -> float { return 42; }
    [] noexcept { foo(); }

(This is EWG issue 135.)

Proposed resolution (May, 2015):

  1. Change the grammar in 7.5.5 [expr.prim.lambda] paragraph 1 as follows:

  2. Change 7.5.5 [expr.prim.lambda] paragraph 4 as follows:

  3. If a lambda-expression does not include a lambda-declarator, it is as if the lambda-declarator were (). The lambda return type...
  4. Change 7.5.5 [expr.prim.lambda] paragraph 5 as follows:

  5. The closure type for a non-generic lambda-expression has a public inline function call operator (12.4.4 [over.call]) whose parameters and return type are described by the lambda-expression's parameter-declaration-clause and trailing-return-type respectively. For a generic lambda... This function call operator or operator template is declared const (11.4.3 [class.mfct.non.static]) if and only if the lambda-expression's parameter-declaration-clause is not followed by lambda-declarator does not contain the keyword mutable. It is neither...

Notes from the October, 2015 meeting:

Additional wording is needed in the proposed resolution in paragraph 5 to handle the potential absence of the parameter declaration clause.




2509. decl-specifier-seq in lambda-specifiers

Section: 7.5.5.1  [expr.prim.lambda.general]     Status: CD6     Submitter: Jens Maurer     Date: 2021-10-28

[Accepted at the February, 2022 meeting.]

(From editorial issue 2338.)

Use of decl-specifier-seq in the production for lambda-specifiers is too general and should be restricted.

Proposed resolution (December, 2021):

  1. Change the grammar in 7.5.5.1 [expr.prim.lambda.general] as follows:

  2. Change 7.5.5.1 [expr.prim.lambda.general] paragrap 3 as follows:

  3. In the decl-specifier-seq of the lambda-declarator, each decl-specifier shall be one of mutable, constexpr, or consteval. A lambda-specifier-seq shall contain at most one of each lambda-specifier and shall not contain both constexpr and consteval. If the lambda-declarator contains an explicit object parameter (9.3.4.6 [dcl.fct]), then no decllambda-specifier in the decllambda-specifier-seq shall be mutable.



1249. Cv-qualification of nested lambda capture

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: CD6     Submitter: James Widman     Date: 2011-03-02

[Accepted as a DR at the October, 2021 meeting.]

Consider the following example:

    void f(int i) {
      auto l1 = [i] {
        auto l2 = [&i] {
          ++i;    // Well-formed?
        };
      };
    }

Because the l1 lambda is not marked as mutable, its operator() is const; however, it is not clear from the wording of 7.5.5 [expr.prim.lambda] paragraph 16 whether the captured member of the enclosing lambda is considered const or not.

Proposed resolution (August, 2021):

Change 7.5.5.3 [expr.prim.lambda.capture] paragraph 14 as follows:

If a lambda-expression m2 captures an entity and that entity is captured by an immediately enclosing lambda-expression m1, then m2's capture is transformed as follows:




2571. Evaluation order for subscripting

Section: 7.6.1.2  [expr.sub]     Status: CD6     Submitter: Corentin Jabot     Date: 2022-04-21

[Accepted at the July, 2022 meeting.]

The specification about the relative sequencing of multiple parameters of the subscripting operator is missing. Also, issue 2507 adds support for default arguments for user-defined subscripting operators, but the sequencing of these is unspecified, too.

Suggested resolution: [SUPERSEDED]

Add a new paragraph 4 at the end of 7.6.1.2 [expr.sub]:

If the subscript operator invokes an operator function, the sequencing restrictions of the corresponding function call expression apply (12.4.5 [over.sub], 7.6.1.3 [expr.call]).

Notes from the 2022-05-20 CWG telecon:

A wording approach amending 12.2.2.3 [over.match.oper] paragraph 2 instead would be preferred.

Possible resolution (2022-05-21): [SUPERSEDED]

Change in 12.2.2.3 [over.match.oper] paragraph 2 as follows:

Therefore, the operator notation is first transformed to the equivalent function-call notation as summarized in Table 17 (where @ denotes one of the operators covered in the specified subclause). However, except for the subscript operator (7.6.1.2 [expr.sub]), the operands are sequenced in the order prescribed for the built-in operator (7.6 [expr.compound]).

Notes from the 2022-06-03 CWG telecon:

Repeating the function call rules for the subscript operator in 7.6.1.2 [expr.sub] instead would be preferred, to avoid any impression of a special case.

Proposed resolution (2022-06-24, amended 2022-07-15, approved by CWG 2022-07-15):

Change in 7.6.1.2 [expr.sub] paragraph 1 as follows:

A subscript expression is a postfix expression followed by square brackets containing a possibly empty, comma-separated list of initializer-clauses which that constitute the arguments to the subscript operator. The postfix-expression and the initialization of the object parameter of any applicable subscript operator function is sequenced before each expression in the expression-list and also before any default argument. The initialization of a non-object parameter of a subscript operator function S (12.4.5 [over.sub]), including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other non-object parameter of S.



2486. Call to noexcept function via noexcept(false) pointer/lvalue

Section: 7.6.1.3  [expr.call]     Status: CD6     Submitter: Jiang An     Date: 2021-03-27

[Accepted as a DR at the October, 2021 meeting.]

According to 7.6.1.3 [expr.call] paragraph 6,

Calling a function through an expression whose function type is different from the function type of the called function's definition results in undefined behavior.

This restriction should exempt calling a noexcept function where the function type of the expression is identical except that it is noexcept(false).

In addition, 7.6.1.9 [expr.static.cast] paragraph 7 currently forbids static_cast from converting a function pointer or member function pointer from noexcept(false) to noexcept:

The inverse of any standard conversion sequence (7.3 [conv]) not containing an lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), function-to-pointer (7.3.4 [conv.func]), null pointer (7.3.12 [conv.ptr]), null member pointer (7.3.13 [conv.mem]), boolean (7.3.15 [conv.bool]), or function pointer (7.3.14 [conv.fctptr]) conversion, can be performed explicitly using static_cast.

This restriction should also be relaxed, allowing binding a constexpr reference to the result of the reversed conversion.

Notes from the August, 2021 teleconference:

CWG agreed that it should be permitted to call a noexcept function via an expression that is noexcept(false); since the implicit conversion is allowed, the failure to allow the call is clearly just an oversight. The question of whether to allow the static_cast in the inverse direction, as well as whether to allow calling a noexcept(false) function via a noexcept expression (which would result in undefined behavior only if the function actually threw an exception) was deemed to be a matter for EWG and was thus split off into issue 2500.

Proposed resolution (September, 2021):

Change 7.6.1.3 [expr.call] paragraph 6 as follows:

Calling a function through an expression whose function type E is different from the function type F of the called function's definition results in undefined behavior unless the type “pointer to F” can be converted to the type “pointer to E” via a function pointer conversion (7.3.14 [conv.fctptr]). [Note: The exception applies when the expression has the type of a potentially-throwing function, but the called function has a non-throwing exception specification, and the function types are otherwise the same. —end note]



2458. Value category of expressions denoting non-static member functions

Section: 7.6.1.5  [expr.ref]     Status: CD6     Submitter: Andrey Erokhin     Date: 2020-07-04

[Accepted as a DR at the June, 2021 meeting.]

Expressions denoting non-static member functions are currently classified as prvalues (7.5.4.3 [expr.prim.id.qual] paragraph 2; 7.6.1.5 [expr.ref] bullet 6.3.2; and 7.6.4 [expr.mptr.oper] paragraph 6). It would simplify the specification if such expressions were categorized as lvalues. (See also this pull request.)

Notes from the August, 2020 teleconference:

CWG preferred that the unbound case (i.e., &X::f) should be an lvalue, while the bound case should be a prvalue.

Proposed resolution (April, 2021):

  1. Change 7.5.4.3 [expr.prim.id.qual] paragraph 5, converting the running text into a bulleted list, as follows:

  2. The result of a qualified-id Q is the entity it denotes (6.5.5 [basic.lookup.qual]). The type of the expression is the type of the result. The result is an lvalue if the member is

    and a prvalue otherwise.

  3. Change 7.6.2.2 [expr.unary.op] paragraph 3 as follows:

  4. The result of the The operand of the unary & operator shall be an lvalue of some type T. The result is a pointer to its operand prvalue.

[Drafting note: neither 7.6.1.5 [expr.ref] bullet 6.3.2,

nor 7.6.4 [expr.mptr.oper] paragraph 6,

...The result of a .* expression whose second operand is a pointer to a member function is a prvalue...

requires any change.]




2534. Value category of pseudo-destructor expression

Section: 7.6.1.5  [expr.ref]     Status: CD6     Submitter: Andrey Erokhin     Date: 2022-02-17

[Accepted at the July, 2022 meeting.]

Subclause 7.6.1.5 [expr.ref] paragraph 3 defines the value category of a pseudo-destructor class member access expression to be an lvalue:

Abbreviating postfix-expression.id-expression as E1.E2, E1 is called the object expression. If the object expression is of scalar type, E2 shall name the pseudo-destructor of that same type (ignoring cv-qualifications) and E1.E2 is an lvalue of type “function of () returning void”.
This is inconsistent with the analogous situation naming the destructor of a class. In that case, the class member access expression is a prvalue, not an lvalue, as specified in 7.6.1.5 [expr.ref] bullet 6.3 (see also issue 2458):
It also contradicts 7.2.1 [basic.lval] bullet 1.1:
A pseudo-destructor does not have an identity.

Proposed resolution (approved by CWG 2022-04-08):

Change 7.6.1.5 [expr.ref] paragraph 3 as follows:

If the object expression is of scalar type, E2 shall name the pseudo-destructor of that same type (ignoring cv-qualifications) and E1.E2 is an lvalue a prvalue of type “function of () returning void”.



2535. Type punning in class member access

Section: 7.6.1.5  [expr.ref]     Status: CD6     Submitter: Andrey Erokhin     Date: 2022-02-17

[Accepted at the July, 2022 meeting.]

The initialization of j ought to have undefined behavior, but the standard does not explicitly say so:

  struct C { int m; };

  int i = 0;
  int j = reinterpret_cast<C&>(i).m; // the same as int j = i ?

A related case for pointer-to-member expressions is covered by 7.6.4 [expr.mptr.oper] paragraph 4:

If the dynamic type of E1 does not contain the member to which E2 refers, the behavior is undefined.

The invocation of non-static member functions is covered by 11.4.3 [class.mfct.non.static] paragraph 2:

If a non-static member function of a class X is called for an object that is not of type X, or of a type derived from X, the behavior is undefined.

Proposed resolution (approved by CWG 2022-06-17):

(updated according to 2022-05-20 and 2022-06-03 CWG guidance)

  1. Add a new paragraph after 7.6.1.5 [expr.ref] paragraph 7:

    If E2 is a non-static data member or a non-static member function, the program is ill-formed if the class of which E2 is directly a member is an ambiguous base (6.5.2 [class.member.lookup]) of the naming class (11.8.3 [class.access.base]) of E2. [Note: The program is also ill-formed if the naming class is an ambiguous base of the class type of the object expression; see 11.8.3 [class.access.base]. —end note -- end note]

    If E2 is a non-static member and the result of E1 is an object whose type is not similar (7.3.6 [conv.qual]) to the type of E1, the behavior is undefined. [ Example:

      struct A { int i; };
      struct B { int j; };
      struct D : A, B {};
      void f() {
        D d;
        static_cast<B&>(d).j;       // OK, object expression designates the B subobject of d
        reinterpret_cast<B&>(d).j;  // undefined behavior
      }
    
    -- end example ]

  2. Change in 7.6.4 [expr.mptr.oper] paragraph 4:

    If the dynamic type of E1 If the result of E1 is an object whose type is not similar to the type of E1, or whose most derived object does not contain the member to which E2 refers, the behavior is undefined. Otherwise, t The expression E1 is sequenced before the expression E2.
  3. Remove 11.4.3 [class.mfct.non.static] paragraph 2:

    If a non-static member function of a class X is called for an object that is not of type X, or of a type derived from X, the behavior is undefined.



2606. static_cast from "pointer to void" does not handle similar types

Section: 7.6.1.9  [expr.static.cast]     Status: CD6     Submitter: Richard Smith     Date: 2022-06-28

[Accepted at the July, 2022 meeting.]

Consider:

  struct S {
    int a[5];
  } s;
  int (*p)[] = reinterpret_cast<int(*)[]>(&s);
  int n = (*p)[0];

This ought to have defined behavior: a pointer to s and a pointer to s.a are pointer-interconvertible, so you should be able to navigate between them this way. But the cast as shown does not work, because the type of the pointer-interconvertible object is int[5], not int[].

Proposed resolution (approved by CWG 2022-07-01):

Change in 7.6.1.9 [expr.static.cast] paragraph 13 as follows:

... Otherwise, if the original pointer value points to an object a, and there is an object b of type similar to T (ignoring cv-qualification) that is pointer-interconvertible (6.8.4 [basic.compound]) with a, the result is a pointer to b. Otherwise, the pointer value is unchanged by the conversion.



2466. co_await should be a single evaluation

Section: 7.6.2.4  [expr.await]     Status: CD6     Submitter: Gor Nishanov     Date: 2020-10-19

[Accepted as a DR at the June, 2021 meeting.]

The description of co_await should not permit reordering the subexpressions constituting the evaluation of a co_await expression. For example, given

  auto z = co_await coro + co_await coro;

the result may be different from the expected

  auto x = co_await coro;
  auto y = co_await coro;
  auto z = x + y;

Suggested resolution:

Add the following as a new paragraph following 7.6.2.4 [expr.await] paragraph 5:

With respect to an indeterminately-sequenced function call, the operation of co_await is a single evaluation. [Note: Therefore a function call cannot intervene between the subexpressions constituting evaluation of a co_await expression. —end note]

[Example 1:...

Proposed resolution, May, 2021:

  1. Change 6.9.1 [intro.execution] paragraph 11 as follows:

  2. When calling invoking a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with and the postfix expression designating the called function, is are sequenced before execution of every expression or statement in the body of the called function. For each function invocation or evaluation of an await-expression F, for every each evaluation A that occurs does not occur within F and every evaluation B that does not occur within F but is evaluated on the same thread and as part of the same signal handler (if any), either A is sequenced before B or B is sequenced before A. is either sequenced before all evaluations that occur within F or sequenced after all evaluations that occur within F; [Footnote: In other words, function executions do not interleave with each other. —end footnote] if F invokes or resumes a coroutine (7.6.2.4 [expr.await]), only evaluations subsequent to the previous suspension (if any) and prior to the next suspension (if any) are considered to occur within F. [Note 7: If A and B would not otherwise be sequenced then they are indeterminately sequenced. —end note]
  3. Add the following note at the end of 7.6.2.4 [expr.await] paragraph 5:

  4. The await-expression evaluates the (possibly-converted) o expression and the await-ready expression, then:

    [Note: With respect to sequencing, an await-expression is indivisible (6.9.1 [intro.execution]). —end note]

Drafting note: No change is needed in 6.9.1 [intro.execution] paragraph 8:

...An expression X is said to be sequenced before an expression Y if every value computation and every side effect associated with the expression X is sequenced before every value computation and every side effect associated with the expression Y.

Additional note, May, 2021:

Note 7 in 6.9.1 [intro.execution] paragraph 11 refers to evaluations A and B, even though the edit to that paragraph above removes those names. This discrepancy was noticed only after CWG approved the change to the normative wording. Since it involves only the wording of a non-normative note, the problem will be addressed editorially. See editorial issue 4612.




2474. Cv-qualification and deletion

Section: 7.6.2.9  [expr.delete]     Status: CD6     Submitter: Unknown     Date: 2020-10-29

[Accepted as a DR at the June, 2021 meeting.]

(From editorial issue 4305.)

According to 7.6.2.9 [expr.delete] paragraph 3,

In a single-object delete expression, if the static type of the object to be deleted is different from its dynamic type and the selected deallocation function (see below) is not a destroying operator delete, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined. In an array delete expression, if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.

Both the static type and the dynamic type include cv-qualification, and requiring agreement in qualification between the two for deletion is not intended. Perhaps the restriction should be to similar types instead of identical types?

Notes from the December, 2020 teleconference:

“Similar types” raises issues with arrays of unknown bounds, but a change to allow for differences in cv-qualification is needed.

Notes from the May 25, 2021 teleconference:

It was observed that current implementations store the total number of class objects in a multi-dimensional array in a “cookie” in the array allocation overhead, rather than the number of top-level array elements, and thus are able to invoke the destructors correctly even if the type being deleted is an array of unknown bound. Consequently, it was decided that use of the “similar” criterion was appropriate.

Proposed resolution, May, 2021:

Change 7.6.2.9 [expr.delete] paragraph 3 as follows:

In a single-object delete expression, if the static type of the object to be deleted is different from not similar (7.3.6 [conv.qual]) to its dynamic type and the selected deallocation function (see below) is not a destroying operator delete, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined. In an array delete expression, if the dynamic type of the object to be deleted differs from is not similar to its static type, the behavior is undefined.



2490. Restrictions on destruction in constant expressions

Section: 7.7  [expr.const]     Status: CD6     Submitter: Jiang An     Date: 2021-05-04

[Accepted as a DR at the October, 2021 meeting.]

According to 7.7 [expr.const] paragraph 6,

For the purposes of determining whether an expression E is a core constant expression, the evaluation of a call to a member function of std::allocator<T> as defined in 20.2.10.2 [allocator.members], where T is a literal type, does not disqualify E from being a core constant expression, even if the actual evaluation of such a call would otherwise fail the requirements for a core constant expression. Similarly, the evaluation of a call to std::destroy_at, std::ranges::destroy_at, std::construct_at, or std::ranges::construct_at does not disqualify E from being a core constant expression unless:

There are, however, no specific restrictions in 7.7 [expr.const] regarding destructor or pseudo-destructor calls. In particular, a constexpr destructor can be called for any object, regardless of how it was constructed or the start of its lifetime, and similarly for pseudo-destructor calls. This seems inconsistent.

If those restrictions are added, would the specific restrictions on library destruction facilities still be needed?

Notes from the August, 2021 teleconference:

CWG agreed that since trivial destructors and pseudo-destructors are now considered to end the lifetime of the object for which they are called, they should be prohibited from being invoked for a runtime object in a constant expression.

Proposed resolution (August, 2021):

  1. Change 7.7 [expr.const] paragraph 5 as follows:

  2. An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine (6.9.1 [intro.execution]), would evaluate one of the following:

  3. Change 7.7 [expr.const] paragraph 6 as follows, merging the single remaining bulleted item into the running text of the paragraph:

  4. For the purposes of determining whether an expression E is a core constant expression, the evaluation of a call to a member function of std::allocator<T> as defined in 20.2.10.2 [allocator.members], where T is a literal type, does not disqualify E from being a core constant expression, even if the actual evaluation of such a call would otherwise fail the requirements for a core constant expression. Similarly, the evaluation of a call to std::destroy_at, std::ranges::destroy_at, std::construct_at, or std::ranges::construct_at does not disqualify E from being a core constant expression unless:




2452. Flowing off the end of a coroutine

Section: 8.7.5  [stmt.return.coroutine]     Status: CD6     Submitter: Lewis Baker     Date: 2020-02-14

[Accepted at the November, 2020 meeting.]

There are two references to “flowing off the end of a coroutine”, specifically in 8.7.5 [stmt.return.coroutine] paragraph 3:

If p.return_void() is a valid expression, flowing off the end of a coroutine is equivalent to a co_return with no operand; otherwise flowing off the end of a coroutine results in undefined behavior.

and 9.5.4 [dcl.fct.def.coroutine] paragraph 11:

The coroutine state is destroyed when control flows off the end of the coroutine or...

These mean different things and should be clarified.

Proposed resolution (July, 2020):

Change 8.7.5 [stmt.return.coroutine] paragraph 3 as follows:

If p.return_void() is a valid expression, flowing off the end of a coroutine's function-body is equivalent to a co_return with no operand;otherwise flowing off the end of a coroutine's function-body results in undefined behavior.



1616. Disambiguation parsing and template parameters

Section: 8.9  [stmt.ambig]     Status: CD6     Submitter: Johannes Schaub     Date: 2013-02-01

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

According to 8.9 [stmt.ambig] paragraph 3,

The disambiguation is purely syntactic; that is, the meaning of the names occurring in such a statement, beyond whether they are type-names or not, is not generally used in or changed by the disambiguation. Class templates are instantiated as necessary to determine if a qualified name is a type-name. Disambiguation precedes parsing, and a statement disambiguated as a declaration may be an ill-formed declaration. If, during parsing, a name in a template parameter is bound differently than it would be bound during a trial parse, the program is ill-formed. No diagnostic is required. [Note: This can occur only when the name is declared earlier in the declaration. —end note]

The statement about template parameters is confusing (and not helped by the fact that the example that follows illustrates the general rule for declarations and does not involve any template parameters). It is attempting to say that a program is ill-formed if a template argument of a class template specialization has a different value in the two parses. With decltype this can now apply to other kinds of templates as well, so the wording should be clarified and made more general.




1820. Qualified typedef names

Section: 9.2.4  [dcl.typedef]     Status: CD6     Submitter: Richard Smith     Date: 2013-12-05

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The resolution of issue 482 allows a typedef to be redeclared in the same or a containing scope using a qualified declarator-id. This was not the principal goal of the issue and is not supported by current implementations. Should the prohibition of qualified declarator-ids be reinstated for typedefs?




1894. typedef-names and using-declarations

Section: 9.2.4  [dcl.typedef]     Status: CD6     Submitter: Richard Smith     Date: 2014-03-16

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The resolution of issue 407 does not cover cases involving using-declarations. For example:

  namespace A { struct S {}; }
  namespace B {
    // This is valid per issue 407
    using A::S;
    typedef A::S S;
    struct S s;
  }
  namespace C {
    // The typedef does not redefine the name S in this
    // scope, so issue 407's resolution does not apply.
    typedef A::S S;
    using A::S;
    // The name lookup here isn't ambiguous, because it only finds one
    // entity, but it finds both a typedef-name and a non-typedef-name referring
    // to that entity, so the standard doesn't appear to say whether this is valid.
    struct S s;
  }

The same issue appears with using-directives:

  namespace D { typedef A::S S; }
  namespace E {
    using namespace A;
    using namespace D;
    struct S s; // ok? issue 407 doesn't apply here either
  }

One possibility might be to remove the rule that a typedef-name declaration redefines an already-defined name and instead rely on struct stat-style hiding, taking the non-typedef-name if name lookup finds both and they refer to the same type.

Notes from the June, 2014 meeting:

CWG felt that these examples should be well-formed.




2199. Typedefs and tags

Section: 9.2.4  [dcl.typedef]     Status: CD6     Submitter: Richard Smith     Date: 2015-11-12

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

It is still unclear how typedefs and elaborated-type-specifiers interact in some cases. For example:

    namespace A {
      struct S {};
    }
    namespace B {
      typedef int S;
    }
    namespace C {
      using namespace A;
      using namespace B;
      struct S s; // clearly ambiguous, S names different entities
    }
    namespace D {
      using A::S;
      typedef struct S S;
      struct S s; // OK under issue 407: S could be used in an
                  //  elaborated-type-specifier before the typedef, so still can be
    }
    namespace E {
      typedef A::S S;
      using A::S;
      struct S s; // ??? the identifier S could not have been used in an
                  // elaborated-type-specifier prior to the typedef, so is this lookup
                  // ill-formed because it finds a typedef-name?
    }
    namespace F {
      typedef A::S S;
    }
    namespace G {
      using namespace A;
      using namespace F;
      struct S s; // ??? F::S could not have been used as an
                  // elaborated-type-specifier before the typedef. is this ill-formed because
                  // the lookup finds a typedef-name?
    }
    namespace H {
      using namespace F;
      using namespace A;
      struct S s; // some implementations give different answers for G and H
    }



2213. Forward declaration of partial specializations

Section: 9.2.9.5  [dcl.type.elab]     Status: CD6     Submitter: Richard Smith     Date: 2015-12-11

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

According to 9.2.9.5 [dcl.type.elab] paragraph 1,

If an elaborated-type-specifier is the sole constituent of a declaration, the declaration is ill-formed unless it is an explicit specialization (13.9.4 [temp.expl.spec]), an explicit instantiation (13.9.3 [temp.explicit]) or it has one of the following forms:

This implies that class template partial specializations cannot be forward-declared, which is probably unintentional.

Notes from the November, 2016 meeting:

CWG felt that forward declarations of partial specializations should be allowed.




2389. Agreement of deduced and explicitly-specified variable types

Section: 9.2.9.7  [dcl.spec.auto]     Status: CD6     Submitter: Nina Ranns     Date: 2018-10-24

The Standard does not explicitly address whether an example like the following is well-formed or not:

  struct S {
    static int i;
  };
  auto S::i = 23;

There is implementation divergence on the handling of this example.

Notes from the July, 2019 meeting

Editorially add the example as well-formed; see editorial issue 2979.

CWG 2019-09-16

Approved editorial change 3227.




1342. Order of initialization with multiple declarators

Section: 9.3  [dcl.decl]     Status: CD6     Submitter: Alberto Ganesh Barbati     Date: 2011-08-11

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting. See 8.8 [stmt.dcl] paragraph 2.]

It is not clear what, if anything, in the existing specification requires that the initialization of multiple init-declarators within a single declaration be performed in declaration order.




1900. Do friend declarations count as “previous declarations”?

Section: 9.3.4  [dcl.meaning]     Status: CD6     Submitter: Hubert Tong     Date: 2014-03-25

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Issue 1477 assumes that a name declared only in a friend declaration can be defined outside its namespace using a qualified-id, but the normative passages in 9.3.4 [dcl.meaning] paragraph 1 and _N4868_.9.8.2.3 [namespace.memdef] paragraph 2 do not settle the question definitively, and there is implementation variance. A clearer statement of intent is needed.




2397. auto specifier for pointers and references to arrays

Section: 9.3.4.5  [dcl.array]     Status: CD6     Submitter: Hubert Tong     Date: 2019-02-04

[Accepted as a DR at the June, 2021 meeting.]

According to 9.3.4.5 [dcl.array] paragraph 1,

In a declaration T D where D has the form

and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is an array type; if the type of the identifier of D contains the auto type-specifier, the program is ill-formed.

This formulation forbids useful constructs like

  int a[3];
  auto (*p)[3] = &a;

(accepted by current implementations) and should be relaxed to accommodate such cases.

Notes from the February, 2019 meeting:

CWG agreed that the example should be accepted.

Notes from the May 25, 2021 teleconference:

It was observed that CWG rejected the same example as being "not a defect" in considering issue 1222. However, the use of auto has significantly expanded since that time and the prohibition of such declarations now seems inconsistent.

Proposed resolution, May, 2021:

  1. Change 9.3.4.5 [dcl.array] paragraph 4 as follows:

  2. U is called the array element type; this type shall not be a placeholder type (9.2.9.7 [dcl.spec.auto]), a reference type, a function type, an array of unknown bound, or cv void.
  3. Change 9.3.4.6 [dcl.fct] paragraph 11 as follows:

  4. The return type shall be a non-array object type, a reference type, or cv void. [Note: An array of placeholder type is considered an array type. —end note]
  5. Change 9.2.9.7.2 [dcl.type.auto.deduct] paragraph 2 as follows:

  6. A type T containing a placeholder type, and a corresponding initializer E, are determined as follows:

    T shall not be an array type. In the case of a return statement with no operand...




2481. Cv-qualification of temporary to which a reference is bound

Section: 9.4.4  [dcl.init.ref]     Status: CD6     Submitter: Jiang An     Date: 2021-03-20

[Accepted as a DR at the June, 2021 meeting.]

According to 9.4.4 [dcl.init.ref] bullet 5.4.2, when a reference is initialized with a non-class value and the referenced type is not reference-related to the type of the initializer,

According to 7.2.2 [expr.type] paragraph 2, the cv-qualification is discarded before invoking the temporary materialization conversion:

If a prvalue initially has the type “cv T”, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.

This results in a reference-to-const being bound to a non-const object, meaning that a const_cast of the reference to a reference-to-nonconst would allow a well-defined modification of the value:

  constexpr const int &r = 42;
  const_cast<int &>(r) = 23;  // Well-defined
  static_assert(r == 42);     // Ill-formed, non-constant expression

This was different from the situation before the advent of the temporary materialization conversion in C++17, when the description of the reference binding created the temporary explicitly with the cv-qualified type:

If T1 is a non-class type, a temporary of type “cv1 T1” is created and copy-initialized (8.5) from the initializer expression. The reference is then bound to the temporary.

Presumably this difference was unintentional and should be reverted.

Proposed resolution, May, 2021:

Change 9.4.4 [dcl.init.ref] bullet 5.4.2 as follows:

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:




1733. Return type and value for operator= with ref-qualifier

Section: 9.5.2  [dcl.fct.def.default]     Status: CD6     Submitter: James Widman     Date: 2013-08-09

[Accepted as a DR at the October, 2021 meeting.]

9.5.2 [dcl.fct.def.default] paragraph 1 specifies that an explicitly-defaulted function shall

have the same declared function type (except for possibly differing ref-qualifiers and except that in the case of a copy constructor or copy assignment operator, the parameter type may be “reference to non-const T”, where T is the name of the member function's class) as if it had been implicitly declared...

This allows an example like

  struct A {
    A& operator=(A const&) && = default;
  };

but forbids

  struct B {
    B&& operator=(B const&) && = default;
  };

which seems backward.

In addition, 11.4.5.3 [class.copy.ctor] paragraph 22 only specifies the return value for implicitly-declared copy/move assignment operators, not for explicitly-defaulted ones.

Proposed resolution (August, 2021):

  1. Change 11.4.6 [class.copy.assign] paragraph 6 as follows:

  2. The implicitly-declared copy/move assignment operator for class X has the return type X&; it returns the object for which the assignment operator is invoked, that is, the object assigned to. An implicitly-declared copy/move assignment operator is an inline public member of its class.
  3. Add the following as a new paragraph following 11.4.6 [class.copy.assign] paragraph 13:

  4. The implicitly-defined copy assignment operator for a union X copies the object representation (6.8 [basic.types]) of X. If the source and destination of the assignment are not the same object, then for each object nested within (6.7.2 [intro.object]) the object that is the source of the copy, a corresponding object o nested within the destination is created, and the lifetime of o begins before the copy is performed.

    The implicitly-defined copy/move assignment operator for a class returns the object for which the assignment operator is invoked, that is, the object assigned to.

[Note: The first point in the issue, that of the relationship between the ref-qualifier and the return type, will be referred to EWG for consideration. The draft resolution above addresses only the second point of the issue.




2221. Copying volatile objects

Section: 9.5.2  [dcl.fct.def.default]     Status: CD6     Submitter: 2016-01-09     Date: Vinny Romano

Subclause 9.5.2 [dcl.fct.def.default] paragraph 1 specifies:

A function that is explicitly defaulted shall

Therefore, the following code is ill-formed:

  struct S
  {
   int i;
   S(const S&) = default;
   S(const volatile S&) = default; // ill-formed
  };

  volatile S s1;
  S s2(s1);

However, C.7.7 [diff.class] paragraph 2 mentions the ability to default such a constructor:

If volatile semantics are required for the copy, a user-declared constructor or assignment must be provided. [ Note: This user-declared constructor may be explicitly defaulted. —end note]

Notes from the November, 2016 meeting:

This issue is to be resolved editorially and is placed in "review' status until the corresponding change appers in a working draft.

Additional note (February, 2022):

The change has been applied editorially with commit 219538.




2465. Coroutine parameters passed to a promise constructor

Section: 9.5.4  [dcl.fct.def.coroutine]     Status: CD6     Submitter: Gor Nishanov     Date: 2020-10-19

[Accepted as a DR at the June, 2021 meeting.]

The resolution of issue 2436 (in P2107R0) deleted the sentence

A reference to a parameter in the function-body of the coroutine and in the call to the coroutine promise constructor is replaced by a reference to its copy.

replacing it with new wording in 7.5.4.2 [expr.prim.id.unqual] paragraph 1:

An identifier that names a coroutine parameter refers to the copy of the parameter (9.5.4 [dcl.fct.def.coroutine]).

This new approach no longer covers coroutine parameters passed to a promise constructor, since the constructor call is implicit, as described in 7.5.4.2 [expr.prim.id.unqual] paragraph 5.

Suggested resolution:

  1. Change 7.5.4.2 [expr.prim.id.unqual] paragraph 4 as follows:

  2. In the following, pi is an lvalue of type Pi, where p1 denotes *this and pi+1 denotes the ith function parameter for a non-static member function, and pi denotes the ith function parameter otherwise. Let qi be the corresponding parameter copy, as described below.
  3. Change 7.5.4.2 [expr.prim.id.unqual] bullet 5.7 as follows:

  4. A coroutine behaves as if its function-body were replaced by...

Proposed resolution (April, 2021):

  1. Change 9.5.4 [dcl.fct.def.coroutine] paragraph 4 as follows:

  2. In the following, pi is an lvalue of type Pi, where p1 denotes *this and pi+1 denotes the ith function parameter for a non-static member function, and pi denotes the ith function parameter otherwise. For a non-static member function, q1 is an lvalue that denotes *this; any other qi is an lvalue that denotes the parameter copy corresponding to pi, as described below.
  3. Change 9.5.4 [dcl.fct.def.coroutine] bullet 5.7 as follows:

  4. A coroutine behaves as if its function-body were replaced by: ... where




2585. Name lookup for coroutine allocation

Section: 9.5.4  [dcl.fct.def.coroutine]     Status: CD6     Submitter: Xu Chuanqi     Date: 2022-05-12

[Accepted at the July, 2022 meeting.]

Consider:

  struct Allocator;

  struct resumable::promise_type {
    void* operator new(std::size_t sz, Allocator&);
    // ...
  };
  resumable foo() {
    co_return;
  }

Subclause 9.5.4 [dcl.fct.def.coroutine] paragraph 9 specifies:

... The allocation function's name is looked up by searching for it in the scope of the promise type. If no viable function is found (12.2.3 [over.match.viable]), overload resolution is performed again on a function call created by passing just the amount of space required as an argument of type std::size_t.

Is the example ill-formed because resumable::promise_type is not viable, or is the example well-formed because the global operator new can be used? There is implementation divergence.

See also LLVM issue 54881.

Proposed resolution (approved by CWG 2022-06-17):

(updated according to 2022-05-20, 2022-06-03, and 2022-06-17 CWG guidance)

Change in 9.5.4 [dcl.fct.def.coroutine] paragraph 9 as follows:

... The allocation function's name is looked up by searching for it in the scope of the promise type.



2312. Structured bindings and mutable

Section: 9.6  [dcl.struct.bind]     Status: CD6     Submitter: Richard Smith     Date: 2016-08-11

[Accepted at the November, 2020 meeting.]

An example like the following is currently ill-formed:

  struct A { mutable int n; };
  void f() {
    const auto [a] = A();
    a = 0;
  }

According to 9.6 [dcl.struct.bind] paragraph 4, the type of a is const int, since the implicitly-declared variable is const. This seems obviously wrong: the member n is mutable, so the member access expression e.n has type int, which should also be the type of a. (mutable should presumably be taken into account when forming the referenced type too, so that decltype(a) is int as would presumably be expected, rather than const int.)

Proposed resolution, March, 2018: [SUPERSEDED]

Change 9.6 [dcl.struct.bind] paragraph 4 as follows:

...Designating the non-static data members of E as m0, m1, m2, ... (in declaration order), each vi is the name of an lvalue that refers to the member mi of e and whose type is cv Ti, where Ti is the declared type of that member e.mi; the referenced type is cv Ti the type of e.mi. The lvalue is a bit-field if...

Notes from the June, 2018 meeting:

It was observed that this resolution does not handle members with reference type correctly. The main problem seems to be the statement in 7.6.1.5 [expr.ref] paragraph 4, which directly handles members with reference type rather than allowing the type of the member to be the result type and relying on the general rule that turns reference-typed expressions into lvalues.

Proposed resolution (April, 2020):

Change 9.6 [dcl.struct.bind] paragraph 5 as follows:

...Designating the non-static data members of E as m0, m1, m2, ... (in declaration order), each vi is the name of an lvalue that refers to the member mi of e and whose type is cv Ti, where Ti is the declared type of that member that of e.mi (7.6.1.5 [expr.ref]); the referenced type is cv Ti the declared type of mi if that type is a reference type, or the type of e.mi otherwise. The lvalue is a bit-field if that member is a bit-field.

[Example 2:

  struct S { mutable int x1 : 2; volatile double y1; };
  S f();
  const auto [ x, y ] = f();

The type of the id-expression x is “const int”, the type of the id-expression y is “const volatile double”. —end example]




2506. Structured bindings and array cv-qualifiers

Section: 9.6  [dcl.struct.bind]     Status: CD6     Submitter: Barry Revzin     Date: 2018-12-11

[Accepted at the February, 2022 meeting.]

According to 9.6 [dcl.struct.bind] paragraph 1,

A structured binding declaration introduces the identifiers v0, v1, v2, ... of the identifier-list as names of structured bindings. Let cv denote the cv-qualifiers in the decl-specifier-seq and S consist of the storage-class-specifiers of the decl-specifier-seq (if any). A cv that includes volatile is deprecated; see D.4 [depr.volatile.type]. First, a variable with a unique name e is introduced. If the assignment-expression in the initializer has array type A and no ref-qualifier is present, e is defined by

and each element is copy-initialized or direct-initialized from the corresponding element of the assignment-expression as specified by the form of the initializer.

This means that in an example like

  const int arr[1]{};
  auto [i] = arr;

i is a reference to const int. Presumably the fact that the array is copied should drop the array's cv-qualification.

Proposed resolution (December, 2021):

Change 9.6 [dcl.struct.bind] paragraph 1 as follows:

If the assignment-expression in the initializer has array type cv1 A and no ref-qualifier is present, e is defined by...




36. using-declarations in multiple-declaration contexts

Section: 9.9  [namespace.udecl]     Status: CD6     Submitter: Andrew Koenig     Date: 20 Aug 1998

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Section 9.9 [namespace.udecl] paragraph 8 says:

A using-declaration is a declaration and can therefore be used repeatedly where (and only where) multiple declarations are allowed.
It contains the following example:
    namespace A {
            int i;
    }

    namespace A1 {
            using A::i;
            using A::i;             // OK: double declaration
    }

    void f()
    {
            using A::i;
            using A::i;             // error: double declaration
    }
However, if "using A::i;" is really a declaration, and not a definition, it is far from clear that repeating it should be an error in either context. Consider:
    namespace A {
            int i;
            void g();
    }

    void f() {
            using A::g;
            using A::g;
    }
Surely the definition of f should be analogous to
    void f() {
            void g();
            void g();
    }
which is well-formed because "void g();" is a declaration and not a definition.

Indeed, if the double using-declaration for A::i is prohibited in f, why should it be allowed in namespace A1?

Proposed Resolution (04/99): Change the comment "// error: double declaration" to "// OK: double declaration". (This should be reviewed against existing practice.)

Notes from 04/00 meeting:

The core language working group was unable to come to consensus over what kind of declaration a using-declaration should emulate. In a straw poll, 7 members favored allowing using-declarations wherever a non-definition declaration could appear, while 4 preferred to allow multiple using-declarations only in namespace scope (the rationale being that the permission for multiple using-declarations is primarily to support its use in multiple header files, which are seldom included anywhere other than namespace scope). John Spicer pointed out that friend declarations can appear multiple times in class scope and asked if using-declarations would have the same property under the "like a declaration" resolution.

As a result of the lack of agreement, the issue was returned to "open" status.

See also issues 56, 85, and 138..

Additional notes (January, 2005):

Some related issues have been raised concerning the following example (modified from a C++ validation suite test):

    struct A
    {
        int i;
        static int j;
    };

    struct B : A { };
    struct C : A { };

    struct D : virtual B, virtual C
    {
        using B::i;
        using C::i;
        using B::j;
        using C::j;
    };

Currently, it appears that the using-declarations of i are ill-formed, on the basis of 9.9 [namespace.udecl] paragraph 10:

Since a using-declaration is a declaration, the restrictions on declarations of the same name in the same declarative region (6.4 [basic.scope]) also apply to using-declarations.

Because the using-declarations of i refer to different objects, declaring them in the same scope is not permitted under 6.4 [basic.scope]. It might, however, be preferable to treat this case as many other ambiguities are: allow the declaration but make the program ill-formed if a name reference resolves to the ambiguous declarations.

The status of the using-declarations of j, however, is less clear. They both declare the same entity and thus do not violate the rules of 6.4 [basic.scope]. This might (or might not) violate the restrictions of 11.4 [class.mem] paragraph 1:

Except when used to declare friends (11.8.4 [class.friend]) or to introduce the name of a member of a base class into a derived class (9.9 [namespace.udecl], _N3225_.11.3 [class.access.dcl]), member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class. A member shall not be declared twice in the member-specification, except that a nested class or member class template can be declared and then later defined.

Do the using-declarations of j repeatedly declare the same member? Or is the preceding sentence an indication that a using-declaration is not a declaration of a member?




386. Friend declaration of name brought in by using-declaration

Section: 9.9  [namespace.udecl]     Status: CD6     Submitter: Herb Sutter     Date: 8 Oct 2002

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The following came up recently on comp.lang.c++.moderated (edited for brevity):

  namespace N1 {
    template<typename T> void f( T* x ) {
      // ... other stuff ...
      delete x;
    }
  }

  namespace N2 {
    using N1::f;

    template<> void f<int>( int* ); // A: ill-formed

    class Test {
      ~Test() { }
      friend void f<>( Test* x );   // B: ill-formed?
    };
  }

I strongly suspect, but don't have standardese to prove, that the friend declaration in line B is ill-formed. Can someone show me the text that allows or disallows line B?

Here's my reasoning: Writing "using" to pull the name into namespace N2 merely allows code in N2 to use the name in a call without qualification (per 9.9 [namespace.udecl]). But just as declaring a specialization must be done in the namespace where the template really lives (hence line A is ill-formed), I suspect that declaring a specialization as a friend must likewise be done using the original namespace name, not obliquely through a "using". I see nothing in 9.9 [namespace.udecl] that would permit this use. Is there?

Andrey Tarasevich: 13.7.5 [temp.friend] paragraph 2 seems to get pretty close: "A friend declaration that is not a template declaration and in which the name of the friend is an unqualified 'template-id' shall refer to a specialization of a function template declared in the nearest enclosing namespace scope".

Herb Sutter: OK, thanks. Then the question in this is the word "declared" -- in particular, we already know we cannot declare a specialization of a template in any other namespace but the original one.

John Spicer: This seems like a simple question, but it isn't.

First of all, I don't think the standard comments on this usage one way or the other.

A similar example using a namespace qualified name is ill-formed based on 9.3.4 [dcl.meaning] paragraph 1:

  namespace N1 {
        void f();
  }

  namespace N2 {
        using N1::f;
        class A {
                friend void N2::f();
        };
  }

Core issue 138 deals with this example:

  void foo();
  namespace A{
    using ::foo;
    class X{
      friend void foo();
    };
  }

The proposed resolution (not yet approved) for issue 138 is that the friend declares a new foo that conflicts with the using-declaration and results in an error.

Your example is different than this though because the presence of the explicit argument list means that this is not declaring a new f but is instead using a previously declared f.

One reservation I have about allowing the example is the desire to have consistent rules for all of the "declaration like" uses of template functions. Issue 275 (in DR status) addresses the issue of unqualified names in explicit instantiation and explicit specialization declarations. It requires that such declarations refer to templates from the namespace containing the explicit instantiation or explicit specialization. I believe this rule is necessary for those directives but is not really required for friend declarations -- but there is the consistency issue.

Notes from April 2003 meeting:

This is related to issue 138. John Spicer is supposed to update his paper on this topic. This is a new case not covered in that paper. We agreed that the B line should be allowed.




852. using-declarations and dependent base classes

Section: 9.9  [namespace.udecl]     Status: CD6     Submitter: Michael Wong     Date: 2 April, 2009

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The status of an example like the following is unclear in the current Standard:

    struct B {
        void f();
    };
    template<typename T> struct S: T {
        using B::f;
    };

9.9 [namespace.udecl] does not deal explicitly with dependent base classes, but does say in paragraph 3,

In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined. If such a using-declaration names a constructor, the nested-name-specier shall name a direct base class of the class being defined; otherwise it introduces the set of declarations found by member name lookup (6.5.2 [class.member.lookup], 6.5.5.2 [class.qual]).

In the definition of S, B::f is not a dependent name but resolves to an apparently unrelated class. However, because S could be instantiated as S<B>, presumably 13.8 [temp.res] paragraph 8 would apply:

No diagnostic shall be issued for a template definition for which a valid specialization can be generated.

Note also the resolution of issue 515, which permitted a similar use of a dependent base class named with a non-dependent name.




1907. using-declarations and default arguments

Section: 9.9  [namespace.udecl]     Status: CD6     Submitter: Richard Smith     Date: 2014-03-30

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The status of an example like the following is not clear:

  void f(int, int);
  template<typename T> void g(T t) { f(t); }
  void f(int, int = 0);
  void h() { g(0); }

According to 13.8.4 [temp.dep.res] paragraph 1,

In resolving dependent names, names from the following sources are considered:

If this is to be interpreted as meaning that only the declarations that are visible at the point of definition can be used in overload resolution for dependent calls, the call g(0) is ill-formed. If, however, it is the names, not the declarations, that are captured, then presumably the second declaration of f should be considered, making the call well-formed. There is implementation divergence for this example.

The resolution of issue 1551 recently clarified the requirements in similar cases involving using-declarations:

  namespace N { void f(int, int); }
  using N::f;
  template<typename T> void g(T t) { f(t); }
  namespace N { void f(int, int = 0); }
  void h() { g(0); }

The note added to 9.9 [namespace.udecl] paragraph 11 makes clear that the call g(0) is well-formed in this example.

This outcome results in an unfortunate discrepancy between how default arguments and overloaded functions are treated, even though default arguments could conceptually be viewed as simply adding extra overloads for the additional arguments.

Notes from the June, 2014 meeting:

CWG was unable to come to consensus regarding the desired outcome, with an approximately equal split between desiring the first example to be well-formed or ill-formed. It was noted that the resolution of issue 1850 makes the corresponding case for non-dependent references ill-formed, with no diagnostic required. Similar questions also apply to completing an array type, which also involves a modification to an existing entity declaration in a given scope.

Notes from the February, 2016 meeting:

CWG determined that the case should be ill-formed, no diagnostic required, to allow implementations to continue to use either strategy.




563. Linkage specification for objects

Section: 9.11  [dcl.link]     Status: CD6     Submitter: Daveed Vandevoorde     Date: 8 March 2006

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

It is not clear whether some of the wording in 9.11 [dcl.link] that applies only to function types and names ought also to apply to object names. In particular, paragraph 3 says,

Every implementation shall provide for linkage to functions written in the C programming language, "C", and linkage to C++ functions, "C++".

Nothing is said about variable names, apparently meaning that implementations need not provide C (or even C++!) linkage for variable names. Also, paragraph 5 says,

Except for functions with C++ linkage, a function declaration without a linkage specification shall not precede the first linkage specification for that function. A function can be declared without a linkage specification after an explicit linkage specification has been seen; the linkage explicitly specified in the earlier declaration is not affected by such a function declaration.

There doesn't seem to be a good reason for these provisions not to apply to variable names, as well.




1818. Visibility and inherited language linkage

Section: 9.11  [dcl.link]     Status: CD6     Submitter: Richard Smith     Date: 2013-12-04

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Does the language linkage of a block-scope declaration determine the language linkage of a subsequent declaration of the same name in a different scope? For example,

   extern "C" void f() {
     void g();    // Implicitly extern "C"
   }
   void g() { }   // Also extern "C" or linkage mismatch?

In other contexts, inheritance of linkage requires that the earlier declaration be visible, as in 6.6 [basic.link] paragraph 6:

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block scope declaration declares that same entity and receives the linkage of the previous declaration.

The specification for language linkage in 9.11 [dcl.link] paragraph 5, however, makes no mention of visibility:

A function can be declared without a linkage specification after an explicit linkage specification has been seen; the linkage explicitly specified in the earlier declaration is not affected by such a function declaration.



2460. C language linkage and constrained non-template friends

Section: 9.11  [dcl.link]     Status: CD6     Submitter: Hubert Tong     Date: 2020-03-23

[Accepted at the November, 2020 meeting.]

According to 13.7.5 [temp.friend] paragraph 9,

A non-template friend declaration with a requires-clause shall be a definition. A friend function template with a constraint that depends on a template parameter from an enclosing template shall be a definition. Such a constrained friend function or function template declaration does not declare the same function or function template as a declaration in any other scope.

However, this specification conflicts with the treatment of functions with C language linkage in 9.11 [dcl.link] paragraph 7:

At most one function with a particular name can have C language linkage. Two declarations for a function with C language linkage with the same function name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same function.

For example:

  template <typename T> struct A { struct B; };

  extern "C" {
  template <typename T>
  struct A<T>::B {
   friend void f(B *) requires true {} // C language linkage applies
  };
  }

  namespace Q {
   extern "C" void f(); // ill-formed redeclaration?
  }

Proposed resolution (April, 2020):

Change 9.11 [dcl.link] paragraph 5 as follows:

...A C language linkage is ignored in determining the language linkage of the names of class members, the names of friend functions with a trailing requires-clause, and the function type of class member functions...



2597. Replaceable allocation and deallocation functions in the global module

Section: 10.1  [module.unit]     Status: CD6     Submitter: Gabriel dos Reis     Date: 2022-06-17     Liaison: EWG

[Accepted at the July, 2022 meeting.]

Subclause 10.1 [module.unit] paragraph 7 implicitly attaches the replaceable global allocation or deallocation functions to the global module. Now that extern "C++" can be used to introduce declarations in the global module, even when in the purview of a named module, the provision seems superfluous.

Proposed resolution [SUPERSEDED]:

  1. Change in 6.7.5.5.1 [basic.stc.dynamic.general] paragraph 2 as follows:

    The library provides default definitions for the global allocation and deallocation functions. Some global allocation and deallocation functions are replaceable (17.6.3 [new.delete]). A C++ program shall provide at most one definition of a replaceable allocation or deallocation function. Any such function definition replaces the default version provided in the library (16.4.5.6 [replacement.functions]). The following allocation and deallocation functions (17.6 [support.dynamic]) are implicitly declared in global scope in each translation unit of a program and are attached to the global module (10.1 [module.unit]).
  2. Change in 10.1 [module.unit] bullet 7.2 as follows:

    • If the declaration is ...
    • Otherwise, if the declaration
      • is a replaceable global allocation or deallocation function (17.6.3.2 [new.delete.single], 17.6.3.3 [new.delete.array]), or
      • is a namespace-definition with external linkage, or
      • appears within a linkage-specification (9.11 [dcl.link]),
      it is attached to the global module.
    • Otherwise, ...

Additional notes (June, 2022):

Forwarded to EWG with paper issue 1273, by decision of the CWG chair.

Approved by EWG telecon 2022-07-07.

Proposed resolution (approved by CWG 2022-07-15):

  1. Change in 6.7.5.5.1 [basic.stc.dynamic.general] paragraph 2 as follows:

    The library provides default definitions for the global allocation and deallocation functions. Some global allocation and deallocation functions are replaceable (17.6.3 [new.delete]) ; these are attached to the global module 10.1 [module.unit]). A C++ program shall provide at most one definition of a replaceable allocation or deallocation function. Any such function definition replaces the default version provided in the library (16.4.5.6 [replacement.functions]). The following allocation and deallocation functions (17.6 [support.dynamic]) are implicitly declared in global scope in each translation unit of a program.
  2. Change in 10.1 [module.unit] bullet 7.2 as follows:

    • If the declaration is ...
    • Otherwise, if the declaration
      • is a replaceable global allocation or deallocation function (17.6.3.2 [new.delete.single], 17.6.3.3 [new.delete.array]), or
      • is a namespace-definition with external linkage, or
      • appears within a linkage-specification (9.11 [dcl.link]),
      it is attached to the global module.
    • Otherwise, ...



2491. Export of typedef after its first declaration

Section: 10.2  [module.interface]     Status: CD6     Submitter: Richard Smith     Date: 2021-04-16

[Accepted as a DR at the October, 2021 meeting.]

According to 10.2 [module.interface] paragraph 6,

A redeclaration of an entity or typedef-name X is implicitly exported if X was introduced by an exported declaration; otherwise it shall not be exported. [Example 4:

  export module M;
  struct S { int n; };
  typedef S S;
  export typedef S S; // OK, does not redeclare an entity
  export struct S;    // error: exported declaration follows non-exported declaration

end example]

The normative text says that exporting a typedef that was not exported on its first declaration is ill-formed, but the example does so and states that it is “OK”. This is a contradiction that was introduced by the changes in paper P1787R6; the previous normative text supported the usage in the example.

(See also editorial issue 4540.)

Proposed resolution, August, 2021:

Change 10.2 [module.interface] paragraph 6 as follows:

A redeclaration of an entity or typedef-name X is implicitly exported if X was introduced by an exported declaration; otherwise it shall not be exported.



1821. Qualified redeclarations in a class member-specification

Section: 11.4  [class.mem]     Status: CD6     Submitter: Richard Smith     Date: 2013-1205

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

11.4 [class.mem] paragraph 1 allows nested classes, class templates, and enumerations to be declared and then later defined in the class member-specification. There does not appear to be a restriction on using a qualified-id in that definition. Should such a restriction be added?




1360. constexpr defaulted default constructors

Section: 11.4.5  [class.ctor]     Status: CD6     Submitter: Richard Smith     Date: 2011-08-16

[ Resolved by P2448R2, applied in July 2022. ]

According to 11.4.5 [class.ctor] paragraph 6, a defaulted default constructor is constexpr if the corresponding user-written constructor would satisfy the constexpr requirements. However, the requirements apply to the definition of a constructor, and a defaulted constructor is defined only if it is odr-used, leaving it indeterminate at declaration time whether the defaulted constructor is constexpr or not.

(See also issue 1358.)

Additional notes (February, 2013):

As an example of this issue, consider:

  struct S {
    int i = sizeof(S);
  };

You can't determine the value of the initializer, and thus whether the initializer is a constant expression, until the class is complete, but you can't complete the class without declaring the default constructor, and whether that constructor is constexpr or not depends on whether the member initializer is a constant expression.

A similar issue arises with the following example:

  struct A {
    int x = 37;
    struct B { int x = 37; } b;
    B b2[2][3] = { { } };
  };

This introduces an order dependency that is not specified in the current text: determining whether the default constructor of A is constexpr requires first determining the characteristics of the initializer of B::x and whether B::B() is constexpr or not.

The problem is exacerbated with class templates, since the current direction of CWG is to instantiate member initializers only when they are needed (see issue 1396). For a specific example:

  struct S;
  template<class T> struct X {
    int i = T().i;
  };
  unsigned n = sizeof(X<S>); // Error?
  struct S { int i; };

This also affects determining whether a class template specialization is a literal type or not; presumably getting the right answer to that requires instantiating the class and all its nonstatic data member initializers.

See also issues 1397 and 1594.

Notes from the September, 2013 meeting:

This issue should be resolved together with issue 1397.

Proposed resolution (May, 2014):

Change 11.4.5 [class.ctor] paragraphs 4-5 as follows:

A defaulted default constructor for class X is defined as deleted if:

An implicitly-declared default constructor is constexpr if:

A default constructor is trivial if it is not user-provided and if:

Otherwise, the default constructor is non-trivial.

A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (6.3 [basic.def.odr]) to create an object of its class type (6.7.2 [intro.object]) or when it is explicitly defaulted after its first declaration. The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer (11.9.3 [class.base.init]) and an empty compound-statement. If that user-written default constructor would be ill-formed, the program is ill-formed. If that user-written default constructor would satisfy the requirements of a constexpr constructor (9.2.6 [dcl.constexpr]), the implicitly-defined default constructor is constexpr. Before the defaulted default constructor for a class is implicitly defined, all the non-user-provided default constructors for its base classes and its non-static data members shall have been implicitly defined. [Note:...

Additional notes, May, 2014:

The proposed resolution inadvertently allows a defaulted default constructor of a class with virtual bases to be constexpr. It has been updated with a change addressing that oversight and returned to "review" status.

See also issue 1890.




2477. Defaulted vs deleted copy constructors/assignment operators

Section: 11.4.5.3  [class.copy.ctor]     Status: CD6     Submitter: Andrew Rogers     Date: 2021-02-04

[Accepted as a DR at the June, 2021 meeting.]

According to 11.4.5.3 [class.copy.ctor] paragraph 6,

If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (9.5 [dcl.fct.def]).

However, this rule is contradicted by paragraph 10, which lists a number of other reasons why a defaulted copy constructor will be defined as deleted, rather than being “defined as defaulted,” as required by paragraph 6:

A defaulted copy/move constructor for a class X is defined as deleted (9.5.3 [dcl.fct.def.delete]) if X has:

A similar contradiction exists for copy assignment operators in 11.4.6 [class.copy.assign] paragraphs 2 and 7.

Proposed resolution (April, 2021):

  1. Change 11.4.5.3 [class.copy.ctor] paragraph 6 as follows:

  2. If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (9.5 [dcl.fct.def]). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor (D.7 [depr.impldec]).
  3. Change 11.4.6 [class.copy.assign] paragraph 2 as follows:

  4. If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted (9.5 [dcl.fct.def]). The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor (D.9). The implicitly-declared...



399. Destructor lookup redux

Section: 11.4.7  [class.dtor]     Status: CD6     Submitter: John Spicer     Date: 17 Jan 2003

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Mark Mitchell raised a number of issues related to the resolution of issue 244 and of destructor lookup in general.

Issue 244 says:

... in a qualified-id of the form: the second class-name is looked up in the same scope as the first.

But if the reference is "p->X::~X()", the first class-name is looked up in two places (normal lookup and a lookup in the class of p). Does the new wording mean:

  1. You look up the second class-name in the scope that you found the first one.
  2. You look up the second class-name using the same kind of lookup that found the first one (normal vs. class).
  3. If you did a dual lookup for the first you do a dual lookup for the second.

This is a test case that illustrates the issue:

  struct A {
    typedef A C;
  };

  typedef A B;

  void f(B* bp) {
    bp->B::~B();  // okay B found by normal lookup
    bp->C::~C();  // okay C found by class lookup
    bp->B::~C();  // B found by normal lookup C by class -- okay?
    bp->C::~B();  // C found by class lookup B by normal -- okay?
  }

A second issue concerns destructor references when the class involved is a template class.

  namespace N {
    template <typename T> struct S {
      ~S();
    };
  }

  void f(N::S<int>* s) {
    s->N::S<int>::~S();
  }

The issue here is that the grammar uses "~class-name" for destructor names, but in this case S is a template name when looked up in N.

Finally, what about cases like:

  template <typename T> void f () {
    typename T::B x;
    x.template A<T>::template B<T>::~B();
  }

When parsing the template definition, what checks can be done on "~B"?

Sandor Mathe adds :

The standard correction for issue 244 (now in DR status) is still incomplete.

Paragraph 5 of 6.5.5 [basic.lookup.qual] is not applicable for p->T::~T since there is no nested-name-specifier. Section _N4868_.6.5.6 [basic.lookup.classref] describes the lookup of p->~T but p->T::~T is still not described. There are examples (which are non-normative) that illustrate this sort of lookup but they still leave questions unanswered. The examples imply that the name after ~ should be looked up in the same scope as the name before the :: but it is not stated. The problem is that the name to the left of the :: can be found in two different scopes. Consider the following:

  struct S {
    struct C { ~C() { } };
  };

  typedef S::C D;

  int main() {
    D* p;
    p->C::~D();  // valid?
  }

Should the destructor call be valid? If there were a nested name specifier, then D should be looked for in the same scope as C. But here, C is looked for in 2 different ways. First, it is searched for in the type of the left hand side of -> and it is also looked for in the lexical context. It is found in one or if both, they must match. So, C is found in the scope of what p points at. Do you only look for D there? If so, this is invalid. If not, you would then look for D in the context of the expression and find it. They refer to the same underlying destructor so this is valid. The intended resolution of the original defect report of the standard was that the name before the :: did not imply a scope and you did not look for D inside of C. However, it was not made clear whether this was to be resolved by using the same lookup mechanism or by introducing a new form of lookup which is to look in the left hand side if that is where C was found, or in the context of the expression if that is where C was found. Of course, this begs the question of what should happen when it is found in both? Consider the modification to the above case when C is also found in the context of the expression. If you only look where you found C, is this now valid because it is in 1 of the two scopes or is it invalid because C was in both and D is only in 1?

  struct S {
    struct C { ~C() { } };
  };

  typedef S::C D;
  typedef S::C C;

  int main() {
    D* p;
    p->C::~D();  // valid?
  }

I agree that the intention of the committee is that the original test case in this defect is broken. The standard committee clearly thinks that the last name before the last :: does not induce a new scope which is our current interpretation. However, how this is supposed to work is not defined. This needs clarification of the standard.

Martin Sebor adds this example (September 2003), along with errors produced by the EDG front end:

namespace N {
    struct A { typedef A NA; };
    template <class T> struct B { typedef B NB; typedef T BT; };
    template <template <class> class T> struct C { typedef C NC; typedef T<A> CA; };
}

void foo (N::A *p)
{
    p->~NA ();
    p->NA::~NA ();
}

template <class T>
void foo (N::B<T> *p)
{
    p->~NB ();
    p->NB::~NB ();
}

template <class T>
void foo (typename N::B<T>::BT *p)
{
    p->~BT ();
    p->BT::~BT ();
}

template <template <class> class T>
void foo (N::C<T> *p)
{
    p->~NC ();
    p->NC::~NC ();
}

template <template <class> class T>
void foo (typename N::C<T>::CA *p)
{
    p->~CA ();
    p->CA::~CA ();
}

Edison Design Group C/C++ Front End, version 3.3 (Sep  3 2003 11:54:55)
Copyright 1988-2003 Edison Design Group, Inc.

"t.cpp", line 16: error: invalid destructor name for type "N::B<T>"
      p->~NB ();
          ^

"t.cpp", line 17: error: qualifier of destructor name "N::B<T>::NB" does not
          match type "N::B<T>"
      p->NB::~NB ();
              ^

"t.cpp", line 30: error: invalid destructor name for type "N::C<T>"
      p->~NC ();
          ^

"t.cpp", line 31: error: qualifier of destructor name "N::C<T>::NC" does not
          match type "N::C<T>"
      p->NC::~NC ();
              ^

4 errors detected in the compilation of "t.cpp".

John Spicer: The issue here is that we're unhappy with the destructor names when doing semantic analysis of the template definitions (not during an instantiation).

My personal feeling is that this is reasonable. After all, why would you call p->~NB for a class that you just named as N::B<T> and you could just say p->~B?

Additional note (September, 2004)

The resolution for issue 244 removed the discussion of p->N::~S, where N is a namespace-name. However, the resolution did not make this construct ill-formed; it simply left the semantics undefined. The meaning should either be defined or the construct made ill-formed.

See also issues 305 and 466.

Additional note, November, 2014:

Here are some additional examples that should be addressed by the resolution of this issue:

   namespace N {
     template<typename T> struct E {};
     typedef E<int> F;
   }
   namespace M {
     typedef N::F H;
   }
   void g(N::F f) {
     typedef N::F G;
     f.G::~E(); // #1
     f.G::~F(); // #2
     f.G::~G(); // #3
     f.N::F::~E(); // #4
     f.N::F::~F(); // #5
     f.N::F::~G(); // #6
     f.M::H::~E(); // #7
     f.M::H::~F(); // #8
     f.M::H::~G(); // #9
     f.M::H::~H(); // #10
   }



1969. Missing exclusion of ~S as an ordinary function name

Section: 11.4.7  [class.dtor]     Status: CD6     Submitter: Richard Smith     Date: 2014-07-14

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting. See 11.4.7 [class.dtor] paragraph 1.]

There does not appear to be wording to exclude use of a name like ~S for entities other than destructors.




1726. Declarator operators and conversion function

Section: 11.4.8.3  [class.conv.fct]     Status: CD6     Submitter: James Widman     Date: 2013-08-02

[Accepted at the February, 2022 meeting.]

Presumably the following example is intended to be ill-formed:

  struct A {
    (*operator int*());
  };
  A a;
  int *x = a; // Ok?

It is not clear, however, which rule is supposed to reject such a member-declaration.

Proposed resolution (December, 2021):

Change 11.4.8.3 [class.conv.fct] paragraph 1 as follows, splitting the paragraph as indicated:

A member function of a class X with a name of the form

A declaration whose declarator-id has an unqualified-id that is a conversion-function-id declares a conversion function; its declarator shall be a function declarator (9.3.4.6 [dcl.fct]) of the form

where the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:

A conversion function shall have no parameters and shall be a non-static member function of a class or class template X; it specifies a conversion from X to the type specified by the conversion-type-id, interpreted as a type-id (9.3.2 [dcl.name]). Such functions are called conversion functions.

A decl-specifier in the decl-specifier-seq of a conversion function (if any) shall not be neither a defining-type-specifier nor static. The type of the conversion function (9.3.4.6 [dcl.fct]) is “noexceptopt function taking no parameter cv-qualifier-seqopt ref-qualifieropt returning conversion-type-id”.

A conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to cv void.102 [Example 1:...




2511. cv-qualified bit-fields

Section: 11.4.10  [class.bit]     Status: CD6     Submitter: Aaron Ballman     Date: 2021-09-15

[Accepted at the February, 2022 meeting.]

According to 11.4.10 [class.bit] paragraph 1,

A bit-field shall have integral or enumeration type

This apparently does not allow for cv-qualification in a bit-field type.

Notes from the December, 2021 meeting:

As of N4901, there is no longer an issue regarding the integral types; 6.8.2 [basic.fundamental] paragraph 11 says,

The character types, bool, the signed and unsigned integer types, and cv-qualified versions (6.8.5 [basic.type.qualifier]) thereof, are collectively termed integral types.

Proposed resolution (December, 2021):

A bit-field shall have integral or (possibly cv-qualified) enumeration type



255. Placement deallocation functions and lookup ambiguity

Section: 11.4.11  [class.free]     Status: CD6     Submitter: Mike Miller     Date: 26 Oct 2000

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Paragraph 4 of 11.4.11 [class.free] speaks of looking up a deallocation function. While it is an error if a placement deallocation function alone is found by this lookup, there seems to be an assumption that a placement deallocation function and a usual deallocation function can both be declared in a given class scope without creating an ambiguity. The normal mechanism by which ambiguity is avoided when functions of the same name are declared in the same scope is overload resolution; however, there is no mention of overload resolution in the description of the lookup. In fact, there appears to be nothing in the current wording that handles this case. That is, the following example appears to be ill-formed, according to the current wording:

    struct S {
        void operator delete(void*);
        void operator delete(void*, int);
    };
    void f(S* p) {
        delete p;    // ill-formed: ambiguous operator delete
    }

Suggested resolution (Mike Miller, March 2002):

I think you might get the right effect by replacing the last sentence of 11.4.11 [class.free] paragraph 4 with something like:

After removing all placement deallocation functions, the result of the lookup shall contain an unambiguous and accessible deallocation function.

Additional notes (October, 2012):

This issue should be reconsidered in list of paper N3396, as it would add additional overloads for allocation and deallocation functions.

The term “usual deallocation function” is defined in 6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 2; perhaps it could be used to good effect in 7.6.2.9 [expr.delete] paragraph 7. The specifications in 11.4.11 [class.free] paragraphs 4 and 5 should probably also be moved into 7.6.2.9 [expr.delete].




2496. ref-qualifiers and virtual overriding

Section: 11.7.3  [class.virtual]     Status: CD6     Submitter: Jens Maurer     Date: 2021-06-16

[Accepted as a DR at the October, 2021 meeting.]

According to 11.7.3 [class.virtual] paragraph 2,

If a virtual member function F is declared in a class B, and, in a class D derived (directly or indirectly) from B, a declaration of a member function G corresponds (6.4.1 [basic.scope.scope]) to a declaration of F, ignoring trailing requires-clauses, then G overrides105 F.

This is different from C++20, where G was considered to hide, rather than to override, F if the ref-qualifiers of the declarations are different. This unintentional change could be addressed in one of two ways. To restore the C++20 behavior, the cited paragraph could be amended to:

...a declaration of a member function G corresponds (6.4.1 [basic.scope.scope]) to a declaration of F, ignoring trailing requires-clauses, and has the same ref-qualifier (if any), then G overrides105 F.

Alternatively, such a situation could be regarded as an ill-formed attempt to override the base class function, which could be specified by adding the following as a new paragraph preceding 11.7.3 [class.virtual] paragraph 7:

The ref-qualifier, or lack thereof, of an overriding function shall be the same as that of the overridden function.

The return type of an overriding function shall be either identical to the return type of the overridden function or covariant...

Notes from the August, 2021 teleconference:

CWG preferred the second option.

Proposed resolution, August, 2021:

Add the following as a new paragraph preceding 11.7.3 [class.virtual] paragraph 7:

The ref-qualifier, or lack thereof, of an overriding function shall be the same as that of the overridden function.

The return type of an overriding function shall be either identical to the return type of the overridden function or covariant...




600. Does access control apply to members or to names?

Section: 11.8  [class.access]     Status: CD6     Submitter: Alisdair Meredith     Date: 3 October 2006

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Referring to a private member of a class, 11.8 [class.access] paragraph 1 says,

its name can be used only by members and friends of the class in which it is declared.

That wording does not appear to reflect the intent of access control, however. Consider the following:

    struct S {
        void f(int);
    private:
        void f(double);
    };

    void g(S* sp) {
        sp->f(2);        // Ill-formed?
    }

The statement from 11.8 [class.access] paragraph 1 says that the name f can be used only by members and friends of S. Function g is neither, and it clearly contains a use of the name f. That appears to make it ill-formed, in spite of the fact that overload resolution will select the public member.

A related question is whether the use of the term “name” in the description of the effect of access control means that it does not apply to constructors and destructors, which do not have names.

Mike Miller: The phrase “its name can be used” should be understood as “it can be referred to by name.” Paragraph 4, among other places, makes it clear that access control is applied after overload resolution. The “name” phrasing is there to indicate that access control does not apply where the name is not used (in a call via a pointer, for example).




360. Using-declaration that reduces access

Section: 11.8.3  [class.access.base]     Status: CD6     Submitter: Steve Clamage     Date: 4 June 2002

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

I have heard a claim that the following code is valid, but I don't see why.

  struct A {
    int foo ();
  };

  struct B: A {
  private:
    using A::foo;
  };

  int main ()
  {
    return B ().foo ();
  }

It seems to me that the using declaration in B should hide the public foo in A. Then the call to B::foo should fail because B::foo is not accessible in main.

Am I missing something?

Steve Adamczyk: This is similar to the last example in 11.8.3 [class.access.base]. In prose, the rule is that if you have access to cast to a base class and you have access to the member in the base class, you are given access in the derived class. In this case, A is a public base class of B and foo is public in A, so you can access foo through a B object. The actual permission for this is in the fourth bullet in 11.8.3 [class.access.base] paragraph 4.

The wording changes for issue 9 make this clearer, but I believe even without them this example could be discerned to be valid.

See my paper J16/96-0034, WG21/N0852 on this topic.

Steve Clamage: But a using-declaration is a declaration (9.9 [namespace.udecl]). Compare with

  struct B : A {
  private:
    int foo();
  };

In this case, the call would certainly be invalid, even though your argument about casting B to an A would make it OK. Your argument basically says that an access adjustment to make something less accessible has no effect. That also doesn't sound right.

Steve Adamczyk: I agree that is strange. I do think that's what 11.8.3 [class.access.base] says, but perhaps that's not what we want it to say.




952. Insufficient description of “naming class”

Section: 11.8.3  [class.access.base]     Status: CD6     Submitter: James Widman     Date: 7 August, 2009

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The access rules in 11.8.3 [class.access.base] do not appear to handle references in nested classes and outside of nonstatic member functions correctly. For example,

    struct A {
        typedef int I;    // public
    };
    struct B: private A { };
    struct C: B {
        void f() {
            I i1;         // error: access violation
        }
        I i2;             // OK
        struct D {
            I i3;         // OK
            void g() {
                I i4;     // OK
            }
        };
    };

The reason for this discrepancy is that the naming class in the reference to I is different in these cases. According to 11.8.3 [class.access.base] paragraph 5,

The access to a member is affected by the class in which the member is named. This naming class is the class in which the member name was looked up and found.

In the case of i1, the reference to I is subject to the transformation described in 11.4.3 [class.mfct.non.static] paragraph 3:

Similarly during name lookup, when an unqualified-id (7.5 [expr.prim]) used in the definition of a member function for class X resolves to a static member, an enumerator or a nested type of class X or of a base class of X, the unqualified-id is transformed into a qualified-id (7.5 [expr.prim]) in which the nested-name-specifier names the class of the member function.

As a result, the reference to I in the declaration of i1 is transformed to C::I, so that the naming class is C, and I is inacessible in C. In the remaining cases, however, the transformation does not apply. Thus, the naming class of I in these references is A, and I is publicly accessible in A.

Presumably either the definition of “naming class” must be changed or the transformation of unqualified-ids must be broadened to include all uses within the scope of a class and not just within nonstatic member functions (and following the declarator-id in the definition of a static member, per 11.4.9 [class.static] paragraph 4).




607. Lookup of mem-initializer-ids

Section: 11.9.3  [class.base.init]     Status: CD6     Submitter: Richard Corden     Date: 5 December 2006

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

In an example like,

    struct Y {};

    template <typename T>
    struct X : public virtual Y { };

    template <typename T>
    class A : public X<T> {
      template <typename S>
      A (S)
        : S ()
      {
      }
    };

    template A<int>::A (Y);

Should S be found? (S is a dependent name, so if it resolves to a base class type in the instantiated template, it should satisfy the requirements.) All the compilers I tried allowed this example, but 11.9.3 [class.base.init] paragraph 2 says,

Names in a mem-initializer-id are looked up in the scope of the constructor's class and, if not found in that scope, are looked up in the scope containing the constructor's definition.

The name S is not declared in those scopes.

Mike Miller: Here's another example that is accepted by most/all compilers but not by the current wording:

    namespace N {
      struct B { B(int); };
      typedef B typedef_B;
      struct D: B {
        D();
      };
    }

    N::D::D(): typedef_B(0) { }

Except for the fact that the constructor function parameter names are ignored (see paragraph 7), what the compilers seem to be doing is essentially ordinary unqualified name lookup.

Notes from the October, 2009 meeting:

The eventual resolution of this issue should take into account the template parameter scope introduced by the resolution of issue 481.




2586. Explicit object parameter for assignment and comparison

Section: 11.10.1  [class.compare.default]     Status: CD6     Submitter: Barry Revzin     Date: 2022-05-07     Liaison: EWG

[Accepted at the July, 2022 meeting.]

"Deducing this" allows to declare assignment and comparison operator functions as explicit object member functions.

However, such an assignment operator can never be a copy or move assignment operator, which means it always conflicts with the implicitly-defined one:

  struct C {
    C& operator=(this C&, C const&); // error: can't overload with the copy assignment operator
  };

Similarly, operator== or operator<=> can be declared with an explicit object parameter, but they cannot be defaulted:

  struct D {
    bool operator==(this D const&, D const&) = default; // error: not a kind of comparison that can be defaulted
  };

There seems to be no reason to disallow that, for people who prefer writing all of their members with explicit object parameters.

Suggested resolution:

  1. Change in 11.4.6 [class.copy.assign] paragraph 1 as follows:

    A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one non-object parameter of type X, X&, const X&, volatile X&, or const volatile X&.
  2. Change in 11.4.6 [class.copy.assign] paragraph 3 as follows:

    A user-declared move assignment operator X::operator= is a non-static non-template member function of class X with exactly one non-object parameter of type X&&, const X&&, volatile X&&, or const volatile X&&.
  3. Change in 11.10.1 [class.compare.default] paragraph 1 as follows:

    A defaulted comparison operator function (12.4.3 [over.binary]) for some class C shall be a non-template function that is
    • a non-static const non-volatile member of C having one parameter of type const C& and either no ref-qualifier or the ref-qualifier &, or or friend of C and
    • a friend of C having either has two parameters of type const C& or two parameters of type C , where the implicit object parameter (if any) is considered to be the first parameter..

Additional notes (May, 2022):

Forwarded to EWG with paper issue 1235, by decision of the CWG chair.

Approved by EWG telecon 2022-06-09 and EWG 2022-06 electronic poll.

See vote.

Additional notes (July, 2022):

The suggested resolution makes the following a copy assignment operator, suppressing the implicitly-declared one, which is surprising:

  struct B {
    B &operator =(this int, const B &); // copy assignment operator
  };

Proposed resolution (approved by CWG 2022-07-15):

  1. Change in 9.5.2 [dcl.fct.def.default] paragraph 2 as follows:

    The type T1 of an An explicitly defaulted special member function F F1 with type T1 is allowed to differ from the corresponding special member function F2 with type T2 it would have had if it were that would have been implicitly declared, as follows:
    • T1 and T2 may have differing ref-qualifiers;
    • if F2 has an implicit object parameter of type "reference to C", F1 may be an explicit object member function whose explicit object parameter is of type "reference to C";
    • T1 and T2 may have differing exception specifications; and
    • if T2 F2 has a non-object parameter of type const C&, the corresponding non-object parameter of T1 F1 may be of type C&.
    If T1 differs from T2 in any other a way other than as allowed by the preceding rules, then:
    • if F F1 is an assignment operator, and the return type of T1 differs from the return type of T2 or T1 F1's non-object parameter type is not a reference, the program is ill-formed;
    • otherwise, if F F1 is explicitly defaulted on its first declaration, it is defined as deleted;
    • otherwise, the program is ill-formed.
  2. Change in 11.4.6 [class.copy.assign] paragraph 1 as follows:

    A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one non-object parameter of type X, X&, const X&, volatile X&, or const volatile X&.
  3. Change in 11.4.6 [class.copy.assign] paragraph 3 as follows:

    A user-declared move assignment operator X::operator= is a non-static non-template member function of class X with exactly one non-object parameter of type X&&, const X&&, volatile X&&, or const volatile X&&.
  4. Change in 11.10.1 [class.compare.default] paragraph 1 as follows:

    A defaulted comparison operator function (12.4.3 [over.binary]) for some class C shall be a non-template function that is
    • a non-static const non-volatile member of C having one parameter of type const C& and either no ref-qualifier or the ref-qualifier &, or or friend of C and
    • a friend of C having either has two parameters of type const C& or two parameters of type C , where the implicit object parameter (if any) is considered to be the first parameter..



2007. Argument-dependent lookup for operator=

Section: 12.2.2.3  [over.match.oper]     Status: CD6     Submitter: Richard Smith     Date: 2014-09-23

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Consider an example like:

  template<typename T> struct A { typename T::error e; };
  template<typename T> struct B { };
  B<A<void>> b1, &b2 = (b1 = b1);

If the assignment operator performs argument-dependent lookup, A<void> will be an associated class and will be instantiated, producing an error. Similar questions apply to the other member-only overloaded operators, operator-> and operator[]. Bullet 3.2 of 12.2.2.3 [over.match.oper] should be changed not to perform unqualified lookup for these operators.




418. Imperfect wording on error on multiple default arguments on a called function

Section: 12.2.4  [over.match.best]     Status: CD6     Submitter: Chris Bowler     Date: 27 May 2003

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

According to 12.2.4 [over.match.best] paragraph 4, the following program appears to be ill-formed:

  void f(int, int=0);
  void f(int=0, int);

  void g() {
    f();
  }

Though I do not expect this is the intent of this paragraph in the standard.

12.2.4 [over.match.best] paragraph 4:

If the best viable function resolves to a function for which multiple declarations were found, and if at least two of these declarations or the declarations they refer to in the case of using-declarations specify a default argument that made the function viable, the program is ill-formed. [Example:
namespace A {
  extern "C" void f(int = 5);
}
namespace B {
  extern "C" void f(int = 5);
}
using A::f;
using B::f;
void use() {
f(3); //OK, default argument was not used for viability
f(); //Error: found default argument twice
}
end example]



2507. Default arguments for operator[]

Section: 12.4.1  [over.oper.general]     Status: CD6     Submitter: Jens Maurer     Date: 2021-12-07

[Accepted at the July, 2022 meeting.]

The intent of paper P2128R6, which permitted multiple parameters in overloaded subscript operators and was adopted at the October, 2021 plenary, was that overloaded operator[] should allow parameters with default arguments. However, the adopted wording did not address the following restriction from 12.4.1 [over.oper.general] paragraph 10:

An operator function cannot have default arguments (9.3.4.7 [dcl.fct.default]), except where explicitly stated below.

Similar wording to that of operator() should be added for operator[].

Proposed resolution (December, 2021):

Change 12.4.5 [over.sub] paragraph 1 as follows:

A subscripting operator function is a function named operator[] that is a non-static member function with an arbitrary number of parameters. It may have default arguments. For an expression...

Approved by EWG 2022-04-14.

Approved by CWG 2022-04-22.




110. Can template functions and classes be declared in the same scope?

Section: Clause 13  [temp]     Status: CD6     Submitter: John Spicer     Date: 28 Apr 1999

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

According to Clause 13 [temp] paragraph 5,

Except that a function template can be overloaded either by (non-template) functions with the same name or by other function templates with the same name (13.10.4 [temp.over] ), a template name declared in namespace scope or in class scope shall be unique in that scope.
_N4868_.6.4.10 [basic.scope.hiding] paragraph 2 agrees that only functions, not function templates, can hide a class name declared in the same scope:
A class name (11.3 [class.name] ) or enumeration name (9.7.1 [dcl.enum] ) can be hidden by the name of an object, function, or enumerator declared in the same scope.
However, 6.4 [basic.scope] paragraph 4 treats functions and template functions together in this regard:
Given a set of declarations in a single declarative region, each of which specifies the same unqualified name,

John Spicer: You should be able to take an existing program and replace an existing function with a function template without breaking unrelated parts of the program. In addition, all of the compilers I tried allow this usage (EDG, Sun, egcs, Watcom, Microsoft, Borland). I would recommend that function templates be handled exactly like functions for purposes of name hiding.

Martin O'Riordan: I don't see any justification for extending the purview of what is decidedly a hack, just for the sake of consistency. In fact, I think we should go further and in the interest of consistency, we should deprecate the hack, scheduling its eventual removal from the C++ language standard.

The hack is there to allow old C programs and especially the 'stat.h' file to compile with minimum effort (also several other Posix and X headers). People changing such older programs have ample opportunity to "do it right". Indeed, if you are adding templates to an existing program, you should probably be placing your templates in a 'namespace', so the issue disappears anyway. The lookup rules should be able to provide the behaviour you need without further hacking.




1478. template keyword for dependent template template arguments

Section: 13.3  [temp.names]     Status: CD6     Submitter: Johannes Schaub     Date: 2012-03-10

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

According to 13.3 [temp.names] paragraph 4,

When the name of a member template specialization appears after . or -> in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (13.8.3.2 [temp.dep.type]), the member template name must be prefixed by the keyword template.

In other words, the template keyword is only required when forming a template-id. However, current compilers reject an example like:

  template<typename T, template<typename> class U = T::X> struct A;

and require the template keyword before X. Should the rule be amended to require the template keyword in cases like this?




1729. Matching declarations and definitions of variable templates

Section: 13.7  [temp.decls]     Status: CD6     Submitter: Larisse Voufo     Date: 2013-08-05

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The relationship between declarations and definitions of variable templates is not clear. For example:

  template<typename T> auto var0 = T();  // #1a.
  template<typename T> extern T var0;    // #1b.

  template<typename T> T var1;           // #2a.
  template<typename T> extern auto var1; // #2b.
  template<typename T> extern T var1;    // #2c.
  template<typename T> T var1;           // #2d.

Questions:

  1. When is a variable template declaration a definition and when a non-defining declaration?

  2. What declarations are valid?

  3. Should auto declarations be allowed?

  4. To what extent, if any, do these involve type matching?

  5. How are types matched, especially in the presence of auto?

Proposed resolution (May, 2017):

This issue is resolved by the resolution of issue 1704.




2062. Class template redeclaration requirements

Section: 13.7.2  [temp.class]     Status: CD6     Submitter: Hubert Tong     Date: 2014-12-19

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

There does not appear to be a rule that two declarations of a class template must have compatible template parameter lists; e.g., it is not clear what makes the following ill-formed:

  template <typename> struct A;
  template <unsigned> struct A;



1711. Missing specification of variable template partial specializations

Section: 13.7.6  [temp.spec.partial]     Status: CD6     Submitter: Richard Smith     Date: 2013-07-08

[Accepted at the November, 2020 meeting as paper P2096R2.]

It appears that partial specializations of variable templates are intended to be supported, as 13.4.4 [temp.arg.template] paragraph 2 says,

Any partial specializations (13.7.6 [temp.spec.partial]) associated with the primary class template or primary variable template are considered when a specialization based on the template template-parameter is instantiated.

However, there is no explicit specification for how they are to be handled, and the wording in 13.7.6 [temp.spec.partial] and its subsections explicitly applies only to partial specializations of class templates.

Additional note, July, 2017:

The term “primary template” appears not to be defined in the current wording; the resolution of this issue might be a good opportunity to add such a definition.




1896. Repeated alias templates

Section: 13.7.8  [temp.alias]     Status: CD6     Submitter: Mike Miller     Date: 2014-03-18

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The current wording of the Standard does not permit repeated alias template declarations within a scope, but some current implementations allow it, presumably by analogy with typedef declarations. Should the Standard be changed to permit this usage?

Notes from the November, 2014 meeting:

CWG agreed that the usage should be permitted, provided that the dependent types are equivalent. Note that this is a weaker requirement than the token-for-token identity of the ODR, since alias templates are not definitions per Clause 13 [temp] paragraph 1.




2413. typename in conversion-function-ids

Section: 13.8  [temp.res]     Status: CD6     Submitter: Davis Herring     Date: 2019-05-10

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The “Down with typename!” paper, P0634R3, overlooked the case of a conversion-type-id in a conversion-function-id:

  template<class T> struct S {
    operator typename T::X(); // typename is not helpful here.
  };

This context should be added to the list of contexts in which a qualified-id is assumed to name a type.




2461. Diagnosing non-bool type constraints

Section: 13.8  [temp.res]     Status: CD6     Submitter: Hubert Tong     Date: 2020-04-20

[Accepted at the November, 2020 meeting.]

Given the following example,

  template <typename T> struct A {};
  template <typename T> void f() requires (sizeof(A<T>)) {}

the current wording does not appear to allow diagnosis of the program as ill-formed. In particular, 13.8 [temp.res] bullet 8.2 says,

The program is ill-formed, no diagnostic required, if:

However, substitution into the requires-clause in this case would result in a valid expression, but not one that is an atomic constraint that can be checked for satisfaction.

Proposed resolution (April, 2020):

Change bullet 8.2 of 13.8 [temp.res] as follows:

The program is ill-formed, no diagnostic required, if:




1841. < following template injected-class-name

Section: 13.8.2  [temp.local]     Status: CD6     Submitter: Ismail Pazarbasi     Date: 2014-01-23

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

According to 13.8.2 [temp.local]paragraph 1,

Like normal (non-template) classes, class templates have an injected-class-name (Clause 11 [class]). The injected-class-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier of a friend class template declaration, it refers to the class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.

The intent is that a < following such an injected-class-name is to be interpreted as the start of a template-argument-list (and an error if the following tokens do not constitute a valid template-argument-list), but that is not said explicitly.




1936. Dependent qualified-ids

Section: 13.8.3  [temp.dep]     Status: CD6     Submitter: Richard Smith     Date: 2014-06-05

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The resolution of issue 1321 changed the term “dependent name” to apply only to unqualified-ids, presumably on the basis that only unqualified-ids affect the lookup set. However, the rule from 13.7.7.2 [temp.over.link] paragraph 5,

For determining whether two dependent names (13.8.3 [temp.dep]) are equivalent, only the name itself is considered, not the result of name lookup in the context of the template. If multiple declarations of the same function template differ in the result of this name lookup, the result for the first declaration is used.

should apply to non-dependent qualified-ids naming functions called with dependent arguments, as well.

There should also be a statement that the name of a member of an unknown specialization is a dependent name and so should fall under the rules of 13.8.4 [temp.dep.res] and not _N4868_.13.8.4 [temp.nondep].




1829. Dependent unnamed types

Section: 13.8.3.2  [temp.dep.type]     Status: CD6     Submitter: Hubert Tong     Date: 2014-01-08

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The specification of dependent types in 13.8.3.2 [temp.dep.type] is given in terms of names. However, one might consider some unnamed types as dependent. Consider the following example:

  template <typename T> struct A {
    struct { } obj;
    void foo() {
      bar(obj); // lookup for bar when/where?
    }
  };

  void bar(...);

  int main() {
    A<int> a;
    a.foo();    // calls bar(...)?
  }

If the type of A::obj had a name, it would be dependent. However, the rationale for making nested types dependent is that they are subject to explicit specialization and thus not knowable at the point of the template definition. An unnamed type, as in this example, cannot be explicitly specialized and thus could be considered as a member of the current instantiation. Which treatment is intended?

Notes from the February, 2014 meeting:

There are other cases in which a named entity is dependnet, even though it cannot be explicitly specialized. CWG felt that the most consistent rule would be to make all nested classes dependent, whether named or not.




2065. Current instantiation of a partial specialization

Section: 13.8.3.2  [temp.dep.type]     Status: CD6     Submitter: Richard Smith     Date: 2014-12-29

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

According to 13.8.3.2 [temp.dep.type] paragraph 1, a name refers to the current instantiation if it is

in the definition of a partial specialization or a member of a partial specialization, the name of the class template followed by the template argument list of the partial specialization enclosed in <> (or an equivalent template alias specialization).

I don't think this works. How are the argument lists compared? If it's using the “equivalent” rules, this doesn't work because we make no provision for “functionally equivalent but not equivalent” here. If it's using 13.6 [temp.type] paragraph 1, that fails because it doesn't handle dependent template arguments at all.

The same issue would come up when defining members of a partial specialization out-of-line.




2457. Unexpanded parameter packs don't make a function type dependent

Section: 13.8.3.2  [temp.dep.type]     Status: CD6     Submitter: Richard Smith     Date: 2020-07-28

[Accepted at the November, 2020 meeting.]

Consider the following example:

  template<typename ...T> auto f() {
    using F = int(*)(int (...p)[sizeof(sizeof(T))]);
    // ...
  }

F is not covered in the list of cases in 13.8.3.2 [temp.dep.type] paragraph 9, because the types from which the function type is constructed are not dependent types. (The parameter pack p is of type int[sizeof(size_t)].) Similar situations arise with non-injective alias templates.

Proposed resolution (August, 2020):

Change 13.8.3.2 [temp.dep.type] paragraph 9 as follows:

A type is dependent if it is

(We do have the relevant wording for pack expansions in simple-template-ids in bullet 9.8, so that similar case is already handled.)




2405. Additional type-dependent expressions

Section: 13.8.3.3  [temp.dep.expr]     Status: CD6     Submitter: Andrey Davydov     Date: 2018-08-20

[Accepted at the July, 2022 meeting.]

According to 13.8.3.3 [temp.dep.expr] paragraph 3,

...Expressions of the following forms are type-dependent only if the type specified by the type-id, simple-type-specifier or new-type-id is dependent, even if any subexpression is type-dependent:

This list is missing cases for:

Proposed resolution (approved by CWG 2022-06-17):

Change in 13.8.3.3 [temp.dep.expr] paragraph 3 as follows:

Expressions of the following forms are type-dependent only if the type specified by the type-id, simple-type-specifier, typename-specifier, or new-type-id is dependent, even if any subexpression is type-dependent:
simple-type-specifier ( expression-listopt )
simple-type-specifier braced-init-list
typename-specifier ( expression-listopt )
typename-specifier braced-init-list
...



1028. Dependent names in non-defining declarations

Section: 13.8.4  [temp.dep.res]     Status: CD6     Submitter: Sean Hunt     Date: 2010-02-03

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

The current wording of 13.8.4 [temp.dep.res] seems to assume that dependent names can only appear in the definition of a template:

In resolving dependent names, names from the following sources are considered:

However, dependent names can occur in non-defining declarations of the template as well; for instance,

    template<typename T>
    T foo(T, decltype(bar(T())));

bar needs to be looked up, even though there is no definition of foo in the translation unit.

Additional note (February, 2011):

The resolution of this issue can't simply replace the word “definition” with the word “declaration,” mutatis mutandis, because there can be multiple declarations in a translation unit (which isn't true of “the definition”). As a result, the issue was moved back to "open" status for further consideration.




1500. Name lookup of dependent conversion function

Section: 13.8.4.2  [temp.dep.candidate]     Status: CD6     Submitter: Johannes Schaub     Date: 2012-04-27

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Consider the following example:

  template<typename T>
  struct A {
   operator int() { return 0; }

   void f() {
    operator T();
   }
  };

  int main() {
   A<int> a;
   a.f();
  }

One might expect this to call operator int when instantiating. But since operator T is a dependent name, it is looked up by unqualified lookup only in the definition context, where it will find no declaration. Argument-dependent lookup will not find anything in the instantiation context either, so this code is ill-formed. If we change operator int() to operator T(), which is a seemingly unrelated change, the code becomes well-formed.

There is implementation variability on this point.




2608. Omitting an empty template argument list

Section: 13.10.2  [temp.arg.explicit]     Status: CD6     Submitter: Anoop Rana     Date: 2022-07-03

[Accepted at the July, 2022 meeting.]

Subclause 13.10.2 [temp.arg.explicit] paragraph 4 specifies:

Trailing template arguments that can be deduced (13.10.3 [temp.deduct]) or obtained from default template-arguments may be omitted from the list of explicit template-arguments.
[Note 1: A trailing template parameter pack (13.7.4 [temp.variadic]) not otherwise deduced will be deduced as an empty sequence of template arguments. —end note]
If all of the template arguments can be deduced, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted.

The wording does not allow omitting the empty template argument list <> if all of the template arguments have been obtained from default template-arguments. For example:

  template<typename T = int>
  int f();

  int x = f();      // ill-formed per the wording
  int (*y)() = f;   // ditto

Proposed resolution (approved by CWG 2022-07-15):

Change in 13.10.2 [temp.arg.explicit] paragraph 4 as follows:

... If all of the template arguments can be deduced or obtained from default template-arguments, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted.



271. Explicit instantiation and template argument deduction

Section: 13.10.3  [temp.deduct]     Status: CD6     Submitter: John Spicer     Date: 20 Feb 2001

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

Nicolai Josuttis sent me an example like the following:

    template <typename RET, typename T1, typename T2>
    const RET& min (const T1& a, const T2& b)
    {
	return (a < b ? a : b);
    }
    template const int& min<int>(const int&,const int&);  // #1
    template const int& min(const int&,const int&);       // #2

Among the questions was whether explicit instantiation #2 is valid, where deduction is required to determine the type of RET.

The first thing I realized when researching this is that the standard does not really spell out the rules for deduction in declarative contexts (friend declarations, explicit specializations, and explicit instantiations). For explicit instantiations, 13.9.3 [temp.explicit] paragraph 2 does mention deduction, but it doesn't say which set of deduction rules from 13.10.3 [temp.deduct] should be applied.

Second, Nicolai pointed out that 13.9.3 [temp.explicit] paragraph 6 says

A trailing template-argument can be left unspecified in an explicit instantiation provided it can be deduced from the type of a function parameter (13.10.3 [temp.deduct]).

This prohibits cases like #2, but I believe this was not considered in the wording as there is no reason not to include the return type in the deduction process.

I think there may have been some confusion because the return type is excluded when doing deduction on a function call. But there are contexts where the return type is included in deduction, for example, when taking the address of a function template specialization.

Suggested resolution:

  1. Update 13.10.3 [temp.deduct] to include a section "Deducing template arguments from a declaration" that describes how deduction is done when finding a template that matches a declaration. This should, I believe, include the return type.
  2. Update 13.9.3 [temp.explicit] to make reference to the new rules in 13.10.3 [temp.deduct] and remove the description of the deduction details from 13.9.3 [temp.explicit] paragraph 6.



1724. Unclear rules for deduction failure

Section: 13.10.3  [temp.deduct]     Status: CD6     Submitter: James Widman     Date: 2013-07-31

[Accepted as a DR at the October, 2021 meeting.]

According to 13.10.3 [temp.deduct] paragraph 8,

If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments.

Presumably the phrase “if written” refers to rewriting the template declaration in situ with the substituted arguments, rather than writing that type or expression at some arbitrary location, e.g.,

  void g(double) = delete;

  template<class T> auto f(T t) -> decltype(g(t));

  void g(int);

  void h() {
    typedef int T;
    T t = 42;
    g(t);  // Ok (I “wrote the substituted arguments”, and it seems fine)
    f(42); // Presumably substitution is meant to fail.
  }

Perhaps a clearer formulation could be used?

Proposed resolution (August, 2021):

Change 13.10.3.1 [temp.deduct.general] paragraph 8 as follows:

If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written in the same context using the substituted arguments.



2369. Ordering between constraints and substitution

Section: 13.10.3  [temp.deduct]     Status: CD6     Submitter: Agustin Bergé     Date: 2017-10-09

[Accepted at the November, 2020 meeting.]

The specification of template argument deduction in 13.10.3 [temp.deduct] paragraph 5 specifies the order of processing as:

  1. substitute explicitly-specified template arguments throughout the template parameter list and type;

  2. deduce template arguments from the resulting function signature;

  3. check that non-dependent parameters can be initialized from their arguments;

  4. substitute deduced template arguments into the template parameter list and particularly into any needed default arguments to form a complete template argument list;;

  5. substitute resulting template arguments throughout the type;

  6. check that the associated constraints are satisfied;

  7. check that remaining parameters can be initialized from their arguments.

This ordering yields unexpected differences between concept and SFINAE implementations. For example:

   template <typename T>
   struct static_assert_integral {
     static_assert(std::is_integral_v<T>);
     using type = T;
   };

   struct fun {
     template <typename T,
       typename Requires = std::enable_if_t<std::is_integral_v<T>>>
       typename static_assert_integral<T>::type
     operator()(T) {}
   };

Here the substitution ordering guarantees are leveraged to prevent static_assert_integral<T> from being instantiated when the constraints are not satisfied. As a result, the following assertion holds:

   static_assert(!std::is_invocable_v<fun, float>);

A version of this code written using constraints unexpectedly behaves differently:

   struct fun {
     template <typename T>
       requires std::is_integral_v<T>
     typename static_assert_integral<T>::type
     operator()(T) {}
   };

or

   struct fun {
     template <typename T>
     typename static_assert_integral<T>::type
     operator()(T) requires std::is_integral_v<T> {}
   };

   static_assert(!std::is_invocable_v<fun, float>); // error: static assertion failed: std::is_integral_v<T>

Perhaps steps 5 and 6 should be interchanged.

Proposed resolution (August, 2020):

  1. Delete paragraph 10 of 13.10.3.2 [temp.deduct.call]:

  2. If deduction succeeds for all parameters that contain template-parameters that participate in template argument deduction, and all template arguments are explicitly specified, deduced, or obtained from default template arguments, remaining parameters are then compared with the corresponding arguments. For each remaining parameter P with a type that was non-dependent before substitution of any explicitly-specified template arguments, if the corresponding argument A cannot be implicitly converted to P, deduction fails. [Note 2: Parameters with dependent types in which no template-parameters participate in template argument deduction, and parameters that became non-dependent due to substitution of explicitly-specified template arguments, will be checked during overload resolution. —end note]

    [Example 9:

      template <class T> struct Z {
        typedef typename T::x xx;
      };
      template <class T> typename Z<T>::xx f(void *, T); // #1
      template <class T> void f(int, T);                 // #2
      struct A {} a;
      int main() {
        f(1, a);   // OK, deduction fails for #1 because there is no conversion from int to void*
      }
    

    end example]

  3. Change 13.10.3.1 [temp.deduct.general] paragraph 5 as follows:

  4. ...When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in the template parameter list of the template and the function type are replaced with the corresponding deduced or default argument values. If the substitution results in an invalid type, as described above, type deduction fails. If the function template has associated constraints (13.5.3 [temp.constr.decl]), those constraints are checked for satisfaction (13.5.2 [temp.constr.constr]). If the constraints are not satisfied, type deduction fails. In the context of a function call, if type deduction has not yet failed, then for those function parameters for which the function call has arguments, each function parameter with a type that was non-dependent before substitution of any explicitly-specified template arguments is checked against its corresponding argument; if the corresponding argument cannot be implicitly converted to the parameter type, type deduction fails. [Note: Overload resolution will check the other parameters, including parameters with dependent types in which no template parameters participate in template argument deduction and parameters that became non-dependent due to substitution of explicitly-specified template arguments. —end note] If type deduction has not yet failed, then all uses of template parameters in the function type are replaced with the corresponding deduced or default argument values. If the substitution results in an invalid type, as described above, type deduction fails. [Example:

      template <class T> struct Z {
        typedef typename T::x xx;
      };
      template <class T> concept C = requires { typename T::A; };
      template <C T> typename Z<T>::xx f(void *, T); // #1
      template <class T> void f(int, T);             // #2
      struct A {} a;
      struct ZZ {
        template <class T, class = typename Z<T>::xx> operator T *();
        operator int();
      };
      int main() {
        ZZ zz;
        f(1, a);   // OK, deduction fails for #1 because there is no conversion from int to void*
        f(zz, 42); // OK, deduction fails for #1 because C<int> is not satisfied
      }
    

    end example]




2355. Deducing noexcept-specifiers

Section: 13.10.3.6  [temp.deduct.type]     Status: CD6     Submitter: John Spicer     Date: 2017-09-06

[Accepted at the July, 2022 meeting.]

The list of deducible forms in 13.10.3.6 [temp.deduct.type] paragraph 8 does not include the ability to deduce the value of the constant in a noexcept-specifier, although implementations appear to allow it.

Notes from the April, 2018 teleconference:

Although this appears to be an obvious omission, CWG felt that EWG should weigh in on whether this capability should be supported or not.

EWG guidance (January, 2021):

Modify the Standard such that the value of a constant in a noexcept-specifier can be deduced. See vote.

Proposed resolution (June, 2022):

  1. Change 13.10.3.6 [temp.deduct.type] paragraph 3 as follows:

  2. A given type P can be composed from a number of other types, templates, and non-type values:

  3. Add the following to Example 3 in 13.10.3.6 [temp.deduct.type] paragraph 7:

  4. Here is an example where two template arguments are deduced from a single function parameter/argument pair...

    Here is an example where the exception specification of a function type is deduced:

      template <bool E> void f1(void (*)() noexcept(E));
      template<bool> struct A { };
      template<bool B> void f2(void (*)(A<B>) noexcept(B));
    
      void g1();
      void g2() noexcept;
      void g3(A<true>);
    
      void h() {
        f1(g1);    // OK: E is false
        f1(g2);    // OK: E is true
        f2(g3);    // error: B deduced as both true and false
      }
    

    Here is an example where a qualification conversion applies...

  5. Change 13.10.3.6 [temp.deduct.type] paragraph 8 as follows:

  6. A template type argument T, a template template argument TT, or a template non-type argument i can be deduced if P and A have one of the following forms:

    where (T) represents a parameter-type-list (9.3.4.6 [dcl.fct]) where at least one parameter type contains a T, and () represents a parameter-type-list where no parameter type contains a T.

    [Note: If a type matches such a form but contains no Ts, is, or TTs, deduction is not possible. —end note]

    Similarly, <T> represents template argument lists where at least one argument contains a T, <i> represents template argument lists where at least one argument contains an i and <> represents template argument lists where no argument contains a T or an i.

  7. Add the following as a new paragraph following 13.10.3.6 [temp.deduct.type] paragraph 14:

  8. The type of N in the type T[N] is std::size_t. [Example 9: ... —end example]

    The type of B in the noexcept-specifier noexcept(B) of a function type is bool.
    [Example:

      template<bool> struct A { };
      template<auto> struct B;
      template<auto X, void (*F)() noexcept(X)> struct B<F> {
        A<X> ax;
      };
      void f_nothrow() noexcept;
      B<f_nothrow> bn;   // OK: type of X deduced as bool
    

    end example]

  9. Change 13.10.3.6 [temp.deduct.type] paragraph 19 as follows:

  10. If P has a form that contains <i>, and if the type of i differs from the type of the corresponding template parameter of the template named by the enclosing simple-template-id, deduction fails. If P has a form that contains [i], and if the type of i is not an integral type, deduction fails.131 If P has a form that includes noexcept(i) and the type of i is not bool, deduction fails.
    [Example 12: ...
  11. Add the following as a new section preceding C.2.7 [diff.cpp20.library]:

  12. C.1.4 Clause 13: templates [diff.cpp20.temp]

    Affected subclause: 13.10.3.6 [temp.deduct.type]
    Change: Deducing template arguments from exception specifications.
    Rationale: Facilitate generic handling of throwing and non-throwing functions.
    Effect on original feature: Valid ISO C++20 code may be ill-formed in this revision of C++.

    [Example 1:

       template<bool> struct A { };
       template<bool B> void f(void (*)(A<B>) noexcept(B));
       void g(A<false>) noexcept;
       void h() {
         f(g);    // ill-formed; previously well-formed.
       }
    

    end example]




1335. Stringizing, extended characters, and universal-character-names

Section: 15.6.3  [cpp.stringize]     Status: CD6     Submitter: Johannes Schaub     Date: 2011-07-03     Liaison: WG14

[Resolved at the October, 2021 meeting by paper P2314R4.]

When a string literal containing an extended character is stringized (15.6.3 [cpp.stringize]), the result contains a universal-character-name instead of the original extended character. The reason is that the extended character is translated to a universal-character-name in translation phase 1 (5.2 [lex.phases]), so that the string literal "@" (where @ represents an extended character) becomes "\uXXXX". Because the preprocessing token is a string literal, when the stringizing occurs in translation phase 4, the \ is doubled, and the resulting string literal is "\"\\uXXXX\"". As a result, the universal-character-name is not recognized as such when the translation to the execution character set occurs in translation phase 5. (Note that phase 5 translation does occur if the stringized extended character does not appear in a string literal.) Existing practice appears to ignore these rules and preserve extended characters in stringized string literals, however.

See also issue 578.

Additional note (August, 2013):

Implementations are granted substantial latitude in their handling of extended characters and universal-character-names in 5.2 [lex.phases] paragraph 1 phase 1, i.e.,

(An implementation may use any internal encoding, so long as an actual extended character encountered in the source file, and the same extended character expressed in the source file as a universal-character-name (i.e., using the \uXXXX notation), are handled equivalently except where this replacement is reverted in a raw string literal.)

However, this freedom is mostly nullified by the requirements of stringizing in 15.6.3 [cpp.stringize] paragraph 2:

If, in the replacement list, a parameter is immediately preceded by a # preprocessing token, both are replaced by a single character string literal preprocessing token that contains the spelling of the preprocessing token sequence for the corresponding argument.

This means that, in order to handle a construct like

  #define STRINGIZE_LITERAL( X ) # X
  #define STRINGIZE( X ) STRINGIZE_LITERAL( X )

  STRINGIZE( STRINGIZE( identifier_\u00fC\U000000Fc ) )

an implementation must recall the original spelling, including the form of UCN and the capitalization of any non-numeric hexadecimal digits, rather than simply translating the characters into a convenient internal representation.

To effect the freedom asserted in 5.2 [lex.phases], the description of stringizing should make the spelling of a universal-character-name implementation-defined.

Additional note (February, 2022):

P2314R4 Character sets and encodings (approved in October, 2021) effected changes so that extended characters are no longer translated to UCNs in phase 1.




2464. Constexpr launder and unions

Section: 17.6.5  [ptr.launder]     Status: CD6     Submitter: Hubert Tong     Date: 2020-11-07

According to 17.6.5 [ptr.launder], referring to std::launder,

An invocation of this function may be used in a core constant expression whenever the value of its argument may be used in a core constant expression.

It is not clear whether this wording is intended to permit an example like

  #include <new>

  struct A { char x; };

  union U {
    A a;
    A a2;
  };

  constexpr A foo() {
    U u = {{42}};
    A *ap = &u.a2;
    return *std::launder(ap);
  }

  extern constexpr A globA = foo();

In particular, is the wording intended to restrict use of std::launder in a constant expression to cases in which the function returns its argument unchanged? As a further example, consider

  #include <new>

  struct A { char x; };
  struct B { A a; };
  struct BytesAndMore {
    unsigned char bytes[sizeof(A)];
    unsigned char more;
  };

  union U {
    BytesAndMore bytes;
    A a;
    B b;
  };

  constexpr B foo() {
    U u;
    A *ap = &u.a;
    B *bp = &u.b;
    u.bytes.more = 0;
    std::launder(ap)->x = 42;
    return *std::launder(bp);
  }

  extern constexpr B globB = foo();

Notes from the December, 2020 teleconference:

See also LWG issue 3495.




2482. bit_cast and indeterminate values

Section: 22.15.3  [bit.cast]     Status: CD6     Submitter: Richard Smith     Date: 2019-06-20

[Resolved by paper P1272R4, adopted at the October, 2021 plenary.]

As currently specified, bit_cast from an indeterminate value produces an unspecified value rather than an indeterminate value. That means this can't be implemented by a simple load on some implementations, and instead will require some kind of removing-the-taint-of-an-uninitialized-value operation to be performed. (A similar concern applies to reading from padding bits.)

The intent is as follows:

Some examples:

  struct A { char c; /* char padding : 8; */ short s; };
  struct B { char x[4]; };

  B one() {
    A a = {1, 2};
    return std::bit_cast<B>(a);
  }

In one(), the second byte of the object representation of a is bad. That means that the second byte of the produced B object is bad, so x[1] in the produced B object is an indeterminate value. The above function, if declared constexpr, would be usable in constant expressions so long as you don't look at one().x[1].

  A two() {
    B b;
    b.x[0] = 'a';
    b.x[2] = 1;
    b.x[3] = 2;
    return std::bit_cast<A>(b);
  }

In two() , the second byte of the object representation of b is bad. But a bit_cast to A doesn't care because it never looks at that byte. The above function returns an A with a fully-defined value. If declared constexpr, it would produce a normal, fully-initialized value.

  int three() {
    int n;
    return std::bit_cast<int>(n);
  }

In three(), the entirety of n is bad. A bit_cast from it produces an int whose value is indeterminate. And because we have an expression of non-byte-like type that produced an indeterminate value, the behavior is undefined.

  B four() {
    int n;
    return std::bit_cast<B>(n);
  }

In four(), just like three(), the entirety of n is bad, so the scalar subobjects of B are bad too. But because they're of byte-like type, that's OK: we can copy them about and produce them from prvalue expressions.

Proposed resolution (May, 2021):

Change 22.15.3 [bit.cast] paragraph 2 as follows:

Returns: An object of type To. Implicitly creates objects nested within the result (6.7.2 [intro.object]). Each bit of the value representation of the result is equal to the corresponding bit in the object representation of from. Padding bits of the result are unspecified. For the result and each object created within it, if there is no value of the object's type corresponding to the value representation produced, the behavior is undefined. If there are multiple such values, which value is produced is unspecified. A bit in the value representation of the result is indeterminate if it does not correspond to a bit in the value representation of from or corresponds to a bit of an object that is not within its lifetime or has an indeterminate value (6.7.4 [basic.indet]). For each bit in the value representation of the result that is indeterminate, the smallest object containing that bit has an indeterminate value; the behavior is undefined unless that object is of unsigned ordinary character type or std::byte type. The result does not otherwise contain any indeterminate values.





Issues with "TC1" Status


131. Typo in Lao characters

Section: _N2691_.E  [extendid]     Status: TC1     Submitter: John Spicer     Date: 23 June 1999

The Lao character 0e0d should be 0e8d. 0e0d is both out of order and already used in the Thai characters.

Proposed resolution (10/99): As suggested.




123. Bad cross-reference

Section: _N4567_.5.1.1  [expr.prim.general]     Status: TC1     Submitter: Mike Miller     Date: 3 June 1999
The cross-reference is incorrect in the first sentence after the grammar in _N4567_.5.1.1 [expr.prim.general] paragraph 7:
A nested-name-specifier that names a class, optionally followed by the keyword template (13.10.2 [temp.arg.explicit] ), ...
The use of the template keyword in this context is discussed in 13.3 [temp.names] , not 13.10.2 [temp.arg.explicit] .


147. Naming the constructor

Section: _N4567_.5.1.1  [expr.prim.general]     Status: TC1     Submitter: John Spicer     Date: 21 Feb 1999

From paper J16/99-0010 = WG21 N1187.

_N4567_.5.1.1 [expr.prim.general] paragraph 7 says that class-name::class-name names the constructor when both class-name refer to the same class. (Note the different perspective, at least, in 11.4.5 [class.ctor] paragraph 1, in which constructors have no names and are recognized by syntactic context rather than by name.)

This formulation does not address the case of classes in which a function template is declared as a constructor, for example:

    template <class T> struct A {
        template <class T2> A(T2);
    };
    template<> template<> A<int>::A<int>(int);

Here there is an ambiguity as to whether the second template argument list is for the injected class name or for the constructor.

Suggested resolution: restate the rule as a component of name lookup. Specifically, if when doing a qualified lookup in a given class you look up a name that is the same as the name of the class, the entity found is the constructor and not the injected class name. In all other cases, the name found is the injected class name. For example:

    class B { };
    class A: public B {
        A::B ab;       // B is the inherited injected B
        A::A aa;       // Error: A::A is the constructor
    };

Without this rule some very nasty backtracking is needed. For example, if the injected class name could be qualified by its own class name, the following code would be well-formed:

    template <class T> struct A {
        template <class T2> A(T2);
        static A x;
    };
    template<> A<int>::A<int>(A<int>::x);

Here the declarator for the definition of the static data member has redundant parentheses, and it's only after seeing the declarator that the parser can know that the second A<int> is the injected class name rather than the constructor.

Proposed resolution (10/00):

In Clause 11 [class] paragraph 2, change

The class-name is also inserted into the scope of the class itself. For purposes of access checking the inserted class name...

to

The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name...

Also, in 6.5.5.2 [class.qual], add the following before paragraph 2:

If the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C ( Clause 11 [class]), the name is instead considered to name the constructor of class C. Such a constructor name shall only be used in the declarator-id of a constructor definition that appears outside of the class definition. [Example:
    struct A { A(); };
    struct B: public A { B(); };

    A::A() { }
    B::B() { }

    B::A ba;    // object of type A
    A::A a;     // error, A::A is not a type name
end example]

Also, change 6.5 [basic.lookup] paragraph 3 from

Because the name of a class is inserted in its class scope ( Clause 11 [class]), the name of a class is also considered a member of that class for the purposes of name hiding and lookup.

to

The injected-class-name of a class (Clause 11 [class]) is also considered to be a member of that class for the purposes of name hiding and lookup.

(See also issue 194.)




166. Friend declarations of template-ids

Section: _N4868_.9.8.2.3  [namespace.memdef]     Status: TC1     Submitter: John Spicer     Date: 8 Sep 1999

John Spicer: I believe the standard is not clear with respect to this example:

    namespace N {
      template <class T> void f(T);
      namespace M {
        struct A {
          friend void f<int>(int);  // okay - refers to N::f
        };
      }
    }
At issue is whether the friend declaration refers to N::f, or whether it is invalid.

A note in 6.4.2 [basic.scope.pdecl] paragraph 6 says

friend declarations refer to functions or classes that are members of the nearest enclosing namespace ...
I believe it is intended to mean unqualified friend declarations. Certainly friend void A::B() need not refer to a member of the nearest enclosing namespace. Only when the declarator is unqualified (i.e., it is a declaration and not a reference) does this rule need to apply. The presence of an explicit template argument list requires that a previous declaration be visible and renders this a reference and not a declaration that is subject to this rule.

Mike Miller: _N4868_.9.8.2.3 [namespace.memdef] paragraph 3 says,

When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered.
On the other hand, the friend declaration would be a syntax error if f weren't declared as a template name; it would seem very strange not to find the declaration that made the friend declaration syntactically correct. However, it also seems strange to treat this case differently from ordinary functions and from templates:
    namespace N {
      template <class T> void f(T);
      void g();
      namespace M {
        struct A {
          friend void f<int>(int);               // N::f
          template <class T> friend void f(T);   // M::f
          friend void g();                       // M::g
        };
      }
    }

John Spicer: This section refers to "looking for a prior declaration". This gets back to an earlier discussion we've had about the difference between matching two declarations of the same name and doing name lookup. I would maintain that in f<int> the f is looked up using a normal lookup. In practice, this is really how it has to be done because the declaration could actually be f<int>::x.

Proposed resolution (10/00):

In _N4868_.9.8.2.3 [namespace.memdef] paragraph 3, change

When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered.
to
When looking for a prior declaration of a class or a function declared as a friend, and when the name of the friend class or function is neither a qualified name nor a template-id, scopes outside the innermost enclosing namespace scope are not considered.
Also, change the example in that paragraph as follows:
    void h(int);
    template <class T> void f2(T);
    namespace A {
        class X {
            friend void f(X);       // A::f(X) is a friend
            friend void f2<>(int);  // ::f2<>(int) is a friend
    ...

(See also issues 95, 136, 138, 139, 143, and 165.)




206. Semantic constraints on non-dependent names

Section: _N4868_.13.8.4  [temp.nondep]     Status: TC1     Submitter: Mike Miller     Date: 23 Feb 2000

At what point are semantic constraints applied to uses of non-dependent names in template definitions? According to _N4868_.13.8.4 [temp.nondep] , such names are looked up and bound at the point at which they are used, i.e., the point of definition and not the point of instantiation. However, the text does not mention the checking of semantic constraints.

Contrast this omission with the treatment of names in default argument expressions given in 9.3.4.7 [dcl.fct.default] paragraph 5, where the treatment of semantic constraints is explicit:

The names in the expression are bound, and the semantic constraints are checked, at the point where the default argument expression appears.
The following code is an example of where this distinction matters:
    struct S;

    template <class T> struct Q {
        S s;    // incomplete type if semantic constraints
                // are applied in the definition context
    };

    struct S { };

    // Point of instantiation of Q<int>; S is complete here

    Q<int> si;
There is real-world code that depends on late checking of semantic constraints. The Standard should be explicit about whether this code is broken or not.

Proposed resolution (10/00):

In 13.8 [temp.res] paragraph 7, add the following immediately preceding the note:

If a type used in a non-dependent name is incomplete at the point at which a template is defined but is complete at the point at which an instantiation is done, and if the completeness of that type affects whether or not the program is well-formed or affects the semantics of the program, the program is ill-formed; no diagnostic is required.



173. Constraints on execution character set

Section: 5.3  [lex.charset]     Status: TC1     Submitter: Markus Mauhart     Date: 27 Sep 1999

30.4.2.2.3 [locale.ctype.virtuals] paragraph 13 states a constraint on the values of the characters representing the decimal digits in the execution character set:

for any digit character c, the expression (do_narrow( c, dfault)-'0') evaluates to the digit value of the character.
This requirement is not reflected in the description of the execution character set (5.3 [lex.charset] paragraph 3) .

Proposed resolution (10/00):

In 5.3 [lex.charset] paragraph 3, after the sentence

For each basic execution character set, the values of the members shall be non-negative and distinct from one another.
insert the following:
In both the source and execution basic character sets, the value of each character after 0 in the above list of decimal digits shall be one greater than the value of the previous.



41. Clarification of lookup of names after declarator-id

Section: 6.5.3  [basic.lookup.unqual]     Status: TC1     Submitter: Mike Miller     Date: 1 Sep 1998

Footnotes 26 and 29 both use the phrase "following the function declarator" incorrectly: the function declarator includes the parameter list, but the footnotes make clear that they intend what's said to apply to names inside the parameter list. Presumably the phrase should be "following the function declarator-id."

Proposed Resolution (04/99): Change the text in 6.5.3 [basic.lookup.unqual] paragraph 6 from:

A name used in the definition of a function [footnote: This refers to unqualified names following the function declarator; such a name may be used as a type or as a default argument name in the parameter-declaration-clause, or may be used in the function body. end footnote] that is ...
to:
A name used in the definition of a function following the function's declarator-id [footnote: This refers to unqualified names that occur, for instance, in a type or default argument expression in the parameter-declaration-clause or used in the function body. end footnote] that is ...
Change the text in 6.5.3 [basic.lookup.unqual] paragraph 8 from:
A name used in the definition of a function that is a member function (11.4.2 [class.mfct] ) [footnote: That is, an unqualified name following the function declarator; such a name may be used as a type or as a default argument name in the parameter-declaration-clause, or may be used in the function body, or, if the function is a constructor, may be used in the expression of a mem-initializer. end footnote] of class X shall be ...
to:
A name used in the definition of a member function (11.4.2 [class.mfct] ) of class X following the function's declarator-id [footnote: That is, an unqualified name that occurs, for instance, in a type or default argument expression in the parameter-declaration-clause, in the function body, or in an expression of a mem-initializer in a constructor definition. end footnote] shall be ...



33. Argument dependent lookup and overloaded functions

Section: 6.5.4  [basic.lookup.argdep]     Status: TC1     Submitter: Jason Merrill     Date: 15 Jul 1998

If an argument used for lookup is the address of a group of overloaded functions, are there any associated namespaces or classes? What if it's the address of a function template?

My inclination is to say no to both.

From Mike Miller:

We discussed this on the reflector a few weeks ago. I'll leave the template case for the Core III experts, but I'd find it surprising if the overload case weren't handled as the obvious generalization of the single-function case. For a single function, the associated namespaces are those of the types used in the parameters and return type; I would expect that using an overloaded function name would simply be the union of the namespaces from the members of the overload set. That would be the simplest and most intuitive, IMHO — is there an argument for doing it differently?

Proposed Resolution (04/99): In 6.5.4 [basic.lookup.argdep] paragraph 2, add following the last bullet in the list of associated classes and namespaces for various argument types (not a bullet itself because overload sets and templates do not have a type):

In addition, if the argument is the name or address of a set of overloaded functions and/or function templates, its associated classes and namespaces are the union of those associated with each of the members of the set: the namespace in which the function or function template is defined and the classes and namespaces associated with its (non-dependent) parameter types and return type.



90. Should the enclosing class be an "associated class" too?

Section: 6.5.4  [basic.lookup.argdep]     Status: TC1     Submitter: John Spicer     Date: 2 Feb 1999

Section 6.5.4 [basic.lookup.argdep] includes the following:

Note that for a union, the enclosing class is an "associated class", but for a class type the enclosing class is not an "associated class". This results in some surprising behavior, as shown in the example below.
    struct A {
        union U {};
        friend void f(U);
    };

    struct B {
        struct S {};
        friend void f(S);
    };

    int main() {
        A::U    u;
        f(u);        // okay: A is an associated class
        B::S    s;
        f(s);        // error: no matching f(), B is not an associated class
    }

Certainly the enclosing class should also be an associated class for nested class types, shouldn't it?

Proposed Resolution (10/99): Change the two referenced bullets to read:

(This proposal also addresses Core issue 91.)


164. Overlap between Koenig and normal lookup

Section: 6.5.4  [basic.lookup.argdep]     Status: TC1     Submitter: Derek Inglis     Date: 3 Sep 1999

The description of Koenig lookup in 6.5.4 [basic.lookup.argdep] paragraph 1 says,

...other namespaces not considered during the usual unqualified lookup (6.5.3 [basic.lookup.unqual] ) may be searched.
Does this mean that Koenig lookup does not search namespaces that were already searched during the usual unqualified lookup? The answer is academic except for the two-stage lookup during template instantiation. If a given namespace is searched in the context of the template definition, are declarations in that namespace in the instantiation context ignored during the Koenig lookup? For instance,
    void f(int);

    template <class T> void g(T t) {
        f(t);
    }

    enum E { e };

    void f(E);

    void h() {
        g(e);
    }
In this example, the call f(t) in the template function will resolve to f(E) if Koenig lookup reexamines already-searched namespaces and to f(int) if not.

Proposed Resolution (10/00):

Immediately preceding the example at the end of 6.5.4 [basic.lookup.argdep] paragraph 2, add the following:

[Note: the namespaces and classes associated with the argument types can include namespaces and classes already considered by the ordinary unqualified lookup.]



85. Redeclaration of member class

Section: 6.5.6  [basic.lookup.elab]     Status: TC1     Submitter: Steve Adamczyk     Date: 25 Jan 1999

In 6.5.6 [basic.lookup.elab] paragraph 3, there is the example

    struct Base {
        // ...
        struct Data { /* ... */ };  // Defines nested Data
        struct Data;                // OK: Redeclares nested Data
    };
The final redeclaration is invalid according to 11.4 [class.mem] paragraph 1 last sentence.

Proposed resolution (10/00): Remove the line

        struct Data;                // OK: Redeclares nested Data

See also Core issue 36 and Core issue 56.




89. Object lifetime does not account for reference rebinding

Section: 6.7.3  [basic.life]     Status: TC1     Submitter: AFNOR     Date: 27 Oct 1998

From J16/98-0026 = WG21 N1169, "Proposed Defect Reports on ISO/IEC 14882, Programming Languages - C++":
A reference is rebindable. This is surprising and unnatural. This can also cause subtle optimizer bugs.

Example:

    struct T {
        int& ri;
        T (int& r) : ri (r) { }
    };

    void bar (T*);

    void foo () {
        int i;
        T x (i);
        x.ri = 3;   // the optimizer understands that this is really i = 3
        bar (&x);
        x.ri = 4;   // optimizer assumes that this writes to i, but this is incorrect
    }

    int gi;

    void bar (T* p) {
        p->~T ();
        new (p) T (gi);
    }
If we replace T& with T* const in the example then undefined behavior result and the optimizer is correct.

Proposal: make T& equivalent to T* const by extending the scope of 6.7.3 [basic.life] paragraph 9 to references.

(See also J16/99-0005 = WG21 N1182, "Proposed Resolutions for Core Language Issues 6, 14, 20, 40, and 89")

In addition, Lisa Lippincott pointed out the following example:

    void f( const bool * );
    void g();

    int main() {
       const bool *b = new const bool( false );
       f(b);
       if (*b)
          g();
    }

    void f( const bool *b ) {
       new ( const_cast<bool *>(b) ) const bool( true );
    }

The proposed wording in the paper would still permit this usage and thus prevent an optimizer from eliminating the call to g().

Proposed Resolution (10/00):

Add a new bullet to the list of restrictions in 6.7.3 [basic.life] paragraph 7, following the second bullet ("the new object is of the same type..."):




93. Missing word in 3.8 basic.life paragraph 2

Section: 6.7.3  [basic.life]     Status: TC1     Submitter: Mike Miller     Date: 6 Feb 1999

The text of 6.7.3 [basic.life] paragraph 2 currently reads,

The phrase "an object of type" is obviously incorrect. I believe it should read "an object of POD type." Does anyone disagree?

Proposed Resolution (10/99): As suggested.




43. Copying base classes (PODs) using memcpy

Section: 6.8  [basic.types]     Status: TC1     Submitter: Nathan Myers     Date: 15 Sep 1998

Can you use memcpy on non-member POD subobjects of non-POD objects?

In 6.8 [basic.types] paragraphs 2 and 3 we have:

For any complete POD object type T, whether or not the object holds a valid value of type T, the underlying bytes (6.7.1 [intro.memory] ) making up the object can be copied into an array of char or unsigned char*. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value. [Example elided]
*[Footnote: By using, for example, the library functions (16.4.2.3 [headers] ) memcpy or memmove. end footnote]
For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2, if the value of obj1 is copied into obj2, using the memcpy library function, obj2 shall subsequently hold the same value as obj1.
Paragraph 3 doesn't repeat the restriction of paragraph 2. Should it be assumed? Otherwise only complete POD types are copyable to an array of char and back, but scribbling over subobjects is OK. (Or perhaps a "distinct T object" is a complete object...)

Proposed Resolution (04/99): Change the text in 6.8 [basic.types] paragraph 2 from:

For any complete POD object type T, ...
to:
For any object (other than a base class subobject) of POD type T, ...
Change the text in 6.8 [basic.types] paragraph 3 from:
For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2,
to:
For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base class subobject, ...



149. Accessibility and ambiguity

Section: 7.3.12  [conv.ptr]     Status: TC1     Submitter: Nathan Sidwell     Date: 31 Jul 1999

The Standard uses confusing terminology when referring to accessibility in connection with ambiguity. For instance:

7.3.12 [conv.ptr] paragraph 3:

If B is an inaccessible or ambiguous base ...
7.6.1.7 [expr.dynamic.cast] paragraph 8:
... has an unambiguous public base ...
11.7.3 [class.virtual] paragraph 5:
... is an unambiguous direct or indirect base ... and is accessible ...
14.4 [except.handle] paragraph 3:
not involving conversions to pointers to private or protected or ambiguous classes

The phrase "unambiguous public base" is unfortunate as it could mean either "an unambiguous base not considering accessibility, which is public" or "an unambiguous base considering only the publicly accessible bases." I believe the former interpretation correct, as accessibility is applied after visibility (11.8 [class.access] paragraph 4) and ambiguity is described in terms of visibility (6.5.2 [class.member.lookup] paragraph 2) .

Suggested Resolution: Use the phrases "public and unambiguous," "accessible and unambiguous," "non-public or ambiguous," or "inaccessible or ambiguous" as appropriate.

Proposed resolution (10/00):




52. Non-static members, member selection and access checking

Section: 7.6.1.5  [expr.ref]     Status: TC1     Submitter: Steve Adamczyk     Date: 13 Oct 1998

7.6.1.5 [expr.ref] paragraph 4 should make it clear that when a nonstatic member is referenced in a member selection operation, the type of the left operand is implicitly cast to the naming class of the member. This allows for the detection of access and ambiguity errors on that implicit cast.

Proposed Resolution (10/00):

  1. In 11.8.3 [class.access.base] paragraph 4, remove the following from the second note:

    If the member m is accessible when named in the naming class according to the rules below, the access to m is nonetheless ill-formed if the type of p cannot be implicitly converted to type T (for example, if T is an inaccessible base class of p's class).
  2. Add the following as a new paragraph 5 of 11.8.3 [class.access.base]:

    If a class member access operator, including an implicit "this->," is used to access a nonstatic data member or nonstatic member function, the reference is ill-formed if the left operand (considered as a pointer in the "." operator case) cannot be implicitly converted to a pointer to the naming class of the right operand. [Note: this requirement is in addition to the requirement that the member be accessible as named.]
  3. In 11.8.3 [class.access.base] paragraph 4, fix a typographical error by adding the missing right parenthesis following the text

    (including cases where an implicit "this->" is added
  4. Add following the first sentence of 7.6.1.3 [expr.call] paragraph 4:

    If the function is a nonstatic member function, the "this" parameter of the function (_N4868_.11.4.3.2 [class.this]) shall be initialized with a pointer to the object of the call, converted as if by an explicit type conversion (7.6.3 [expr.cast]). [Note: there is no access checking on this conversion; the access checking is done as part of the (possibly implicit) class member access operator. See 11.8.3 [class.access.base].]



53. Lvalue-to-rvalue conversion before certain static_casts

Section: 7.6.1.9  [expr.static.cast]     Status: TC1     Submitter: Steve Adamczyk     Date: 13 Oct 1998

Section 7.6.1.9 [expr.static.cast] paragraph 6 should make it clear that when any of the "inverse of any standard conversion sequence" static_casts are done, the operand undergoes the lvalue-to-rvalue conversions first.

Proposed Resolution (10/00):

In 7.6.1.9 [expr.static.cast] paragraph 6, change

can be performed explicitly using static_cast subject to the restriction that the explicit conversion does not cast away constness (7.6.1.11 [expr.const.cast]), ...

to

can be performed explicitly using static_cast. The lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) conversions are applied to the operand. Such a static_cast is subject to the restriction that it does not cast away constness (7.6.1.11 [expr.const.cast]), ...



128. Casting between enum types

Section: 7.6.1.9  [expr.static.cast]     Status: TC1     Submitter: Clark Nelson     Date: 10 June 1999

According to 9.7.1 [dcl.enum] paragraph 9, it is permitted to convert from one enumeration type to another. However, neither 7.6.1.9 [expr.static.cast] nor 7.6.3 [expr.cast] allows this conversion.

Proposed resolution (10/00): Change the first two sentences of 7.6.1.9 [expr.static.cast] paragraph 7 to read

A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (9.7.1 [dcl.enum] ).



137. static_cast of cv void*

Section: 7.6.1.9  [expr.static.cast]     Status: TC1     Submitter: Mike Miller     Date: 13 July 1999

According to 7.6.1.9 [expr.static.cast] paragraph 10,

An rvalue of type "pointer to cv void" can be explicitly converted to a pointer to object type.
No requirements are stated regarding the cv-qualification of the pointer to object type. Contrast this with the formula used in paragraphs 5, 8, and 9, where the treatment of cv-qualification is explicit, requiring that the target type be at least as cv-qualified as the source. There is an apparently general requirement on all forms of static_cast in 7.6.1.9 [expr.static.cast] paragraph 1 that it "shall not cast away constness." Assuming that this restriction applies to paragraph 10, since there is no explicit exception to the general rule, that still leaves open the question of whether one can "cast away volatility" in a conversion from volatile void* to a pointer to object type. Should 7.6.1.9 [expr.static.cast] paragraph 10 be rewritten to handle cv-qualification in the same way as paragraphs 5, 8, and 9?

Proposed resolution (10/00):

Change the first sentence of 7.6.1.9 [expr.static.cast] paragraph 10 to

An rvalue of type "pointer to cv1 void" can be converted to an rvalue of type "pointer to cv2 T", where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.



74. Enumeration value in direct-new-declarator

Section: 7.6.2.8  [expr.new]     Status: TC1     Submitter: Jason Merrill     Date: 16 Nov 1998

7.6.2.8 [expr.new] paragraph 6 says:

The expression in a direct-new-declarator shall have integral type (6.8.2 [basic.fundamental] ) with a non-negative value.
I assume the intent was to also allow enumeral types, as we do in 7.6.1.2 [expr.sub] ?

Proposed Resolution (10/99): Replace "integral type" by "integral or enumeration type" in 7.6.2.8 [expr.new] paragraph 6.




127. Ambiguity in description of matching deallocation function

Section: 7.6.2.8  [expr.new]     Status: TC1     Submitter: Alexander Schiemann     Date: 8 June 1999

If a placement allocation function has default arguments for all its parameters except the first, it can be called using non-placement syntax. In such a case, it is not clear whether the deallocation function to be called if the constructor terminates by throwing an expression is determined on the basis of the syntax of the new-expression (i.e., a non-placement deallocation function) or the declaration of the selected (placement) allocation function. 7.6.2.8 [expr.new] paragraph 19 indicates that the deallocation function must match the declaration of the allocation function. However, 14.3 [except.ctor] says that the distinction is based on whether the new-expression contains a new-placement or not.

Proposed resolution (10/00):

In 14.3 [except.ctor] paragraph 2, replace

If the object or array was allocated in a new-expression and the new-expression does not contain a new-placement, the deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation], 11.4.11 [class.free]) is called to free the storage occupied by the object; the deallocation function is chosen as specified in 7.6.2.8 [expr.new]. If the object or array was allocated in a new-expression and the new-expression contains a new-placement, the storage occupied by the object is deallocated only if an appropriate placement operator delete is found, as specified in 7.6.2.8 [expr.new].

with

If the object or array was allocated in a new-expression, the matching deallocation function (6.7.5.5.3 [basic.stc.dynamic.deallocation], 7.6.2.8 [expr.new], 11.4.11 [class.free]), if any, is called to free the storage occupied by the object.

See also issue 429.




179. Function pointers and subtraction

Section: 7.6.6  [expr.add]     Status: TC1     Submitter: Mike Miller     Date: Nov 1999

7.6.6 [expr.add] paragraph 8 explicitly allows subtraction of two pointers to functions:

If two pointers point to the same object or function... and the two pointers are subtracted...
However, 7.6.6 [expr.add] paragraph 2 requires that two pointers that are subtracted be pointers to an object type; function pointers are not allowed.

Being able to subtract two pointers to functions doesn't seem terribly useful, especially considering that subtracting two pointers to different functions appears to produce undefined behavior rather than simply a non-zero result, according to paragraph 6:

Unless both pointers point to elements of the same array object, or one past the last element of the array object, the behavior is undefined.

Proposed resolution (10/00):

Remove the words or function from paragraph 8.




73. Pointer equality

Section: 7.6.10  [expr.eq]     Status: TC1     Submitter: Nathan Myers     Date: 13 Nov 1998

Nathan Myers: In 7.6.10 [expr.eq] , we have:

Pointers to objects or functions of the same type (after pointer conversions) can be compared for equality. Two pointers of the same type compare equal if and only if they are both null, both point to the same object or function, or both point one past the end of the same array.
What does this say, when we have
    int i[1];
    int j[1];
about the expression (i+1 == j) ? It seems to require padding between i[0] and j[0] so that the comparison will come out false.

I think this may be a defect, in that the quoted paragraph extends operator=='s domain too far beyond operator<'s. It should permit (but not require) an off-the-end pointer to compare equal to another object, but not to any element of the same array.

Mike Miller: I think this is reading more into the statement in 7.6.10 [expr.eq] paragraph 1 than is actually there. What does it mean for a pointer to "point to" an object? I can't find anything that definitively says that i+1 cannot "point to" j[0] (although it's obviously not required to do so). If i+1 is allowed to "point to" j[0], then i+1==j is allowed to be true, and there's no defect. There are places where aliasing is forbidden, but the N+1th element of an array doesn't appear to be one of them.

To put it another way, "points to" is undefined in the Standard. The only definition I can think of that encompasses the possible ways in which a pointer can get its value (e.g., the implementation-defined conversion of an arbitrary integer value to a pointer) is that it means "having the same value representation as would be produced by applying the (builtin) & operator to an lvalue expression designating that object". In other words, if the bits are right, it doesn't matter how you produced the value, as long as you didn't perform any operations that have undefined results. The expression i+1 is not undefined, so if the bits of i+1 are the same as those of &j[0], then i+1 "points to" j[0] and i+i==j is allowed to be true.

Tom MacDonald: C9X contains the following words for the "==" operator:

Two pointers compare equal if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.
Matt Austern: I don't think there's anything wrong with saying that the result of
    int x[1];
    int y[1];
    std::cout << (y == x + 1) << std::endl;
is implementation defined, or even that it's undefined.

Mike Miller: A similar question could be raised about different objects that (sequentially) share the same storage. Consider the following:

    struct B {
        virtual void f();
    };
    struct D1: B { };
    struct D2: B { };
    void g() {
        B* bp1 = new D1;
        B* bp2 = new (bp1) D2;
        bp1 == bp2; // ???
    }
Section 6.7.3 [basic.life] paragraph 5 does not list this kind of comparison among the pointer operations that cause undefined behavior, so presumably the comparison is allowed. However, 7.6.10 [expr.eq] paragraph 1 describes pointer comparison in terms of "[pointing] to the same object," which bp1 and bp2 clearly do not do. How should we describe the result of this comparison?

Jason Merrill: When you consider comparing pointers to void, this seems to suggest that no two objects can have the same address, depending on your interpretation of "point to the same object." This would cripple the empty base optimization.

6.8.4 [basic.compound] refers to 'pointers to void or objects or functions'. In that case, 7.6.10 [expr.eq] does not allow you to compare them; it only allows comparing pointers to objects and functions.

Proposed Resolution (10/00):

(See also paper J16/00-0011 = WG21 N1234.)




188. Comma operator and rvalue conversion

Section: 7.6.20  [expr.comma]     Status: TC1     Submitter: Mike Miller     Date: 20 Dec 1999

Given

    char arr[100];
    sizeof(0,arr);

What does the sizeof expression return? According to 7.6.20 [expr.comma] paragraph 1, the comma operator yields an lvalue if the second argument is an lvalue. Since 7.3.3 [conv.array] paragraph 1 says that the array-to-pointer conversion yields an rvalue, it seems that sizeof should see an array type and give the answer 100. If so, the value of the sizeof expression would be different from that of the corresponding expression in C, but there is nothing in Annex Clause Annex C [diff] to indicate that an incompatible change was intended.

Proposed resolution (10/00):

Add the following as paragraph 3 of C.7.4 [diff.expr]:

5.16, 5.17, 5.18

Change: The result of a conditional expression, an assignment expression, or a comma expression may be an lvalue.
Rationale: C++ is an object-oriented language, placing relatively more emphasis on lvalues. For example, functions may return lvalues.
Effect on original feature: Change to semantics of well-defined feature. Some C expressions that implicitly rely on lvalue-to-rvalue conversions will yield different results. For example,

    char arr[100];
    sizeof(0, arr)
yields 100 in C++ and sizeof(char*) in C.
Difficulty of converting: Programs must add explicit casts to the appropriate rvalue.
How widely used: Rare.




94. Inconsistencies in the descriptions of constant expressions

Section: 7.7  [expr.const]     Status: TC1     Submitter: Mike Miller     Date: 8 Feb 1999
  1. According to 11.4.9.3 [class.static.data] paragraph 4, a static const integral or const enumeration data member initialized with an integral constant expression "can appear in integral constant expressions within its scope" [emphasis mine]. This means that the following is not permitted:
        struct S {
            static const int c = 5;
        };
        int a[S::c];    // error: S::c not in scope
    
    Is this restriction intentional? If so, what was the rationale for the restriction?

    Bjarne Stroustrup: I think that once you have said S::, c is in scope so that

        int a[S::c];
    
    is ok.

    Mike Miller: I'd like to think that's what it meant, but I don't believe that's what it said. According to 6.4 [basic.scope] paragraph 1, the scope of a name is the region "in which that name may be used as an unqualified name." You can, indeed, use a qualified name to refer to a name that is not in scope, but that only goes to reinforce my point that "S::c" is not in scope at the point where the expression containing it is used. I think the phrase "within its scope" is at best misleading and should be removed. (Unless there's a reason I'm missing for restricting the use of static member constants to their scope.)

  2. According to 7.7 [expr.const] paragraph 1, integral constant expressions can "involve...const variables or static data members of integral or enumeration types initialized with constant expressions." However, in 7.7 [expr.const] paragraph 3, arithmetic constant expressions cannot include them. This seems a rather gratuitous distinction and one likely to bite programmers trained always to use const variables instead of preprocessor definitions. Again, is there a rationale for the difference?

    As far as I can tell from 7.7 [expr.const] paragraph 2, "arithmetic constant expressions" (as distinct from "integral constant expressions") are used only in static initializers to distinguish between static and dynamic initialization. They include floating point types and exclude non-type template parameters, as well as the const variables and static data members.

  3. There is a minor error in 7.7 [expr.const] paragraph 2. The first sentence says, "Other expressions are considered constant expressions only for the purpose of non-local static object initialization." However, 8.8 [stmt.dcl] paragraph 4 appears to rely on the same definition dealing with the initialization of local static objects. I think that the words "non-local" should be dropped and a cross reference to 8.8 [stmt.dcl] added.

  4. 7.7 [expr.const] paragraph 4 says, "An expression that designates the address of a member or base class of a non-POD class object (clause 9) is not an address constant expression (11.9.5 [class.cdtor] )."

    I'm guessing that should be "non-static member," like the similar prohibition in 11.9.5 [class.cdtor] regarding out-of-lifetime access to members of non-POD class objects.

Proposed resolutions (10/00):

  1. Remove the phrase "within its scope" in 11.4.9.3 [class.static.data] paragraph 4.

  2. Replace 7.7 [expr.const] paragraph 3 with the following:
    An arithmetic constant expression shall satisfy the requirements for an integral constant expression, except that
    • floating literals need not be cast to integral or enumeration type, and
    • conversions to floating point types are permitted.
  3. This is not a defect; no change is required. The suggested wording would be more accurate, but since the effect on local initialization is unobservable the current wording is adequate.

  4. Change the referenced sentence in 7.7 [expr.const] paragraph 4 to "An expression that designates the address of a subobject of a non-POD class object is not an address constant expression."




227. How many scopes in an if statement?

Section: 8.5  [stmt.select]     Status: TC1     Submitter: Marc Paterno     Date: 21 Apr 2000

The wording of 8.5 [stmt.select] paragraph 1 is misleading. Instead of

The substatement in a selection-statement (both substatements, in the else form of the if statement) implicitly defines a local scope (6.4 [basic.scope]).

it should say

... each substatement, in the else form...

As is, one is left with the impression that both "then" and "else" clauses together form a single scope.

Proposed resolution (10/00): As suggested.




69. Storage class specifiers on template declarations

Section: 9.2.2  [dcl.stc]     Status: TC1     Submitter: Mike Ball     Date: 17 Oct 1998

Mike Ball: I cannot find anything in the standard that tells me the meaning of a storage-class-specifier on a function template declaration. In particular, there is no indication what effect, if any, it has on the storage class of the instantiations.

There is an explicit prohibition of storage-class-specifiers on explicit specializations.

For example, if we have

    template<class T> static int foo(T) { return sizeof(T); }
does this generate static functions for all instantiations? By 9.2.2 [dcl.stc] the storage class applies to the name declared in the declarator, which is the template foo, not an instantiation of foo, which is named with a template-id. There is a statement in clause 14 that template names have linkage, which supports the contention that "static" applies to the template, not to instantiations.

So what does the specifier mean? Lacking a direct statement in the standard, I see the following posibilities, in my preference order.

  1. storage-class-specifiers have no meaning on template declarations, their use being subsumed by "export" (for the template name) and the unnamed namespace (for instantiations)
  2. storage-class-specifiers have no effect on the template name, but do affect the linkage of the instantiations, though this now applies linkage to template-ids, which I can find no support for. I suspect this is what was intended, though I don't remember
Of course, if anybody can find some concrete statement, that would settle it.

From John Spicer

The standard does say that a namespace scope template has external linkage unless it is a function template declared "static". It doesn't explicitly say that the linkage of the template is also the linkage of the instantiations, but I believe that is the intent. For example, a storage class is prohibited on an explicit specialization to ensure that a specialization cannot be given a different storage class than the template on which it is based.

Mike: This makes sense, but I couldn't find much support in the document. Sounds like yet another interpretation to add to the list.

John: Agreed.

The standard does not talk about the linkage of instantiations, because only "names" are considered to have linkage, and instances are not really names. So, from an implementation point of view, instances have linkage, but from a language point of view, only the template from which the instances are generated has linkage.
Mike: Which is why I think it would be cleaner to eliminate storage class specifiers entirely and rely on the unnamed namespace. There is a statement that specializations go into the namespace of the template. No big deal, it's not something it says, so we live with what's there.

John: That would mean prohibiting static function templates. I doubt those are common, but I don't really see much motivation for getting rid of them at this point.

"export" is an additional attribute that is separate from linkage, but that can only be applied to templates with external linkage.
Mike: I can't find that restriction in the standard, though there is one that templates in an unnamed namespace can't be exported. I'm pretty sure that we intended it, though.

John: I can't find it either. The "inline" case seems to be addressed, but not static. Surely this is an error as, by definition, a static template can't be used from elsewhere.

Proposed resolution (10/00):

Change the text in Clause 13 [temp] paragraph 4 from:
A template name may have linkage (6.6 [basic.link]).
to:
A template name has linkage (6.6 [basic.link]). A non-member function template can have internal linkage; any other template name shall have external linkage. Entities generated from a template with internal linkage are distinct from all entities generated in other translation units.



56. Redeclaring typedefs within classes

Section: 9.2.4  [dcl.typedef]     Status: TC1     Submitter: Steve Adamczyk     Date: 13 Oct 1998

Can a typedef redeclaration be done within a class?

    class X {
        typedef int I;
        typedef int I;
    };
See also 11.4 [class.mem] , Core issue 36, and Core issue 85.

Proposed Resolution (10/99): Change 9.2.4 [dcl.typedef] paragraph 2 from "In a given scope" to "In a given non-class scope."




76. Are const volatile variables considered "constant expressions"?

Section: 9.2.9.2  [dcl.type.cv]     Status: TC1     Submitter: Judy Ward     Date: 15 Dec 1998

The following code does not compile with the EDG compiler:

    volatile const int a = 5;
    int b[a];
The standard, 9.2.9.2 [dcl.type.cv] , says:
A variable of const-qualified integral or enumeration type initialized by an integral constant expression can be used in integral constant expressions.
This doesn't say it can't be const volatile-qualified, although I think that was what was intended.

Proposed Resolution (10/99): Change the referenced text in paragraph 2 of 9.2.9.2 [dcl.type.cv] to read:




68. Grammar does not allow "friend class A<int>;"

Section: 9.2.9.5  [dcl.type.elab]     Status: TC1     Submitter: Mike Ball     Date: 17 Oct 1998

I can't find the answer to the following in the standard. Does anybody have a reference?

The syntax for elaborated type specifier is

Which does not allow the production

    class foo<int> // foo is a template
On the other hand, a friend declaration seems to require this production,
An elaborated-type-specifier shall be used in a friend declaration for a class.*

[Footnote: The class-key of the elaborated-type-specifier is required. —end footnote]

And in 13.7.5 [temp.friend] we find the example
[Example:
    template<class T> class task;
    template<class T> task<T>* preempt(task<T>*);

    template<class T> class task {
        // ...
        friend void next_time();
        friend void process(task<T>*);
        friend task<T>* preempt<T>(task<T>*);
        template<class C> friend int func(C);

        friend class task<int>;
        template<class P> friend class frd;
        // ...
    };
Is there some special dispensation somewhere to allow the syntax in this context? Is there something I've missed about elaborated-type-specifier? Is it just another bug in the standard?

An additional problem was reported via comp.std.c++: the grammar does not allow the following example:

    namespace A{
      class B{};
    };

    namespace B{
      class A{};
      class C{
	friend class ::A::B;
      };
    };

Proposed resolution (10/00):

Change the grammar in 9.2.9.5 [dcl.type.elab] to read

and change the forms allowed in paragraph 1 to




40. Syntax of declarator-id

Section: 9.3.4  [dcl.meaning]     Status: TC1     Submitter: Mike Miller     Date: 01 Sep 1998

(From J16/99-0005 = WG21 N1182, "Proposed Resolutions for Core Language Issues 6, 14, 20, 40, and 89")

There are two sub-issues. The first concerns the statement in 9.3.4 [dcl.meaning] paragraph 1,

The id-expression of a declarator-id shall be a simple identifier except for the declaration of some special functions (11.4.8 [class.conv] , 11.4.7 [class.dtor] , 12.4 [over.oper] ) and for the declaration of template specializations or partial specializations (13.9 [temp.spec] ).
The second sub-issue is regarding another statement in the same paragraph:
A declarator-id shall not be qualified except for the definition of a member function (11.4.2 [class.mfct] ) or static data member (11.4.9 [class.static] ) or nested class (11.4.12 [class.nest] ) outside of its class, the definition or explicit instantiation of a function, variable or class member of a namespace outside of its namespace, or...
Analysis

The problem in the first sub-issue is that the wrong syntactic non-terminal is mentioned. The relevant portions of the grammar are:

The exceptions in the citation from 9.3.4 [dcl.meaning] paragraph 1 are all the non-identifier cases of unqualified-id: 11.4.8 [class.conv] is for conversion-function-ids, 11.4.7 [class.dtor] is for destructors, 12.4 [over.oper] is for overloaded operators, and 13.9 [temp.spec] is for template-ids. If taken literally, this sentence would exclude all qualified-ids, which it obviously is not intended to do. Instead, the apparent intent is something along the lines of
If an unqualified-id is used as the id-expression of a declarator-id, it shall be a simple identifier except...
However, it does not appear that this restriction has any meaning; all of the possible cases of unqualified-ids are represented in the list of exceptions! Rather than recasting the sentence into a correct but useless form, it would be better to remove it altogether.

The second sub-issue deals with the conditions under which a qualified-id can be used in a declarator, including "the definition of a...nested class" and "the definition or explicit instantiation of a...class member of a namespace." However, the name in a class definition is not part of a declarator; these constructs do not belong in a list of declarator contexts.

Proposed Resolution for sub-issue 1 (04/99):

The suggested resolution for the first sub-issue overlooked the fact that the existing wording has the additional effect of prohibiting the use of the non-identifier syntax for declaring other than the listed entities. Thus the proposed wording for the first sub-issue is:

Change 9.3.4 [dcl.meaning] paragraph 1 from:

The id-expression of a declarator-id shall be a simple identifier except...
to:
An unqualified-id occurring in a declarator-id shall be a simple identifier except...

Proposed Resolution for sub-issue 2 (10/99):

Change 9.3.4 [dcl.meaning] paragraph 1 from:

A declarator-id shall not be qualified except for the definition of a member function (11.4.2 [class.mfct] ) or static data member (11.4.9 [class.static] ) or nested class (11.4.12 [class.nest] ) outside of its class, the definition or explicit instantiation of a function, variable or class member of a namespace outside of its namespace, or...
to
A declarator-id shall not be qualified except for the definition of a member function (11.4.2 [class.mfct] ) or static data member (11.4.9 [class.static] ) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or...



159. Namespace qualification in declarators

Section: 9.3.4  [dcl.meaning]     Status: TC1     Submitter: John Spicer     Date: 23 Aug 1999

9.3.4 [dcl.meaning] paragraph 1 says:

In the qualified declarator-id for a class or namespace member definition that appears outside of the member's class or namespace, the nested-name-specifier shall not name any of the namespaces that enclose the member's definition.
This results in the following behavior:
    namespace N {
        namespace M {
            void f();
            void g();
        }
        void M::f(){}     // okay
        void N::M::g(){}  // error
    }
I was very surprised when this rule was pointed out to me. The change appears to have been introduced around the time of the first Santa Cruz meeting, but I don't recall discussion of it and could not find a motion related to it.

Regardless of where it came from, I also can't understand why it is there. Certainly it shouldn't matter how you name a given class or namespace.

For example, the standard permits:

    namespace N {
        namespace M {
            void f();
            void g();
        }
        namespace X = M;
        namespace Y = N::M;
        void X::f(){}  // okay
        void Y::g(){}  // okay
    }
So, it is okay to use an alias for N::M, but not to use N::M directly. Note that it is okay to use N::M in any other context at this point in the program (i.e., the rule is a specific restriction on declarator names, not a general rule on the use of qualified names).

Does anyone recall the intent of this rule or any rationale for its existence?

Notes from 04/00 meeting:

There was some question as to whether this issue actually constituted a defect in the Standard. John Spicer suggested that machine-generated source code would be likely to run afoul of this prohibition. Francis Glassborow expressed support for a rule that would allow full qualification, or qualification relative to the namespace containing the definition, but not qualification relative to a containing namespace. There was no consensus for moving forward with a DR at this point, so the issue was left in "review" status.

Proposed resolution (10/00):

Remove the last sentence of 9.3.4 [dcl.meaning] paragraph 1 (cited above) and the example that follows.




135. Class type in in-class member function definitions

Section: 9.3.4.6  [dcl.fct]     Status: TC1     Submitter: Gabriel Netterdag     Date: 1 July 1999

6.3 [basic.def.odr] paragraph 4 and 9.3.4.6 [dcl.fct] paragraph 6 indicate that the return type and parameter types must be complete in a function definition. However, when 11.4 [class.mem] paragraph 2 lists the contexts in a class member-specification in which the class is considered complete, the return type and parameter types of a member function defined in the class definition are not included. It thus appears that the following example is ill-formed:

    struct S {
        S f() { return S(); }    // error: incomplete return type
        void g(S) { }            // error: incomplete parameter type
    };
Jack Rouse: I suggest supplementing the text in 8.3.5p6 with something like:
The type of a parameter or the return type for a function definition shall not be an incomplete class type unless the function definition is nested in the member-specification for that class (including definitions in nested classes defined within the class).

Proposed resolution (10/00): Replace the last sentence of 9.3.4.6 [dcl.fct] paragraph 6 with

The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).



1. What if two using-declarations refer to the same function but the declarations introduce different default-arguments?

Section: 9.3.4.7  [dcl.fct.default]     Status: TC1     Submitter: Bill Gibbons     Date: unknown

6.4 [basic.scope] paragraph 4 says:

Given a set of declarations in a single declarative region, each of which specifies the same unqualified name,
9.3.4.7 [dcl.fct.default] paragraph 9 says:
When a declaration of a function is introduced by way of a using-declaration (9.9 [namespace.udecl]), any default argument information associated with the declaration is imported as well.
This is not really clear regarding what happens in the following case:
    namespace A {
            extern "C" void f(int = 5);
    }
    namespace B {
            extern "C" void f(int = 7);
    }

    using A::f;
    using B::f;

    f(); // ???
Proposed resolution (10/00):

Add the following at the end of 12.2.4 [over.match.best]:

If the best viable function resolves to a function for which multiple declarations were found, and if at least two of these declarations — or the declarations they refer to in the case of using-declarations — specify a default argument that made the function viable, the program is ill-formed. [Example:
    namespace A {
       extern "C" void f(int = 5);
    }
    namespace B {
       extern "C" void f(int = 5);
    }

    using A::f;
    using B::f;

    void use() {
       f(3);       // OK, default argument was not used for viability
       f();        // Error: found default argument twice
    }

  —end example]




65. Typo in default argument example

Section: 9.3.4.7  [dcl.fct.default]     Status: TC1     Submitter: Mike Miller     Date: 6 Oct 1998

Proposed Resolution (04/99): Change the text in the example of section 9.3.4.7 [dcl.fct.default] paragraph 5 from:

... g will be called with the value f(1).
to:
... g will be called with the value f(2).



217. Default arguments for non-template member functions of class templates

Section: 9.3.4.7  [dcl.fct.default]     Status: TC1     Submitter: Martin Sebor     Date: 22 Mar 2000

According to 9.3.4.7 [dcl.fct.default] paragraphs 4 and 6,

For non-template functions, default arguments can be added in later declarations of a function in the same scope.

The default arguments in a member function definition that appears outside of the class definition are added to the set of default arguments provided by the member function declaration in the class definition.

This would appear to allow the following example, in which a default argument is added to a non-template member function of a class template:

    template <class T>
    struct S
    {
	void foo (int);
    };

    template <class T>
    void S<T>::foo (int = 0) { }

John Spicer: The wording "non-template functions" is somewhat unclear with respect to member functions of class templates, but I know that this was intended to include them because it originates from issue 3.13 of the template issues list that I maintained for several years.

Having said that, the rationale for this restriction has since been made obsolete, so this could (in theory) be changed in the standard if it is problematic for users.

(See also issue 205.)

Proposed resolution (10/00):

In 9.3.4.7 [dcl.fct.default] paragraph 6, replace

The default arguments in a member function definition that appears outside of the class definition are added to the set of default arguments provided by the member function declaration in the class definition.

with

Except for member functions of class templates, the default arguments in a member function definition that appears outside of the class definition are added to the set of default arguments provided by the member function declaration in the class definition. Default arguments for a member function of a class template must be specified on the initial declaration of the member function within the class template.



35. Definition of default-initialization

Section: 9.4  [dcl.init]     Status: TC1     Submitter: Andrew Koenig     Date: 29 Jul 1998

Given:

    struct S1 {
        int x;
    };

    struct S2 {
        int x;
        double y;
    };

    struct S3 {
        int x;
        double y;
        string s;
    };
Once upon a time, we went through a fairly protracted discussion to ensure that S1().x would be guaranteed to be 0. Note that if we declare
    void f()
    {
        S1 s1;

        // ...
    }
there is no guarantee of the value of s1.x, and that is intentional. But S1().x is different, because S1() is an rvalue, and unless all of its members are defined, the effect of copying it is undefined.

Similarly, S2().x and S2().y are also defined to be equal to zero, and here it really matters for many implementations, because if S2().y is just a bunch of random bits, it is entirely possible that trying to copy S2().y will yield a floating-point trap.

However, rather to my surprise, the standard does not define the value of S3().x or S3().y, because S3 is not a POD. It does define S3().s (by running the string constructor), but once a structure is no longer a POD, the values of uninitialized members are no longer guaranteed in expressions of the form T().

In my opinion, this definition is a mistake, and the committee's intention was to zero-initialize all members that do not have an explicitly defined constructor, whether or not the class is a POD.

See also paper J16/99-0014 = WG21 N1191.

[Note: this issue is resolved by the resolution of issue 178.]




151. Terminology of zero-initialization

Section: 9.4  [dcl.init]     Status: TC1     Submitter: Valentin Bonnard     Date: 4 August 1999

In 6.9.3.2 [basic.start.static] paragraph 1 and 9.4 [dcl.init] paragraphs 5 and 6, the terms "memory" and "storage" are used in connection with zero-initialization. This is inaccurate; it is the variables that are zero-initialized, not the storage. (An all-zero bit pattern in the storage may, in fact, not correspond to the representation of zero converted to the appropriate type, and it is the latter that is being described.)

Suggested resolution: remove the words "storage" and "memory" in these contexts.

Proposed resolution (10/00):

Delete the words "The storage for" from the first sentence of 6.9.3.2 [basic.start.static] paragraph 1.

[Note: Revised wording in 9.4 [dcl.init] relating to this issue is also found in issue 178.]




178. More on value-initialization

Section: 9.4  [dcl.init]     Status: TC1     Submitter: Andrew Koenig     Date: 25 Oct 1999

When the Committee considered issue 35, another context in which value initialization might be relevant was overlooked: mem-initializers. It would seem reasonable that if T() as an expression invokes value initialization, that the same syntactic construct in a mem-initializer-list would do the same, and the usefulness of value initialization in that context is at least as great as the standalone case.

Proposed resolution (10/00):

[Note: this resolution supersedes the resolution to issue 35.]

In 7.6.1.4 [expr.type.conv] paragraph 2, replace "whose value is determined by default-initialization" by "which is value-initialized".

In 7.6.2.8 [expr.new] paragraph 15,

Replace 9.4 [dcl.init] paragraph 5 by:

To zero-initialize an object of type T means:

To default-initialize an object of type T means:

To value-initialize an object of type T means:

A program that calls for default-initialization of an entity of reference type is ill-formed. If T is a cv-qualified type, the cv-unqualified version of T is used for these definitions of zero-initialization, default-initialization, and value-initialization.

In 9.4 [dcl.init] paragraph 6, change "The memory occupied by any" to "Every".

In 9.4 [dcl.init] paragraph 7, replace "default-initialized" by "value-initialized".

In 9.4.2 [dcl.init.aggr] paragraph 7, replace "default-initialized" by "value-initialized".

In 11.4.8.2 [class.conv.ctor] paragraph 2, insert "or value-initialization" after the first occurrence of "default-initialization".

In 11.9 [class.init] paragraph 1, replace the note by "The object is default-initialized if there is no initializer, or value-initialized if the initializer is ()" [i.e., replace the non-normative note by different, normative text].

In 11.9.2 [class.expl.init] paragraph 2, replace "default-initialized" by "value-initialized".

In 11.9.3 [class.base.init] paragraph 3, replace "default-initialized" by "value-initialized" in the first bulleted item.

In 11.9.3 [class.base.init] paragraph 4, replace "default-initialized, nor initialized" by "default-initialized, nor value-initialized, nor assigned".




304. Value-initialization of a reference

Section: 9.4  [dcl.init]     Status: TC1     Submitter: Steve Adamczyk     Date: 25 Jul 2001

Another glitch in the TC1/core issue 178 definition of value-initialization: it's no longer an error to value-initialize a reference. That makes an example like

typedef struct { int &r; } S;
int main() {
  S();  // Error in C++98, okay in TC1!
}
valid, which has got to be wrong. See 9.4 [dcl.init] paragraph 5, where there is wording that forbids default-initialization of a reference, but not value-initialization thereof. As noted in issue 302, if the default constructor were required to be generated when a value-initialization is done, that would force an error.

Proposed resolution (10/01):

Add the indicated wording to the indicated sentence in 9.4 [dcl.init] paragraph 5:

A program that calls for default-initialization or value-initialization of an entity of reference type is ill-formed.



163. Description of subaggregate initializer

Section: 9.4.2  [dcl.init.aggr]     Status: TC1     Submitter: Mike Miller     Date: 12 Aug 1999

9.4.2 [dcl.init.aggr] paragraph 2 says,

When an aggregate is initialized the initializer can be an initializer-clause consisting of a brace-enclosed, comma-separated list of initializers for the members of the aggregate.
Neither of these uses of the syntactic nonterminal initializer corresponds to the grammar:

Proposed resolution (10/99): replace the quoted words with:

When an aggregate is initialized the initializer can contain an initializer-clause consisting of a brace-enclosed, comma-separated list of initializer-clauses for the members of the aggregate.



171. Global namespace scope

Section: 9.8  [basic.namespace]     Status: TC1     Submitter: Greg Lutz     Date: 19 Sep 1999

9.8 [basic.namespace] paragraph 2 says:

A name declared outside all named namespaces, blocks (8.4 [stmt.block] ) and classes (Clause 11 [class] ) has global namespace scope (6.4.6 [basic.scope.namespace] ).
But 6.4.6 [basic.scope.namespace] paragraph 3 says:
A name declared outside all named or unnamed namespaces (9.8 [basic.namespace] ), blocks (8.4 [stmt.block] ), function declarations (9.3.4.6 [dcl.fct] ), function definitions (9.5 [dcl.fct.def] ) and classes (Clause 11 [class] ) has global namespace scope (also called global scope).
9.8 [basic.namespace] should evidently be changed to match the wording in 6.4.6 [basic.scope.namespace] — the unnamed namespace is not global scope.

Proposed resolution (10/00):

  1. Replace the first sentence of 6.4.6 [basic.scope.namespace] paragraph 3 with

    The outermost declarative region of a translation unit is also a namespace, called the global namespace. A name declared in the global namespace has global namespace scope (also called global scope).
  2. In the last sentence of the same paragraph, change "Names declared in the global namespace scope" to "Names with global namespace scope."

  3. Replace 9.8 [basic.namespace] paragraph 2 with

    The outermost declarative region of a translation unit is a namespace; see 6.4.6 [basic.scope.namespace].




103. Is it extended-namespace-definition or extension-namespace-definition ?

Section: 9.8.4  [namespace.udir]     Status: TC1     Submitter: Herb Sutter     Date: 20 Mar 1999

Section 9.8.4 [namespace.udir] paragraph 3 uses the term extended-namespace-definition three times:

If a namespace is extended by an extended-namespace-definition after a using-directive for that namespace is given, the additional members of the extended namespace and the members of namespaces nominated by using-directives in the extended-namespace-definition can be used after the extended-namespace-definition.
I think the intent is clear, but unfortunately I cannot find any other mention (or definition) of this term.

Mike Miller: True enough; in Section 9.8.2 [namespace.def] [the grammar] it's called an extension-namespace-definition.

Proposed Resolution (10/99): Systematically replace "extended-namespace-definition" by "extension-namespace-definition".




101. Redeclaration of extern "C" names via using-declarations

Section: 9.9  [namespace.udecl]     Status: TC1     Submitter: Mike Miller     Date: 10 Mar 1999

Consider the following:

    extern "C" void f();
    namespace N {
        extern "C" void f();
    }
    using N::f;
According to 9.9 [namespace.udecl] paragraph 11, the using-declaration is an error:
If a function declaration in namespace scope or block scope has the same name and the same parameter types as a function introduced by a using-declaration, the program is ill-formed.
Based on the context (9.9 [namespace.udecl] paragraph 10 simply reiterates the requirements of 6.4 [basic.scope] ), one might wonder if the failure to exempt extern "C" functions was intentional or an oversight. After all, there is only one function f() involved, because it's extern "C", so ambiguity is not a reason to prohibit the using-declaration.

This also breaks the relatively strong parallel between extern "C" functions and typedefs established in our discussion of Core issue 14 in Santa Cruz. There the question was for using-directives:

    typedef unsigned int size_t;
    extern "C" int f();
    namespace N {
        typedef unsigned int size_t;
        extern "C" int f();
    }
    using namespace N;
    int i = f();        // ambiguous "f"?
    size_t x;           // ambiguous "size_t"?
We decided for both that there was no ambiguity because each pair of declarations declares the same entity. (According to 6.1 [basic.pre] paragraph 3, a typedef name is not an entity, but a type is; thus the declarations of size_t declare the same entity "unsigned int".)

In the context of using-declarations, there is no explicit extension of the restrictions in 6.4 [basic.scope] paragraph 4 except as noted above for function declarations; thus the parallel scenario for a typedef is not ill-formed:

    typedef unsigned int size_t;
    namespace N {
        typedef unsigned int size_t;
    };
    using N::size_t;        // okay, both declarations
                            // refer to the same entity
I think the first sentence of 9.9 [namespace.udecl] paragraph 11 ought to be rewritten as:
If a function declaration in namespace scope or block scope has the same name and the same parameter types as a function introduced by a using-declaration, and the declarations do not declare the same function, the program is ill-formed.

Proposed Resolution (10/99): As suggested.




148. POD classes and pointers to members

Section: Clause 11  [class]     Status: TC1     Submitter: Nathan Sidwell     Date: 31 Jul 1999

6.8 [basic.types] paragraph 10 defines pointer to member types to be scalar types. It also defines scalar types to be one of the POD types.

Clause 11 [class] paragraph 4 defines a POD struct as an aggregate class with no non-static data members of type pointer to member.

It seems contradictory that a type can be POD, yet a class containing that type is non-POD.

Suggested resolution: Alter Clause 11 [class] paragraph 4 to allow pointer to member objects as non-static data members of POD class.

Proposed resolution (10/00):

In Clause 11 [class] paragraph 4, remove all occurrences of "pointer to member."




176. Name injection and templates

Section: Clause 11  [class]     Status: TC1     Submitter: John Spicer     Date: 21 February 1999

There is some controversy about whether class name injection applies to class templates. If it does apply, what is injected? Is a class name injected or is the thing that is injected actually a template?

Clause 11 [class] paragraph 2 says,

The class-name is also inserted into the scope of the class itself.
In general, clause 9 applies to both classes and class templates, so I would take this to mean that class name imjection does indeed apply to class templates. One problem with this is that clause 9 uses the syntactic term class-name, which I would take to imply that the inserted name is always a class. This is clearly unacceptable for class templates as it makes the template itself unusable from with the template. For example:
    template <class T> struct A {
        A<T*> ptr;    // Invalid: A refers to a class
    };

Clearly the injected name must be usable as both a class and a class template. This kind of magic already exists in the standard. In 13.8.2 [temp.local] it says,

Within the scope of a class template, when the name of the template is neither qualified nor followed by <, it is equivalent to the name of the template followed by the template-parameters enclosed in <>.

The proposal here is that we clarify that name injection does indeed apply to class templates, and that it is the injected name that has the special property of being usable as both a class and a template name (as described in 13.8.2 [temp.local] ). This would eliminate the need for special wording regarding the qualification of the name, but would achieve the same result. This would also make this "special" name available to a derived class of a class template — something which is necessary if the benefits of class name injection are to be made uniformly available for class templates, too.

    template <class T> struct Base {
        Base* p;
        Base<T*>* p2;
        ::Base* p3;    // Error: only injected name usable as class
    };

    template <class T> struct Derived: public Base<T> {
        Base* p;    // Now okay
        Base<T*>* p2;    // Still okay
        Derived::Base* p3;    // Now okay
Note that by giving the special attribute of being usable as both a class and a template to the injected name it is now clear where this attribute can and cannot be used.

(See paper J16/99-0010 = WG21 N1187.)

Proposed resolution (10/00):

[Note: these changes depend on the resolution for issue 147.]

Replace 13.8.2 [temp.local] paragraphs 1 and 2 with the following:

Like normal (non-template) classes, class templates have an injected-class-name (Clause 11 [class]). The injected-class-name can be used with or without a template-argument-list. When it is used without a template-argument-list, it is equivalent to the injected-class-name followed by the template-parameters of the class template enclosed in <>. When it is used with a template-argument-list, it refers to the specified class template specialization, which could be the current specialization or another specialization.

Within the scope of a class template specialization or partial specialization, when the injected-class-name is not followed by a <, it is equivalent to the injected-class-name followed by the template-arguments of the class template specialization or partial specialization enclosed in <>. [Example:

    template<class T> class Y;
    template<> class Y<int> {
        Y* p;          // meaning Y<int>
        Y<char>* q;    // meaning Y<char>
    };

end example]

The injected-class-name of a class template or class template specialization can be used either with or without a template-argument-list wherever it is in scope. [Example:

    template <class T> struct Base {
        Base* p;
    };

    template <class T> struct Derived: public Base<T> {
        typename Derived::Base* p;  // meaning Derived::Base<T>
    };

end example]

A lookup that finds an injected-class-name (6.5.2 [class.member.lookup]) can result in an ambiguity in certain cases (for example, if it is found in more than one base class). If all of the injected-class-names that are found refer to specializations of the same class template, and if the name is followed by a template-argument-list, the reference refers to the class template itself and not a specialization thereof, and is not ambiguous. [Example:

    template <class T> struct Base { };
    template <class T> struct Derived: Base<int>, Base<char> {
        typename Derived::Base b;            // error: ambiguous
        typename Derived::Base<double> d;    // OK
    };

end example]

When the normal name of the template (i.e., the name from the enclosing scope, not the injected-class-name) is used without a template-argument-list, it refers to the class template itself and not a specialization of the template. [Example:

    template <class T> class X {
        X* p;         // meaning X<T>
        X<T>* p2;
        X<int>* p3;
        ::X* p4;      // error: missing template argument list
                      // ::X does not refer to the injected-class-name
    };

end example]




75. In-class initialized members must be const

Section: 11.4  [class.mem]     Status: TC1     Submitter: John Wiegley     Date: 29 Dec 1998

The standard says, in 11.4 [class.mem] paragraph 4:

A member-declarator can contain a constant-initializer only if it declares a static member (11.4.9 [class.static] ) of integral or enumeration type, see 11.4.9.3 [class.static.data] .
But later, in the section on static class data member initialization, 11.4.9.3 [class.static.data] paragraph 4, it says:
If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (7.7 [expr.const] ). In that case, the member can appear in integral constant expressions within its scope.
The first paragraph should be modified to make it clear that it is not possible to initialize a static data member in-line with a constant-initializer if that data member is of integral (or enumeration) type, and yet not const.

Proposed Resolution (10/99): Change the sentence in 11.4 [class.mem] paragraph 4 to read:

A member-declarator can contain a constant-initializer only if it declares a static member (11.4.9 [class.static] ) of const integral or const enumeration type, see 11.4.9.3 [class.static.data] .



80. Class members with same name as class

Section: 11.4  [class.mem]     Status: TC1     Submitter: Jason Merrill     Date: 5 Dec 1998

Between the May '96 and September '96 working papers, the text in 11.4 [class.mem] paragraph 13:

If T is the name of a class, then each of the following shall have a name different from T:
was changed by removing the word 'static'. Looking over the meeting minutes from Stockholm, none of the proposals seem to include this change, which breaks C compatibility and is not mentioned in the compatibility annex. Was this change actually voted in by the committee?

Specifically, this breaks /usr/include/netinet/in.h under Linux, in which "struct ip_opts" shares its name with one of its members.

Proposed resolution (10/00):

  1. Change the first bullet of 11.4 [class.mem] paragraph 13 to say
  2. Add another paragraph before 11.4 [class.mem] paragraph 14, reading
    In addition, if class T has a user-declared constructor (11.4.5 [class.ctor] ), every nonstatic data member of class T shall have a name different from T.



190. Layout-compatible POD-struct types

Section: 11.4  [class.mem]     Status: TC1     Submitter: Steve Adamczyk     Date: 20 Dec 1999

The definition of layout-compatible POD-struct types in 11.4 [class.mem] paragraph 14 requires that the two types

have the same number of members, and corresponding members (in order) have layout-compatible types (3.9).
There does not appear to be any reason for including member functions and static data members in this requirement. It would be more logical to require only that the non-static data members of the two types must match.

The characteristics of layout-compatible types are not well described in the current wording, either. Apart from their use in 11.4 [class.mem] paragraph 16 to define the term "common initial sequence," there appears to be nothing said about which operations are possible between objects of layout-compatible types. For example, 6.8 [basic.types] paragraphs 2-3 give certain guarantees regarding use of memcpy on objects of the same type; it might be reasonable to assume that the same kinds of guarantees might apply to objects of layout-compatible types, but that is not said. Similarly, 7.2.1 [basic.lval] paragraph 15 describes permissible "type punning" but does not mention layout-compatible types.

Proposed resolution (10/00):

In 11.4 [class.mem] paragraphs 14 and 15, change all occurrences of "members" to "nonstatic data members."




194. Identifying constructors

Section: 11.4.5  [class.ctor]     Status: TC1     Submitter: Jamie Schmeiser     Date: 11 Jan 2000

According to 11.4.5 [class.ctor] paragraph 1, the syntax used in declaring a constructor allows at most one function-specifier. It is thus not permitted to declare a constructor both inline and explicit. This seems overly restrictive.

On a related note, there doesn't seem to be any explicit prohibition against member functions with the same name as the class. (Such a prohibition might reasonably be expected to occur in 11.4 [class.mem] paragraph 13, but member functions are not listed there.)

One possible interpretation would be that such member functions would violate the restrictions in 6.4.7 [basic.scope.class] paragraph 1, because the class name would refer to the class at some points in the class scope and to the member function at others. However, this seems a bit tenuous. Is an explicit prohibition needed?

(See also issue 147.)

Proposed resolution (10/00):

  1. Add to 11.4 [class.mem] paragraph 13

    • every member function of class T [Note: this restriction does not apply to constructors, which do not have names (11.4.5 [class.ctor]). ];

    immediately following the line

    • every data member of class T;
  2. Change 11.4.5 [class.ctor] paragraph 1 from

    A special declarator syntax using an optional function-specifier (9.2.3 [dcl.fct.spec])...

    to

    A special declarator syntax using an optional sequence of function-specifiers (9.2.3 [dcl.fct.spec])...



20. Some clarifications needed for 12.8 para 15

Section: 11.4.5.3  [class.copy.ctor]     Status: TC1     Submitter: unknown     Date: unknown

Issue 1

11.4.5.3 [class.copy.ctor] (From J16/99-0005 = WG21 N1182, "Proposed Resolutions for Core Language Issues 6, 14, 20, 40, and 89")

There are three related sub-issues in this issue, all dealing with the elision of copy constructors as described in 11.4.5.3 [class.copy.ctor] paragraph 15:

  1. The text should make clear that the requirement that the copy constructor be accessible and unambiguous is not relaxed in cases where a call to a copy constructor is elided.
  2. It is not clear from the text that the two optimizations described can be applied transitively, and, if so, the implications for the order of destruction are not spelled out.
  3. The text should exclude applying the function-return optimization if the expression names a static or volatile local object.
Analysis

After discussion in Santa Cruz, the core group decided that sub-issue #1 required no change; the necessity of an accessible and unambiguous copy constructor is made clear in 6.7.7 [class.temporary] paragraph 1 and need not be repeated in this text. The remaining two sub-issues appear to be valid criticisms and should be addressed.

Proposed Resolution (10/99):

[Note: a small portion of this wording is superseded by the resolution of issue 185.]

The paragraph in question should be rewritten as follows. In addition, references to this section should be added to the index under "temporary, elimination of," "elimination of temporary," and "copy, constructor elision."

Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing: the copying of the local automatic object t into the temporary object for the return value of function f() and the copying of that temporary object into object t2. Effectively, the construction of the local object t can be viewed as directly initializing the global object t2, and that object's destruction will occur at program exit. —end example]


185. "Named" temporaries and copy elision

Section: 11.4.5.3  [class.copy.ctor]     Status: TC1     Submitter: Bill Wade     Date: 11 Nov 1999

11.4.5.3 [class.copy.ctor] paragraph 15 refers only to "temporary class objects." It needs to be made clear that these provisions do not apply to temporaries that have been bound to references. For instance,

    struct A {
        mutable int value;
        explicit A(int i) : value(i) {}
        void mutate(int i) const { value = i; }
    };

    int foo() {
        A const& t = A(1);
        A n(t);          // can this copy be elided?
        t.mutate(2);
        return n.value;  // can this return 2?
    }
The current wording seems to allow an implementation not to perform the copy in A N(t) because the source object is a temporary (created explicitly by A(1)).

Proposed resolution (10/00):

Change the wording proposed in the resolution of issue 20 from

to




193. Order of destruction of local automatics of destructor

Section: 11.4.7  [class.dtor]     Status: TC1     Submitter: Gerhard Menzl     Date: 7 Jan 2000

The Standard is not clear whether automatic objects in a destructor are destroyed before or after the destruction of the class's base and member subobjects. That is, given

    struct S { ~S(); };

    struct T {
        S x;
        ~T() {
            S y;
        };
    };

which will be destroyed first, x or y?

Proposed resolution (10/00):

In 11.4.7 [class.dtor] paragraph 6, change

A destructor for class X calls the destructors for X's direct members, ...
to
After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X's direct members, ...




152. explicit copy constructors

Section: 11.4.8.2  [class.conv.ctor]     Status: TC1     Submitter: Steve Adamczyk     Date: 4 August 1999

Can a copy-constructor declared as explicit be used to copy class values implicitly? For example,

   struct X {
      X();
      explicit X(const X&);
   };
   void f(X);
   int main() { X x; f(x); }
According to 11.4.8.2 [class.conv.ctor] paragraphs 2-3,
An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (9.4 [dcl.init] ) or where casts (7.6.1.9 [expr.static.cast] , 7.6.3 [expr.cast] ) are explicitly used... A copy-constructor (11.4.5.3 [class.copy.ctor] ) is a converting constructor. An implicitly-declared copy constructor is not an explicit constructor; it may be called for implicit type conversions.
This passage would appear to indicate that the call in the example is ill-formed, since it uses neither the direct-initialization syntax nor an explicit cast. The last sentences are especially interesting in this regard, indicating that explicit and non-explicit copy constructors are handled differently.

On the other hand, 9.4 [dcl.init] paragraph 14, bullet 4, sub-bullet 2 says,

If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination... [the] applicable constructors are enumerated (12.2.2.4 [over.match.ctor] )...
The cited passage says that
The candidate functions are all the constructors of the class of the object being initialized.

Notes from 04/01 meeting:

After the issue was accepted as a DR with the proposed resolution to change 12.2.2.4 [over.match.ctor] paragraph 1 as described below, it was noticed that 11.4.8.2 [class.conv.ctor] paragraph 3 states that:

A copy-constructor (11.4.5.3 [class.copy.ctor]) is a converting constructor.

In addition to making the proposed resolution for this issue ineffectual, the wording of paragraph 3 also contradicts that of paragraph 1:

A constructor declared without the function-specifier explicit that can be called with a single parameter specifies a conversion from the type of its first parameter to the type of its class. Such a constructor is called a converting constructor.

These considerations led to the addition of the second point of the proposed resolution.

Proposed resolution (04/01):

  1. Change the first two sentences of 12.2.2.4 [over.match.ctor] paragraph 1 to

    When objects of class type are direct-initialized (9.4 [dcl.init]), or copy-initialized from an expression of the same or a derived class type (9.4 [dcl.init]), overload resolution selects the constructor. For direct-initialization, the candidate functions are all the constructors of the class of the object being initialized. For copy-initialization, the candidate functions are all the converting constructors (11.4.8.2 [class.conv.ctor] ) of that class.
  2. Change the first sentence of 11.4.8.2 [class.conv.ctor] paragraph 3 to read:

    A non-explicit copy constructor (11.4.5.3 [class.copy.ctor]) is a converting constructor.



67. Evaluation of left side of object-expression

Section: 11.4.9  [class.static]     Status: TC1     Submitter: Mike Miller     Date: 6 Oct 1998

Paragraph 2 says that "the object-expression is always evaluated" when the class member syntax is used to refer to a static member. This presumably should say that the object expression is evaluated if the member access is performed, i.e., not if the overall expression is the operand of sizeof or the unevaluated branch of ?:, ||, or &&.

Proposed Resolution (10/99): Replace "is always evaluated" by "is evaluated" in 11.4.9 [class.static] paragraph 2.




48. Definitions of unused static members

Section: 11.4.9.3  [class.static.data]     Status: TC1     Submitter: Bill Gibbons     Date: 23 Nov 1997

Also see section: 6.3 [basic.def.odr] .

Originally, all static data members still had to be defined outside the class whether they were used or not.

But that restriction was supposed to be lifted so that static data members need not be defined outside the class unless they are used in a manner which requires their definition, in the same manner as namespace-scope variables. In particular, if an integral/enum const static data member is initialized within the class, and its address is never taken, we agreed that no namespace-scope definition was required.

For example:

    struct A {
        static const int size = 10;
        int array[size];
    };

    int main() {
        A a;
        return 0;
    }
However, 11.4.9.3 [class.static.data] paragraph 4 says:
The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.
A narrow interpreration of "used" in this rule would make the example ill-formed because there is no namespace-scope definition of "size". A better wording for this rule would be:
The member shall still be defined in a namespace scope if it is used in the program in the manner described in 6.3 [basic.def.odr] . The namespace scope definition shall not contain an initializer.
Also, the wording in 6.3 [basic.def.odr] paragraph 2:
An expression is potentially evaluated unless either it is the operand of the sizeof operator (7.6.2.5 [expr.sizeof] ), or it is the operand of the typeid operator and does not designate an lvalue of polymorphic class type (7.6.1.8 [expr.typeid] ).
is incomplete because it does not mention the use of a compile-time constant as an array bound or template argument. It should say something like:
An expression is potentially evaluated unless it is the operand of the sizeof operator (7.6.2.5 [expr.sizeof] ), the operand of the typeid operator, an integral constant-expression used as an array bound or an integral constant-expression used as a template-argument for a non-reference template-parameter; and the expression does not designate an lvalue of polymorphic class type (7.6.1.8 [expr.typeid] ).

Proposed Resolution (04/99): Change the first sentence of 6.3 [basic.def.odr] paragraph 2 from:

An expression is potentially evaluated unless either it is the operand of the sizeof operator (7.6.2.5 [expr.sizeof] ), or it is the operand of the typeid operator and does not designate an lvalue of polymorphic class type (7.6.1.8 [expr.typeid] ).
to:
An expression is potentially evaluated unless it appears where an integral constant expression is required (see 7.7 [expr.const] ), is the operand of the sizeof operator (7.6.2.5 [expr.sizeof] ), or is the operand of the typeid operator and the expression does not designate an lvalue of polymorphic class type (7.6.1.8 [expr.typeid] ).



142. Injection-related errors in access example

Section: 11.8.3  [class.access.base]     Status: TC1     Submitter: Steve Adamczyk     Date: 16 Jul 1999

In the example in paragraph 3 of 11.8.3 [class.access.base] , all the references to B in DD::f() should be replaced by ::B. The reason is that the class name B is private in D and thus inaccessible in DD. (The example was probably not updated when class name injection was added.)

Proposed resolution (10/00):

Replace the example in 11.8.3 [class.access.base] paragraph 3 with:

    class B {
    public:
        int mi;                 // nonstatic member
        static int si;          // static member
    };
    class D: private B {
    };
    class DD: public D {
        void f();
    };
    void DD::f() {
        mi = 3;                 // error: mi is private in D
        si = 3;                 // error: si is private in D
        ::B b;
        b.mi = 3;               // OK (b.mi is different from this->mi)
        b.si = 3;               // OK (b.si is different from this->si)
        ::B::si = 3;            // OK
        ::B* bp1 = this;        // error: B is a private base class
        ::B* bp2 = (::B*)this;  // OK with cast
        bp2->mi = 3;            // OK: access through a pointer to B
    }



161. Access to protected nested type

Section: 11.8.5  [class.protected]     Status: TC1     Submitter: Steve Adamczyk     Date: 26 Aug 1999

11.8.5 [class.protected] paragraph 1 begins:

When a friend or a member function of a derived class references a protected nonstatic member of a base class, an access check applies in addition to those described earlier in 11.8 [class.access] .

This was intended to refer to nonstatic member functions and nonstatic data members. However, a protected nested type declared in a base class is, by some definition of the word, a "nonstatic" member, and therefore subject to this additional access check.

Proposed resolution (10/99): change "protected nonstatic member" in the above to "protected nonstatic member function or protected nonstatic data member" to make the intent clear.




235. Assignment vs initialization

Section: 11.9.3  [class.base.init]     Status: TC1     Submitter: Mike Miller     Date: 16 Sep 2000

In 11.9.3 [class.base.init] paragraph 4 we read:

After the call to a constructor for class X has completed, if a member of X is neither specified in the constructor's mem-initializers, nor default-initialized, nor initialized during execution of the body of the constructor, the member has indeterminate value.

Using the term "initialized" to describe setting the value of a member inside the body of a constructor is a misuse of the term: only by use of a placement new expression can a member be initialized "during the execution of the body of the constructor."

Suggested resolution: Change "initialized" to "given a value."

Proposed resolution (10/00): As suggested.




59. Clarification of overloading and UDC to reference type

Section: 12.2.2.5  [over.match.copy]     Status: TC1     Submitter: Steve Adamczyk     Date: 13 Oct 1998

Sections 12.2.2.5 [over.match.copy] and 12.2.2.6 [over.match.conv] should be clarified regarding the treatment of conversion functions which return reference types.

Proposed resolution (10/99):

In 12.2.2.5 [over.match.copy] paragraph 1, change

Conversion functions that return "reference to T" return lvalues of type T and are therefore considered to yield T for this process of selecting candidate functions.
to
Conversion functions that return "reference to X" return lvalues of type X and are therefore considered to yield X for this process of selecting candidate functions.
In 12.2.2.6 [over.match.conv] paragraph 1, change
Conversion functions that return "reference to T" return lvalues of type T and are therefore considered to yield T for this process of selecting candidate functions.
to
Conversion functions that return "reference to cv2 X" return lvalues of type "cv2 X" and are therefore considered to yield X for this process of selecting candidate functions.



51. Overloading and user-defined conversions

Section: 12.2.4  [over.match.best]     Status: TC1     Submitter: Steve Adamczyk     Date: 13 Oct 1998

In 12.2.4 [over.match.best] paragraph 1, bullet 4 of the second set of bullets, there is a cross-reference to 9.4 [dcl.init] and 12.2.2.6 [over.match.conv] . I believe it should also reference 12.2.2.7 [over.match.ref] . I think the phrase "initialization by user-defined conversion" was intended to refer to all initializations using user-defined conversions, and not just the case in 12.2.2.6 [over.match.conv] . Referring to only 12.2.2.6 [over.match.conv] suggests a narrower meaning of the phrase.

12.2.2.5 [over.match.copy] , although it does deal with initialization by user-defined conversion, does not need to be referenced because it deals with class —> class cases, and therefore there are no standard conversions involved that could be compared.




84. Overloading and conversion loophole used by auto_ptr

Section: 12.2.4.2  [over.best.ics]     Status: TC1     Submitter: Steve Adamczyk     Date: 10 Dec 1998

By the letter of the standard, the conversions required to make auto_ptr work should be accepted.

However, there's good reason to wonder if there isn't a bug in the standard here. Here's the issue: line 16 in the example below comes down to

copy-initialize an auto_ptr<Base> from an auto_ptr<Derived> rvalue
To do that, we first look to see whether we can convert an auto_ptr<Derived> to an auto_ptr<Base>, by enumerating the constructors of auto_ptr<Base> and the conversion functions of auto_ptr<Derived>. There's a single possible way to do the conversion, namely the conversion function

    auto_ptr<Derived>::operator auto_ptr<Base>()
(generated from the template). (The constructor auto_ptr<Base>(auto_ptr_ref<Base>) doesn't work because it requires a user-defined conversion on the argument.)

So far, so good. Now, we do the copy step:

direct-initialize an auto_ptr<Base> from an auto_ptr<Base> rvalue
This, as we've gone to great lengths to set up, is done by calling the conversion function
    auto_ptr<Base>::operator auto_ptr_ref<Base>()
(generated from the template), and then the constructor
    auto_ptr<Base>(auto_ptr_ref<Base>)
(generated from the template).

The problem with this interpretation is that it violates the long-standing common-law rule that only a single user-defined conversion will be called to do an implicit conversion. I find that pretty disturbing. (In fact, the full operation involves two conversion functions and two constructors, but "copy" constructors are generally considered not to be conversions.)

The direct-initialization second step of a copy-initialization was intended to be a simple copy — you've made a temporary, and now you use a copy constructor to copy it. Because it is defined in terms of direct initialization, however, it can exploit the loophole that auto_ptr is based on.

To switch to personal opinion for a second, I think it's bad enough that auto_ptr has to exploit a really arcane loophole of overload resolution, but in this case it seems like it's exploiting a loophole on a loophole.

    struct Base {                             //  2
       static void sink(auto_ptr<Base>);      //  3
    };                                        //  4

    struct Derived : Base {                   //  5
       static void sink(auto_ptr<Derived>);   //  6
    };                                        //  7

    auto_ptr<Derived> source() {              //  8
       auto_ptr<Derived> p(source());         //  9
       auto_ptr<Derived> pp(p);               // 10
       Derived::sink(source());               // 11
       p = pp;                                // 12
       p = source();                          // 13
       auto_ptr<Base> q(source());            // 14
       auto_ptr<Base> qp(p);                  // 15
       Base::sink(source());                  // 16
       q = pp;                                // 17
       q = source();                          // 18
       return p;                              // 19
       return source();
    }
Derek Inglis:

It seems clear to me that the result of this direct initilization must be the second standard conversion sequence in a user defined conversion sequence. Otherwise the resulting conversion sequence is not an implicit conversion sequence. By the letter of the standard, the sequence of conversions making up a copy-initialization must be an implicit conversion sequence.

Paragraph 3 of 7.3 [conv]:

An expression e can be implicitly converted to a type T if and only if the declaration "T t=e;" is well-formed, for some invented temporary variable t (9.4 [dcl.init]).

Paragraph 1 of 12.2.4.2 [over.best.ics]:

An implicit conversion sequence is a sequence of conversions used to convert an argument in a function call to the type of the corresponding parameter of the function being called. The sequence of conversions is an implicit conversion as defined in 7.3 [conv], which means it is governed by the rules for initialization of an object or reference by a single expression (9.4 [dcl.init], 9.4.4 [dcl.init.ref]).
Sentence 1 of paragraph 12 of 9.4 [dcl.init]:
The initialization that occurs in argument passing ... is called copy-initialization and is equivalent to the form
     T x = a;

For me, these sentences imply that all sequences of conversions permitted on a function argument must be valid implicit conversion sequences.

The 'loophole' can be closed by adding a sentence (or note) to the section describing the 'direct initialization second step of a copy initialization' stating that the copy initialization is ill-formed if the conversion sequence resulting from the direct initialization is not a standard conversion sequence.

(See also issue 177 and paper J16/00-0009 = WG21 N1232.)

Proposed resolution (10/00):

Change 12.2.4.2 [over.best.ics] paragraphs 3 and 4 from

Except in the context of an initialization by user-defined conversion (12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv]), a well-formed implicit conversion sequence is one of the following forms:

In the context of an initialization by user-defined conversion (i.e., when considering the argument of a user-defined conversion function; see 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv]), only standard conversion sequences and ellipsis conversion sequences are allowed.

to

A well-formed implicit conversion sequence is one of the following forms:

However, when considering the argument of a user-defined conversion function that is a candidate by 12.2.2.4 [over.match.ctor] when invoked for the copying of the temporary in the second step of a class copy-initialization, or by 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv], or 12.2.2.7 [over.match.ref] in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed.




83. Overloading and deprecated conversion of string literal

Section: 12.2.4.3  [over.ics.rank]     Status: TC1     Submitter: Steve Adamczyk     Date: 24 Jan 1999

In 12.2.4.3 [over.ics.rank] , we have

This does not work right with respect to the deprecated conversion from string literal to "char *". Consider
    void f(char *);
    void f(const char *);

    f("abc");
The two conversion sequences differ only in their qualification conversions, and the destination types are similar. The cv-qualification signature of "char *", is a proper subset of the cv-qualification signature of "const char *", so f(char *) is chosen, which is wrong. The rule should be like the one for conversion to bool — the deprecated conversion should be worse than another exact match that is not the deprecated conversion.

Proposed resolution (10/00):

Change 12.2.4.3 [over.ics.rank] bullet 3.1 sub-bullet 3 from

S1 and S2 differ only in their qualification conversion and yield similar types T1 and T2 (7.3.6 [conv.qual] ), respectively, and the cv-qualification signature of type T1 is a proper subset of the cv-qualification signature of type T2.
to
S1 and S2 differ only in their qualification conversion and yield similar types T1 and T2 (7.3.6 [conv.qual] ), respectively, and the cv-qualification signature of type T1 is a proper subset of the cv-qualification signature of type T2, and S1 is not the deprecated string literal array-to-pointer conversion (7.3.3 [conv.array] ).



153. Misleading wording (rank of conversion)

Section: 12.2.4.3  [over.ics.rank]     Status: TC1     Submitter: Valentin Bonnard     Date: 6 Aug 1999

12.2.4.3 [over.ics.rank] bullet 3.1 sub-bullet 2 says,

the rank of S1 is better than the rank of S2 (by the rules defined below)...
This wording is confusing. The word "below" refers to paragraph 4 (which may not be clear), and the bulk of paragraph 4 deals with comparing conversion sequences whose "rank" is the same.

Proposed resolution (10/00):

In 12.2.4.3 [over.ics.rank] paragraph 3, change

the rank of S1 is better than the rank of S2 (by the rules defined below)
to
the rank of S1 is better than the rank of S2, or S1 and S2 have the same rank and are distinguishable by the rules in the paragraph below



202. Use of overloaded function name

Section: 12.3  [over.over]     Status: TC1     Submitter: Steve Clamage     Date: 2 Feb 2000

12.3 [over.over] paragraph 1 contains a supposedly exhaustive list of contexts in which the name of an overloaded function can be used without an argument list ("...shall not be used without arguments in contexts other than those listed"). However, 13.4.3 [temp.arg.nontype] paragraph 5, bullet 4 gives another context: as a template nontype argument.

Suggested resolution: Add the missing case to 12.3 [over.over].

Proposed resolution (10/00):

Add as the final bullet in 12.3 [over.over] paragraph 1:

and adjust the "or" and final period on the preceding two bullets.




250. Address of function template specialization with non-deduced template arguments

Section: 12.3  [over.over]     Status: TC1     Submitter: Nikolas Kauer     Date: 10 Oct 2000

12.3 [over.over] paragraph 2 says,

If the name is a function template, template argument deduction is done (13.10.3.3 [temp.deduct.funcaddr]), and if the argument deduction succeeds, the deduced template arguments are used to generate a single template function, which is added to the set of overloaded functions considered.

It is not clear whether this formulation allows explicit specification of non-deduced template arguments. For instance,

    template <int I> void f(double x[]);
    typedef void (*FPtr)(double x[]);
    FPtr fp = &f<3>;

If only deduced arguments can be used, this example is ill-formed.

Suggested resolution: Clarify 12.3 [over.over] paragraph 2 to allow both deduced and explicitly-specified template arguments to be used to determine the function template specialization to be added to the overload set.

(See also issues 115 and 214.)

Proposed resolution (10/00):

In 12.3 [over.over] paragraph 2, change

...if the argument deduction succeeds, the deduced template arguments are used to generate a single template function...

to

...if the argument deduction succeeds, the resulting template argument list is used to generate a single function template specialization...



32. Clarification of explicit instantiation of non-exported templates

Section: Clause 13  [temp]     Status: TC1     Submitter: Daveed Vandevoorde     Date: 10 Jul 1998

Section Clause 13 [temp] paragraph 8 says:

A non-exported template that is neither explicitly specialized nor explicitly instantiated must be defined in every translation unit in which it is implicitly instantiated (13.9.2 [temp.inst] ) or explicitly instantiated (13.9.3 [temp.explicit] ); no diagnostic is required.
Shouldn't the first underlined phrase be omitted to avoid conflict with the second underlined phrase?

From John Spicer:

The first "explicitly instantiated" is intended to mean "explicitly instantiated in some other translation unit".

Proposed Resolution (04/99): Change the text in Clause 13 [temp] paragraph 8 from:

A non-exported template that is neither explicitly specialized nor explicitly instantiated must be defined in every translation unit in which it is implicitly instantiated (13.9.2 [temp.inst] ) or explicitly instantiated (13.9.3 [temp.explicit] ); no diagnostic is required.
to:
A non-exported template must be defined in every translation unit in which it is implicitly instantiated (13.9.2 [temp.inst] ), unless the corresponding specialization is explicitly instantiated (13.9.3 [temp.explicit] ) in some translation unit; no diagnostic is required. [Note: See also 13.9.3 [temp.explicit] ]



105. Meaning of "template function"

Section: Clause 13  [temp]     Status: TC1     Submitter: Daveed Vandevoorde     Date: 16 Apr 1999

The phrase "template function" is sometimes used to refer to a template (e.g., in Clause 13 [temp] paragraph 8) and sometimes to refer to a function generated from a template (e.g., 12.3 [over.over] paragraph 4) .

Suggested Resolution:

The phrase should mean "a function generated from a template" (or might perhaps include explicit specializations).

Proposed resolution (10/00):

In Clause 3 [intro.defs] “signature,” replace "template function specialization" by "function template specialization".

In 11.4.2 [class.mfct] paragraph 2, replace "template member functions" by "member functions of class templates and member function templates."

In 12.2.2 [over.match.funcs] paragraph 7 and footnote, replace all instances of "template functions" by "function template specializations."

In 12.2.4 [over.match.best] paragraph 1, fourth bullet (counting all bullets in that paragraph), replace "template function specialization" by "function template specialization". In the fifth bullet, replace "template functions" by "function template specializations."

In 12.3 [over.over] paragraph 2, replace "template function" by "function template specialization."

Change 12.3 [over.over] paragraph 4 from:

If more than one function is selected, any template functions in the set are eliminated if the set also contains a non-template function, and any given template function is eliminated if the set contains a second template function that is more specialized than the first according to the partial ordering rules of 13.7.7.3 [temp.func.order]. After such eliminations, if any, there shall remain exactly one selected function.
to:
If more than one function is selected, any function template specializations in the set are eliminated if the set also contains a non-template function, and any given function template specialization F1 is eliminated if the set contains a second function template specialization whose function template is more specialized than the function template of F1 according to the partial ordering rules of 13.7.7.3 [temp.func.order]. After such eliminations, if any, there shall remain exactly one selected function.

Change text in section Clause 13 [temp] paragraph 8 from:

A template function declared both exported and inline is just inline and not exported.
to:
A function template declared both exported and inline is just inline and not exported.

In 13.7.5 [temp.friend] paragraph 1, third bullet, replace "template function" by "function template" and "function specialization" by "function template specialization."

In footnote 130 (13.7.7 [temp.fct] paragraph 2), replace "template functions" by "function template specializations."

In 13.7.7.3 [temp.func.order] paragraph 1, third bullet change "template function specialization" to "function template specialization".

In 13.10.3 [temp.deduct] paragraph 1, change "template function specialization" to "function template specialization".

In _N4567_.17.3 [definitions] “component” change "non-member template functions that operate" to "non-member function templates that operate".

In _N4567_.17.3 [definitions] “traits class” change "template classes and template functions" to "class templates and function templates".

In 22.2 [utility] paragraph 1 change:

This subclause contains some basic template functions and classes that are used throughout the rest of the library.
to:
This subclause contains some basic function and class templates that are used throughout the rest of the library.

In 22.3 [pairs] paragrah 1 change "template function" to "function template".

In footnote 215 (_N3225_.20.8.11 [function.pointer.adaptors] paragraph 6) change "template functions" to "function templates".

In 30.3.1 [locale] paragraph 4 change "template function" to "function template".

In 25.3 [iterator.requirements] paragraph 2 change "template function" to "function template".

In 25.4.2 [std.iterator.tags] paragraph 1, change "template function" to "function template specialization."

In 25.4.3 [iterator.operations] paragraph 1 change "template function" to "function template", and "These functions use" to "These function templates use".

In the section heading of 31.7.6.3.4 [ostream.inserters.character] change "template functions" to "function templates".

In 16.3.2.3 [structure.requirements] paragraph 2 change "template class name char_traits" to "class template char_traits".

In the section heading of 17.3.5 [numeric.limits] change "Template class" to "Class template".

In 16.4.4.6 [allocator.requirements] paragraph 3 change "template class member rebind" to "member class template rebind" and change "template typedef" to "typedef template".

In the section heading of _N4140_.D.9.1 [depr.lib.binder.1st] change "Template class" to "Class template".

In the section heading of _N4140_.D.9.3 [depr.lib.binder.2nd] change "Template class" to "Class template".

In the section heading of _N4140_.D.10.1 [auto.ptr] change "Template class" to "Class template".

In the section heading of 23.4.3 [basic.string] change "Template class" to "Class template".

In 23.4.3 [basic.string] paragraphs 1 and 2 change "template class basic_string" to "class template basic_string".

In the section heading of 30.4.2.2 [locale.ctype] change "Template class" to "Class template".

In the section heading of 30.4.2.3 [locale.ctype.byname] change "Template class" to "Class template".

In the section heading of 30.4.2.5 [locale.codecvt] change "Template class" to "Class template".

In the section heading of 30.4.2.6 [locale.codecvt.byname] change "Template class" to "Class template".

In the section heading of 30.4.3.2 [locale.num.get] change "Template class" to "Class template".

In the section heading of 30.4.3.3 [locale.nm.put] change "Template class" to "Class template".

In the section heading of 30.4.4.1 [locale.numpunct] change "Template class" to "Class template".

In the section heading of 30.4.4.2 [locale.numpunct.byname] change "Template class" to "Class template".

In the section heading of 30.4.5.1 [locale.collate] change "Template class" to "Class template".

In the section heading of 30.4.5.2 [locale.collate.byname] change "Template class" to "Class template".

In the section heading of 30.4.6.2 [locale.time.get] change "Template class" to "Class template".

In the section heading of 30.4.6.3 [locale.time.get.byname] change "Template class" to "Class template".

In the section heading of 30.4.6.4 [locale.time.put] change "Template class" to "Class template".

In the section heading of 30.4.6.5 [locale.time.put.byname] change "Template class" to "Class template".

In the section heading of 30.4.7.2 [locale.money.get] change "Template class" to "Class template".

In the section heading of 30.4.7.3 [locale.money.put] change "Template class" to "Class template".

In the section heading of 30.4.7.4 [locale.moneypunct] change "Template class" to "Class template".

In the section heading of 30.4.7.5 [locale.moneypunct.byname] change "Template class" to "Class template".

In the section heading of 30.4.8.2 [locale.messages] change "Template class" to "Class template".

In the section heading of 30.4.8.3 [locale.messages.byname] change "Template class" to "Class template".

In the section heading of 24.3.8 [deque] change "Template class" to "Class template".

In the section heading of 24.3.10 [list] change "Template class" to "Class template".

In the section heading of 24.6.6 [queue] change "Template class" to "Class template".

In the section heading of 24.6.7 [priority.queue] change "Template class" to "Class template".

In the section heading of 24.6.8 [stack] change "Template class" to "Class template".

In the section heading of 24.3.11 [vector] change "Template class" to "Class template".

In the section heading of 24.4.4 [map] change "Template class" to "Class template".

In the section heading of 24.4.5 [multimap] change "Template class" to "Class template".

In the section heading of 24.4.6 [set] change "Template class" to "Class template".

In the section heading of 24.4.7 [multiset] change "Template class" to "Class template".

In the section heading of 22.9.2 [template.bitset] change "Template class" to "Class template".

In 22.9.2 [template.bitset] paragraph 1, change "template class" to "class template".

In the section heading of 25.5.1.2 [reverse.iterator] change "Template class" to "Class template".

In the section heading of 25.5.2.2 [back.insert.iterator] change "Template class" to "Class template".

In the section heading of 25.5.2.3 [front.insert.iterator] change "Template class" to "Class template".

In the section heading of 25.5.2.4 [insert.iterator] change "Template class" to "Class template".

In 25.6 [stream.iterators] paragraph 1, change "template classes" to "class templates".

In the section heading of 25.6.2 [istream.iterator] change "Template class" to "Class template".

In the section heading of 25.6.3 [ostream.iterator] [lib.ostream.iterator] change "Template class" to "Class template".

In the section heading of 25.6.4 [istreambuf.iterator] change "Template class" to "Class template".

In 25.6.4 [istreambuf.iterator] paragraph 1, change "template class" to "class template".

In the section heading of 25.6.4.2 [istreambuf.iterator.proxy] change "Template class" to "Class template".

In the section heading of 25.6.5 [ostreambuf.iterator] change "Template class" to "Class template".

In 25.6.5 [ostreambuf.iterator] paragraph 1, change "template class" to "class template".

In 28.4 [complex.numbers] paragraph 1, change "template class" to "class template".

In the section heading of 28.4.3 [complex] change "Template class" to "Class template".

In 28.6.1 [valarray.syn] paragraph 1, change "template classes" to "class templates" and change "function signatures" to "function templates".

In the section heading of 28.6.2 [template.valarray] change "Template class" to "Class template".

In the section heading of 28.6.5 [template.slice.array] change "Template class" to "Class template".

In the section heading of 28.6.7 [template.gslice.array] change "Template class" to "Class template".

In the section heading of 28.6.8 [template.mask.array] change "Template class" to "Class template".

In the section heading of 28.6.9 [template.indirect.array] change "Template class" to "Class template".

In 31.3 [iostream.forward] [lib.iostream.forward] paragraphs 3 to 7, change "template classes" to "class templates". [Note: Some editorial changes were made in paragraphs 2 to 8 when these changes were applied in September 2001.]

In the section heading of 31.5.3 [fpos] change "Template class" to "Class template".

In the section heading of 31.5.4 [ios] change "Template class" to "Class template".

In the section heading of 31.6.3 [streambuf] change "Template class" to "Class template".

In 31.6.3 [streambuf] paragraphs 2 and 3, change "template class" to "class template".

In the section heading of 31.7.5.2 [istream] change "Template class" to "Class template".

In the section heading of 31.7.5.7 [iostreamclass] change "Template class" to "Class template".

In the section heading of 31.7.6.2 [ostream] change "Template class" to "Class template".

In 31.8 [string.streams] paragraph 1 change "template classes" to "class templates".

In the section heading of 31.8.2 [stringbuf] change "Template class" to "Class template".

In the section heading of 31.8.3 [istringstream] change "Template class" to "Class template".

In the section heading of 31.8.5 [stringstream] change "Template class" to "Class template".

In the section heading of 31.10.3 [filebuf] change "Template class" to "Class template".

In the section heading of 31.10.4 [ifstream] change "Template class" to "Class template".

In the section heading of 31.10.5 [ofstream] change "Template class" to "Class template".

In the section heading of 31.10.6 [fstream] change "Template class" to "Class template".




134. Template classes and declarator-ids

Section: Clause 13  [temp]     Status: TC1     Submitter: Mike Miller     Date: 17 June 1999

Clause 13 [temp] paragraph 2 says,

[Note: in a class template declaration, if the declarator-id is a template-id, the declaration declares a class template partial specialization (13.7.6 [temp.spec.partial] ). ]
There is no declarator-id in a class template declaration (cf paragraph 3).

Proposed resolution (10/00):

Replace the phrase "if the declarator-id is a template-id" with "if the class name is a template-id."




21. Can a default argument for a template parameter appear in a friend declaration?

Section: 13.2  [temp.param]     Status: TC1     Submitter: unknown     Date: unknown

13.2 [temp.param] paragraph 10 says:

The set of default template-arguments available for use with a template declaration or definition is obtained by merging the default arguments from the definition (if in scope) and all declarations in scope in the same way as default function arguments are (9.3.4.7 [dcl.fct.default] )."
Can a default argument for a template argument appear in a friend declaration? If so, when is this default argument considered for template instantiations?

For example,

    template<class T1, class T2 = int> class A;

    class B {
        template<class T1 = int, class T2> friend class A;
    };
Is this well-formed? If it is, should the IS say when the default argument for T1 is considered for instantiations of class A?

Proposed resolution (10/00): Add to the end of 13.2 [temp.param] paragraph 9,

A default template-argument shall not be specified in a friend template declaration.

(See also issue 136.)




49. Restriction on non-type, non-value template arguments

Section: 13.2  [temp.param]     Status: TC1     Submitter: Mike Miller     Date: 16 Oct 1998

The example in 13.2 [temp.param] paragraph 8 is:

    template<int* a> struct R { /*...*/ };
    int* p;
    R<p> w;
There was a French comment was that this is an error, and there was general agreement with that.

I've been looking for the verbiage that specifies that this is an error and haven't found it. In particular, nothing in 13.2 [temp.param] ("Template parameters") nor 13.4.3 [temp.arg.nontype] ("Template non-type arguments") appears to rule out this case. (13.4.3 [temp.arg.nontype] paragraph 1 allows an argument to be "the name of an object or function with external linkage," with no limitation on the kinds of parameters such a name can match; "p" is, in fact, such a name.)

Should the resolution of the French comment include beefing up one or both of these sections to cover the applicable rules explicitly?

Proposed Resolution (04/99): Change the example in 13.2 [temp.param] paragraph 8 from:

    template<int *a> struct R { /* ... */ };
    template<int b[5]> struct S { /* ... */ };
    int *p;
    R<p> w; // OK
    S<p> x; // OK due to parameter adjustment
    int v[5];
    R<v> y; // OK due to implicit argument conversion
    S<v> z; // OK due to both adjustment and conversion
to:
    template<int *a> struct R { /* ... */ };
    template<int b[5]> struct S { /* ... */ };
    int p;
    R<&p> w; // OK
    S<&p> x; // OK due to parameter adjustment
    int v[5];
    R<v> y; // OK due to implicit argument conversion
    S<v> z; // OK due to both adjustment and conversion
Furthermore, in 13.4.3 [temp.arg.nontype] paragraph 1:


187. Scope of template parameter names

Section: 13.2  [temp.param]     Status: TC1     Submitter: John Spicer     Date: 15 Nov 1999

At the Dublin meeting (04/99), the Committee proposed to resolve issue 22 by simply changing the wording to make clear that a template parameter cannot be used in its own default argument. This creates a third treatment of this kind of situation, in addition to 6.4.2 [basic.scope.pdecl] paragraph 1, where declarators are in scope and can be used in their initializers, and paragraph 3, where an enumerator is not in scope until after its complete enumerator-definition. The Dublin resolution is for the template parameter to be in scope in its default argument but not usable. It would be more consistent to treat template parameters like enumerators: simply not in scope until the entire template-parameter declaration is seen.

On a related note, 13.2 [temp.param] paragraph 14 should be rewritten to connect the prohibition with visibility rules; otherwise, it sounds as if the following example is not permitted:

    const int Z = 1;
    template <int X = Z, int Z> class A {};

Notes from 04/00 meeting:

The core working group did not reach consensus on the suggested approach to issue 22. However, it was agreed that the intent expressed in the earlier resolution would be better served by different wording.

Proposed resolution (10/00):

[Note: This resolution supersedes the resolution to issue 22.]

Replace 13.2 [temp.param] paragraph 14 as follows:

A template parameter shall not be used in its own default argument.



30. Valid uses of "::template"

Section: 13.3  [temp.names]     Status: TC1     Submitter: Daveed Vandevoorde     Date: 28 May 1998

I have a request for clarification regarding a issue similar to John Wiegley's, but wrt. the ::template syntax. More precisely, where is

    X::template Y
allowed? (It is required for dependent X where Y is a template-id, I believe, but it doesn't seem to be disallowed elsewhere.)

The question also holds for '.template' and '->template'.

Proposed Resolution (04/99): Append to 13.3 [temp.names] paragraph 5:

Furthermore, names of member templates shall not be prefixed by the keyword template if the postfix-expression or qualified-id does not appear in the scope of a template. [Note: just as is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the expression on the left of the -> or ., or the nested-name-specifier is not dependent on a template-parameter. ]



38. Explicit template arguments and operator functions

Section: 13.3  [temp.names]     Status: TC1     Submitter: John Wiegley     Date: 17 Aug 1998

It appears from the grammar that explicit template arguments cannot be specified for overloaded operator names. Does this mean that template operators can never be friends?

But assuming that I read things wrong, then I should be able to specify a global template 'operator +' by writing:

    friend A::B operator + <>(char&);
John Spicer:

You should be able to have explicit template arguments on operator functions, but the grammar does seem to prohibit it (unless I'm reading it incorrectly). This is an error in the grammar, they should be permitted.

Proposed resolution (10/00):

Change the grammar specified in 12.4 [over.oper] paragraph 1 from

to




100. Clarify why string literals are not allowed as template arguments

Section: 13.4.3  [temp.arg.nontype]     Status: TC1     Submitter: Mike Miller     Date: 9 Mar 1999

The explanation in 13.4.3 [temp.arg.nontype] paragraph 2 of why a string literal cannot be used as a template argument leaves something to be desired:

...because a string literal is an object with internal linkage.
I can't find anything that says that a string literal has internal linkage. In fact, I'd be pretty surprised if I did, since linkage is defined (in 6.6 [basic.link] ) strictly in terms of names, and a string literal doesn't have a name. Actually, I think that it's the namelessness of a string literal that prevents it from being a template argument; only the third and fourth bullets of 13.4.3 [temp.arg.nontype] paragraph 1 could conceivably apply, and both of those require that the entity have a name (i.e., that they be given as an id-expression).

Proposed Resolution (10/99): In 13.4.3 [temp.arg.nontype] paragraph 2, change

[Note: a string literal (5.13.5 [lex.string] ) is not an acceptable template-argument because a string literal is an object with internal linkage.
to
[Note: a string literal (5.13.5 [lex.string] ) does not satisfy the requirements of any of these categories and thus is not an acceptable template-argument.



249. What is a member function template?

Section: 13.7.2.2  [temp.mem.func]     Status: TC1     Submitter: David Thornley     Date: 11 Oct 2000

The phrase "member function template" is used in 6.3 [basic.def.odr] paragraph 5 in the list of entities whose definitions can appear more than once in a program, with a cross-reference to 13.7.2.2 [temp.mem.func]. The title of that section is "Member functions of class templates," and paragraph 1 of that section says,

A member function template may be defined outside of the class template in which it is declared.

The example in that paragraph shows a non-template member function of a class template being defined. This gives the impression that the phrase "member function template" is intended to refer to a member function of a class template.

If this usage were intended, much of the rest of the Standard would be unintelligible: objects of class template specializations could not be copied (11.4.5.3 [class.copy.ctor] paragraph 3), member functions of class templates could not be declared virtual (13.7.3 [temp.mem] paragraph 3), etc.

Suggested resolution:

Change "member function template" to "member function of a class template" in both 6.3 [basic.def.odr] paragraph 5 and 13.7.2.2 [temp.mem.func] paragraph 1.

(See also issue 205.)

Proposed resolution (10/00): As suggested.




116. Equivalent and functionally-equivalent function templates

Section: 13.7.7.2  [temp.over.link]     Status: TC1     Submitter: Mike Miller     Date: 11 May 1999

13.7.7.2 [temp.over.link] , paragraphs 5 and 6, describes equivalence and functional equivalence for expressions involving template parameters. As a note in paragraph 5 points out, such expressions may involve type parameters as well as non-type parameters.

Paragraph 7, however, describes the equivalence of function templates only with respect to non-type template parameters. It appears to be unspecified how to determine the equivalence of template functions whose types involve expressions that use template type parameters.

    template <int I> struct S { };

    // The following two declarations are equivalent:
    template <int I> void f(S<I>);
    template <int J> void f(S<J>);

    // The IS doesn't say whether these are equivalent:
    template <class T> void f(S<sizeof(T)>);
    template <class T> void f(S<sizeof(T)>);

Proposed resolution (10/99): Remove the three uses of the words "non-type" in 13.7.7.2 [temp.over.link] paragraph 7.




120. Nonexistent non-terminal qualified-name

Section: 13.8  [temp.res]     Status: TC1     Submitter: Bill Gibbons     Date: 28 May 1999

In 13.8 [temp.res] , references to the nonexistent syntactic non-terminal qualified-name occur twice in paragraph 3, twice in paragraph 4, and once in paragraph 5. There is also a reference in 13.2 [temp.param] paragraph 2.

Proposed resolution (10/99): Change the reference in all these cases to qualified-id.




121. Dependent type names with non-dependent nested-name-specifiers

Section: 13.8  [temp.res]     Status: TC1     Submitter: Bill Gibbons     Date: 28 May 1999

The wording in 13.8 [temp.res] paragraph 3:

A qualified-name that refers to a type and that depends on a template-parameter (13.8.3 [temp.dep] ) shall be prefixed by the keyword typename to indicate that the qualified-name denotes a type, forming an elaborated-type-specifier (9.2.9.5 [dcl.type.elab] ).
was intended to say:
A qualified-id that refers to a type and in which the nested-name-specifier depends on a template-parameter (13.8.3 [temp.dep] ) shall ...
in much the same vein as 13.8.3.2 [temp.dep.type], second bullet, first half.

Proposed resolution (10/00): As suggested.




183. typename in explicit specializations

Section: 13.8  [temp.res]     Status: TC1     Submitter: John Spicer     Date: 9 Nov 1999

John Spicer: In 13.8 [temp.res] paragraph 5, the standard says

The keyword typename shall only be used in template declarations and definitions...
My understanding of the intent of this restriction is to say that typename is only allowed in contexts in which template dependent names can be found, but the wording leaves open to interpretation whether typename is allowed in an explicit specialization, such as:
    template <class T> struct A {};
    template <class T> struct B { typedef int X; };
    template <> struct A<int> {
        typename B<int>::X x;
    };
My understanding is that such usage is not permitted. This should be clarified one way or the other.

Mike Miller: I agree with your understanding that you are not allowed to use typename in an explicit specialization. However, I think the standard already says that — an explicit specialization is not a template declaration. According to the grammar in Clause 13 [temp] paragraph 1, a template-declaration must have a non-empty template-parameter-list.

Nathan Myers: Is there any actual reason for this restriction? Its only apparent effect is to make it harder to specialize templates, with no corresponding benefit.

Proposed resolution (10/00):

In 13.8 [temp.res] paragraph 5, replace

The keyword typename shall only be applied to qualified names, but those names need not be dependent.

with

The keyword typename shall be applied only to qualified names, but those names need not be dependent. The keyword typename shall be used only in contexts in which dependent names can be used. This includes template declarations and definitions but excludes explicit specialization declarations and explicit instantiation declarations.



213. Lookup in dependent base classes

Section: 13.8.3  [temp.dep]     Status: TC1     Submitter: John Spicer     Date: 10 Mar 2000

Paragraphs 3-4 of 13.8.3 [temp.dep] say, in part,

if a base class of [a class] template depends on a template-parameter, the base class scope is not examined during name lookup until the class template is instantiated... If a base class is a dependent type, a member of that class cannot hide a name declared within a template, or a name from the template's enclosing scope.

John Spicer: The wording in paragraph 4 seems particularly odd to me. It essentially changes the order in which scopes are considered. If a scope outside of the template declares a given name, that declaration hides entities of the same name from template dependent base classes (but not from nondependent base classes).

In the following example, the calls of f and g are handled differently because B::f cannot hide ::f, but B::g doesn't try to hide anything, so it can be called.

    extern "C" int printf(char *, ...);
    template <class T> struct A : T {
        void h(T t) {
            f(t);  // calls ::f(B)
            g(t);  // calls B::g
        }
    };

    struct B {
        void f(B){printf("%s", "in B::f\n");}
        void g(B){printf("%s", "in B::g\n");}
    };

    void f(B){printf("%s", "in ::f\n");}

    int main()
    {
        A<B> ab;
        B b;
        ab.h(b);
    }

I don't think the current wording in the standard provides a useful facility. The author of class A can't be sure that a given call is going to call a base class function unless the base class is explicitly specified. Adding a new global function could cause the program to suddenly change meaning.

What I thought the rule was is, "If a base class is a dependent type a member of that class is not found by unqualified lookup".

Derek Inglis: My understanding is the same except that I'd remove the word "qualified" from your sentence.

Erwin Unruh: My interpretation is based on 13.8.4 [temp.dep.res] and especially 13.8.4.2 [temp.dep.candidate] (and largely on my memory of the discussions). For all unqualified names you do something like the following algorithm:

  1. check whether it is a dependent function call
  2. Do a lookup in the definition context and remember what you found there
  3. Do a Koenig-Lookup at instantiation time
  4. perform overloading if necessary

Regarding names from base classes you cannot find them in 2) because you don't know what base class you have. You cannot find them in 3) because members of classes are not found by Koenig lookup (only namespaces are considered). So you don't find them at all (for unqualified names).

For a qualified name, you start lookup for each 'part' of the qualification. Once you reach a dependent part, you stop and continue lookup at the instantiation point. For example:

    namespace A {
      namepace B {
	template <class T> class C {
	  template <class U> class D {
	    typedef int E;
	    // ...
	  };
	};
      };
    };

    template <class T> class F : public T {
      typename A::B::C<int>::D<T>::E var1;
      typename A::B::C<T>::D<int>::E var2;
      typename F::T::X var3;
    }

For var1 you do lookup for A::B::C<int>::D at definition time, for var2 you only do lookup for A::B::C. The rest of the lookup is done at instantiation time since specialisations could change part of the lookup. Similarly the lookup for var3 stops after F::T at definition time.

My impression was that an unqualified name never refers to a name in a dependent base class.

(See also issue 197.)

Proposed resolution (10/00):

  1. In 13.8.3 [temp.dep] paragraph 3, replace

    In the definition of a class template or in the definition of a member of such a template that appears outside of the template definition, if a base class of this template depends on a template-parameter, the base class scope is not examined during name lookup until the class template is instantiated.

    with

    In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.
  2. Remove from 13.8.3 [temp.dep] paragraph 4:

    If a base class is a dependent type, a member of that class cannot hide a name declared within a template, or a name from the template's enclosing scopes.



108. Are classes nested in templates dependent?

Section: 13.8.3.2  [temp.dep.type]     Status: TC1     Submitter: Mark Mitchell     Date: 14 Apr 1999

Mark Mitchell (via John Spicer): Given:

  template <class T> struct S {
     struct I1 {
       typedef int X;
     };
     struct I2 : public I1 {
        X x;
     };
  };

Is this legal? The question really boils down to asking whether or not I1 is a dependent type. On the one hand, it doesn't seem to fit any of the qualifications in 13.8.3.2 [temp.dep.type] . On the other, 13.9.4 [temp.expl.spec] allows explicit specialization of a member class of a class template, so something like:

  template <>
  struct S<double>::I1 {
     int X;
  };

is apparently legal. But, then, `X' no longer refers to a type name. So, it seems like `I1' should be classified as dependent. What am I missing?

Erwin Unruh: I wrote that particular piece of text and I just missed the problem above. It is intended to be a dependent type. The reasoning is that I1 is just a shorthand for S<T>::I1 which clearly is dependent.

Suggested Resolution: (Erwin Unruh)

I think the list of what is a dependent type should be extended to cover "a type declared and used within the same template" modulo of phrasing.

(See also paper J16/00-0009 = WG21 N1231. This issue is also somewhat related to issue 205: classes nested inside template classes are, in some sense, "templates," just as non-template member functions of class templates and static data members of class templates are "templates.")

Proposed resolution (10/00):

Add after 13.8.2 [temp.local] paragraph 2:

Within the scope of a class template, when the unqualified name of a nested class of the class template is referred to, it is equivalent to the name of the nested class qualified by the name of the enclosing class template. [Example:
    template <class T> struct A {
	class B {};
	// B is equivalent to A::B, which is equivalent to A<T>::B,
	// which is dependent.
	class C : B { };
    };
end example]



22. Template parameter with a default argument that refers to itself

Section: 13.8.4  [temp.dep.res]     Status: TC1     Submitter: unknown     Date: unknown

13.2 [temp.param] paragraph 13 says:

The scope of a template-parameter extends from its point of declaration until the end of its template. In particular, a template-parameter can be used in the declaration of subsequent template-parameters and their default arguments.
Is the following well-formed?
    template<class U = U> class X { ... };

[Note: this issue is resolved by the resolution of issue 187.]




24. Errors in examples in 14.7.3

Section: 13.9.4  [temp.expl.spec]     Status: TC1     Submitter: unknown     Date: unknown

Problem Description: At least four of the examples in 13.9.4 [temp.expl.spec] have errors.

Proposed Resolution (10/99):

1. Change the example in paragraph 8 from:

[Example:
    // file #1 
    #include <vector>
    // Primary class template vector
    export template<class T> void f(t) {
        vector<T> vec;         // should match the specialization
        /* ... */
    }

    // file #2 
    #include <vector>
    class B { };
    // Explicit specialization of vector for vector<B>
    template<class T> class vector<B> { /* ... */ }
    template<class T> void f(T);
    void g(B b) {
        f(b);                   // ill formed: 
                                // f<B> should refer to vector<B>, but the 
                                // specialization was not declared with the 
                                // definition of f in file #1 
    }
—end example]
to:
[Example:
    // file #1 
    #include <vector>
    // Primary class template vector
    export template<class T> void f(T) {
        std::vector<T> vec;     // should match the specialization 
        /* ... */
    };

    // file #2 
    #include <vector>
    class B { };
    // Explicit specialization of vector for vector<B>
    namespace std {
        template<> class vector<B> { /* ... */ };
    }
    template<class T> void f(T);
    void g(B b) {
        f(b);                   // ill formed: 
                                // f<B> should refer to vector<B>, but the 
                                // specialization was not declared with the 
                                // definition of f in file #1 
    }
—end example]

2. The example in paragraph 16 as it appears in the IS:

[Example:
    template<class T> struct A {
        void f(T);
        template<class X> void g(T, X);
        void h(T) { }
    };

    // specialization 
    template<> void A<int>::f(int);

    // out of class member template definition 
    template<class T> template<class X> void A<T>::g(T,X) { }

    // member template partial specialization 
    template<> template<class X> void A<int>::g(int, X);

    // member template specialization 
    template<> template<>
        void A<int>::g(int, char);        // X deduced as char
    template<> template<>
        void A<int>::g<char>(int, char);  // X specified as char

    // member specialization even if defined in class definition 
    template<> void A<int>::h(int) { }
end example]
The word 'partial' in the third comment in the example should be removed because this example does not illustrate partial specialization. Also, the two specializations of template<> template<> void A<int>::g(int, char); violate 13.9 [temp.spec] , paragraph 5, which reads:
No program shall explicitly instantiate any template more than once, both explicitly instantiate and explicitly specialize a template, or specialize a template more than once for a given set of template-arguments. An implementation is not required to diagnose a violation of this rule.
Proposed resolution (10/99):
[Example:
    template<class T> struct A {
        void f(T);
        template<class X1> void g1(T, X1);
        template<class X2> void g2(T, X2);
        void h(T) { }
    };

    // specialization 
    template<> void A<int>::f(int);

    // out of class member template definition 
    template<class T> template<class X1> void A<T>::g1(T,X1) { }

    // member template specialization 
    template<> template<class X1> void A<int>::g1(int, X1);

    // member template specialization 
    template<> template<>
        void A<int>::g1(int, char);        // X1 deduced as char
    template<> template<>
        void A<int>::g2<char>(int, char);  // X2 specified as char

    // member specialization even if defined in class definition 
    template<> void A<int>::h(int) { }
end example]

3. Remove the spurious semicolon (or the curly brackets) from the end of the last line in the example in paragraph 17. This is the example as it appears in the IS:

[Example:
    template<class T1> class A {
        template<class T2> class B {
            void mf();
        };
    };
    template<> template<> A<int>::B<double> { };
    template<> template<> void A<char>::B<char>::mf() {};
end example]
Proposed resolution (10/99):
[Example:
    template<class T1> class A {
        template<class T2> class B {
            void mf();
        };
    };
    template<> template<> A<int>::B<double>;
    template<> template<> void A<char>::B<char>::mf();
end example]

Note (Steve Adamczyk, March 2002): that's still incorrect. The missing "class" was added editorially when TC1 was prepared.

4. Remove spurious semicolons (or curly brackets) from the specializations of mf1 and mf2 in the example in paragraph 18. This is the text of the example as it appears in the IS:

[Example:
    template<class T1> class A {
        template<class T2> class B {
            template<class T3> void mf1(T3);
            void mf2();
        };
    };
    template<> template<class X>
        class A<int>::B { };
    template<> template<> template<class T>
        void A<int>::B<double>::mf1(T t) { };
    template<class Y> template<>
        void A<Y>::B<double>::mf2() { }; // ill-formed; B<double> is specialized but 
                                         // its enclosing class template A is not 
end example]
Proposed resolution (10/99):
[Example:
    template<class T1> class A {
        template<class T2> class B {
            template<class T3> void mf1(T3);
            void mf2();
        };
    };
    template<> template<class X>
        class A<int>::B { };
    template<> template<> template<class T>
        void A<int>::B<double>::mf1(T t) { }
    template<class Y> template<>
        void A<Y>::B<double>::mf2() { } // ill-formed; B<double> is specialized but 
                                         // its enclosing class template A is not 
end example]

Note (Steve Adamczyk, March 2002): that's still incorrect. See issue 336.




64. Partial ordering to disambiguate explicit specialization

Section: 13.9.4  [temp.expl.spec]     Status: TC1     Submitter: Steve Adamczyk     Date: 13 Oct 1998

Paragraph 12 should address partial ordering. It wasn't updated when that change was made and conflicts with 13.7.7.3 [temp.func.order] paragraph 1.

Proposed resolution (10/00):

Remove 13.9.4 [temp.expl.spec] paragraph 12 and the example that follows.




241. Error in example in 14.8.1

Section: 13.10.2  [temp.arg.explicit]     Status: TC1     Submitter: Mike Miller     Date: 9 Aug 2000

13.10.2 [temp.arg.explicit] paragraph 6 contains the following example:

    namespace A {
        struct B { };
        template<int X> void f();
    }
    namespace C {
        template<class T> void f(T t);
    }
    void g(A::B b) {
        f<3>(b);    // ill-formed: not a function call
        A::f<3>(b); // well-formed
        C::f<3>(b); // ill-formed; argument dependent lookup
                    // only applies to unqualified names
        using C::f;
        f<3>(b);    // well-formed because C::f is visible; then
                    // A::f is found by argument dependent lookup
    }

A::f() should have a parameter of type A::B.

Proposed resolution (10/00):

In the example in 13.10.2 [temp.arg.explicit] paragraph 6, change the third line from

        template <int X> void f();

to

        template <int X> void f(B);



181. Errors in template template-parameter example

Section: 13.10.3.6  [temp.deduct.type]     Status: TC1     Submitter: John Spicer     Date: 4 Nov 1999

13.10.3.6 [temp.deduct.type] paragraph 18 uses incorrect syntax. Instead of

    template <template X<class T> > struct A { };
    template <template X<class T> > void f(A<X>) { }
it should be
    template <template <class T> class X> struct A { };
    template <template <class T> class X> void f(A<X>) { }

Proposed resolution (10/00): As suggested.

[Note: this section was numbered 14.8.2.4 in ISO/IEC 14882:2003.]




98. Branching into try block

Section: Clause 14  [except]     Status: TC1     Submitter: Jack Rouse     Date: 23 Feb 1999

At the top of clause 15, in paragraph 2, it says:

A goto, break, return, or continue statement can be used to transfer control out of a try block or handler, but not into one.
What about switch statements?
    switch ( f() )
    {
    case 1:
         try {
             g();
    case 2:
             h();
         }
         catch (...)
         {
             // handler
         }
    break;
    }
Daveed Vandevoorde:

Consider:

    void f() {
        try {
        label:
            ;
        } catch(...) {
            goto label;
        }
    }
Now the phrase "try block" (without a hyphen) is used in paragraph 1 in a way that causes me to think that it is not intended to include the corresponding handlers. On the other hand, the grammar entity "try-block" (with hyphen) does include the handlers. So is the intent to prohibit the above or not?

Proposed resolution (10/00:

Change text in Clause 14 [except] paragraph 2 from:

A goto, break, return, or continue statement can be used to transfer control out of a try block or handler, but not into one.
to:
A goto or switch statement shall not be used to transfer control into a try block or into a handler.
[ Example:
void f() {
  goto l1;  // Ill-formed
  goto l2;  // Ill-formed
  try {
    goto l1;  // OK
    goto l2;  // Ill-formed
    l1: ;
  } catch (...) {
    l2: ;
    goto l1;  // Ill-formed
    goto l2;  // OK
  }
}
end example ]
A goto, break, return, or continue statement can be used to transfer control out of a try block or handler.

(See also issue 246.)




210. What is the type matched by an exception handler?

Section: 14.4  [except.handle]     Status: TC1     Submitter: Scott Douglass     Date: 6 Mar 2000

14.4 [except.handle] paragraph 3 says,

A handler is a match for a throw-expression with an object of type E...

This wording leaves it unclear whether it is the dynamic type of the object being thrown or the static type of the expression that determines whether a handler is a match for a given exception. For instance,

    struct B { B(); virtual ~B(); };
    struct D : B { D(); };
    void toss(const B* b) { throw *b; }
    void f() { const D d; toss(&d); }

In this code, presumably the type to be matched is B and not const D (14.2 [except.throw]).

Suggested resolution: Replace the cited wording as follows:

A handler is a match for a throw-expression which initialized a temporary (14.2 [except.throw]) of type E...

Proposed resolution (10/00):

  1. Change 14.2 [except.throw] paragraph 3 from

    A throw-expression initializes a temporary object, the type of which is determined...

    to

    A throw-expression initializes a temporary object, called the exception object, the type of which is determined...
  2. Change 14.4 [except.handle] paragraph 3 from

    A handler is a match for a throw-expression with an object of type E if...

    to

    A handler is a match for an exception object of type E if...



25. Exception specifications and pointers to members

Section: 14.5  [except.spec]     Status: TC1     Submitter: unknown     Date: unknown

14.5 [except.spec] paragraph 3 should say what happens when two pointers to members with different exception specifications are assigned to each other, initialized with one another, etc.

Proposed Resolution (04/99): Change the text in 14.5 [except.spec] paragraph 3 from:

Similarly, any function or pointer to function assigned to, or initializing, a pointer to function shall only allow exceptions that are allowed by the pointer or function being assigned to or initialized.
to:
A similar restriction applies to assignment to and initialization of pointers to functions, pointers to member functions, and references to functions: the target entity shall allow at least the exceptions allowed by the source value in the assignment or initialization.



126. Exception specifications and const

Section: 14.5  [except.spec]     Status: TC1     Submitter: Martin von Loewis     Date: 8 June 1999

The standard is inconsistent about constness inside exception specifications.

    struct X {};
    struct Y:X {};

    const Y bar() {return Y();}

    void foo()throw(const X)
    {
      throw bar();
    }
It is unclear whether calling foo will result in a call to std::unexpected. According to 14.5 [except.spec] paragraph 7, only two cases are treated specially with regard to inheritance: If "class X" appears in the type-id-list, or if "class X*" appears in the type-id-list. Neither is the case here, so foo only allows exceptions of the same type (const X). As a result, std::unexpected should be called.

On the other hand, the intent of exception specification appears to allow an implementation of this example as

    void foo()
    try{
      throw bar();
    }catch(const X){
      throw;
    }catch(...){
      std::unexpected();
    }
According to 14.4 [except.handle] , this replacement code would catch the exception, so std::unexpected would not be called.

Suggested resolution: Change 14.5 [except.spec] paragraph 7 to read

A function is said to allow all exception objects of all types E for which one of the types T in the type-id-list would be a handler, according to 14.4 [except.handle] .

Proposed resolution (10/00):

Replace 14.5 [except.spec] paragraph 7 with the following:

A function is said to allow an exception of type E if its exception-specification contains a type T for which a handler of type T would be a match (14.4 [except.handle]) for an exception of type E.



145. Deprecation of prefix ++

Section: D.7  [depr.impldec]     Status: TC1     Submitter: Mike Miller     Date: 23 Jul 1999

D.7 [depr.impldec] indicates that use of the postfix ++ with a bool operand is deprecated. Annex Clause Annex D [depr] says nothing about prefix ++. However, this use of prefix ++ is also deprecated, according to 7.6.2.3 [expr.pre.incr] paragraph 1. Presumably D.7 [depr.impldec] should be expanded to cover prefix ++, or another section should be added to Annex Clause Annex D [depr].

Proposed resolution (10/00):

Change the entire section D.7 [depr.impldec], including its heading, to read as follows:

D.1 Increment operator with bool operand [depr.incr.bool]

The use of an operand of type bool with the ++ operator is deprecated (see 7.6.2.3 [expr.pre.incr] and 7.6.1.6 [expr.post.incr]).






Issues with "C++11" Status


248. Identifier characters

Section: _N2691_.E  [extendid]     Status: C++11     Submitter: John Spicer     Date: 6 Oct 2000

[Voted into the WP at the November, 2010 meeting as paper N3146.]

The list of identifier characters specified in the C++ standard annex _N2691_.E [extendid] and the C99 standard annex D are different. The C99 standard includes more characters.

The C++ standard says that the characters are from "ISO/IEC PDTR 10176" while the C99 standard says "ISO/IEC TR 10176". I'm guessing that the PDTR is an earlier draft of the TR.

Should the list in the C++ standard be updated?

Tom Plum: In my opinion, the "identifier character" issue has not been resolved with certainty within SC22.

One critical difference in C99 was the decision to allow a compiler to accept more characters than are given in the annex. This allows for future expansion.

The broader issue concerns the venue in which the "identifier character" issue will receive ongoing resolution.

Notes from 10/00 meeting:

The core language working group expressed a strong preference (13/0/5 in favor/opposed/abstaining) that the list of identifier characters should be extensible, as is the case in C99. However, the fact that this topic is under active discussion by other bodies was deemed sufficient reason to defer any changes to the C++ specification until the situation is more stable.

Notes from October, 2005 meeting:

The working group expressed interest in the kind of approach taken by XML 1.1, in which the definition of an identifier character is done by excluding large ranges of the Unicode character set and accepting any character outside those ranges, rather than by affirmatively designating each identifier character in each language. As noted above, consideration of this issue was previously deferred pending other related standardization efforts. Clark Nelson will investigate whether these have reached a point at which progress on this issue in C++ is now possible.

Additional note (May, 2008):

Issue 663 also deals with this appendix, and the proposed resolution there is to update the table to reflect the newest available technical report, ISO/IEC TR 10176:2003. That resolution might be seen as sufficient for this issue, as well. However, that approach does not address several of the concerns mentioned in the discussion above: coordination with WG14, the extensibility of the list of identifiers, the alternative approach used in the XML specification, etc.




1063. [[hiding]] with non-attribute declarations

Section: _N3225_.7.6.5  [dcl.attr.override]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 2010-03-23

[Voted into the WP at the March, 2011 meeting as part of paper N3272.]

There are some kinds of declarations that can appear in a derived class and hide names from a base class, but for which the syntax does not permit a [[hiding]] attribute. For example:

    struct B1 {
        int N;
        int M;
    };
    struct B2 {
        int M;
    };
    struct [[base_check]] D: B1, B2 {
        enum { N };    // hides B1::N but cannot take an attribute
        using B1::M;   // hides B2::M but cannot take an attribute
    };

Additional note (October, 2010):

alias-declarations should also be considered in this regard.

Notes from the November, 2010 meeting:

Paper N3206 did not address these cases; in fact, it introduced additional member declarations that cannot be annotated as hiding a base class member (function-definitions and template-declarations), because the new virt-specifier applies to a member-declarator and none of these member-declarations uses a member-declarator.

Additional note (November, 2010):

The injected-class-name can also hide a name from a base class but cannot be annotated with new.




1065. [[hiding]] with [[override]]

Section: _N3225_.7.6.5  [dcl.attr.override]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 2010-03-24

[Voted into the WP at the November, 2010 meeting as part of paper N3206.]

The meaning of the [[base_check]] and [[hiding]] attributes is defined in terms of hiding as described in _N4868_.6.4.10 [basic.scope.hiding]. In that section, however, hiding is orthogonal to overriding: practically by definition, a function that overrides a base class virtual function also hides it. According to the current specification, the [[override]] and [[hiding]] attributes would always need to be specified together on every overriding function in a [[base_check]] class. This is presumably unintended, so the current wording should be amended so that [[override]] implies [[hiding]] or some such.




1133. Keywords vs attributes for control of hiding and overriding

Section: _N3225_.7.6.5  [dcl.attr.override]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting in paper N3206.]

N3092 comment US 44

The facility for checking hiding and overriding of base class members should not use the attribute syntax but should use keywords instead. Concerns about breaking code by changing current identifiers into keywords can be addressed by using contextual keywords, i.e., by putting the keywords into syntactic locations where identifiers cannot appear and thus continuing to allow their use as ordinary identifiers in other contexts.

Notes from the August, 2010 meeting:

CWG expressed a preference for non-contextual keywords for these features.




1144. Remove access declarations

Section: _N3225_.11.3  [class.access.dcl]     Status: C++11     Submitter: US     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 56

Access declarations were deprecated in the 1998 standard and have no benefits over using-declarations. They should be removed in C++0x.

Proposed resolution (August, 2010):

  1. Delete _N3225_.11.3 [class.access.dcl].

  2. Delete _N3225_.D.3 [depr.access.dcl].

  3. Delete the following production from the grammar in 11.4 [class.mem] paragraph 1:

  4. Change 11.4 [class.mem] paragraph 1 as follows:
  5. ...Except when used to declare friends (11.4) or to introduce the name of a member of a base class into a derived class (7.3.3, 11.3), member-declarations declare members of the class...
  6. Delete 9.9 [namespace.udecl] paragraph 19:

  7. [Note: use of access-declarations (_N3225_.11.3 [class.access.dcl]) is deprecated; member using-declarations provide a better alternative. —end note]



945. Use of this in a late-specified return type

Section: _N4567_.5.1.1  [expr.prim.general]     Status: C++11     Submitter: Alisdair Meredith     Date: 18 July, 2009

(Moved from issue 760.)

Although it was considered and rejected as part of issue 643, more recent developments may argue in favor of allowing the use of this in a late-specified return type. In particular, declaring the return type for a forwarding function in a derived class template that invokes a member function of a dependent base class is difficult without this facility. For example:

    template <typename T> struct derived: base<T> {
      auto invoke() -> decltype(this->base_func()) {
        return this->base_func();
      }
    };

(See also issue 1207 for another potential motivation for a change to this rule.)

Additional note (October, 2010):

The question should also be considered for parameter types; for example,

class comparable {
public:
   bool is_equal(decltype(*this) other) { // should be X&
       return /*...*/;
   }
};

Proposed resolution (March, 2011):

This issue is resolved by the resolution of issues 1017 and 1207 in document N3282.




1111. Remove dual-scope lookup of member template names

Section: _N4868_.6.5.6  [basic.lookup.classref]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the March, 2011 meeting.]

N3092 comment US 23

According to _N4868_.6.5.6 [basic.lookup.classref] paragraph 1,

In a class member access expression (7.6.1.5 [expr.ref]), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (13.3 [temp.names]) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template. If the lookup in the class of the object expression finds a template, the name is also looked up in the context of the entire postfix-expression and

This makes the following ill-formed:

    #include <set>
    using std::set;
    struct X {
      template <typename T> void set(const T& value);
    };
    void foo() {
      X x;
      x.set<double>(3.2);
    }

That's confusing and unnecessary. The compiler has already done the lookup in X's scope, and the obviously-correct resolution is that one, not the identifier from the postfix-expression's scope. Issue 305 fixed a similar issue for destructor names but missed member functions.

Suggested resolution: Delete the end of paragraph 1, starting with “If the lookup in the class...” and including all three bullets.

Proposed resolution (November, 2010):

  1. Change 6.5.5.2 [class.qual] bullet 1.2 as follows:

  2. Change _N4868_.6.5.6 [basic.lookup.classref] paragraph 1 as follows:

  3. In a class member access expression (7.6.1.5 [expr.ref]), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (13.3 [temp.names]) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template. If the lookup in the class of the object expression finds a template, the name is also looked up in the context of the entire postfix-expression and

  4. Change _N4868_.6.5.6 [basic.lookup.classref] paragraph 4 as follows:

  5. If the id-expression in a class member access is a qualified-id of the form

        class-name-or-namespace-name::...
    

    the class-name-or-namespace-name following the . or -> operator is looked up both in the context of the entire postfix-expression and in the scope of the class of the object expression. If the name is found only in the scope of the class of the object expression, the name shall refer to a class-name. If the name is found only in the context of the entire postfix-expression, the name shall refer to a class-name or namespace-name. If the name is found in both contexts, the class-name-or-namespace-name shall refer to the same entity. first looked up in the class of the object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire postfix-expression. [Note: See 6.5.5 [basic.lookup.qual], which describes the lookup of a name before ::, which will only find a type or namespace name. —end note]

  6. Change _N4868_.6.5.6 [basic.lookup.classref] paragraph 7 as follows:

  7. If the id-expression is a conversion-function-id, its conversion-type-id shall denote the same type in both the context in which the entire postfix-expression occurs and in the context of the class of the object expression (or the class pointed to by the pointer expression). is first looked up in the class of the object expression and the name, if found and denotes a type, is used. Otherwise it is looked up in the context of the entire postfix-expression and the name shall denote a type. [Example:

      struct A { };
      namespace N {
        struct A {
          void g() { }
          template <class T> operator T();
        };
      }
    
      int main() {
        N::A a;
        a.operator A();    // calls N::A::operator N::A
      }
    

    end example]




1220. Looking up conversion-type-ids

Section: _N4868_.6.5.6  [basic.lookup.classref]     Status: C++11     Submitter: Mike Miller     Date: 2010-11-13

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The resolution of issue 1111 changes _N4868_.6.5.6 [basic.lookup.classref] paragraph 7 to read,

[A] conversion-type-id is first looked up in the class of the object expression and the name, if found and denotes a type, is used. Otherwise it is looked up in the context of the entire postfix-expression and the name shall denote a type.

The result of this specification is that a non-type member declaration in the class scope of the object expression will not be found (although it will hide a base class type member of the same name), but a non-type declaration in the context of the expression will be found (and make the program ill-formed).

This is inconsistent with the way other lookups are handled when they occur in a context that requires a type. For example, the lookup for a nested-name-specifier “considers only namespaces, types, and templates whose specializations are types” (6.5.5 [basic.lookup.qual] paragraph 1); the lookup for a name appearing in an elaborated-type-specifier is done “ignoring any non-type names that have been declared” (6.5.6 [basic.lookup.elab] paragraph 2); and in the lookup for a name in a base-type-specifier, “non-type names are ignored” (11.7 [class.derived] paragraph 2). The lookup for a conversion-type-id should be similar, and the wording in _N4868_.6.5.6 [basic.lookup.classref] paragraph 7 adjusted accordingly.




1190. Operations on non-safely-derived pointers

Section: _N4885_6.7.5.5.4  [basic.stc.dynamic.safety]     Status: C++11     Submitter: Hans Boehm     Date: 2010-09-01

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

_N4885_6.7.5.5.4 [basic.stc.dynamic.safety] paragraph 4 only prohibits the dereferencing and deallocation of non-safely-derived pointers. This is insufficient. Explicit deallocation of storage is described as rendering invalid all pointers to that storage, with the result that all operations on such a pointer value causes undefined behavior (6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 4). The same should be true if the storage pointed to by a non-safely-derived pointer is garbage collected. In particular, the promise of objects having distinct addresses (6.7.2 [intro.object] paragraph 6) should not apply if one of those objects is designated by a non-safely-derived pointer.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change _N4885_6.7.5.5.4 [basic.stc.dynamic.safety] paragraph 4 as follows:

...Alternatively, an implementation may have strict pointer safety, in which case, if a pointer value that is not a safely-derived pointer value is dereferenced or deallocated, and an invalid pointer value, unless the referenced complete object is of dynamic storage duration and has not previously been declared reachable (_N4700_.23.11.2 [util.smartptr]), the behavior is undefined. [Note: this The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined, see 6.7.5.5.3 [basic.stc.dynamic.deallocation]. This is true even if the unsafely-derived pointer value might compare equal to some safely-derived pointer value. —end note] It is implementation defined...



784. List of incompatibilities with the previous Standard

Section: 4.2  [intro.structure]     Status: C++11     Submitter: UK     Date: 3 March, 2009

[Voted into the WP at the March, 2011 meeting as document N3288.]

N2800 comment UK 6

There should be a list of incompatibilities between the current and previous Standards, as in ISO/IEC TR 10176 4.1.1 paragraph 9.

(See document N2733 for an initial list of this information.)




1103. Reversion of phase 1 and 2 transformations in raw string literals

Section: 5.2  [lex.phases]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 13
N3092 comment US 14

“Raw” strings are still only Pittsburgh-rare strings: the reversion in phase 3 only applies to an r-char-sequence. It should apply to the entire raw string literal.

Proposed resolution (August, 2010):

  1. Change 5.2 [lex.phases] paragraph 1 phase 1 as follows:

  2. ...(An implementation may use any internal encoding, so long as an actual extended character encountered in the source file, and the same extended character expressed in the source file as a universal-character-name (i.e., using the \uXXXX notation), are handled equivalently except where this replacement is reverted in a raw string literal.).)
  3. Change 5.2 [lex.phases] paragraph 1 phase 3 as follows:

  4. ...[Example: see the handling of < within a #include preprocessing directive. —end example] Within the r-char-sequence of a raw string literal, any transformations performed in phases 1 and 2 (trigraphs, universal-character-names, and line splicing) are reverted.
  5. Change 5.2 [lex.phases] paragraph 1 phase 5 as follows:

  6. Each source character set member and universal-character-name in a character literal or a string literal, as well as each escape sequence and universal-character-name in a character literal or a non-raw string literal, is converted to the corresponding member of the execution character set (5.13.3 [lex.ccon], 5.13.5 [lex.string]); if there is no corresponding member, it is converted to an implementation-defined member other than the null (wide) character.
  7. Change 5.3 [lex.charset] paragraph 2 as follows:

  8. ...Additionally, if the hexadecimal value for a universal-character-name outside the c-char-sequence, s-char-sequence, or r-char-sequence of a character or string literal corresponds to a control character (in either of the ranges 0x000x1F or 0x7F0x9F, both inclusive) or to a character in the basic source character set, the program is ill-formed. [Footnote: A sequence of characters resembling a universal-character-name in an r-char-sequence (5.13.5 [lex.string]) does not form a universal-character-name. —end footnote]
  9. Change 5.4 [lex.pptoken] paragraph 3 as follows:

  10. If the input stream has been parsed into preprocessing tokens up to a given character:
  11. Delete footnote 24 in 5.13.5 [lex.string] paragraph 2:

  12. Use of characters with trigraph equivalents in a d-char-sequence may produce unintended results.
  13. Insert the following examples after 5.13.5 [lex.string] paragraph 4:

  14. [Example: The raw string

      R"a(
      )\
      a"
      )a"
    

    is equivalent to "\n)\\\na\"\n". The raw string

      R"(??)"
    

    is equivalent to "\?\?". The raw string

      R"#(
      )??="
      )#"
    

    is equivalent to "\n)\?\?=\"\n". —end example]




985. Alternative tokens and user-defined literals

Section: 5.5  [lex.digraph]     Status: C++11     Submitter: Michael Wong     Date: 19 October, 2009

5.11 [lex.key] paragraph 2 says,

Furthermore, the alternative representations shown in Table 4 for certain operators and punctuators (5.5 [lex.digraph]) are reserved and shall not be used otherwise:

Also, 5.5 [lex.digraph] paragraph 2 says,

In all respects of the language, each alternative token behaves the same, respectively, as its primary token, except for its spelling.

It is not clear whether the following example is well-formed:

    #define STR2(x)  #x
    #define STR(x)   STR2(x)
    int main() {
        return sizeof STR('\0'bitor 0) - sizeof STR('\0'bitor 0);
    }

In this example, bitor is not the | operator but the identifier in a user-defined-character-literal. Does this violate the restrictions of 5.11 [lex.key] and 5.5 [lex.digraph]?

Proposed resolution (March, 2011):

This issue is resolved by the resolution of issue 1239 in document N3262, since literal suffix identifiers must begin with an underscore.




1104. Global-scope template arguments vs the <: digraph

Section: 5.5  [lex.digraph]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 15

Passing a name qualified by the global scope operator :: as a template argument can inadvertently trigger recognition of the <: digraph, causing a syntax error. This should be handled by a lexical rule similar to the special treament given to >> so that <:: would be recognized as an open angle-bracket followed by the scope-resolution operator.

Proposed resolution (August, 2010):

Insert a bullet into the list in 5.4 [lex.pptoken] paragraph 3 as follows:

If the input stream has been parsed into preprocessing tokens up to a given character:



1105. Issues relating to TR 10176:2003

Section: 5.10  [lex.name]     Status: C++11     Submitter: CA     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting as paper N3146.]

N3092 comment CA 24
  1. “Combining characters should not appear as the first character of an identifier.” [Reference: ISO/IEC TR 10176:2003 (Annex A)] This is not reflected in FCD.

  2. Restrictions on the first character of an identifier are not observed as recommended in TR 10176:2003. The inclusion of digits (outside of those in the basic character set) under identifer-nondigit is implied by FCD.

  3. It is implied that only the “main listing” from Annex A is included for C++. That is, the list ends with the Special Characters section. This is not made explicit in FCD. Existing practice in C++03 as well as WG 14 (C, as of N1425) and WG 4 (COBOL, as of N4315) is to include a list in a normative Annex.

  4. Specify width sensitivity as implied by C++03: \uFF21 is not the same as A. Case sensitivity is already stated in 5.10 [lex.name].

Notes from the August, 2010 meeting:

CWG expressed interest in an approach by which all characters except for those in specifically-excluded categories would be acceptable identifier characters. This suggestion will be brought up with WG14 as a liaison matter in hopes of agreeing on a uniform approach between C and C++.




1106. Need more detail in nullptr keyword description

Section: 5.13.8  [lex.nullptr]     Status: C++11     Submitter: DE     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment DE 3

It is not sufficiently clear that std::nullptr_t is a distinct type and neither a pointer type nor a pointer-to-member type. Add a note in 5.13.8 [lex.nullptr] stating that, preferably with cross-references to the normative statements in 6.8 [basic.types].

Proposed resolution (September, 2010):

Change 5.13.8 [lex.nullptr] paragraph 1 as follows:

The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t. [Note: std::nullptr_t is a distinct type that is neither a pointer type nor a pointer to member type; rather, a prvalue of this type is a null pointer constant and can be converted to a null pointer value or null member pointer value. See 7.3.12 [conv.ptr] and 7.3.13 [conv.mem]. —end note]



1107. Overload resolution for user-defined integer literals

Section: 5.13.9  [lex.ext]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 17

In general, the parameter type of a literal operator must be the same as the argument passed to it. That is not the case for a user-defined-character-literal, where the argument could inadvertently match a literal operator intended for use with user-defined-integer-literals:

    typedef unsigned long long ULL;
    int operator "" X(ULL);
    int i = 'c'X; // operator"" X(ULL('c'))

Suggested resolution: Add the following phrase to the description in paragraph 6:

S shall contain a literal operator whose parameter type is the same as the type of ch.

Proposed resolution (August, 2010):

Change 5.13.9 [lex.ext] paragraph 6 as follows:

If L is a user-defined-character-literal, let ch be the literal without its ud-suffix. The S shall contain a literal operator (12.6 [over.literal]) whose only parameter has the type of ch and the literal L is treated as a call of the form
    operator "" X (ch )



1175. Disambiguating user-defined literals

Section: 5.13.9  [lex.ext]     Status: C++11     Submitter: Sebastian Gesemann     Date: 2010-08-10

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

A user-defined literal like 0x123DZ could be parsed either as a hexadecimal-literal of 0x123 and a ud-suffix of DZ or as a hexadecimal-literal of 0x123D and a ud-suffix of Z. There does not appear to be a rule that disambiguates the two possible parses.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 5.13.9 [lex.ext] paragraph 1 as follows:

If a token matches both user-defined-literal and another literal kind, it is treated as the latter. [Example: 123_km, 1.2LL, "Hello"s are all user-defined-literals, but 12LL is an integer-literal. —end example] The syntactic nonterminal preceding the ud-suffix in a user-defined-literal is taken to be the longest sequence of characters that could match that nonterminal. [Example: The ud-suffix in 1.0e0X is X, not e0X; in 0x1DZ, the ud-suffix is Z, not DZ. —end example]



1239. Hexadecimal floating-point literals vs user-defined literals

Section: 5.13.9  [lex.ext]     Status: C++11     Submitter: Howard Hinnant     Date: 2011-01-28

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

5.10 [lex.name] paragraph 3 says,

In addition, some identifiers are reserved for use by C++ implementations and standard libraries (_N4140_.17.6.4.3.2 [global.names]) and shall not be used otherwise; no diagnostic is required.

There is no corresponding mention in 5.13.9 [lex.ext] of the restrictions on user-defined literal suffixes in 16.4.5.3.6 [usrlit.suffix]. Furthermore, considering the likelihood of adding hexadecimal floating-point literals, whose syntax overlaps that of user-defined literals except for that restriction, it would be a good idea to require a diagnostic for a violation of that rule.




676. static_assert-declarations and general requirements for declarations

Section: 6.2  [basic.def]     Status: C++11     Submitter: Alisdair Meredith     Date: 12 February, 2008

[Voted into WP at August, 2010 meeting.]

6.2 [basic.def] makes statements about declarations that do not appear to apply to static_assert-declarations. For example, paragraph 1 says,

A declaration (9.1 [dcl.pre]) introduces names into a translation unit or redeclares names introduced by previous declarations. A declaration specifies the interpretation and attributes of these names.

What name is being declared or described by a static_assert-declaration?

Also, paragraph 2 lists the kinds of declarations that are not definitions, and a static_assert-declaration is not among them. Is it intentional that static_assert-declarations are definitions?

Proposed resolution (March, 2010):

Change 6.2 [basic.def] paragraphs 1-2 as follows:

A declaration (9.1 [dcl.pre]) may introduces one or more names into a translation unit or redeclares names introduced by previous declarations. A If so, the declaration specifies the interpretation and attributes of these names. A declaration may also have effects including

A declaration is a definition unless it declares a function without specifying the function's body (9.5 [dcl.fct.def]), it contains the extern specifier (9.2.2 [dcl.stc]) or a linkage-specification25 (9.11 [dcl.link]) and neither an initializer nor a function-body, it declares a static data member in a class definition (11.4.9 [class.static]), it is a class name declaration (11.3 [class.name]), it is an opaque-enum-declaration (9.7.1 [dcl.enum]), or it is a typedef declaration (9.2.4 [dcl.typedef]), a using-declaration (9.9 [namespace.udecl]), a static_assert-declaration (9.1 [dcl.pre]), an attribute-declaration (9.1 [dcl.pre]), an empty-declaration (9.1 [dcl.pre]), or a using-directive (9.8.4 [namespace.udir]). [Example:...




758. Missing cases of declarations that are not definitions

Section: 6.2  [basic.def]     Status: C++11     Submitter: Bjarne Stroustrup     Date: 22 January, 2009

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

N2800 comment UK 23
N2800 comment UK 24

The list of declarations that are not definitions given in 6.2 [basic.def] paragraph 2 does not mention several plausible candidates: parameter declarations in non-defining function declarations, non-static data members, and template parameters. It might be argued that none of these are declarations (paragraph 1 does not use the syntactic non-terminal declaration but does explicitly refer to 9.1 [dcl.pre], where that non-terminal is defined). However, the list in paragraph 2 does mention static member declarations, which also are not declarations, so the intent is not clear.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 6.2 [basic.def] paragraph 2 as follows:

A declaration is a definition unless it declares a function without specifying the function's body (9.5 [dcl.fct.def]), it contains the extern specifier (9.2.2 [dcl.stc]) or a linkage-specification25 (9.11 [dcl.link]) and neither an initializer nor a function-body, it declares a static data member in a class definition (11.4 [class.mem], 11.4.9 [class.static]), it is a class name declaration (11.3 [class.name]), it is an opaque-enum-declaration (9.7.1 [dcl.enum]), it is a template-parameter (13.2 [temp.param]), it is a parameter-declaration (9.3.4.6 [dcl.fct]) in a function declaration that is not a definition, or it is a typedef declaration (9.2.4 [dcl.typedef]), an alias-declaration (9.2.4 [dcl.typedef]), a using-declaration (9.9 [namespace.udecl]), a static_assert-declaration (9.1 [dcl.pre]), an attribute-declaration (9.1 [dcl.pre]), an empty-declaration (9.1 [dcl.pre]), or a using-directive (9.8.4 [namespace.udir]).



1201. Are deleted and defaulted functions definitions?

Section: 6.2  [basic.def]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-09-20

[Voted into the WP at the March, 2011 meeting.]

According to 6.3 [basic.def.odr] paragraph 2,

A declaration is a definition unless it declares a function without specifying the function's body (9.5 [dcl.fct.def]), it contains the extern specifier (9.2.2 [dcl.stc]) or a linkage-specification25 (9.11 [dcl.link]) and neither an initializer nor a function-body...

Because = delete and = default are not forms of function-body, this description does not cover defaulted and deleted functions, even though these declarations are elsewhere referred to as being definitions.

Proposed resolution (January, 2011):

Change the grammar in 9.5.1 [dcl.fct.def.general] paragraph 1 as follows:




678. Language linkage of member function parameter types and the ODR

Section: 6.3  [basic.def.odr]     Status: C++11     Submitter: James Widman     Date: 15 February, 2008

[Voted into WP at August, 2010 meeting.]

I thought this case would result in undefined behavior according to 6.3 [basic.def.odr]:

    // t.h:
    struct A { void (*p)(); };

    // t1.cpp:
    #include "t.h" // A::p is a pointer to C++ func

    // t2.cpp:
    extern "C" {
    #include "t.h" // A::p is a pointer to C func
    }

...but I don't see how any of the bullets in the list in paragraph 5 apply.

Proposed resolution (March, 2010):

Add a new bullet following 6.3 [basic.def.odr] paragraph 5, second bullet:

...Given such an entity named D defined in more than one translation unit, then




1109. When is “use” a reference to the ODR meaning?

Section: 6.3  [basic.def.odr]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting as paper N3214.]

N3092 comment US 19

It is not always clear when an occurrence of the word “use” is intended as an implicit reference to 6.3 [basic.def.odr] and when it is simply the ordinary English meaning. This could be addressed by:

  1. Replacing all the non-technical appearances of the word “use” with synonyms such as “occurrence” or “appearance” or “reference to.”

  2. Following each occurrence of the word “use” that is intended in the technical sense with a cross-reference to 6.3 [basic.def.odr].

  3. Replacing all the technical occurrences of the word “use” with a different word or phrase, such as “odr-use,” that would unambiguously indicate the intent.




1174. When is a pure virtual function “used?”

Section: 6.3  [basic.def.odr]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-08-07

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

According to 6.3 [basic.def.odr] paragraph 2,

A variable or non-overloaded function whose name appears as a potentially-evaluated expression is used... A virtual member function is used if it is not pure.

However, that does not adequately address when a pure virtual function is used or not used. For example,

    struct S {
      virtual void pure1() = 0;
      virtual void pure2() = 0;
    };
    void f(S* p) {
      p->pure1();
      p->S::pure2();
    };

Both pure1 and pure2 satisfy the criterion that their name appears in a potentially-evaluated expression, but pure1 should not be considered “used” (which would require that it be defined); pure2 is “used” and must be defined.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 6.3 [basic.def.odr] paragraph 2 as follows:

...A variable or non-overloaded function whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (7.7 [expr.const]) and the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is immediately applied... A virtual member function is odr-used if it is not pure. A non-overloaded function whose name appears as a potentially-evaluated expression or a member of a set of candidate functions is odr-used if it is selected by overload resolution when referred to from a potentially-evaluated expression, are odr-used, unless the function is pure and its name is not explicitly qualified. [Note:...



1192. Inadvertent change to ODR and templates

Section: 6.3  [basic.def.odr]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-09-03

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

Issue 678 added a bullet to the list in 6.3 [basic.def.odr] paragraph 5, inadvertently removing the second bullet from the reach of the part of that paragraph that reads,

If D is a template and is defined in more than one translation unit, then the last four requirements from the list above shall apply to names from the template's enclosing scope used in the template definition (_N4868_.13.8.4 [temp.nondep]),

In fixing this error, the wording should be recast to be more robust in the face of possible further edits to the list (i.e., not just changing “four” to “five”).

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 6.3 [basic.def.odr] paragraph 5 as follows:

...If D is a template and is defined in more than one translation unit, then the last four preceding requirements from the list above shall apply both to names from the template's enclosing scope used in the template definition (_N4868_.13.8.4 [temp.nondep]), and also to dependent names at the point of instantiation (13.8.3 [temp.dep])...



1044. Point of declaration for an alias-declaration

Section: 6.4.2  [basic.scope.pdecl]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 2010-03-05

[Voted into the WP at the March, 2011 meeting.]

The current wording of 6.4.2 [basic.scope.pdecl] does not specify the point of declaration for an alias-declaration (although it does do so in paragraph 3 for a template alias: “The point of declaration of a template alias immediately follows the identifier for the alias being declared”). One might assume that an alias-declaration would be the same, but it's not clear that that is the right resolution (for either declaration, but especially for the alias-declaration).

An alias-declaration is intended to be essentially a different syntactic form of a typedef declaration (9.2.4 [dcl.typedef] paragraph 2). Placing the point of declaration at the trailing semicolon instead of following the name of the alias would allow more compatibility with the capabilities of typedefs, for instance:

    struct S { };
    namespace N {
        using S = S;
    }

Notes from the November, 2010 meeting:

The CWG agreed that the point of declaration for both template and non-template cases should be at the semicolon.

Proposed resolution (January, 2011):

Change 6.4.2 [basic.scope.pdecl] paragraph 3 as follows:

...The point of declaration of a template an alias or alias template immediately follows the identifier for the alias being declared the type-id to which the alias refers.



1210. Injection of elaborated-type-specifier in enumeration scope

Section: 6.4.2  [basic.scope.pdecl]     Status: C++11     Submitter: Johannes Schaub     Date: 2010-10-13

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

According to 6.4.2 [basic.scope.pdecl] paragraph 6,

for an elaborated-type-specifier of the form

if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest non-class, non-function-prototype scope that contains the declaration.

This should have been, but was not, updated when enumeration scope (6.4.8 [basic.scope.enum]) was added:

    enum class E {
        e = sizeof((struct S*)0)
    };

Presumably the name S belongs to the same scope as E, not the enumeration scope of E.




997. Argument-dependent lookup and dependent function template parameter types

Section: 6.5.4  [basic.lookup.argdep]     Status: C++11     Submitter: James Widman     Date: 6 November, 2009

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

6.5.4 [basic.lookup.argdep] paragraph 2 excludes dependent parameter types and return types from consideration in determining the associated classes and namespaces of a function template. Presumably this means that an example like

    namespace N {
      template<class T> struct A { };
      void f(void (*)());
    }

    template <class T>
    void g(T, N::A<T>);

    void g();

    int main() {
      f(g);
    }

is ill-formed because the second parameter of the function template g does not add namespace N to the list of associated namespaces. This was probably unintentional.

See also issue 1015.

Notes from the November, 2010 meeting:

The CWG agreed that the rules should be changed to make this example well-formed.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 6.5.4 [basic.lookup.argdep] paragraph 2 as follows:

...In addition, if the argument is the name or address of a set of overloaded functions and/or function templates, its associated classes and namespaces are the union of those associated with each of the members of the set, i.e., the classes and namespaces associated with its (non-dependent) parameter types and return type. Additionally, if the aforementioned set of overloaded functions is named with a template-id, its associated classes and namespaces are those of its type template-arguments and its template template-arguments.

This resolution also resolves issue 1015.

[Drafting note: It's not clear that we need the inserted text above, because for the example in issue 1015, the type N::S is already represented in the type of the function address, so there is no need to pull it from template arguments. For cases where template parameters are not represented in the function type, it's not clear that we want ADL to reach further.]




1015. Template arguments and argument-dependent lookup

Section: 6.5.4  [basic.lookup.argdep]     Status: C++11     Submitter: Jason Merrill     Date: 2009-12-24

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

Currently, according to 6.5.4 [basic.lookup.argdep] paragraph 2, explicit template arguments in a function argument do not contribute to the associated namespaces in a function call, although they plausibly should in an example like the following:

    namespace N {
        struct S { };
        void f(void (*)(S));
    };

    template<typename T> void g(T);

    void h() {
        f(g<N::S>);    // Should find N::f
    }

See also issue 997.

Proposed resolution (November, 2010) [SUPERSEDED]:

This issue is resolved by the resolution of issue 997.




373. Lookup on namespace qualified name in using-directive

Section: 6.5.7  [basic.lookup.udir]     Status: C++11     Submitter: Clark Nelson     Date: 15 August 2002

[Voted into WP at August, 2010 meeting.]

Is this case valid? G++ compiles it.

namespace X {
  namespace Y {
    struct X {
      void f()
      {
        using namespace X::Y;
        namespace Z = X::Y;
      }
    };
  }
}

The relevant citation from the standard is 6.5.7 [basic.lookup.udir]: "When looking up a namespace-name in a using-directive or namespace-alias-definition, only namespace names are considered." This statement could reasonably be interpreted to apply only to the last element of a qualified name, and that's the way EDG and Microsoft seem to interpret it.

However, since a class can't contain a namespace, it seems to me that this interpretation is, shall we say, sub optimal. If the X qualifiers in the above example are interpreted as referring to the struct X, an error of some sort is inevitable, since there can be no namespace for the qualified name to refer to. G++ apparently interprets 6.5.7 [basic.lookup.udir] as applying to nested-name-specifiers in those contexts as well, which makes a valid interpretation of the test possible.

I'm thinking it might be worth tweaking the words in 6.5.7 [basic.lookup.udir] to basically mandate the more useful interpretation. Of course a person could argue that the difference would matter only to a perverse program. On the other hand, namespaces were invented specifically to enable the building of programs that would otherwise be considered perverse. Where name clashes are concerned, one man's perverse is another man's real world.

Proposed Resolution (November, 2006):

Change 6.5.7 [basic.lookup.udir] paragraph 1 as follows:

When looking up a namespace-name in a using-directive or namespace-alias-definition, In a using-directive or namespace-alias-definition, during the lookup for a namespace-name or for a name in a nested-name-specifier, only namespace names are considered.



1112. constexpr variables should have internal linkage like const

Section: 6.6  [basic.link]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 24

One of the critieria for giving a name internal linkage is “a variable that is explicitly declared const and neither explicitly declared extern nor previously declared to have external linkage.” This should presumably apply to variables declared constexpr as well.

Proposed resolution (August, 2010):

Change 6.6 [basic.link] bullet 3.2 as follows:




1113. Linkage of namespace member of unnamed namespace

Section: 6.6  [basic.link]     Status: C++11     Submitter: DE     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment DE 4

It is odd that, in the following example, N has no linkage but g has external linkage:

    namespace {
      namespace N // has no linkage
      {
        void g(); // has external linkage
      }
    }

Proposed resolution (August, 2010):

Change 6.6 [basic.link] paragraph 4 as follows:

An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage. All other namespaces have external linkage. A name having namespace scope that has not been given internal linkage above has external linkage the same linkage as the enclosing namespace if it is the name of



1189. Address of distinct base class subobjects

Section: 6.7.2  [intro.object]     Status: C++11     Submitter: Gabriel Dos Reis     Date: 2010-08-31

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

6.7.2 [intro.object] paragraph 6 says,

Two distinct objects that are neither bit-fields nor base class subobjects of zero size shall have distinct addresses.

This formulation leaves open the possibility that two base class subobjects of the same type could have the same address (because one or both might be zero-length base class subobjects).

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 6.7.2 [intro.object] paragraph 6 as follows:

Unless an object is a bit-field or a base class subobject of zero size, the address of that object is the address of the first byte it occupies. Two distinct objects that are neither not bit-fields nor base class subobjects of zero size shall have distinct addresses, if both have the same type or if not both are base class subobjects of zero size...



1114. Incorrect use of placement new in example

Section: 6.7.3  [basic.life]     Status: C++11     Submitter: GB     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment GB 18

The example in 6.7.3 [basic.life] paragraph 9 reads,

    struct B {
      B();
      ~B();
    };

    const B b;

    void h() {
      b.~B();
      new (&b) const B;  // undefined behavior
    }

Assuming that the placement new is intended to use the operator defined in the Standard library, the new-expression is ill-formed, because there is no implicit conversion from “pointer to const B” to void*.

Proposed resolution (August, 2010):

Change the example in 6.7.3 [basic.life] paragraph 9 as follows:

    ...
    new (const_cast<B *>(&b)) const B;  // undefined behavior
    ...



1090. Alignment of subobjects

Section: 6.7.6  [basic.align]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 2010-06-23

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The current wording of the Standard does not recognize the fact that the alignment of a complete object of a given type may be different from its alignment as a subobject. This arises in particular with virtual base classes. For example,

    struct B { long double d; };
    struct D: virtual B { char c; };

When D is a complete object, it will have a subobject of type B, which must be aligned appropriately for a long double. On the other hand, if D appears as a subobject of another object, the B subobject might be part of a different subobject, reducing the alignment requirement on the D subobject.

The Standard should make clear that it is the complete-object alignment that is being described, in parallel with the distinction between the size of a complete object and a subobject of the same type.




1115. C-compatible alignment specification

Section: 6.7.6  [basic.align]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting in document N3190.]

N3092 comment US 25
N3092 comment GB 31

The C and C++ approaches to alignment are incompatible. See document PL22.16 10-0083 = WG21 N3093 for details.

Notes from the August, 2010 meeting:

CWG agreed that the alignment specifier should be a keyword instead of an attributes.




1180. Over-aligned class types

Section: 6.7.6  [basic.align]     Status: C++11     Submitter: Clark Nelson     Date: 2010-08-25

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

Now that alignment can be applied directly to class types, the current wording of the note at the end of 6.7.6 [basic.align] paragraph 3 is no longer correct:

[Note: every over-aligned type is or contains a class type with a non-static data member to which an extended alignment has been applied. —end note]

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 6.7.6 [basic.align] paragraph 3 as follows:

[Note: every over-aligned type is or contains a class type with a non-static data member to which an extended alignment has been applied to which extended alignment applies (possibly through a non-static data member). —end note]



1237. Deprecated implicit copy assignment in example

Section: 6.7.7  [class.temporary]     Status: C++11     Submitter: Ryou Ezoe     Date: 2011-01-25

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

An implicit declaration of a copy assignment operator is deprecated if the class has a user-declared copy constructor or a user-declared destructor. However, the example in 6.7.7 [class.temporary] relies on such an implicit declaration; an explicit declaration for the copy assignment operator for class X should be provided:

    class X {
    public:
      X(int);
      X(const X&);
      ~X();
    };

    class Y {
    public:
      Y(int);
      Y(Y&&);
      ~Y();
    };

    X f(X);
    Y g(Y);

    void h() {
      X a(1);
      X b = f(X(2));
      Y c = g(Y(3));
      a = f(a);  // relies on implicitly-declared X::operator=(const X&)
    }



619. Completeness of array types

Section: 6.8  [basic.types]     Status: C++11     Submitter: Steve Clamage     Date: 16 February 2007

[Voted into WP at August, 2010 meeting.]

Is the following example well-formed?

    struct S {
        static char a[5];
    };
    char S::a[];    // Unspecified bound in definition

6.6 [basic.link] paragraph 10 certainly makes allowance for declarations to differ in the presence or absence of a major array bound. However, 6.2 [basic.def] paragraph 5 says that

A program is ill-formed if the definition of any object gives the object an incomplete type (6.8 [basic.types]).

6.8 [basic.types] paragraph 7 says,

The declared type of an array object might be an array of unknown size and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points (“array of unknown bound of T” and “array of N T”) are different types.

This wording appears to make no allowance for the C concept of “composite type;” instead, each declaration is said to have its own type. By this interpretation, the example is ill-formed, because the type declared by the definition of S::a is incomplete.

If the example is intended to be well-formed, the Standard needs explicit wording stating that an omitted array bound in a declaration is implicitly taken from that of a visible declaration of that object, if any.

Notes from the April, 2007 meeting:

The CWG agreed that this usage should be permitted.

Proposed resolution (June, 2008):

  1. Change 9.3.4.5 [dcl.array] paragraph 1 as follows:

  2. ...If Except as noted below, if the constant expression is omitted, the type of the identifier of D is “derived-declarator-type-list array of unknown bound of T,” an incomplete object type...
  3. Change 9.3.4.5 [dcl.array] paragraph 3 as follows:

  4. When several “array of” specifications are adjacent, a multidimensional array is created; only the first of the constant expressions that specify the bounds of the arrays can may be omitted only for the first member of the sequence. [Note: this elision is useful for function parameters of array types, and when the array is external and the definition, which allocates storage, is given elsewhere. —end note] In addition to declarations in which an incomplete object type is allowed, an array bound may be omitted in the declaration of a function parameter (9.3.4.6 [dcl.fct]). The first constant-expression can An array bound may also be omitted when the declarator is followed by an initializer (9.4 [dcl.init]). In this case the bound is calculated from the number of initial elements (say, N) supplied (9.4.2 [dcl.init.aggr]), and the type of the identifier of D is “array of N T.” Furthermore, if there is a visible declaration of the name declared by the declarator-id (if any) in which the bound was specified, an omitted array bound is taken to be the same as in that earlier declaration.

Notes from the September, 2008 meeting:

The proposed resolution does not capture the result favored by the CWG: array bound information should be accumulated across declarations within the same scope, but a block extern declaration in a nested scope should not inherit array bound information from the outer declaration. (This is consistent with the treatment of default arguments in function declarations.) For example:

    int a[5];
    void f() {
        extern int a[];
        sizeof(a);
    }

Although there was some confusion about the C99 wording dealing with this case, it is probably well-formed in C99. However, it should be ill-formed in C++, because we want to avoid the concept of “compatible types” as it exists in C.

Proposed resolution (March, 2010):

  1. Change 9.3.4.5 [dcl.array] paragraph 1 as follows:

  2. ...If Except as noted below, if the constant expression is omitted, the type of the identifier of D is “derived-declarator-type-list array of unknown bound of T,” an incomplete object type...
  3. Change 9.3.4.5 [dcl.array] paragraphs 3-4 as follows:

  4. When several “array of” specifications are adjacent, a multidimensional array is created; only the first of the constant expressions that specify the bounds of the arrays can may be omitted only for the first member of the sequence. [Note: this elision is useful for function parameters of array types, and when the array is external and the definition, which allocates storage, is given elsewhere. —end note] In addition to declarations in which an incomplete object type is allowed, an array bound may be omitted in some cases in the declaration of a function parameter (9.3.4.6 [dcl.fct]). The first constant-expression can An array bound may also be omitted when the declarator is followed by an initializer (9.4 [dcl.init]). In this case the bound is calculated from the number of initial elements (say, N) supplied (9.4.2 [dcl.init.aggr]), and the type of the identifier of D is “array of N T.” Furthermore, if there is a preceding declaration of the entity in the same scope in which the bound was specified, an omitted array bound is taken to be the same as in that earlier declaration, and similarly for the definition of a static data member of a class.

    [Example:...

    ...can reasonably appear in an expression. Finally,

      extern int x[10];
      struct S {
        static int y[10];
      };
    
      int x[];                //OK: bound is 10
      int S::y[];             //OK: bound is 10
    
      void f() {
        extern int x[];
        int i = sizeof(x);    //error: incomplete object type
      }
    

    end example]




981. Constexpr constructor templates and literal types

Section: 6.8  [basic.types]     Status: C++11     Submitter: Gabriel Dos Reis     Date: 16 October, 2009

[Voted into the WP at the March, 2011 meeting.]

6.8 [basic.types] paragraph 10 requires that a class have at least one constexpr constructor other than the copy constructor in order to be considered a literal type. However, a constexpr constructor template might be instantiated in such a way that the constexpr specifier is ignored (9.2.6 [dcl.constexpr] paragraph 5) . It is therefore not known whether a class with a constexpr constructor template is a literal type or not until the constructor template is specialized, which could mean that an example like

    struct IntValue {
      template<typename T>
        constexpr IntValue(T t) : val(t) { }

      constexpr intmax_t get_value() { return val; }

     private:
       intmax_t val;
    };

is ill-formed, because it is an error to declare a member function (like get_value()) of a non-literal class to be constexpr (9.2.6 [dcl.constexpr] paragraph 6).

6.8 [basic.types] paragraph 10 should be revised so that either a constexpr constructor or constexpr constructor template allows a class to be a literal type.

Proposed resolution (November, 2010):

Change 6.8 [basic.types] paragraph 10 as follows:

A type is a literal type if it is:

This resolution also resolves issues 1071 and 1198.




1071. Literal class types and trivial default constructors

Section: 6.8  [basic.types]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-06-02

[Voted into the WP at the March, 2011 meeting.]

According to 6.8 [basic.types] paragraph 10, one of the requirements for a literal class type is

This rule has unfortunate consequences. For example, in

    struct A { int x; };
    struct B: A { int y; };

B is a literal type, even though it is impossible to initialize a constant of that type. Conversely, in

    struct C {
        int a, b;
        constexpr C(int x, int y): a(x), b(y) { }
    };
    struct D {
        int x;
        C c;
    };

D is not a literal type, even though it could be initialized as an aggregate.

It would be an improvement to replace the requirement for a trivial default constructor with a requirement that the class be an aggregate.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 981.




1181. What is a “built-in type?”

Section: 6.8  [basic.types]     Status: C++11     Submitter: Barry Hedquist     Date: 2010-08-26

[Voted into the WP at the March, 2011 meeting.]

The current draft uses the term “built-in type” several times, but it is not defined anywhere. The Index appears to make it synonymous with “fundamental type,” but the implication of 7.3 [conv] paragraph 1 is that compound types like pointers should also be considered as “built-in.”

Proposed resolution (January, 2011):

  1. Change 6.7.2 [intro.object] paragraph 7 as follows:

  2. [Note: C++ provides a variety of built-in fundamental types and several ways of composing new types from existing types (6.8 [basic.types]). —end note]
  3. Change 5.4 [lex.pptoken] as follows:

  4. [Example: The program fragment x+++++y is parsed as x ++ ++ + y, which, if x and y are of built-in have integral types, violates a constraint on increment operators, even though the parse x ++ + ++ y might yield a correct expression. —end example]
  5. Change 17.3.5.2 [numeric.limits.members] paragraph 58 as follows:

  6. True if the set of values representable by the type is finite.220 [Note: All built-in fundamental types (6.8.2 [basic.fundamental]) are bounded. This member would be false for arbitrary precision types. —end note]
  7. Change 25.3.1 [iterator.requirements.general] paragraph 1 as follows:

  8. ...All input iterators i support the expression *i, resulting in a value of some class, enumeration, or built-in object type T, called the value type of the iterator...
  9. Change 25.3.5.3 [input.iterators] paragraph 1 as follows:

  10. A class or a built-in pointer type X satisfies the requirements of an input iterator for the value type T if X satisfies the Iterator (25.3.5.2 [iterator.iterators]) and EqualityComparable (Table 33) requirements and the expressions in Table 107 are valid and have the indicated semantics.
  11. Change 25.3.5.4 [output.iterators] paragraph 1 as follows:

  12. A class or a built-in pointer type X satisfies the requirements of an output iterator if X if X satisfies the Iterator requirements (25.3.5.2 [iterator.iterators]) and the expressions in Table 108 are valid and have the indicated semantics.
  13. Change 25.3.5.5 [forward.iterators] paragraph 1 as follows:

  14. A class or a built-in pointer type X satisfies the requirements of a forward iterator if...
  15. Change 25.3.5.6 [bidirectional.iterators] paragraph 1 as follows:

  16. A class or a built-in pointer type X satisfies the requirements of a bidirectional iterator if, in addition to satisfying the requirements for forward iterators, the following expressions are valid as shown in Table 110.
  17. Change 25.3.5.7 [random.access.iterators] paragraph 1 as follows:

  18. A class or a built-in pointer type X satisfies the requirements of a random access iterator if, in addition to satisfying the requirements for bidirectional iterators, the following expressions are valid as shown in Table 111.
  19. Change C.7.3 [diff.basic] section 3.1 as follows:

  20. Rationale: This avoids having different initialization rules for built-in fundamental types and user-defined types.
  21. Change C.7.7 [diff.class] section 9.1 as follows:

  22. ...This new name space definition provides important notational conveniences to C++ programmers and helps making the use of the user-defined types as similar as possible to the use of built-in fundamental types...
  23. Delete the index entry, “built-in type; see fundamental type

    .”

(Note: This resolution assumes that the resolution for issue 572 has been applied, removing “built-in type” from 7.3 [conv] paragraph 1.)




1198. Literal types and copy constructors

Section: 6.8  [basic.types]     Status: C++11     Submitter: Jason Merrill     Date: 2010-09-16

[Voted into the WP at the March, 2011 meeting.]

According to 6.8 [basic.types] paragraph 10, a literal class type has

Is this intended to mean that

    struct A {
       A(const A&) = default;
       A(A&);
    };

is a literal class because it does have a trivial copy constructor even though it also has a non-trivial one? That seems inconsistent with the prohibition of non-trivial move constructors.

My preference would be to resolve this inconsistency by dropping the restriction on non-trivial move constructors. It seems to me that having a trivial copy or move constructor is sufficient, we don't need to prohibit additional non-trivial ones. Actually, it's not clear to me that we need the first condition either; a literal type could be used for singleton variables even if it can't be copied.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 981.




1219. Non-static data member initializers in constant expressions

Section: 6.8  [basic.types]     Status: C++11     Submitter: Jens Maurer     Date: 2010-11-13

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The current treatment of constexpr constructors and constant expressions does not deal with the initializers for non-static data members, which should also be required to be constant expressions.

Proposed resolution (November, 2010) [SUPERSEDED]:

  1. Change 6.9.3.2 [basic.start.static] paragraph 2 as follows:

  2. Change 6.8 [basic.types] paragraph 10 as follows (wording assumes the proposed resolution of issue 981)

  3. A type is a literal type if it is:




1055. Permissible uses of void

Section: 6.8.2  [basic.fundamental]     Status: C++11     Submitter: Nikolay Ivchenkov     Date: 2010-03-17

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

According to 6.8.2 [basic.fundamental] paragraph 9,

Any expression can be explicitly converted to type cv void (7.6.3 [expr.cast]). An expression of type void shall be used only as an expression statement (8.3 [stmt.expr]), as an operand of a comma expression (7.6.20 [expr.comma]), as a second or third operand of ?: (7.6.16 [expr.cond]), as the operand of typeid, or as the expression in a return statement (8.7.4 [stmt.return]) for a function with the return type void.

First, this is self-contradictory: if “any expression” can be converted to void, why is such a conversion not listed among the acceptable uses of an expression of type void?

Second, presumably an expression of type void can be used as an operand of decltype, but this use is not listed.

Finally, there are several places in the Standard that speak of expressions having a cv-qualified void type (7.6.16 [expr.cond] paragraph 2, 8.7.4 [stmt.return] paragraph 3) . However, an expression of type void is a non-class prvalue, and there are no cv-qualified non-class prvalues (7.2.1 [basic.lval] paragraph 4).

Proposed resolution (February, 2011) [SUPERSEDED]:

  1. Change 6.8.2 [basic.fundamental] paragraph 9 as follows:

  2. ...Any expression can be explicitly converted to type cv void (7.6.3 [expr.cast]). An expression of type void shall be used only as an expression statement (8.3 [stmt.expr]), as an operand of a comma expression (7.6.20 [expr.comma]), as a second or third operand of ?: (7.6.16 [expr.cond]), as the operand of typeid or decltype, or as the expression in a return statement (8.7.4 [stmt.return]) for a function with the return type void, or as the operand of an explicit conversion to type cv void.
  3. Change 7.6.16 [expr.cond] paragraph 2 as follows:

  4. If either the second or the third operand has type (possibly cv-qualified) void, then...
  5. Change 8.7.4 [stmt.return] paragraph 3 as follows:

  6. A return statement with an expression of type cv void can be used only in functions with a return type of cv void; the expression is evaluated just before the function returns to its caller.



1102. Better example of undefined behavior

Section: 6.9.1  [intro.execution]     Status: C++11     Submitter: GB     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment GB 6

There are core issues surrounding the undefined behavior of dereferencing a null pointer. It appears the intent is that dereferencing is well defined, but using the result of the dereference will yield undefined behavior. This topic is too confused to be the reference example of undefined behavior, or should be stated more precisely if it is to be retained.

(See also issue 232.)

Proposed resolution (September, 2010):

Change 6.9.1 [intro.execution] paragraph 4 as follows:

Certain other operations are described in this International Standard as undefined (for example, the effect of dereferencing the null pointer attempting to modify a const object). [Note:...



1173. Unclear specification of effects of signal handling

Section: 6.9.1  [intro.execution]     Status: C++11     Submitter: GB     Date: 2010-08-06

[Voted into the WP at the November, 2010 meeting.]

N3092 comment GB 7

The current wording of 6.9.1 [intro.execution] paragraph 6 could be read as saying that any signal would leave the program in an unspecified state after completing.

Proposed resolution (August, 2010):

Change 6.9.1 [intro.execution] paragraph 6 as follows:

When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects which are neither

are unspecified during the execution of the signal handler, and the value of any object not in either of these two categories that is modified by the handler becomes undefined.




1176. Definition of release sequence

Section: 6.9.2  [intro.multithread]     Status: C++11     Submitter: CA, GB     Date: 2010-08-10

[Voted into the WP at the November, 2010 meeting as part of paper N3196.]

N3092 comment CA 12
N3092 comment GB 9

The current wording of the standard suggests that release sequences are maximal with respect to sequence inclusion, i.e. that if there are two release operations in the modification order,

mod       mod
rel1----->rel2----->w

then [rel1;rel2;w] is the only release sequence, as the other candidate [rel2;w] is included in it. This interpretation precludes synchronizing with releases which have other releases sequenced-before them. We believe that the intention is actually to define the maximal release sequence from a particular release operation, which would admit both [rel1;rel2;w] and [rel2;w].

Proposed resolution (August, 2010):

Change 6.9.2 [intro.multithread] paragraph 6 as follows:

A release sequence from a release operation A on an atomic object M is a maximal contiguous sub-sequence of side effects in the modification order of M, where the first operation is a release A, and every subsequent operation




1177. Intra-thread dependency-ordered-before

Section: 6.9.2  [intro.multithread]     Status: C++11     Submitter: CA     Date: 2010-08-10

[Voted into the WP at the November, 2010 meeting as part of paper N3196.]

N3092 comment CA 15

The current draft has release/acquire synchronize-with edges only between a release on one thread and an acquire on a different thread, whereas the definition of dependency-ordered-before permits the release and consume to be on the same thread; it seems odd to permit the latter. (At the moment function arguments can't race or sync with each other, but they can be dependency ordered before each other.)

We don't currently have an example in which this makes a real difference, but for symmetry could suggest changing the definition of dependency-ordered-before in 6.9.2 [intro.multithread].

Proposed resolution (August, 2010):

Change 6.9.2 [intro.multithread] paragraph 9 as follows:

An evaluation A is dependency-ordered before an evaluation B if

[Note:...




1187. Problems in initialization example

Section: 6.9.3.2  [basic.start.static]     Status: C++11     Submitter: Jason Merrill     Date: 2010-08-31

[Voted into the WP at the March, 2011 meeting.]

The note in 6.9.3.2 [basic.start.static] paragraph 3 contains the following example:

  inline double fd() { return 1.0; }
  extern double d1;
  double d2 = d1;     // unspecified:
                      // may be statically initialized to 0.0 or
                      // dynamically initialized to 1.0
  double d1 = fd();   // may be initialized statically to 1.0

The comment for d2 overlooks the third possibility: if both d1 and d2 are dynamically initialized, d2 will be initialized to 0.

Proposed resolution (November, 2010):

Change the comments in the example in 6.9.3.2 [basic.start.static] paragraph 3 as follows:

  inline double fd() { return 1.0; }
  extern double d1;
  double d2 = d1;     // unspecified:
                      // may be statically initialized to 0.0 or
                      // dynamically initialized to 1.0 to 0.0 if d1 is dynamically initialized, or 1.0 otherwise
  double d1 = fd();   // may be initialized statically or dynamically to 1.0

(The note should also be in running text following the bulleted list instead of appearing as a bulleted item, as well.)




1117. Incorrect note about xvalue member access expressions

Section: Clause 7  [expr]     Status: C++11     Submitter: JP     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment JP 1

One of the bullets in the note in Clause 7 [expr] paragraph 6 says that an expression is an xvalue if it is:

This is incorrect if the type of the non-static data member is a reference type (cf 7.6.1.5 [expr.ref] paragraph 4.)

Proposed resolution (September, 2010):

Change Clause 7 [expr] bullet 6.3 as follows:




964. Incorrect description of when the lvalue-to-rvalue conversion applies

Section: 7.2.1  [basic.lval]     Status: C++11     Submitter: Doug Gregor     Date: 14 September, 2009

[Voted into the WP at the November, 2010 meeting.]

7.2.1 [basic.lval] paragraph 7 says,

Whenever an lvalue appears in a context where an rvalue is expected, the lvalue is converted to an rvalue

That is not correct in the context of an attempt to bind an rvalue reference to an lvalue (9.4.4 [dcl.init.ref]).

Proposed resolution (October, 2009):

Change 7.2.1 [basic.lval] paragraph 7 as follows:

Whenever an lvalue appears in a context where an rvalue is expected and an lvalue is not explicitly prohibited (as, for example, in 9.4.4 [dcl.init.ref]), the lvalue it is converted to an rvalue; see 7.3.2 [conv.lval], 7.3.3 [conv.array], and 7.3.4 [conv.func].

Notes from the March, 2010 meeting:

This resolution needs to be reconsidered in light of the new expression taxonomy.

Proposed resolution (September, 2010):

Change 7.2.1 [basic.lval] paragraph 2 as follows:

Whenever a glvalue appears in a context where a prvalue is expected, the glvalue is converted to a prvalue; see 7.3.2 [conv.lval], 7.3.3 [conv.array], and 7.3.4 [conv.func]. [Note: An attempt to bind an rvalue reference to an lvalue is not such a context; see 9.4.4 [dcl.init.ref]. —end note]



572. Standard conversions for non-built-in types

Section: 7.3  [conv]     Status: C++11     Submitter: Jens Maurer     Date: 6 April 2006

[Voted into the WP at the March, 2011 meeting.]

7.3 [conv] paragraph 1 says,

Standard conversions are implicit conversions defined for built-in types.

However, enumeration types (which take part in the integral promotions) and class types (which take part in the lvalue-to-rvalue conversion) are not “built-in” types, so the definition of “standard conversions” is wrong.

Proposed resolution (October, 2006):

Change 7.3 [conv] paragraph 1 as follows:

Standard conversions are implicit conversions defined for built-in types with built-in meaning...



1034. Attributes for return statements in lambdas

Section: 7.5.5  [expr.prim.lambda]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 2010-02-18

[Voted into the WP at the November, 2010 meeting.]

7.5.5 [expr.prim.lambda] bullet 4.1 says,

if the compound-statement if [sic] of the form

the type of the returned expression...

The problem (besides the typo “if”) is that the attribute-specifier for a return statement precedes, rather than following, the keyword (Clause 8 [stmt.stmt] paragraph 1).

Proposed resolution (September, 2010):

Change 7.5.5 [expr.prim.lambda] bullet 4.1 as follows:




1062. Syntax of attribute-specifiers in lambdas

Section: 7.5.5  [expr.prim.lambda]     Status: C++11     Submitter: Peter Sommerlad     Date: 2010-03-23

[Voted into the WP at the November, 2010 meeting.]

N3092 comment CH 4

The adoption of paper N3067 at the March, 2010 meeting moved the position of the optional attribute-specifier in a function declarator from immediately following the parameter-declaration-clause to after the exception-specification. However, the grammar in 7.5.5 [expr.prim.lambda] paragraph 1 and the verbal description in paragraph 5 still have the attribute-specifier in a lambda-declarator at its old position. These should be updated to reflect the new function declarator syntax.

Proposed resolution (August, 2010):

  1. Change the grammar in 7.5.5 [expr.prim.lambda] paragraph 1 as follows:

  2. Change 7.5.5 [expr.prim.lambda] paragraph 5 as follows:

  3. ...Any attribute-specifiers appearing immediately after the lambda-expression's parameter-declaration-clause appertain An attribute-specifier in a lambda-declarator appertains to the type of the corresponding function call operator...



798. Overloaded subscript operator described in clause 5

Section: 7.6.1.2  [expr.sub]     Status: C++11     Submitter: UK     Date: 3 March, 2009

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

N2800 comment UK 53

7.6.1.2 [expr.sub] paragraph 2 deals with one particular aspect of the overloaded operator[], which seems out of place. Either 7.6.1.2 [expr.sub] should be augmented to discuss the overloaded operator[] in general or the information in paragraph 2 should be moved into 12.4.5 [over.sub].




1083. Passing an object to ellipsis with non-trivial move constructor

Section: 7.6.1.3  [expr.call]     Status: C++11     Submitter: Jason Merrill     Date: 2010-06-22

[Voted into the WP at the November, 2010 meeting.]

7.6.1.3 [expr.call] paragraph 7 should mention a non-trivial move constructor as well, for consistency with “trivially copyable.”

Proposed resolution (September, 2010):

Change 7.6.1.3 [expr.call] paragraph 7 as follows:

...Passing a potentially-evaluated argument of class type ( Clause 11 [class]) with having a non-trivial copy constructor, a non-trivial move constructor, or a non-trivial destructor, with no corresponding parameter is conditionally-supported, with implementation-defined semantics. If the argument...



1119. Missing case in description of member access ambiguity

Section: 7.6.1.5  [expr.ref]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 32

According to 7.6.1.5 [expr.ref] paragraph 5,

If E2 is a non-static data member or a non-static member function, the program is ill-formed if the class of which E2 is directly a member is an ambiguous base (6.5.2 [class.member.lookup]) of the naming class (11.8.3 [class.access.base]) of E2.

This does not cover the following case:

    struct A { int i; };
    struct B: A { };
    struct C: A, B { };
    void f(C* p) {
      p->A::i; // Should be ambiguous
    }

Notes (August, 2010):

The example in the FCD National Body comment is incorrect: it is missing the A:: in the next-to-last line.

The ambiguity actually is covered in the Standard but in an unexpected location: 11.8.3 [class.access.base] paragraph 6:

If a class member access operator, including an implicit “this->,” is used to access a non-static data member or non-static member function, the reference is ill-formed if the left operand (considered as a pointer in the “.” operator case) cannot be implicitly converted to a pointer to the naming class of the right operand.

An explanatory note, including a cross-reference to 11.8.3 [class.access.base], should be added to 7.6.1.5 [expr.ref] paragraph 6.

Proposed resolution (September, 2010):

Change 7.6.1.5 [expr.ref] paragraph 5 as follows:

If E2 is a non-static data member or a non-static member function, the program is ill-formed if the class of which E2 is directly a member is an ambiguous base (6.5.2 [class.member.lookup]) of the naming class (11.8.3 [class.access.base]) of E2. [Note: The program is also ill-formed if the naming class is an ambiguous base of the class type of the object expression; see 11.8.3 [class.access.base]. —end note]



1011. Standard conversions that cannot be inverted

Section: 7.6.1.9  [expr.static.cast]     Status: C++11     Submitter: John Spicer     Date: 2009-12-03

[Voted into the WP at the November, 2010 meeting.]

According to 7.6.1.9 [expr.static.cast] paragraph 7, static_cast can be used to perform the inverse of any standard conversion sequence except the lvalue-to-rvalue, array-to-pointer, function-to-pointer, and boolean conversions. The null pointer and null pointer-to-member conversions should also be listed — it should not be permitted to cast a pointer or pointer-to-member to either integral type or std::nullptr_t.

Proposed resolution (October, 2010):

  1. Change 7.3.12 [conv.ptr] paragraph 1 as follows:

  2. ...A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of pointer to object or pointer to function type. Such a conversion is called a null pointer conversion. Two null pointer values...
  3. Change 7.3.13 [conv.mem] paragraph 1 as follows:

  4. A null pointer constant (7.3.12 [conv.ptr]) can be converted to a pointer to member type; the result is the null member pointer value of that type and is distinguishable from any pointer to member not created from a null pointer constant. Such a conversion is called a null member pointer conversion. Two null member pointer values...
  5. Change 7.6.1.9 [expr.static.cast] paragraph 7 as follows:

  6. The inverse of any standard conversion sequence (7.3 [conv]), other than the not containing an lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), function-to-pointer (7.3.4 [conv.func]), and null pointer (7.3.12 [conv.ptr]), null member pointer (7.3.13 [conv.mem]), or boolean (7.3.14 [conv.fctptr]) conversions, can be performed explicitly using static_cast. A program is ill-formed...



1094. Converting floating-point values to scoped enumeration types

Section: 7.6.1.9  [expr.static.cast]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-07-17

[Voted into the WP at the March, 2011 meeting.]

According to 9.7.1 [dcl.enum] paragraph 10,

An expression of arithmetic or enumeration type can be converted to an enumeration type explicitly.

However, 7.6.1.9 [expr.static.cast] paragraph 10 says only,

A value of integral or enumeration type can be explicitly converted to an enumeration type.

This omits floating-point values. Presumably unscoped enumeration types are covered by paragraph 7,

The inverse of any standard conversion sequence ( 7.3 [conv]), other than the lvalue-to-rvalue (7.3.2 [conv.lval]), array-to- pointer (7.3.3 [conv.array]), function-to-pointer (7.3.4 [conv.func]), and boolean (7.3.14 [conv.fctptr]) conversions, can be performed explicitly using static_cast.

because 7.3.11 [conv.fpint] paragraph 2 allows an unscoped enumeration value to be implicitly converted to a floating point type. (Although that also covers the integral types, so it's not clear why they would be mentioned specifically in 7.6.1.9 [expr.static.cast] paragraph 10.) However, this should presumably say “arithmetic” instead of “integral” to match the statement in 9.7.1 [dcl.enum] paragraph 10.

Proposed resolution (November, 2010):

  1. Change 7.6.1.9 [expr.static.cast] paragraph 10 as follows:

  2. A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (9.7.1 [dcl.enum]). Otherwise, the resulting enumeration value is unspecified (and might not be in that range). A value of floating-point type can also be converted to an enumeration type. The resulting value is the same as converting the original value to the underlying type of the enumeration (7.3.11 [conv.fpint]), and subsequently to the enumeration type.
  3. Add the following footnote to the end of 9.7.1 [dcl.enum] paragraph 7:

  4. ...If the enumerator-list is empty, the values of the enumeration are as if the enumeration had a single enumerator with value 0. [Footnote: This set of values is used to define promotion and conversion semantics for the enumeration type; it does not exclude an expression of enumeration type from having a value that falls outside this range. —end footnote]
  5. Delete 9.7.1 [dcl.enum] paragraph 10:

  6. An expression of arithmetic or enumeration type can be converted to an enumeration type explicitly. The value is unchanged if it is in the range of enumeration values of the enumeration type; otherwise the resulting enumeration value is unspecified.



573. Conversions between function pointers and void*

Section: 7.6.1.10  [expr.reinterpret.cast]     Status: C++11     Submitter: Steve Adamczyk     Date: 13 April 2006

[Voted into the WP at the March, 2011 meeting.]

The resolution to issue 195 makes “converting a pointer to a function into a pointer to an object type or vice versa” conditionally-supported behavior. In doing so, however, it overlooked the fact that void is not an “object type” (6.8 [basic.types] paragraph 9). The wording should be amended to allow conversion to and from void* types.

Proposed resolution (November, 2010):

  1. Change _N4885_6.7.5.5.4 [basic.stc.dynamic.safety] paragraphs 1-2 as follows:

  2. A traceable pointer object is

    A pointer value is a safely-derived pointer to a dynamic object only if it has pointer-to-object an object pointer type and it is...

  3. Change 6.8.4 [basic.compound] paragraphs 3-4 as follows:

  4. The type of a pointer to void or a pointer to an object type is called an object pointer type. [Note: A pointer to void does not have a pointer-to-object type, however, because void is not an object type. —end note] The type of a pointer that can designate a function is called a function pointer type. A pointer to objects of type T is referred to as a “pointer to T.” [Example:...

    Objects of cv-qualified (6.8.5 [basic.type.qualifier]) or cv-unqualified type void* (pointer to void), A pointer to cv-qualified (6.8.5 [basic.type.qualifier]) or cv-unqualified void can be used to point to objects of unknown type. A void* Such a pointer shall be able to hold any object pointer. A cv-qualified or cv-unqualified (6.8.5 [basic.type.qualifier]) An object of type cv void* shall have the same representation and alignment requirements as a cv-qualified or cv-unqualified cv char*.

  5. Change 7.3.12 [conv.ptr] paragraph 1 as follows:

  6. ...A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of pointer to object or pointer to function object pointer or function pointer type...
  7. Change 7.3.13 [conv.mem] paragraph 2 footnote 58 as follows:

  8. ...Note that a pointer to member is not a pointer to object or a pointer to function an object pointer or a function pointer and...
  9. Change 7.6.1.10 [expr.reinterpret.cast] paragraphs 6-8 as follows:

  10. A pointer to a function pointer can be explicitly converted to a pointer to a function pointer of a different type...

    A pointer to an An object pointer can be explicitly converted to a pointer to a different object type an object pointer of a different type...

    Converting a pointer to a function into a pointer to an object function pointer to an object pointer type or vice versa is conditionally-supported...

  11. Change the note in 9.3.4.6 [dcl.fct] paragraph 6 as follows:

  12. [Note: function types are checked during the assignments and initializations of pointer-to-functions, reference-to-functions, and pointer-to-member-functions pointers to functions, references to functions, and pointers to member functions. —end note]
  13. In the “Index of Implementation-defined Behavior,” change the following item as indicated:

  14. converting pointer to function into pointer to object function pointer to object pointer and vice versa

[Drafting note: 7.6.2.9 [expr.delete] paragraph 1 was not changed, so the operand of delete still cannot be a void*. 12.5 [over.built] paragraph 14 was not changed, so void* pointers still do not get overloads for operator-. 13.2 [temp.param] paragraph 4 was not changed and thus continues to allow only pointers to objects, not object pointers, as non-type template parameters.]

(See also issue 1120.)




1120. reinterpret_cast and void*

Section: 7.6.1.10  [expr.reinterpret.cast]     Status: C++11     Submitter: GB     Date: 2010-08-02

[Voted into the WP at the March, 2011 meeting.]

N3092 comment GB 22

It is not permitted to use reinterpret_cast to convert between pointers to object type and pointers to void.

See also issue 573.

Proposed resolution (August, 2010):

Change 7.6.1.10 [expr.reinterpret.cast] paragraph 7 as follows:

A pointer to an object An object pointer can be explicitly converted to a pointer to a different object type an object pointer of a different type.70 When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (6.8 [basic.types]) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.

(Note: this resolution depends on that of issue 573.)




1086. const_cast to rvalue reference to function type

Section: 7.6.1.11  [expr.const.cast]     Status: C++11     Submitter: Steve Adamczyk     Date: 2010-06-25

[Voted into the WP at the November, 2010 meeting.]

7.6.1.11 [expr.const.cast] paragraph 1 refers to casting to an rvalue reference to function type. This was an accidental edit in the application of the value categories and should be removed.

Proposed resolution (October, 2010):

Change 7.6.1.11 [expr.const.cast] paragraph 1 as follows:

If T is an lvalue reference to object type or an rvalue reference to function type, the result is an lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue and the lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are performed on the expression v...
[Drafting note: with this change to the wording, an attempt to cast a function lvalue to either an lvalue or rvalue reference will be ill-formed because the function-to-pointer conversion will be applied to the operand and the resulting operand base type will be incompatible with the target base type.]


1121. Unnecessary ambiguity error in formation of pointer to member

Section: 7.6.2.2  [expr.unary.op]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 33

The resolution of issue 983 restored an error, inadvertently removed by the resolution of issue 39, for the formation of a member of an ambiguous base class. For example:

    struct B { int i; };
    struct I1: B { };
    struct I2: B { };
    struct D: I1, I2 { };
    int B::* pm = &D::i;    // Originally and again ambiguous

This error is not necessary, because the result of taking the address of a member of an ambiguous base class is a pointer to a member of that class; an actual ambiguity would occur only if that pointer to a base class member is converted to a pointer to a member of the derived class. (See issue 203, which suggests changing the result of taking the address of a member to reflect the naming class of the member instead of the class of which it is directly a member; if that change were made, the ambiguity error would be needed.)

The resolution of issue 983 should be reverted.

Proposed resolution (August, 2010):

  1. Change 7.6.2.2 [expr.unary.op] paragraph 3 as follows:

  2. ...If the operand is a qualified-id naming a non-static member m of some class C with type T, the result has type “pointer to member of class C of type T and is a prvalue designating C::m; the program is ill formed if C is an ambiguous base (10.2) of the class designated by the nested-name-specifier of the qualified-id. Otherwise...
  3. Change 6.5.2 [class.member.lookup] paragraph 13 as follows:

  4. [Note: Even if the result of name lookup is unambiguous, use of a name found in multiple subobjects might still be ambiguous (7.3.13 [conv.mem], 7.6.1.5 [expr.ref], 7.6.2.2 [expr.unary.op], 11.8.3 [class.access.base]). —end note]...



1122. Circular definition of std::size_t

Section: 7.6.2.5  [expr.sizeof]     Status: C++11     Submitter: GB     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment GB 24

The return type of the sizeof operator is defined as being of type std::size_t, defined in library 17.2 [support.types]. This, in turn, says that size_t is defined in the C standard, which in turn says that size_t is defined as the type of the result of the sizeof operator!

The C definition of sizeof returns an implementation-defined unsigned integer type, recommended not to have “an integer conversion rank greater than signed long int, unless the implementation supports objects large enough to make this necessary.”

Proposed resolution (September, 2010):

Add the following three paragraphs after 17.2 [support.types] paragraph 4:

The type ptrdiff_t is an implementation-defined signed integer type that can hold the difference of two subscripts in an array object, as described in 7.6.6 [expr.add].

The type size_t is an implementation-defined unsigned integer type that is large enough to contain the size in bytes of any object.

[Note: It is recommended that implementations choose types for ptrdiff_t and size_t whose integer conversion ranks (7.3.15 [conv.bool]) are no greater than that of signed long int unless a larger size is necessary to contain all the possible values. —end note]




1123. Destructors should be noexcept by default

Section: 7.6.2.7  [expr.unary.noexcept]     Status: C++11     Submitter: FI     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting as paper N3204.]

N3092 comment FI 17

Destructors should by default be noexcept. Such a rule should be obeyed even for cases where a destructor is defaulted. Then a throwing destructor would need to be declared noexcept(false), and the resulting code breakage is acceptable.

Notes from the August, 2010 meeting:

CWG agreed with the suggested direction.

(Duplicate of issue 1147.)




1061. Negative array bounds in a new-expression

Section: 7.6.2.8  [expr.new]     Status: C++11     Submitter: Sean Hunt     Date: 2010-03-23

[Voted into the WP at the November, 2010 meeting.]

Recent changes have added the requirement (7.6.2.8 [expr.new] paragraph 7) ,

If the value of that expression is such that the size of the allocated object would exceed the implementation-defined limit, no storage is obtained and the new-expression terminates by throwing an exception of a type that would match a handler (14.4 [except.handle]) of type std::bad_array_new_length (17.6.4.2 [new.badlength]).

Given this checking, is there any current reason for the statement in the preceding paragraph,

If the value of the expression is negative, the behavior is undefined.

Presumably for most negative expressions on most platforms, a negative value would result in a too-large request anyway, and even if not the check could easily be expanded to look explicitly for a negative value in addition to a too-large request.

Proposed resolution (September, 2010):

  1. Change 7.6.2.8 [expr.new] paragraphs 6 and 7 as follows:

  2. ...If the value of the expression is negative, the behavior is undefined. [Example: given the definition int n = 42, new float[n][5] is well-formed (because n is the expression of a noptr-new-declarator), but new float[5][n] is ill-formed (because n is not a constant expression). If n is negative, the effect of new float[n][5] is undefined.end example]

    When the value of the expression in a noptr-new-declarator is zero, the allocation function is called to allocate an array with no elements. If the value of that expression is less than zero or such that the size of the allocated object would exceed the implementation-defined limit, no storage is obtained and the new-expression terminates by throwing an exception of a type that would match a handler (14.4 [except.handle]) of type std::bad_array_new_length (17.6.4.2 [new.badlength]).

  3. Change 17.6.4.2 [new.badlength] paragraph 1 as follows:

  4. The class bad_array_new_length defines the type of objects thrown as exceptions by the implementation to report an attempt to allocate an array of size less than zero or greater than an implementation-defined limit (7.6.2.8 [expr.new]).



1037. Requirements for operands of delete-expressions and deallocation functions

Section: 7.6.2.9  [expr.delete]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-03-01

[Voted into the WP at the November, 2010 meeting.]

According to 7.6.2.9 [expr.delete] paragraph 2,

...in the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a subobject (6.7.2 [intro.object]) representing a base class of such an object (11.7 [class.derived]). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete shall be the pointer value which resulted from a previous array new-expression.79 If not, the behavior is undefined.

The second part of this specification makes it clear that an array object being deleted must have been allocated via new. However, the first part, for the non-array object, completely omits this vital requirement, requiring only that it not be an array.

The corresponding requirement for an argument to a deallocation function is found in 6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 3:

...the value supplied to operator delete(void*) in the standard library shall be one of the values returned by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, const std::nothrow_t&) in the standard library, and the value supplied to operator delete[](void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[](std::size_t) or operator new[](std::size_t, const std::nothrow_t&) in the standard library.

This correctly states the required provenance of the pointer, but it does so using “shall,” which is inappropriate for a runtime requirement. This wording should be recast in terms of undefined behavior if the requirement is not met.

Proposed resolution (October, 2010):

  1. Change 7.6.2.9 [expr.delete] paragraph 2 as follows:

  2. If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, and the converted operand is used in place of the original operand for the remainder of this section. In either alternative, the value of the operand of delete may be a null pointer value. If it is not a null pointer value, in In the first alternative (delete object), the value of the operand of delete shall may be a null pointer value, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject (6.7.2 [intro.object]) representing a base class of such an object ( 11.7 [class.derived]). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete shall may be the a null pointer value or a pointer value which that resulted from a previous array new-expression.79 If not, the behavior is undefined. [Note: this means that the syntax of the delete-expression must match the type of the object allocated by new new, not the syntax of the new-expression. —end note]...
  3. Change 6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 3 as follows:

  4. ...Otherwise, the behavior is undefined if the value supplied to operator delete(void*) in the standard library shall be is not one of the values returned by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, const std::nothrow_t&) in the standard library, and the behavior is undefined if the value supplied to operator delete[](void*) in the standard library shall be is not one of the values returned by a previous invocation of either operator new[](std::size_t) or operator new[](std::size_t, const std::nothrow_t&) in the standard library.



1091. Inconsistent use of the term “object expression”

Section: 7.6.4  [expr.mptr.oper]     Status: C++11     Submitter: Tom Plum     Date: 2010-07-10

[Voted into the WP at the March, 2011 meeting.]

The description of class member access expressions in 7.6.1.5 [expr.ref] paragraph 2 defines the terms “object expression” and “pointer expression:”

For the first option (dot) the type of the first expression (the object expression) shall be “class object” (of a complete type). For the second option (arrow) the type of the first expression (the pointer expression) shall be “pointer to class object” (of a complete type).

(Note in passing that the phrase “class object” seems very odd when describing a type.) The rest of that section is based on the equivalence of the expression E1->E2 to (*(E1)).E2 and thus is phrased only in terms of “object expression.” This terminology appears to have been misapplied in other parts of the Standard, using the term “object expression” to refer both to class types and pointers to class types. The most egregious of these is 7.6.4 [expr.mptr.oper] paragraph 4, describing the operands of a pointer-to-member expression:

The first operand is called the object expression. If the dynamic type of the object expression does not contain the member to which the pointer refers, the behavior is undefined.

The dynamic type of the first operand in the ->* case is a pointer type, not a class type, so it cannot “contain the member to which the pointer refers.” Another example is _N4868_.6.5.6 [basic.lookup.classref], describing the lookup in a class member access expression. The first paragraph uses the term consistently with its use in 7.6.1.5 [expr.ref], but paragraph 2 reads:

If the id-expression in a class member access (7.6.1.5 [expr.ref]) is an unqualified-id, and the type of the object expression is of a class type C, the unqualified-id is looked up in the scope of class C. If the type of the object expression is of pointer to scalar type, the unqualified-id is looked up in the context of the complete postfix-expression.

Paragraph 7 gets it right:

...in the context of the class of the object expression (or the class pointed to by the pointer expression).

Another misapplication of the term occurs in 7.6.1.3 [expr.call] paragraph 1:

...the call is as a member of the object pointed to or referred to by the object expression (7.6.1.5 [expr.ref], 7.6.4 [expr.mptr.oper])... its final overrider (11.7.3 [class.virtual]) in the dynamic type of the object expression is called. [Note: the dynamic type is the type of the object pointed or referred to by the current value of the object expression...

Here again we have the idea that an object expression can “point to” an object.

Another minor complication is that Clause 7 [expr] paragraph 7 has a separate definition for the (hyphenated) term “object-expression:”

An expression designating an object is called an object-expression.

This term is used several times in the Standard, apparently interchangeably with the non-hyphenated version defined in 7.6.1.5 [expr.ref]; for example, _N4567_.5.1.1 [expr.prim.general] bullet 10.1 mentions

a class member access (7.6.1.5 [expr.ref]) in which the object-expression refers to the member's class

using the term defined in Clause 7 [expr] paragraph 7 but linking it with 7.6.1.5 [expr.ref].

These uses of “object expression” and “object-expression” need to be made consistent, especially the reference in 7.6.4 [expr.mptr.oper] that implies that the dynamic type of a pointer is that of the complete object to which it points.

Proposed resolution (February, 2011):

  1. Change _N4868_.6.5.6 [basic.lookup.classref] paragraph 2 as follows:

  2. ...If the type of the object expression is of pointer to scalar type For a pseudo-destructor call (_N4778_.7.6.1.4 [expr.pseudo]), the unqualified-id is looked up in the context of the complete postfix-expression.
  3. Delete Clause 7 [expr] paragraph 7

  4. An expression designating an object is called an object-expression.
  5. Change _N4567_.5.1.1 [expr.prim.general] paragraph 10 as follows:

  6. An id-expression that denotes a non-static data member or non-static member function of a class can only be used:

  7. Change 7.6.1.3 [expr.call] paragraph 1 as follows:

  8. ...For a member function call, the postfix expression shall be an implicit (11.4.3 [class.mfct.non.static], 11.4.9 [class.static]) or explicit class member access (7.6.1.5 [expr.ref]) whose id-expression is a function member name, or a pointer-to-member expression (7.6.4 [expr.mptr.oper]) selecting a function member; the call is as a member of the class object pointed to or referred to by the object expression (7.6.1.5 [expr.ref], 7.6.4 [expr.mptr.oper])... [Note: the dynamic type is the type of the object pointed or referred to by the current value of the object expression. 11.9.5 [class.cdtor] describes the behavior of virtual function calls when the object-expression object expression refers to an object under construction or destruction. —end note]
  9. Change 7.6.1.5 [expr.ref] paragraph 2 as follows:

  10. For the first option (dot) the type of the first expression (the object expression) shall be “class object” (of a complete type) have complete class type. For the second option (arrow) the type of the first expression (the pointer expression) shall be “pointer to class object” (of a complete type) have pointer to complete class type. The expression E1->E2 is converted to the equivalent form (*(E1)).E2; the remainder of 7.6.1.5 [expr.ref] will address only the first option (dot) [Footnote: Note that (*(E1)) is an lvalue. —end footnote]. In these cases either case, the id-expression shall name a member of the class or of one of its base classes...
  11. Change 7.6.1.5 [expr.ref] paragraph 3 as follows:

  12. If E1 has the type “pointer to class X,” then the expression E1->E2 is converted to the equivalent form (*(E1)).E2; the remainder of 7.6.1.5 [expr.ref] will address only the first option (dot)66. Abbreviating object-expressionpostfix-expression.id-expression as E1.E2, then the E1 is called the object expression. The type and value category of this expression E1.E2 are determined as follows...
  13. Change 7.6.4 [expr.mptr.oper] paragraph 3 as follows:

  14. ...The result is an object or a function of the type specified by the second operand. The expression E1->*E2 is converted into the equivalent form (*(E1)).*E2.
  15. Change 7.6.4 [expr.mptr.oper] paragraph 4 as follows:

  16. The first operand Abbreviating pm-expression.*cast-expression as E1.*E2, E1 is called the object expression. If the dynamic type of the object expression E1 does not contain the member to which the pointer E2 refers, the behavior is undefined.
  17. Change 7.6.4 [expr.mptr.oper] paragraph 6 as follows:

  18. ...In a .* expression whose object expression is an rvalue, the program is ill-formed if the second operand is a pointer to member function with ref-qualifier &. In a ->* expression or in a .* expression whose object expression is an lvalue, the program is ill-formed if the second operand is a pointer to member function with ref-qualifier &&. The result of a .* expression whose second operand is a pointer to a data member is of the same value category (7.2.1 [basic.lval]) as its first operand. The result of a .* expression whose second operand is a pointer to a member function is a prvalue. The result of an ->* expression is an lvalue if its second operand is a pointer to data member and a prvalue otherwise. If the second operand is the null pointer to member value (7.3.13 [conv.mem]), the behavior is undefined.
  19. Change 11.4.9 [class.static] paragraph 2 as follows:

  20. ...A static member may be referred to using the class member access syntax, in which case the object-expression object expression is evaluated...
  21. Change 11.9.5 [class.cdtor] paragraph 4 as follows:

  22. ...If the virtual function call uses an explicit class member access (5.2.5) and the object-expression object expression refers to the object under construction...
  23. Change 13.3 [temp.names] paragraph 4 as follows:

  24. When the name of a member template specialization appears after . or -> in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object or pointer expression of the postfix-expression or...

[Note: although the current text of _N4868_.6.5.6 [basic.lookup.classref] paragraph 7 mentions the phrase “pointer expression,” that wording will be replaced by issue 1111 or issue 1220 and is thus not addressed here.]




1060. Scoped enumerators in integral constant expressions

Section: 7.7  [expr.const]     Status: C++11     Submitter: Jonathan Caves     Date: 2010-03-21

[Voted into the WP at the March, 2011 meeting as part of paper N3260.]

According to 7.7 [expr.const] paragraph 3,

A constant expression is an integral constant expression if it is of integral or enumeration type. [Note: such expressions may be used as array bounds (9.3.4.5 [dcl.array], 7.6.2.8 [expr.new]), as case expressions (8.5.3 [stmt.switch]), as bit-field lengths (11.4.10 [class.bit]), as enumerator initializers (9.7.1 [dcl.enum]), and as integral or enumeration non-type template arguments (13.4 [temp.arg]). —end note]

Although there is conceptually a conversion from enumeration type to integral type involved in using an enumerator as an array bound or bit-field length, the normative wording for those uses does not explicitly mention it and simply requires an integral constant expression. Consequently, the current wording permits uses like the following:

    enum class E { e = 10; };
    struct S {
        int arr[E::e];
        int i: E::e;
    };

This seems surprising.

Proposed resolution (February, 2011):

This issue is resolved by the resolution of issue 1197.




1098. Pointer conversions in constant expressions

Section: 7.7  [expr.const]     Status: C++11     Submitter: Jason Merrill     Date: 2010-08-01

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

One of the bullets in 7.7 [expr.const] paragraph 2 says,

This appears to prohibit conversion from one pointer type to another; for example,

    int x;
    constexpr void* p = &x;   // ill-formed

This seems excessive and probably unintentional.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 7.7 [expr.const] paragraph 2 as follows:

[Note: the proposed resolution of issue 1188 edits this bullet in an incompatible fashion.]




1099. Infinite recursion in constexpr functions

Section: 7.7  [expr.const]     Status: C++11     Submitter: Jason Merrill     Date: 2010-08-01

[Voted into the WP at the March, 2011 meeting.]

It is not clear what happens when a program violates the limits on constexpr function recursion in a context that does not require a constant expression. For example,

  constexpr int f(int i) { return f(i); }
  const int i = f(1);   // error, undefined behavior, or dynamic initialization?

(Presumably the “within its resource limits” caveat of 4.1 [intro.compliance] paragraph 2 would effectively result in undefined behavior in a context that required a constant expression.)

Notes from the November, 2010 meeting:

The CWG was of mixed opinion as to whether an infinite recursion in a constexpr function should be ill-formed or simply render an expression non-constant.

Proposed resolution (January, 2011):

Add the following bullet in 7.7 [expr.const] paragraph 2:




1100. constexpr conversion functions and non-type template arguments

Section: 7.7  [expr.const]     Status: C++11     Submitter: Jason Merrill     Date: 2010-08-01

[Voted into the WP at the March, 2011 meeting as part of paper N3260.]

According to 13.4.3 [temp.arg.nontype] paragraph 1, one of the possibilities for a template-argument for a non-type, non-template template-parameter is

However, the requirement for such a literal class type is (7.7 [expr.const] paragraph 5):

...that class type shall have a single non-explicit conversion function to an integral or enumeration type and that conversion function shall be constexpr.

Note that this normative requirement for a single conversion function is contradicted by the example in that paragraph, which reads in significant part,

    struct A {
      constexpr A(int i) : val(i) { }
      constexpr operator int() { return val; }
      constexpr operator long() { return 43; }
    private:
      int val;
    };
    template<int> struct X { };
    constexpr A a = 42;
    X<a> x; // OK: unique conversion to int

Proposed resolution (February, 2011):

This issue is resolved by the resolution of issue 1197.




1125. Unclear definition of “potential constant expression”

Section: 7.7  [expr.const]     Status: C++11     Submitter: DE     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting as paper N3218.]

N3092 comment DE 8

In the definition of “potential constant expression” in 7.7 [expr.const] paragraph 6, it is unclear how “arbitrary” the substitution of the function parameters is. Does it mean “there exists a value for which the result is a constant expression” or does it mean “for all possible values, the result needs to be a constant expression?” Example:

    constexpr int f(int x){ return x + 1; }

is a constant expression under the first interpretation, but not under the second (because overflow occurs for x == INT_MAX, cf 7.7 [expr.const] paragraph 2 bullet 5). The answer also affects expressions such as:

    constexpr int f2(bool v) { return v ? throw 0 : 0; }
    constexpr int f3(bool v) { return v && (throw 0, 0); }

See also issue 1129.




1126. constexpr functions in const initializers

Section: 7.7  [expr.const]     Status: C++11     Submitter: GB     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment GB 25

It does not appear to be clearly enough stated that the example

    constexpr int f() { return 42 + 84; }
    const int sz = f();
    int a[sz];

is equivalent to

    const int sz = 42 + 84;
    int a[sz];

Proposed resolution (August, 2010):

Change 7.7 [expr.const] paragraph 1 as follows:

Certain contexts require expressions that satisfy additional requirements as detailed in this sub-clause; other contexts have different semantics depending on whether or not an expression satisfies these requirements. Such expressions Expressions that satisfy these requirements are called constant expressions. [Note: Those Constant expressions can be evaluated during translation. —end note]



1127. Overload resolution in constexpr functions

Section: 7.7  [expr.const]     Status: C++11     Submitter: GB     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting as paper N3218.]

N3092 comment GB 26

It is not clear how overload resolution is performed inside the body of a constexpr function. In particular, if the function is invoked with parameters such that an expression evaluates to an integral 0, does that make the expression eligible for the null pointer conversion and thus potentially select a different overloaded function than in invocations in which the expression has a non-zero value? (Suggested answer: no.)




1188. Type punning in constant expressions

Section: 7.7  [expr.const]     Status: C++11     Submitter: Jason Merrill     Date: 2010-08-31

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The status of the following example is not clear:

    union U { float f; unsigned long u; };

    constexpr U u1 = { 1.0 };
    constexpr unsigned long u2 = u1.u;

This might be ill-formed because the aliasing causes undefined behavior, which should make the expression not a constant expression. However, a similar example using a permitted aliasing would presumably be acceptable:

    union U {
        unsigned char c[sizeof(double)];
        double d;
    };
    constexpr U c1u = { 0x12, 0x34 /* etc. */ };
    constexpr double c1 = c1u.d;

One suggestion was that unions should not be considered literal types, but some in the ensuing discussion deemed that excessive. That also would not address similar examples using reinterpret_cast, which is currently also permitted in constant expressions.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 7.7 [expr.const] paragraph 2 as follows:

[Note: the proposed resolution of issue 1098 edits this bullet in an incompatible fashion.]




1193. Use of address-constant pointers in constant expressions

Section: 7.7  [expr.const]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-09-02

[Voted into the WP at the March, 2011 meeting.]

It's not clear whether the current rules for constant expressions allow indirect calls of constexpr functions and constexpr member functions; for example,

    constexpr bool is_negative(int x) { return x < 0; }
    constexpr bool check(int x, bool (*p)(int)) { return p(x); }
    static_assert(check(-2, is_negative), "Error");

If this is to be permitted, there does not seem to be a reason to prohibit equality comparison of pointers to functions or pointers to objects of static storage duration -- these can be tracked as is already done for non-type template parameters.

Proposed resolution (November, 2010):

Change 7.7 [expr.const] bullet 2.19 as follows:




1197. Constexpr arrays

Section: 7.7  [expr.const]     Status: C++11     Submitter: Jason Merrill     Date: 2010-09-08

[Voted into the WP at the March, 2011 meeting as part of paper N3260.]

The requirement in 7.7 [expr.const] that a constant expression cannot contain

effectively eliminates the use of automatic constexpr arrays such as

    void f() {
       constexpr int ar[] = { 1, 2 };
       constexpr int i = ar[1];
    }

There does not seem to be a problem with this kind of usage.

Proposed resolution (February, 2011):

The proposed resolution will be submitted as a separate document.




1054. Lvalue-to-rvalue conversions in expression statements

Section: 8.3  [stmt.expr]     Status: C++11     Submitter: Hans Boehm     Date: 2010-03-16

[Voted into the WP at the March, 2011 meeting.]

C and C++ differ in the treatment of an expression statement, in particular with regard to whether a volatile lvalue is fetched. For example,

    volatile int x;
    void f() {
        x;    // Fetches x in C, not in C++
    }

The reason C++ is different in this regard is principally due to the fact that an assignment expression is an lvalue in C++ but not in C. If the lvalue-to-rvalue conversion were applied to expression statements, a statement like

    x = 5;

would write to x and then immediately read it.

It is not clear that the current approach to dealing with the difference in assignment expressions is the only or best approach; it might be possible to avoid the unwanted fetch on the result of an assignment statement without giving up the fetch for a variable appearing by itself in an expression statement.

Proposed resolution (January, 2011):

  1. Add a new paragraph after Clause 7 [expr] paragraph 10:

  2. In some contexts, an expression only appears for its side-effects. Such an expression is called a discarded-value expression. The expression is evaluated and its value is discarded. The array-to-pointer (7.3.3 [conv.array]) and function-to-pointer (7.3.4 [conv.func]) standard conversions are not applied. The lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is applied only if the expression is an lvalue of volatile-qualified type and it has one of the following forms:

  3. Change 7.6.1.9 [expr.static.cast] paragraph 6 as follows:

  4. Any expression can be explicitly converted to type cv void, in which case it becomes a discarded-value expression (Clause 7 [expr]). The expression value is discarded. [Note: however, if the value is in a temporary object (6.7.7 [class.temporary]), the destructor for that object is not executed until the usual time, and the value of the object is preserved for the purpose of executing the destructor. —end note] The lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are not applied to the expression.
  5. Change 7.6.20 [expr.comma] paragraph 1 as follows:

  6. ...A pair of expressions separated by a comma is evaluated left-to-right; and the value of the left expression is discarded a discarded-value expression (Clause 7 [expr]).83 The lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are not applied to the left expression. Every value computation...
  7. Change 8.3 [stmt.expr] paragraph 1 as follows:

  8. ...The expression is evaluated and its value is discarded a discarded-value expression (Clause 7 [expr]). The lvalue-to-rvalue (7.3.2 [conv.lval]), array-to-pointer (7.3.3 [conv.array]), and function-to-pointer (7.3.4 [conv.func]) standard conversions are not applied to the expression. All side effects...



948. constexpr in conditions

Section: 8.5  [stmt.select]     Status: C++11     Submitter: Gabriel Dos Reis     Date: 2 August, 2009

[Voted into WP at August, 2010 meeting.]

The grammar for condition in 8.5 [stmt.select] paragraph 1 does not allow for the constexpr specifier. This was not intended by the original proposal.

Proposed resolution (March, 2010):

  1. Change the definition of condition in 8.5 [stmt.select] paragraph 1 as follows:

  2. Insert the following text as a new paragraph at the end of 8.5 [stmt.select]:

  3. In the decl-specifier-seq of a condition, each decl-specifier shall be either a type-specifier or constexpr.



1204. Specifiers in a for-range-declaration

Section: 8.6  [stmt.iter]     Status: C++11     Submitter: Jason Merrill     Date: 2010-10-01

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

It seems unfortunate that the beginning of a C-style for loop can look like

whereas the beginning of a range-based for loop looks like

So that we don't know what constraints we are trying to apply to the specifiers until we see, or don't see, a :. The inconsistency between decl-specifier-seq and type-specifier-seq seems gratuitous and inconvenient.

Proposed resolution (November, 2010) [SUPERSEDED]:

  1. Change the grammar 8.6 [stmt.iter] paragraph 1 as follows:

  2. Add the following as a new paragraph at the end of 8.6.5 [stmt.ranged]:

  3. The the decl-specifier-seq of a for-range-declaration, each decl-specifier shall be either a type-specifier or constexpr.



864. braced-init-list in the range-based for statement

Section: 8.6.5  [stmt.ranged]     Status: C++11     Submitter: James Widman     Date: 7 April, 2009

[Voted into WP at August, 2010 meeting.]

The intent is that the range-based for statement should be able to be used with a braced-init-list as the range over which to iterate. However, this does not work grammatically: a braced-init-list is not an expression, as required by the syntax in 8.6.5 [stmt.ranged] paragraph 1:

Even if this were resolved, the “equivalent to” code is not correct. It contains the declaration,

This has a similar problem, in that 9.2.9.7 [dcl.spec.auto] paragraph 3 requires that the initializer have one of the forms

which does not allow for a braced-initializer-list. In addition, although not allowed by the grammar, 9.2.9.7 [dcl.spec.auto] paragraph 6 treats the braced-init-list specially, in order for the type deduction to work correctly:

Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list (9.4.5 [dcl.init.list]), with std::initializer_list<U>.

The problem here is that a parenthesized initializer, as in the code expansion of the range-based for statement, is not a braced-init-list.

Proposed resolution (June, 2010):

  1. Change 8.6 [stmt.iter] paragraph 1 as follows:

  2. Iteration statements specify looping.




    [Note: a for-init-statement ends with a semicolon. —end note]

  3. Change 8.6.5 [stmt.ranged] paragraph 1 as follows:

  4. The For a range-based for statement of the form

    let range-init be equivalent to the expression surrounded by parentheses:

    [Footnote: this ensures that a top-level comma operator cannot be reinterpreted as a delimiter between init-declarators in the declaration of __range. —end footnote] and for a range-based for statement of the form

    let range-init be equivalent to the braced-init-list. In each case, a range-based for statement is equivalent to

      {
        auto && __range = ( expression ) range-init;
        for ( auto __begin = begin-expr,
        ...
    

    Note to editor:

    The formatting in the preceding change for range-init follows that of the existing text for begin-expr and end-expr. However, CWG is concerned that this style makes all of these elements look too much like grammar nonterminals and asks that the editor consider some other formatting convention.




1018. Ambiguity between simple-declaration and attribute-declaration

Section: 9.1  [dcl.pre]     Status: C++11     Submitter: Mike Miller     Date: 2010-01-04

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The grammar for declarations includes the following two nonterminals:

An attribute-specifier followed by a semicolon could thus be parsed as either an attribute-declaration or as a simple-declaration that omits the optional decl-specifier-seq and init-declarator-list, and the current wording does not disambiguate the two. (There doesn't seem to be a compelling need for attribute-declaration as a separate nonterminal, given that simple-declaration can accommodate that form.)

Proposed resolution (February, 2011) [SUPERSEDED]:

Change 9.1 [dcl.pre] paragraph 1 as follows:

...The optional attribute-specifier-seq in a simple-declaration appertains to each of the entities declared by the declarators; it shall not appear if the optional of the init-declarator-list is omitted...



1042. Attributes in alias-declarations

Section: 9.1  [dcl.pre]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 2010-03-04

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The grammar for an alias-declaration does not have a place for an attribute-specifier, although a typedef declaration does. Since an alias-declaration is essentially a different syntactic form of a typedef declaration (9.2.4 [dcl.typedef] paragraph 2), this could be surprising.

Proposed resolution (February, 2011) [SUPERSEDED]:

  1. Change the grammar in 9.1 [dcl.pre] paragraph 1 as follows:

  2. Change 9.2.4 [dcl.typedef] paragraph 2 as follows:

  3. A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics...



1128. attribute-specifiers in decl-specifier-seqs

Section: 9.2  [dcl.spec]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 39

The current wording of 9.2 [dcl.spec] paragraph 1 says,

The optional attribute-specifier in a decl-specifier-seq appertains to the type determined by the decl-specifier-seq (9.3.4 [dcl.meaning]).

However, decl-specifier-seq is a recursive production. The intent is that the attribute-specifier appertains to the type determined by the top-level decl-specifier-seq, but this makes it sound as if it appertains to the one that directly contains the attribute-specifier.

Proposed resolution (August, 2010):

Change 9.2 [dcl.spec] paragraph 1 as follows:

The optional attribute-specifier in a decl-specifier-seq appertains to the type determined by the decl-specifier-seq preceding decl-specifiers (9.3.4 [dcl.meaning]).



407. Named class with associated typedef: two names or one?

Section: 9.2.4  [dcl.typedef]     Status: C++11     Submitter: Clark Nelson     Date: 31 March 2003

[Voted into the WP at the March, 2011 meeting.]

Here's an example:

  typedef struct S { ... } S;
  void fs(S *x) { ... }

The big question is, to what declaration does the reference to identifier S actually refer? Is it the S that's declared as a typedef name, or the S that's declared as a class name (or in C terms, as a struct tag)? (In either case, there's clearly only one type to which it could refer, since a typedef declaration does not introduce a new type. But the debugger apparently cares about more than just the identity of the type.)

Here's a classical, closely related example:

  struct stat { ... };
  int stat();
  ... stat( ... ) ...

Does the identifier stat refer to the class or the function? Obviously, in C, you can't refer to the struct tag without using the struct keyword, because it is in a different name space, so the reference must be to the function. In C++, the reference is also to the function, but for a completely different reason.

Now in C, typedef names and function names are in the same name space, so the natural extrapolation would be that, in the first example, S refers to the typedef declaration, as it would in C. But C++ is not C. For the purposes of this discussion, there are two important differences between C and C++

The first difference is that, in C++, typedef names and class names are not in separate name spaces. On the other hand, according to section _N4868_.6.4.10 [basic.scope.hiding] (Name hiding), paragraph 2:

A class name (9.1) or enumeration name (7.2) can be hidden by the name of an object, function, or enumerator declared in the same scope. If a class or enumeration name and an object, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the object, function, or enumerator name is visible.

Please consider carefully the phrase I have highlighted, and the fact that a typedef name is not the name of an object, function or enumerator. As a result, this example:

  struct stat { ... };
  typedef int stat;

Which would be perfectly legal in C, is disallowed in C++, both implicitly (see the above quote) and explicitly (see section 9.2.4 [dcl.typedef] (The typedef specifier), paragraph 3):

In a given scope, a typedef specifier shall not be used to redefine the name of any type declared in that scope to refer to a different type. Similarly, in a given scope, a class or enumeration shall not be declared with the same name as a typedef-name that is declared in that scope and refers to a type other than the class or enumeration itself.

From which we can conclude that in C++ typedef names do not hide class names declared in the same scope. If they did, the above example would be legal.

The second difference is that, in C++, a typedef name that refers to a class is a class-name; see 9.2.4 [dcl.typedef] paragraph 4:

A typedef-name that names a class is a class-name(9.1). If a typedef-name is used following the class-key in an elaborated-type-specifier (7.1.5.3) or in the class-head of a class declaration (9), or is used as the identifier in the declarator for a constructor or destructor declaration (12.1, 12.4), the program is ill-formed.

This implies, for instance, that a typedef-name referring to a class can be used in a nested-name-specifier (i.e. before :: in a qualified name) or following ~ to refer to a destructor. Note that using a typedef-name as a class-name in an elaborated-type-specifier is not allowed. For example:

  struct X { };
  typedef struct X X2;
  X x; // legal
  X2 x2; // legal
  struct X sx; // legal
  struct X2 sx2; // illegal

The final relevant piece of the standard is 9.2.4 [dcl.typedef] paragraph 2:

In a given scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers.

This of course is what allows the original example, to which let us now return:

  typedef struct S { ... } S;
  void fs(S *x) { ... }

The question, again is, to which declaration of S does the reference actually refer? In C, it would clearly be to the second, since the first would be accessible only by using the struct keyword. In C++, if typedef names hid class names declared in the same scope, the answer would be the same. But we've already seen that typedef names do not hide class names declared in the same scope.

So to which declaration does the reference to S refer? The answer is that it doesn't matter. The second declaration of S, which appears to be a declaration of a typedef name, is actually a declaration of a class name (9.2.4 [dcl.typedef] paragraph 4), and as such is simply a redeclaration. Consider the following example:

  typedef int I, I;
  extern int x, x;
  void f(), f();

To which declaration would a reference to I, x or f refer? It doesn't matter, because the second declaration of each is really just a redeclaration of the thing declared in the first declaration. So to save time, effort and complexity, the second declaration of each doesn't add any entry to the compiler's symbol table.

Note (March, 2005):

Matt Austern: Is this legal?

    struct A { };
    typedef struct A A;
    struct A* p;

Am I right in reading the standard [to say that this is ill-formed]? On the one hand it's a nice uniform rule. On the other hand, it seems likely to confuse users. Most people are probably used to thinking that 'typedef struct A A' is a null operation, and, if this code really is illegal, it would seem to be a gratuitous C/C++ incompatibility.

Mike Miller: I think you're right. 9.2.4 [dcl.typedef] paragraph 1:

A name declared with the typedef specifier becomes a typedef-name.

9.2.4 [dcl.typedef] paragraph 2:

In a given non-class scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers.

After the typedef declaration in the example, the name X has been “redefined” — it is no longer just a class-name, it has been “redefined” to be a typedef-name (that, by virtue of the fact that it refers to a class type, is also a class-name).

John Spicer: In C, and originally in C++, an elaborated-type-specifier did not consider typedef names, so “struct X* x” would find the class and not the typedef.

When C++ was changed to make typedefs visible to elaborated-type-specifier lookups, I believe this issue was overlooked and inadvertantly made ill-formed.

I suspect we need add text saying that if a given scope contains both a class/enum and a typedef, that an elaborated type specifier lookup finds the class/enum.

Mike Miller: I'm a little uncomfortable with this approach. The model we have for declaring a typedef in the same scope as a class/enum is redefinition, not hiding (like the “struct stat” hack). This approach seems to assume that the typedef hides the class/enum, which can then be found by an elaborated-type-specifier, just as if it were hidden by a variable, function, or enumerator.

Also, this approach reduces but doesn't eliminate the incompatibility with C. For example:

    struct S { };
    {
        typedef struct S S;
        struct S* p;        // still ill-formed
    }

My preference would be for something following the basic principle that declaring a typedef-name T in a scope where T already names the type designated by the typedef should have no effect on whether an elaborated-type-specifier in that or a nested scope is well-formed or not. Another way of saying that is that a typedef-name that designates a same-named class or enumeration in the same or a containing scope is transparent with respect to elaborated-type-specifiers.

John Spicer: This strikes me as being a rather complicated solution. When we made the change to make typedefs visible to elaborated-type-specifiers we did so knowing it would make some C cases ill-formed, so this does not bother me. We've lived with the C incompatibility for many years now, so I don't personally feel a need to undo it. I also don't like the fact that you have to essentially do the old-style elaborated-type-specifier lookup to check the result of the lookup that found the typedef.

I continue to prefer the direction I described earlier where if a given scope contains both a class/enum and a typedef, that an elaborated-type-specifier lookup finds the class/enum.

Notes from the April, 2005 meeting:

The CWG agreed with John Spicer's approach, i.e., permitting a typedef-name to be used in an elaborated-type-specifier only if it is declared in the same scope as the class or enumeration it names.

Proposed resolution (January, 2011):

Add the following new paragraph after 9.2.4 [dcl.typedef] paragraph 4:

If a typedef specifier is used to redefine in a given scope an entity that can be referenced using an elaborated-type-specifier, the entity can continue to be referenced by an elaborated-type-specifier or as an enumeration or class name in an enumeration or class definition respectively. [Example:

  struct S;
  typedef struct S S;
  int main() {
    struct S* p; // OK
  }
  struct S {};   // OK

end example]




700. Constexpr member functions of class templates

Section: 9.2.6  [dcl.constexpr]     Status: C++11     Submitter: Jens Maurer     Date: 27 June, 2008

[Voted into WP at August, 2010 meeting.]

9.2.6 [dcl.constexpr] paragraph 5 applies only to “the instantiated template specialization of a constexpr function template;” it should presumably apply to non-template member functions of a class template, as well.

Notes from the September, 2008 meeting:

This question is more involved than it might appear. For example, a constexpr member function is implicitly const; if the constexpr specifier is ignored, does that make the member function non-const? Also, should this provision apply only to dependent expressions in the function? Should it be an error if no constexpr function can be instantiated from the template, along the lines of the permission given in 13.8 [temp.res] paragraph 8 for an implementation to diagnose a template definition from which no valid specialization can be instantiated?

Notes from the July, 2009 meeting:

The consensus of the CWG was that an “ignored” constexpr specifier in this case simply means that the specialization is not constexpr, not that it is not const. The CWG also decided not to address the question of non-dependent expressions that render a function template specialization non-constexpr, leaving it to quality of implementation whether a (warning) diagnostic is issued in such cases.

Proposed resolution (February, 2010):

Change 9.2.6 [dcl.constexpr] paragraph 5 as follows:

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, the constexpr specifier is ignored that specialization is not a constexpr function or constexpr constructor. [Note: if the function is a member function it will still be const as described below. Implementations are encouraged to issue a warning if a function was rendered not constexpr by a non-dependent construct. —end note]



837. Constexpr functions and return braced-init-list

Section: 9.2.6  [dcl.constexpr]     Status: C++11     Submitter: Mike Miller     Date: 11 March, 2009

[Voted into the WP at the March, 2011 meeting as part of paper N3268.]

The body of a constexpr function is required by 9.2.6 [dcl.constexpr] paragraph 3 to be of the form

However, there does not seem to be any good reason for prohibiting the alternate return syntax involving a braced-init-list. The restriction should be removed.

Proposed resolution (March, 2010):

  1. Change 8.7.4 [stmt.return] paragraph 2 as follows:

  2. A return statement without an expression with neither an expression nor a braced-init-list can be used only in functions that do not return a value...
  3. Change 9.2.6 [dcl.constexpr] paragraph 3 bullets 4 and 5 as follows:

Notes from the March, 2010 meeting:

The new wording added in 7.7 [expr.const] in support of reference parameters for constexpr functions should also be considered to see whether additional changes are needed.




860. Explicit qualification of constexpr member functions

Section: 9.2.6  [dcl.constexpr]     Status: C++11     Submitter: Daniel Krügler     Date: 6 April, 2009

[Voted into WP at August, 2010 meeting.]

9.2.6 [dcl.constexpr] paragraph 6 says,

A constexpr specifier for a non-static member function that is not a constructor declares that member function to be const (11.4.3 [class.mfct.non.static]).

Is a const qualifier on such a member function redundant or ill-formed?

Notes from the July, 2009 meeting:

The CWG agreed that a const qualifier on a constexpr member function is simply redundant and not an error.

Proposed resolution (February, 2010):

Change 9.2.6 [dcl.constexpr] paragraph 6 as follows:

A constexpr specifier for a non-static member function that is not a constructor declares that member function to be const (11.4.3 [class.mfct.non.static]). [Note: the constexpr specifier has no other effect on the function type. —end note] The keyword const is ignored if it appears in the cv-qualifier-seq of the function declarator of the declaration of such a member function. The class of which that function is a member shall be a literal type (6.8 [basic.types]). [Example:...



892. Missing requirements for constexpr constructors

Section: 9.2.6  [dcl.constexpr]     Status: C++11     Submitter: Alisdair Meredith     Date: 8 May, 2009

[Voted into WP at August, 2010 meeting.]

The rules for constexpr constructors are missing some necessary requirements. In particular, there is no requirement that a brace-or-equal-initializer for a non-static data member be a constant expression, and the requirement for constexpr constructors for initializing non-static data members applies only to members named in a mem-initializer, allowing a non-constexpr default constructor to be invoked.

Proposed resolution (February, 2010):

Change 9.2.6 [dcl.constexpr] paragraph 4 as follows:

The definition of a constexpr constructor shall satisfy the following constraints:




898. Declarations in constexpr functions

Section: 9.2.6  [dcl.constexpr]     Status: C++11     Submitter: Daniel Krügler     Date: 9 May, 2009

[Voted into the WP at the March, 2011 meeting as part of paper N3268.]

According to 9.2.6 [dcl.constexpr] paragraph 3, no declarations are permitted in the body of a constexpr function. This seems overly restrictive. At least three kinds of declarations would seem to be helpful in writing such functions: static_assert, typedef and alias declarations, and local automatic variables initialized with constant expressions. (Similar considerations apply to lambdas in which the lambda-return-type-clause is omitted.)

Rationale (July, 2009):

This suggestion needs a proposal and analysis by EWG before it can be considered by CWG.




1129. Default nothrow for constexpr functions

Section: 9.2.6  [dcl.constexpr]     Status: C++11     Submitter: GB     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment GB 29

A constexpr function is not permitted to return via an exception. This should be recognised, and a function declared constexpr without an explicit exception specification should be treated as if declared noexcept(true) rather than the usual noexcept(false). For a function template declared constexpr without an explicit exception specification, it should be considered noexcept(true) if and only if the constexpr keyword is respected on a given instantiation.

See also issue 1125.

Notes from the August, 2010 meeting:

The premise is not correct: an exception is forbidden only when a constexpr function is invoked in a context that requires a constant expression. Used as an ordinary function, it can throw.

Proposed resolution (August, 2010):

Change 7.6.2.7 [expr.unary.noexcept] bullet 3.1 as follows:




1186. Non-dependent constexpr violations in function templates

Section: 9.2.6  [dcl.constexpr]     Status: C++11     Submitter: Jason Merrill     Date: 2010-08-30

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

9.2.6 [dcl.constexpr] paragraph 5 says,

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is not a constexpr function or constexpr constructor. [Note: if the function is a member function it will still be const as described below. Implementations are encouraged to issue a warning if a function is rendered not constexpr by a non-dependent construct. —end note]

A non-dependent error in a function template renders it ill-formed with no diagnostic required (13.8 [temp.res] paragraph 8). A similar approach is being taken in the proposed resolution of issue 1125 with respect to constexpr functions. It would be more consistent to treat constexpr function templates in the same way, along the lines of

If no specialization of the template would be constexpr, the program is ill-formed, no diagnostic required.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 9.2.6 [dcl.constexpr] paragraph 6 as follows:

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is not a constexpr function or constexpr constructor. [Note: if the function is a member function it will still be const as described below. Implementations are encouraged to issue a warning if a function is rendered not constexpr by a non-dependent construct.end note] If no specialization of the template would yield a constexpr function or constexpr constructor, the program is ill-formed; no diagnostic required.



1194. Constexpr references

Section: 9.2.6  [dcl.constexpr]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-09-04

[Voted into the WP at the March, 2011 meeting as part of paper N3277.]

9.2.6 [dcl.constexpr] restricts the constexpr specifier to object and function declarations. Especially given the support for reference types in constexpr functions, and considering that constexpr pointer declarations are permitted, there does not seem to be a good reason for excluding constexpr references.

(See also issue 1195.)

Proposed resolution (November. 2010):

  1. Change 9.2.6 [dcl.constexpr] paragraph 1 as follows:

  2. The constexpr specifier shall be applied only to the definition of an object a variable, the declaration of a function...
  3. Change 9.2.6 [dcl.constexpr] paragraph 8 as follows:

  4. A constexpr specifier used in an object declaration declares the object as const. Such an object shall be initialized. If it is initialized by a constructor call, the constructor shall be a constexpr constructor and every argument to the constructor shall be a constant expression. Otherwise, or if a constexpr specifier is used in a reference declaration, every full-expression that appears in its initializer shall be a constant expression...



1195. References to non-literal types in constexpr functions

Section: 9.2.6  [dcl.constexpr]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-09-05

[Voted into the WP at the March, 2011 meeting as part of paper N3277.]

9.2.6 [dcl.constexpr] paragraph 3 is overly restrictive in requiring that reference parameter and return types of a constexpr function or constructor must refer to a literal type. 7.7 [expr.const] paragraph 2 already prevents any problematic uses of lvalues of non-literal types, and it permits use of pointers to non-literal types as address constants. The same should be permitted via reference parameters and return types of constexpr functions.

(See also issue 1194.)

Proposed resolution (November, 2010):

  1. Change 6.8 [basic.types] paragraph 10 as follows:

  2. A type is a literal type if it is:

  3. Change 9.2.6 [dcl.constexpr] paragraph 3 as follows:

  4. The definition of a constexpr function shall satisfy the following constraints:

  5. Change 9.2.6 [dcl.constexpr] paragraph 4 as follows:

  6. The definition of a constexpr constructor shall satisfy the following constraints:




1199. Deleted constexpr functions

Section: 9.2.6  [dcl.constexpr]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-09-17

[Voted into the WP at the March, 2011 meeting as part of paper N3277.]

The current requirements for constexpr functions do not permit a deleted constexpr function because the definition does not consist of a compound-statement containing just a return statement. However, it could be useful to allow this form in a case where a single piece of code is used in multiple configurations, in some of which the function is constexpr and others deleted; having to update all declarations of the function to remove the constexpr specifier is unnecessarily onerous.

Proposed resolution (January, 2011):

  1. Change 9.2.6 [dcl.constexpr] paragraph 3 as follows:

  2. Change 9.2.6 [dcl.constexpr] paragraph 4 as follows:

  3. The definition of a constexpr constructor In the definition of a constexpr constructor, each of the parameter types shall be a literal type or a reference to a literal type. In addition, either its function-body shall be = delete or it shall satisfy the following constraints:




1225. constexpr constructors and virtual bases

Section: 9.2.6  [dcl.constexpr]     Status: C++11     Submitter: Jason Merrill     Date: 2010-10-26

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

A class with a virtual base should not be allowed to have a constexpr constructor.

Proposed resolution (November, 2010) [SUPERSEDED]:

Add the following bullet to the list in 9.2.6 [dcl.constexpr] paragraph 4:




1075. Grammar does not allow template alias in type-name

Section: 9.2.9.3  [dcl.type.simple]     Status: C++11     Submitter: Jason Merrill     Date: 2010-06-06

[Voted into the WP at the November, 2010 meeting.]

According to 13.3 [temp.names] paragraph 7,

A template-id that names a template alias specialization is a type-name.

However, the grammar for type-name in 9.2.9.3 [dcl.type.simple] does not include a production for simple-template-id.

Proposed resolution (September, 2010):

Change the definition of type-name in 9.2.9.3 [dcl.type.simple] paragraph 1 as follows:




1130. Function parameter type adjustments and decltype

Section: 9.2.9.3  [dcl.type.simple]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 40

The description of decltype does not specify whether the type of a parameter is the declared type or the type as adjusted in 9.3.4.6 [dcl.fct] paragraph 5:

    auto f(int a[])->decltype(a);     // ill-formed or int*?
    auto g(const int i)->decltype(i); // int or const int?

Suggested resolution: Clarify the wording to indicate that the type of a parameter is after the array- and function-to-pointer decay but before the removal of cv-qualification.

Proposed resolution (August, 2010):

Change 9.3.4.6 [dcl.fct] paragraph 5 as follows:

...After producing the list of parameter types, several transformations take place upon these types to determine the function type. Any any top-level cv-qualifiers modifying a parameter type is are deleted when forming the function type. [Example: the type void(*)(const int) becomes void(*)(int)end example] Such cv-qualifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. If a storage-class-specifier modifies a parameter type, the specifier is deleted. [Example: register char* becomes char*end example] Such storage-class-specifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list. [Note: This transformation does not affect the types of the parameters. For example, int(*)(const int p, decltype(p)*) and int(*)(int, const int*) are identical types. —end note]



1212. Non-function-call xvalues and decltype

Section: 9.2.9.3  [dcl.type.simple]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-10-21

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

Given

    int&& f();
    int i;

it is surprising that decltype(f()) and decltype(static_cast<int&&>(i)) are not the same type.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 9.2.9.3 [dcl.type.simple] paragraph 4 as follows:

The type denoted by decltype(e) is defined as follows:




1131. Template aliases in elaborated-type-specifiers

Section: 9.2.9.5  [dcl.type.elab]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 41

The current wording disallows use of typedef-names in elaborated-type-specifiers. This prohibition should also apply to template aliases:

    struct A { };
    template<typename T> using X = A;
    struct X<int>* p2; // ill-formed

Proposed resolution (August, 2010):

Change 9.2.9.5 [dcl.type.elab] paragraph 2 as follows:

...If the identifier resolves to a typedef-name or the simple-template-id resolves to an alias template specialization, the elaborated-type-specifier is ill-formed. [Note:...

[Note: this wording assumes the change from “template alias” to “alias template” requested by comment FI 11 on FCD N3092.]




1243. Misleading footnote regarding multiple-declarator declarations

Section: 9.3  [dcl.decl]     Status: C++11     Submitter: Johannes Schaub     Date: 2011-02-08

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The footnote for 9.3 [dcl.decl] paragraph 3 reads,

A declaration with several declarators is usually equivalent to the corresponding sequence of declarations each with a single declarator... The exception occurs when a name introduced by one of the declarators hides a type name used by the decl-specifiers, so that when the same decl-specifiers are used in a subsequent declaration, they do not have the same meaning...

A more important exception to the rule has been added in C++0x, specifically with the auto specifier when the deduced type is not the same for all declarators, which renders the declaration ill-formed. The footnote should be updated accordingly.




1234. abstract-declarator does not permit ... after ptr-operator

Section: 9.3.2  [dcl.name]     Status: C++11     Submitter: Johannes Schaub     Date: 2011-01-18

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The ellipsis for a parameter pack enters the normal declarator grammar as part of the declarator-id nonterminal. In contrast, however, the abstract-declarator grammar has no counterpart to declarator-id; instead, the ellipsis is one of the productions for the abstract-declarator nonterminal itself. It is thus impossible to declare a parameter pack for a pointer or reference using an abstract declarator, e.g.,

  template<typename... T> void f(T& ...t);   // t is a parameter pack
  template<typename... T> void f(T& ...);    // equivalent to void f(T&, ...)



1240. constexpr defaulted constructors

Section: 9.3.2  [dcl.name]     Status: C++11     Submitter: Jens Maurer     Date: 2011-02-02

[Voted into the WP at the March, 2011 meeting.]

Issue 1199 proposes to add the capability of defining a constexpr special function as deleted. It would be similarly useful to be able to mark a defaulted constructor as constexpr. (It should be noted that the existing text of 11.4.5 [class.ctor] and the proposed resolution of issue 1224 already allow for implicitly-defined constructors to be implicitly constexpr; this issue simply proposes allowing the explicit use of the constexpr specifier.)

Proposed resolution (February, 2011):

  1. Change 9.2.6 [dcl.constexpr] paragraph 3 as follows:

  2. Change 9.2.6 [dcl.constexpr] paragraph 4 as follows:

  3. In the definition of a constexpr constructor, each of the parameter types shall be a literal type or a reference to a literal type. In addition, either its function-body shall be = delete or = default or it shall satisfy the following constraints:

    A trivial copy/move constructor is also a constexpr constructor.

[Note: this resolution assumes that the changes for issue 1199 have been applied.]


547. Partial specialization on member function types

Section: 9.3.4.6  [dcl.fct]     Status: C++11     Submitter: Peter Dimov     Date: 04 November 2005

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The following example appears to be well-formed, with the partial specialization matching the type of Y::f(), even though it is rejected by many compilers:

    template<class T> struct X;

    template<class R> struct X< R() > {
    };

    template<class F, class T> void test(F T::* pmf) {
        X<F> x;
    }

    struct Y {
        void f() {
        }
    };

    int main() {
        test( &Y::f );
    }

However, 9.3.4.6 [dcl.fct] paragraph 4 says,

A cv-qualifier-seq shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration. The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. In the latter case, the cv-qualifiers are ignored.

This specification makes it impossible to write a partial specialization for a const member function:

    template<class R> struct X<R() const> {
    };

A template argument is not one of the permitted contexts for cv-qualification of a function type. This restriction should be removed.

Notes from the April, 2006 meeting:

During the meeting the CWG was of the opinion that the “R() const” specialization would not match the const member function even if it were allowed and so classified the issue as NAD. Questions have been raised since the meeting, however, suggesting that the template argument in the partial specialization would, in fact, match the type of a const member function (see, for example, the very similar usage via typedefs in 11.4.2 [class.mfct] paragraph 9). The issue is thus being left open for renewed discussion at the next meeting.

Proposed resolution (June, 2008) [SUPERSEDED]:

Change 9.3.4.6 [dcl.fct] paragraph 7 as follows:

A cv-qualifier-seq shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration, or the top-level function type of a type-id that is a template-argument for a type template-parameter. The effect... A ref-qualifier shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration, or the top-level function type of a type-id that is a template-argument for a type template-parameter. The return type...



994. braced-init-list as a default argument

Section: 9.3.4.6  [dcl.fct]     Status: C++11     Submitter: Daniel Krügler     Date: 25 Oct, 2009

[Voted into the WP at the November, 2010 meeting.]

It seems strange that it is possible to call a function with an explict argument of {} but that it is not possible to specify that same argument as a default in a function declaration.

Rationale (August, 2010):

This was previously considered and rejected by EWG.

Note (October, 2010):

Additional discussion has indicated a potential willingness to revisit this question.

Proposed resolution (November, 2010):

See paper N3217.




1069. Incorrect function type with trailing-return-type

Section: 9.3.4.6  [dcl.fct]     Status: C++11     Submitter: James Widman     Date: 2010-04-30

[Voted into the WP at the November, 2010 meeting.]

In 9.3.4.6 [dcl.fct] paragraph 2, the type of a function declarator with a trailing-return-type is said to be

“function of (parameter-declaration-clause) cv-qualifier-seqopt ref-qualifieropt returning type-id”.

This formulation incorrectly omits the derived-declarator-type-list modifier for the type, and it should refer to “the trailing-type-specifier-seq of the trailing-return-type” as the return type instead of type-id (which is left over from before the introduction of trailing-return-type).

Proposed resolution (September, 2010):

Change 9.3.4.6 [dcl.fct] paragraph 2 as follows:

...T shall be the single type-specifier auto. The type of the declarator-id in D is “derived-declarator-type-list function of (parameter-declaration-clause) cv-qualifier-seqopt ref-qualifieropt returning type-id trailing-return-type”. The optional...



1183. Expansion of parameter packs in declarators

Section: 9.3.4.6  [dcl.fct]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 2010-08-26

[Voted into the WP at the March, 2011 meeting as part of paper N3270.]

9.3.4.6 [dcl.fct] paragraph 13 says,

The type T of the declarator-id of the function parameter pack shall contain a template parameter pack; each template parameter pack in T is expanded by the function parameter pack.

I think that's incorrect. For example, I think

    template<class... P> void f(int (* ...p)[sizeof...(P)]);

should be an error, and that the function parameter pack p does not expand the template parameter pack P in this case.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 778.




508. Non-constructed value-initialized objects

Section: 9.4  [dcl.init]     Status: C++11     Submitter: Alisdair Meredith     Date: 18 Mar 2005

[Voted into WP at August, 2010 meeting.]

According to the definition of value initialization (9.4 [dcl.init] paragraph 5), non-union class types without user-declared constructors are value-initialized by value-initializing each of their members rather than by executing the (generated) default constructor. However, a number of other items in the Standard are described in relationship to the execution of the constructor:

Proposed resolution (October, 2005):

Add the indicated words to 9.4 [dcl.init] paragraph 6:

A program that calls for default-initialization or value-initialization of an entity of reference type is ill-formed. If T is a cv-qualified type, the cv-unqualified version of T is used for these definitions of zero-initialization, default-initialization, and value-initialization. Even when value-initialization of an object does not call that object's constructor, the object is deemed to have been fully constructed once its initialization is complete and thus subject to provisions of this International Standard applying to “constructed” objects, objects “for which the constructor has completed execution,” etc.

Notes from April, 2006 meeting:

There was some concern about whether this wording covered (or needed to cover) cases where an object is “partially constructed.” Another approach might be simply to define value initialization to be “construction.” Returned to “drafting” status for further investigation.

Proposed resolution (February, 2010):

Change 9.4 [dcl.init] paragraph 7 as follows:

To value-initialize an object of type T means:

An object that is value-initialized is deemed to be constructed and thus subject to provisions of this International Standard applying to “constructed” objects, objects “for which the constructor has completed,” etc., even if no constructor is invoked for the object's initialization.




615. Incorrect description of variables that can be initialized

Section: 9.4  [dcl.init]     Status: C++11     Submitter: comp.std.c++     Date: 30 January 2007

[Voted into WP at August, 2010 meeting.]

9.4 [dcl.init] paragraph 2 reads,

Automatic, register, static, and external variables of namespace scope can be initialized by arbitrary expressions involving literals and previously declared variables and functions.

Both “automatic” and “static” are used to describe storage durations, “register” is a storage class specifier which indicates the object has automatic storage duration, “external” describes linkage, and “namespace scope” is a kind of scope. Automatic, register, static and external, together with namespace scope, are used to restrict the “variables.”

Register objects are only a sub-set of automatic objects and thus the word “register” is redundant and should be elided. If register objects are to be emphasized, they should be mentioned like “Automatic (including register)...”

Variables having namespace scope can never be automatic; they can only be static, with either external or internal linkage. Therefore, there are in fact no “automatic variables of namespace scope,” and the “static” in “static variables of namespace scope” is useless.

In fact, automatic and static variables already compose all variables with either external linkage or not, and thus the “external” becomes redundant, too, and the quoted sentence seems to mean that all variables of namespace scope can be initialized by arbitrary expressions. But this is not true because not all internal variables of namespace scope can. Therefore, the restrictive “external” is really necessary, not redundant.

As a result, the erroneous restrictive “automatic, register, static” should be removed and the quoted sentence may be changed to:

External variables of namespace scope can be initialized by arbitrary expressions involving literals and previously declared variables and functions.

Notes from the April, 2007 meeting:

This sentence is poorly worded, but the analysis given in the issue description is incorrect. The intent is simply that the storage class of a variable places no restrictions on the kind of expression that can be used to initialize it (in contrast to C, where variables of static storage duration can only be initialized by constant expressions).

Proposed resolution (June, 2008):

Change 9.4 [dcl.init] paragraph 2 as follows:

Automatic, register, static, and external variables of namespace scope Variables of automatic, thread, and static storage duration can be initialized by arbitrary expressions involving literals and previously declared variables and functions...

Notes from the September, 2008 meeting:

The existing wording is intended to exclude block-scope extern declarations but to allow initializers in all other forms of variable declarations. The best way to phrase that is probably to say that all variable definitions (except for function parameters, where the initializer syntax is used for default arguments) can have arbitrary expressions as initializers, regardless of storage duration.

Proposed resolution (February, 2010):

Change 9.4 [dcl.init] paragraph 2 as follows:

Automatic, register, thread_local, static, and namespace-scoped external variables can be initialized by Except for objects declared with the constexpr specifier, for which see 9.2.6 [dcl.constexpr], an initializer in the definition of a variable can consist of arbitrary expressions involving literals and previously declared variables and functions, regardless of the variable's storage duration. [Example:...



694. Zero- and value-initialization of union objects

Section: 9.4  [dcl.init]     Status: C++11     Submitter: Clark Nelson     Date: 14 May, 2008     Liaison: WG14

[Voted into the WP at the November, 2010 meeting.]

The C committee is considering changing the definition of zero-initialization of unions to guarantee that the bytes of the entire union are set to zero before assigning 0, converted to the appropriate type, to the first member. The argument (summarized here) is for backward compatibility. The C++ Committee may want to consider the same change.

Proposed resolution (August, 2008):

Change bullet 3 of 9.4 [dcl.init] paragraph 5 (in the first list, dealing with zero-initialization) as follows:

[Drafting notes: Ask a C liaison about the progress of WG14 paper N1311, which deals with this issue. Since the adoption of WG21 paper N2544, unions may have static data members, hence the change to refer to the first non-static data member and the deletion of the footnote.]

Notes from the September, 2008 meeting:

It was observed that padding bytes in structs are zero-initialized in C, so if we are changing the treatment of unions in this way we should consider adding the C behavior for padding bytes at the same time. In particular, using memcmp to compare structs only works reliably if the padding bytes are zero-initialized.

Additional notes (February, 2010):

The C Committee has changed its approach to this question and is no longer using a two-phase process; in C, zero-initialization is specific to program start-up and thus is not appropriate for use with thread-local storage. See C paper N1387.

Proposed resolution (October, 2010):

Change 9.4 [dcl.init] paragraph 5 as follows:

To zero-initialize an object or reference of type T means:




1214. Kinds of initializers

Section: 9.4  [dcl.init]     Status: C++11     Submitter: Johannes Schaub     Date: 2010-10-30

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

9.4 [dcl.init] paragraph 16 describes three kinds of initializers: a single expression, a braced-init-list, and a parenthesized list of expressions. It is not clear which, if any, of these categories is the appropriate description for an initializer like

    T t( { 1, 2 } )

and thus not clear which of the bullets in the list applies.




938. Initializer lists and array new

Section: 9.4.2  [dcl.init.aggr]     Status: C++11     Submitter: Alisdair Meredith     Date: 12 July, 2009

[Voted into the WP at the March, 2011 meeting.]

9.4.2 [dcl.init.aggr] paragraph 4 says,

An initializer-list is ill-formed if the number of initializer-clauses exceeds the number of members or elements to initialize.

However, in a new-expression, the number of elements to be initialized is potentially unknown at compile time. How should an overly-long initializer-list in a new-expression be treated?

Notes from the August, 2010 meeting:

The consensus of the CWG was that this case should throw an exception at runtime.

Proposed resolution (January, 2011):

Change 7.6.2.8 [expr.new] paragraph 7 as follows:

When the value of the expression in a noptr-new-declarator is zero, the allocation function is called to allocate an array with no elements. If the value of that expression is less than zero or such that the size of the allocated object would exceed the implementation-defined limit, or if the new-initializer is a braced-init-list for which the number of initializer-clauses exceeds the number of elements to initialize, no storage is obtained and the new-expression terminates by throwing an exception of a type that would match a handler (14.4 [except.handle]) of type std::bad_array_new_length (17.6.4.2 [new.badlength]).



1030. Evaluation order in initializer-lists used in aggregate initialization

Section: 9.4.2  [dcl.init.aggr]     Status: C++11     Submitter: Scott Meyers     Date: 2010-02-09

[Voted into the WP at the March, 2011 meeting.]

The ordering imposed by 9.4.2 [dcl.init.aggr] paragraph 17 applies only to “the full-expressions in an initializer-clause” (i.e., what follows an = in an aggregate initializer); this leaves unspecified the order in which the expressions in an initializer-list (the term used by the braced-init-list form of initializer, with no =) are evaluated.

Notes from the November, 2010 meeting:

The CWG favored guaranteeing the order of evaluation of initializer-clauses appearing in a braced-init-list, regardless of whether the braced-init-list is an aggregate initialization or constructor call.

Proposed resolution (January, 2011):

  1. Delete 9.4.2 [dcl.init.aggr] paragraph 17:

  2. The full-expressions in an initializer-clause are evaluated in the order in which they appear.
  3. Insert the following as a new paragraph between paragraphs 3 and 4 of 9.4.5 [dcl.init.list]

  4. Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (13.7.4 [temp.variadic]), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list. [Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call. —end note]



1070. Missing initializer clauses in aggregate initialization

Section: 9.4.2  [dcl.init.aggr]     Status: C++11     Submitter: Jason Merrill     Date: 2010-06-01

[Voted into the WP at the November, 2010 meeting.]

Issue 990 added the following text to 9.4.5 [dcl.init.list] paragraph 3:

A better way to handle this would be to delete that bullet and change 9.4.2 [dcl.init.aggr] paragraph 7 as follows:

If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be value-initialized (9.4 [dcl.init]) initialized from an empty initializer list (9.4.5 [dcl.init.list]).

This makes { } less of a special case and makes the following example work:

    struct A {
       A(std::initializer_list<int>);
    };
    struct B {
       int i;
       A a;
    };
    B b = { 1 };

Proposed resolution (August, 2010):

  1. Delete 9.4.5 [dcl.init.list] bullet 3.2, includeing the example:

  2. Change 9.4.2 [dcl.init.aggr] paragraph 7 as follows:

  3. If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be value-initialized (9.4 [dcl.init]) initialized from an empty initializer list (9.4.5 [dcl.init.list]).



1138. Rvalue-ness check for rvalue reference binding is wrong

Section: 9.4.4  [dcl.init.ref]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 48
N3092 comment GB 37
N3092 comment DE 10

The requirement that an rvalue reference must be bound to an rvalue is found in 9.4.4 [dcl.init.ref] bullet 5.2:

This is not quite correct, as it is phrased in terms of the value category of the initializer expression itself rather than that of the result of any conversions applied to the initializer. It should be permitted to bind an rvalue reference to a temporary created from an lvalue, for instance, or to the rvalue result of a conversion function for an lvalue object of class type. Also, it should not be permitted to bind an rvalue reference to the lvalue result of a conversion function for a class rvalue.

Proposed resolution (August, 2010):

Change 9.4.4 [dcl.init.ref] paragraph 5 as follows:

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

In all cases except the last (i.e., creating and initializing a temporary from the initializer expression), the reference is said to bind directly to the initializer expression.

This resolution also resolves issue 1139.




1139. Rvalue reference binding to scalar xvalues

Section: 9.4.4  [dcl.init.ref]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 49

The current wording of 9.4.4 [dcl.init.ref] paragraph 5 does not specify direct binding of an rvalue reference to a scalar xvalue; instead, it requires creation of a temporary and binding the rvalue reference to the temporary.

Proposed resolution (August, 2010):

This issue is resolved by the resolution of 1138.




1236. Inconsistently-interrelated examples

Section: 9.4.4  [dcl.init.ref]     Status: C++11     Submitter: Doug Gregor     Date: 2011-01-20

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The examples in 9.4.4 [dcl.init.ref] paragraph 5 are not consistent as to whether earlier code fragments are assumed to be part of the example or not. For instance, the variables i and d are used as initializers without declaration in later fragments, presumably intended to refer to the declarations introduced in the first couple. However, the third fragment declares rca, which is an incompatible redeclaration of a name declared in the first fragment.




1095. List-initialization of references

Section: 9.4.5  [dcl.init.list]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-07-26

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The current wording of the WP appears not to allow for list-initialization of a reference like the following:

    int i;
    int& ir{i};

First, 9.4 [dcl.init] bullet 16.1 reads,

A reference is not an object, so this does not appear to apply; however, the second bullet sends reference initialization off to 9.4.4 [dcl.init.ref], which does not cover braced-init-lists: paragraph 5 of that section deals only with initilizer expressions, and a braced-init-list is not an expression.

Assuming that the use of “object” in the first bullet is just an oversight, 9.4.5 [dcl.init.list] also does not cover the case of a reference to a scalar type whose initalizer is a braced-init-list with a single element. Bullet 7 of paragraph 3 reads,

and would cover this case except that, again, a reference is not an object. As a result, such an initialization would end up in the last bullet and consequently be ill-formed.

Presumably all that is needed is to add “or reference” to the appropriate bullets of 9.4 [dcl.init] paragraph 16 and 9.4.5 [dcl.init.list] paragraph 3.

Proposed resolution (November, 2010) [SUPERSEDED]:

  1. Change 9.4 [dcl.init] bullet 16.1 as follows:

  2. Change 9.4.5 [dcl.init.list] bullet 3.7 as follows:




1232. Creation of array temporaries using a braced-init-list

Section: 9.4.5  [dcl.init.list]     Status: C++11     Submitter: Andrew Koenig     Date: 2011-01-03

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

By analogy with the variable definition

    int arr[3] = {1, 2, 3};

it should be possible to write something like

   void f(const int(&)[3]);
   void g() {
     f({1, 2, 3});
   }

There are currently at least two problems with the latter usage. First, the variable initializer relies on brace elision, which appears to be defined only for variable declarations (9.4.2 [dcl.init.aggr] paragraph 11) , and possibly only for certain forms of variable declarations.

Second, the call would require creation of an array temporary to which the parameter reference would be bound, and the current Standard does not describe array temporaries (although rvalue arrays can occur as members of class rvalues). This is also contrary to the direction established by CWG in considering 1058.




1134. When is an explicitly-defaulted function defined?

Section: 9.5.2  [dcl.fct.def.default]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 47

9.5.2 [dcl.fct.def.default] paragraph 4 says,

A special member function is user-provided if it is user-declared and not explicitly defaulted on its first declaration. A user-provided explicitly-defaulted function is defined at the point where it is explicitly defaulted...

The second sentence should say “user-declared” instead of “user-provided.”

Proposed resolution (September, 2010):

Change 9.5.2 [dcl.fct.def.default] paragraph 4 as follows:

...A special member function is user-provided if it is user-declared and not explicitly defaulted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed. [Note:...
[Drafting note: the suggestion in the NB comment is incorrect. The proposed resolution clarifies the intent.]


1135. Explicitly-defaulted non-public special member functions

Section: 9.5.2  [dcl.fct.def.default]     Status: C++11     Submitter: FI     Date: 2010-08-02

[Voted into the WP at the March, 2011 meeting.]

N3092 comment FI 1

It should be allowed to explicitly default a non-public special member function on its first declaration. It is very likely that users will want to default protected/private constructors and copy constructors without having to write such defaulting outside the class.

Proposed resolution (November, 2010):

  1. Change 9.5.2 [dcl.fct.def.default] paragraphs 1-5 as follows:

  2. A function definition of the form:

    is called an explicitly-defaulted definition. A function that is explicitly defaulted shall

    [Note: This implies that parameter types, return type, and cv-qualifiers must match the hypothetical implicit declaration. —end note]

    An explicitly-defaulted function may be declared constexpr only if it would have been implicitly declared as constexpr, and may have an explicit exception-specification only if it is compatible (14.5 [except.spec]) with the exception-specification on the implicit declaration. If it a function is explicitly defaulted on its first declaration,

    [Note: Such a special member function may be trivial, and thus its accessibility and explicitness should match the hypothetical implicit definition; see below. —end note] [Example:

      struct S {
        constexpr S() = default;                 //ill-formed: implicit S() is not constexpr
        S(int a = 0) = default;                  // ill-formed: default argument
        void operator=(const S&) = default;      // ill-formed: non-matching return type
        ~S() throw(int) = default;               // ill-formed: exception specification doesn't match
      private:
        int i;
        S(S&);                                   // OK: private copy constructor
      };
      S::S(S&) = default;                        // OK: defines copy constructor
    

    end example]

    Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall provide implicit definitions for them (11.4.5 [class.ctor] 11.4.7 [class.dtor], 11.4.5.3 [class.copy.ctor]), which might mean defining them as deleted. A special member function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed. [Note: while an implicitly-declared special member function is inline (11.4.4 [special]), an explicitly-defaulted definition may be non-inline. Non-inline definitions are user-provided, and hence non-trivial (11.4.5 [class.ctor], 11.4.7 [class.dtor], 11.4.5.3 [class.copy.ctor]). This rule enables Declaring a function as defaulted after its first declaration can provide efficient execution and concise definition while enabling a stable binary interface to an evolving code base. —end note]

    [Example:

      struct trivial {
        trivial() = default;
        trivial(const trivial&) = default;
        trivial(trivial&&) = default;
        trivial& operator=(const trivial&) = default;
        trivial& operator=(trivial&&) = default;
        ~trivial() = default;
      };
    
      struct nontrivial1 {
        nontrivial1();
      };
    
      nontrivial1::nontrivial1() = default;           // not inline first declaration
    
      struct nontrivial2 {
        nontrivial2();
      };
      inline nontrivial2::nontrivial2() = default;    // not first declaration
    
      struct nontrivial3 {
        virtual ~nontrivial3() = 0;                   // virtual
      };
      inline nontrivial3::~nontrivial3() = default;   // not first declaration
    

    end example]

  3. Change 11.4.5 [class.ctor] paragraph 5 as follows:

  4. ...A default constructor is trivial if it is neither not user-provided nor deleted and...
  5. Change 11.4.7 [class.dtor] paragraph 3 as follows:

  6. ...A destructor is trivial if it is neither not user-provided nor deleted and...
  7. Change 11.4.5.3 [class.copy.ctor] paragraph 13 as follows:

  8. A copy/move constructor for class X is trivial if it is neither not user-provided nor deleted and...
  9. Change 11.4.5.3 [class.copy.ctor] paragraph 27 as follows:

  10. A copy/move assignment operator for class X is trivial trivial if it is neither not user-provided nor deleted and...

This resolution also resolves issues 1136, 1137, 1140, 1145, 1149, and 1208.




1136. Explicitly-defaulted explicit constructors

Section: 9.5.2  [dcl.fct.def.default]     Status: C++11     Submitter: FI     Date: 2010-08-02

[Voted into the WP at the March, 2011 meeting.]

N3092 comment FI 2

It should be allowed to explicitly default an explicit special member function on its first declaration. It is very likely that users will want to default explicit copy constructors without having to write such defaulting outside of the class.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1135.




1137. Explicitly-defaulted virtual special member functions

Section: 9.5.2  [dcl.fct.def.default]     Status: C++11     Submitter: FI     Date: 2010-08-02

[Voted into the WP at the March, 2011 meeting.]

N3092 comment FI 3

It should be allowed to explicitly default a virtual special member function on its first declaration. It is very likely that users will want to default virtual copy assignment operators and destructors without having to write such defaulting outside of the class.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1135.




1022. Can an enumeration variable have values outside the values of the enumeration?

Section: 9.7.1  [dcl.enum]     Status: C++11     Submitter: Jason Merrill     Date: 2010-01-22

[Voted into the WP at the March, 2011 meeting.]

N3092 comment US 31

According to 9.7.1 [dcl.enum] paragraph 10,

An expression of arithmetic or enumeration type can be converted to an enumeration type explicitly. The value is unchanged if it is in the range of enumeration values of the enumeration type; otherwise the resulting enumeration value is unspecified.

(There is similar wording in 7.6.1.9 [expr.static.cast].) Does the phrase “resulting enumeration value” mean that the result, although unspecified, must lie within the range of enumeration values of the enumeration type? Existing practice seems to allow out-of-range values to be preserved if the underlying type is large enough to represent the value. This freedom is important both for efficiency (to avoid having to mask values while storing and/or fetching) and to prevent optimizers from removing code that tests for out-of-range values.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1094.




1012. Undeprecating static

Section: 9.8.2.2  [namespace.unnamed]     Status: C++11     Submitter: Ville Voutilainen     Date: 2009-12-09

[Voted into the WP at the November, 2010 meeting.]

N3092 comment FI 6

Although 9.8.2.2 [namespace.unnamed] states that the use of the static keyword for declaring variables in namespace scope is deprecated because the unnamed namespace provides a superior alternative, it is unlikely that the feature will be removed at any point in the foreseeable future, especially in light of C compatibility concerns. The Committee should consider removing the deprecation.

Proposed resolution (August, 2010):

  1. Delete 9.8.2.2 [namespace.unnamed] paragraph 2:

  2. The use of the static keyword is deprecated when declaring variables in a namespace scope (see annex Clause Annex D [depr]); the unnamed-namespace provides a superior alternative.
  3. Delete _N3225_.D.2 [depr.static]:

  4. D.2 static keyword [depr.static]

    The use of the static keyword is deprecated when declaring objects in namespace scope (see 6.4.6 [basic.scope.namespace]).




341. extern "C" namespace member function versus global variable

Section: 9.11  [dcl.link]     Status: C++11     Submitter: Steve Adamczyk     Date: 1 Mar 2002

[Voted into the WP at the November, 2010 meeting.]

Here's an interesting case:

  int f;
  namespace N {
    extern "C" void f () {}
  }
As far as I can tell, this is not precluded by the ODR section (6.3 [basic.def.odr]) or the extern "C" section (9.11 [dcl.link]). However, I believe many compilers do not do name mangling on variables and (more-or-less by definition) on extern "C" functions. That means the variable and the function in the above end up having the same name at link time. EDG's front end, g++, and the Sun compiler all get essentially the same error, which is a compile-time assembler-level error because of the duplicate symbols (in other words, they fail to check for this, and the assembler complains). MSVC++ 7 links the program without error, though I'm not sure how it is interpreted.

Do we intend for this case to be valid? If not, is it a compile time error (required), or some sort of ODR violation (no diagnostic required)? If we do intend for it to be valid, are we forcing many implementations to break binary compatibility by requiring them to mangle variable names?

Personally, I favor a compile-time error, and an ODR prohibition on such things in separate translation units.

Notes from the 4/02 meeting:

The working group agreed with the proposal. We feel a diagnostic should be required for declarations within one translation unit. We also noted that if the variable in global scope in the above example were declared static we would still expect an error.

Relevant sections in the standard are 9.11 [dcl.link] paragraph 6 and 6.6 [basic.link] paragraph 9. We feel that the definition should be written such that the entities in conflict are not "the same entity" but merely not allowed together.

Additional note (September, 2004)

This problem need not involve a conflict between a function and a variable; it can also arise with two variable declarations:

    int x;
    namespace N {
        extern "C" int x;
    }

Proposed resolution (March, 2008):

Change 9.11 [dcl.link] paragraph 6 as follows:

At most one function with a particular name can have C language linkage. Two declarations for a function with C language linkage with the same function name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same function. Two declarations for an object with C language linkage with the same name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same object. A function or object with C linkage shall not be declared with the same name (6.1 [basic.pre]) as an object or reference declared in global scope, unless both declarations denote the same object; no diagnostic is required if the declarations appear in different translation units. [Note: because of the one definition rule (6.3 [basic.def.odr]), only Only one definition for a function or object with C linkage may appear in the program (see 6.3 [basic.def.odr]); that is, implies that such a function or object must not be defined in more than one namespace scope. For example,

    int x;
    namespace A {
      extern "C" int f();
      extern "C" int g() { return 1; }
      extern "C" int h();
      extern "C" int x();               // ill-formed: same name as global-scope object x
    }

    namespace B {
      extern "C" int f();               // A::f and B::f refer
                                        // to the same function
      extern "C" int g() { return 1; }  // ill-formed, the function g
                                        // with C language linkage
                                        // has two definitions
    }

    int A::f() { return 98; }           // definition for the function f
                                        // with C language linkage
    extern "C" int h() { return 97; }
                                        // definition for the function h
                                        // with C language linkage
                                        // A::h and ::h refer to the same function

end note]

Notes from the September, 2008 meeting:

It should also be possible to declare references with C name linkage (although the meaning the first sentence of 9.11 [dcl.link] paragraph 1 with respect to the meaning of such a declaration is not clear), which would mean that the changed wording should refer to declaring “the same entity” instead of “the same object.” The formulation here would probably benefit from the approach currently envisioned for issues 570 and 633, in which “variable” is defined as being either an object or a reference.

Proposed resolution (February, 2010):

Change 9.11 [dcl.link] paragraph 6 as follows:

At most one function with a particular name can have C language linkage. Two declarations for a function with C language linkage with the same function name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same function. Two declarations for an object or reference with C language linkage with the same name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same object or reference. An entity with C language linkage shall not be declared with the same name as an entity in global scope, unless both declarations denote the same object or reference; no diagnostic is required if the declarations appear in different translation units. [Note: because of the one definition rule (6.3 [basic.def.odr]), only Only one definition for a function or object an entity with C linkage may appear in the program (see 6.3 [basic.def.odr]); that is, implies that such a function or object an entity must not be defined in more than one namespace scope. end note] For example, [Example:

  int x;
  namespace A {
    extern "C" int f();
    extern "C" int g() { return 1; }
    extern "C" int h();
    extern "C" int x();               // ill-formed: same name as global-scope object x
  }

  namespace B {
   extern "C" int f();                // A::f and B::f refer
                                      // to the same function
   extern "C" int g() { return 1; }   // ill-formed, the function g
                                      // with C language linkage
                                      // has two definitions
  }

  int A::f() { return 98; }           // definition for the function f
                                      // with C language linkage
  extern "C" int h() { return 97; }
                                      // definition for the function h
                                      // with C language linkage
                                      // A::h and ::h refer to the same function

end note example]

Additional note (February, 2010):

The proposed wording above does not cover the case where two different entities with C linkage are declared in different namespaces, only the case where one of the entities is in global scope.

Proposed resolution (August, 2010):

Change 9.11 [dcl.link] paragraph 6 as follows:

At most one function with a particular name can have C language linkage. Two declarations for a function with C language linkage with the same function name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same function. Two declarations for an object a variable with C language linkage with the same name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same object variable. An entity with C language linkage shall not be declared with the same name as an entity in global scope, unless both declarations denote the same entity; no diagnostic is required if the declarations appear in different translation units. A variable with C language linkage shall not be declared with the same name as a function with C language linkage (ignoring the namespace names that qualify the respective names); no diagnostic is required if the declarations appear in different translation units. [Note: because of the one definition rule (6.3 [basic.def.odr]), only Only one definition for a function or object an entity with a given name with C language linkage may appear in the program (see 6.3 [basic.def.odr]); that is, implies such a function or object an entity must not be defined in more than one namespace scope. end note] For example, [Example:

  int x;
  namespace A {
    extern "C" int f();
    extern "C" int g() { return 1; }
    extern "C" int h();
    extern "C" int x();               // ill-formed: same name as global-scope object x
  }

  namespace B {
    extern "C" int f();               // A::f and B::f refer
                                      // to the same function
    extern "C" int g() { return 1; }  // ill-formed, the function g
                                      // with C language linkage
                                      // has two definitions
  }

  int A::f() { return 98; }           // definition for the function f
                                      // with C language linkage
  extern "C" int h() { return 97; }
                                      // definition for the function h
                                      // with C language linkage
                                      // A::h and ::h refer to the same function

end note example]

[Note to editor: please consider reformatting the comments in the example.]




1185. Misleading description of language linkage and member function types

Section: 9.11  [dcl.link]     Status: C++11     Submitter: Mike Miller     Date: 2010-08-29

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The current wording of 9.11 [dcl.link] paragraph 4 is:

...A C language linkage is ignored for the names of class members and the member function type of class member functions...

This has two problems. First, it sounds as if a class member function has a “member function type,” while in fact the type of a class member function is an ordinary function type (cf 11.4 [class.mem] paragraph 11).

Second, even if that problem is fixed, it is not accurate to say that a C language linkage is “ignored” for the type of a member function. It does not affect the language linkage of the type of the member function, but it does affect the language linkage of any function declarators appearing in the parameter and return types of the function and thus the type of the function.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 9.11 [dcl.link] paragraph 4 as follows:

...A C language linkage is ignored for in determining the language linkage of the names of class members and the member function type of class member functions...



972. Allowing multiple attribute-specifiers

Section: 9.12.1  [dcl.attr.grammar]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 5 October, 2009

[Voted into the WP at the November, 2010 meeting as part of paper N3190.

The current syntax requires that multiple attributes that appertain to the same entity be grouped into a single attribute-specifier. The migration from existing vendor-specific attributes would be easier if the syntax allowed multiple attribute-specifiers at each location where an attribute-specifier currently appears.

Proposed resolution (October, 2010):

  1. Replace every occurrence of attribute-specifier with attribute-specifier-seq except in 9.12.1 [dcl.attr.grammar] paragraphs 1, 3, and 6 (but including paragraph 4).

  2. Insert the following production at the beginning of the grammar of 9.12.1 [dcl.attr.grammar] paragraph 1:




1031. Optional elements in attributes

Section: 9.12.1  [dcl.attr.grammar]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 2010-02-10

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

An attribute-argument-clause should be allowed to consist solely of (), i.e., with no balanced-token-seq. Furthermore, the grammar for balanced-token should make the balanced-token-seq optional. Both of these goals could be accomplished by making the balanced-token optional in the first production of the rule for balanced-token-seq.

Proposed resolution (February, 2011) [SUPERSEDED]:

Change the grammar of 9.12.1 [dcl.attr.grammar] paragraph 1 as follows:




1033. Restrictions on alignment attributes

Section: 9.12.2  [dcl.align]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 2010-02-17

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

According to 9.12.2 [dcl.align] paragraph 5,

The combined effect of all alignment attributes in a declaration shall not specify an alignment that is less strict than the alignment that would otherwise be required for the entity being declared.

“...would otherwise be required” could be read as referring to the alignment set by another declaration of the entity. However, it was intended to prevent specifying an alignment smaller than the natural alignment the entity would have in the absence of an align attribute. The wording should be changed to make that clearer.

Proposed resolution (February, 2011) [SUPERSEDED]:

Change 9.12.2 [dcl.align] paragraph 5 as follows:

The combined effect of all alignment-specifiers in a declaration shall not specify an alignment that is less strict than the alignment that would otherwise be required for the entity being declared if all alignment-specifiers were ignored (including those in other declarations).



1036. Alignment attribute in an exception-declaration

Section: 9.12.2  [dcl.align]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 2010-02-26

[Voted into the WP at the November, 2010 meeting.]

The Standard explicitly bans alignment attributes for function parameters (9.12.2 [dcl.align] paragraph 1), but it is silent regarding the “parameter” of an exception handler. This should be clarified one way or the other.

Proposed resolution (October, 2010):

Change 9.12.2 [dcl.align] paragraph 1 as follows:

...The attribute may be followed by an ellipsis. The attribute may be applied to a variable that is neither a function parameter nor declared with the register storage class specifier and to a class data member that is not a bit-field or to a class data member, but it shall not be applied to a bit-field, a function parameter, the formal parameter of a catch clause (14.4 [except.handle]), or a variable declared with the register storage class specifier. The attribute may also be applied to the declaration of a class or enumeration type.



355. Global-scope :: in nested-name-specifier

Section: Clause 11  [class]     Status: C++11     Submitter: Clark Nelson     Date: 16 May 2002

[Voted into the WP at the March, 2011 meeting as paper N3259.]

In looking at a large handful of core issues related to elaborated-type-specifiers and the naming of classes in general, I discovered an odd fact. It turns out that there is exactly one place in the grammar where nested-name-specifier is not immediately preceded by "::opt": in class-head, which is used only for class definitions. So technically, this example is ill-formed, and should evoke a syntax error:

  struct A;
  struct ::A { };

However, all of EDG, GCC and Microsoft's compiler accept it without a qualm. In fact, I couldn't get any of them to even warn about it.

Suggested resolution:

It would simplify the grammar, and apparently better reflect existing practice, to factor the global-scope operator into the rule for nested-name-specifier.

Proposed resolution (February, 2011):

The proposed resolution will be submitted as a separate document.




1140. Incorrect redefinition of POD class

Section: Clause 11  [class]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the March, 2011 meeting.]

N3092 comment US 50

The class

    struct A { const int i; };

was a POD in C++98, but is not a POD under the FCD rules because it does not have a trivial default constructor. C++0x POD was intended to be a superset of C++98 POD.

Suggested resolution: Change POD to be standard layout and trivially copyable.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1135.




1215. Definition of POD struct

Section: Clause 11  [class]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-10-28

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The definition of a POD struct in Clause 11 [class] paragraph 9 is not (but should be) restricted to non-union class types.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change Clause 11 [class] paragraph 9 as follows:

A POD struct109 is a non-union class that is both a trivial class and a standard-layout class...



924. alias-declaration as a class member

Section: 11.4  [class.mem]     Status: C++11     Submitter: Alisdair Meredith     Date: 23 June, 2009

N3092 comment FI 10

[Voted into WP at August, 2010 meeting.]

The grammar for member-declaration in 11.4 [class.mem] does not include a production for the alias-declaration form of typedef declarations, meaning that something like

    struct S {
      using UINT = unsigned int;
    };

is ill-formed. This seems like an oversight.

Proposed resolution (February, 2010):

In the grammar in 11.4 [class.mem], add the indicated production to the definition of member-declaration:




1035. Omitted and required decl-specifiers

Section: 11.4  [class.mem]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 2010-02-25

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

According to 11.4 [class.mem] paragraph 6,

The decl-specifier-seq is omitted in constructor, destructor, and conversion function declarations only.

This is incorrect, as some decl-specifiers (explicit, virtual, inline, constexpr) are permitted in these declarations. Conversely, C.7.6 [diff.dcl], “Banning implicit int,” says,

In C++ a decl-specifier-seq must contain a type-specifier.

This is also incorrect for these declarations.

Proposed resolution (February, 2011) [SUPERSEDED]:

  1. Change 11.4 [class.mem] paragraph 7 as follows:

  2. The decl-specifier-seq is may be omitted in constructor, destructor, and conversion function declarations only; when declaring another kind of member the decl-specifier-seq shall contain a type-specifier that is not a cv-qualifier. The member-declarator-list can be omitted...
  3. Change C.7.6 [diff.dcl], “Banning implicit int,” as follows:

  4. In C++ a decl-specifier-seq must contain a type-specifier, unless it is followed by a declarator for a constructor, a destructor, or a conversion function. In the following example...



1072. Scoped enumerator with the same name as its containing class

Section: 11.4  [class.mem]     Status: C++11     Submitter: Pavel Minaev     Date: 2010-06-02

[Voted into the WP at the November, 2010 meeting.]

11.4 [class.mem] paragraph 13 requires that all enumerators of a member enumeration type have names different from that of the containing class. This is only necessary for an unscoped enumeration; scoped enumerators are not in the scope of the containing class.

Proposed resolution (September, 2010):

Change 11.4 [class.mem] bullet 14.4 as follows:




1142. friend declaration of member function of containing class

Section: 11.4.2  [class.mfct]     Status: C++11     Submitter: US     Date: 2010-08-02

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 52

The current wording of 11.4.2 [class.mfct] paragraph 7 allows friend declarations to name member functions “after their class has been defined.” This appears to prohibit a friend declaration in a nested class defined inside its containing class that names a member function of the containing class, because the containing class is still considered to be incomplete at that point.

Proposed resolution (September, 2010):

Change 11.4.2 [class.mfct] paragraph 7 as follows:

Member Previously-declared member functions may be mentioned in friend declarations after their class has been defined.



1017. Member access transformation in unevaluated operands

Section: 11.4.3  [class.mfct.non.static]     Status: C++11     Submitter: Johannes Schaub     Date: 2009-12-30

[Voted into the WP at the March, 2011 meeting as part of paper N3282.]

The following innocuous-appearing code is currently ill-formed:

    struct A {
        int a;
    };

    struct B {
        void f() {
            decltype(A::a) i;    // ill-formed
        }
    };

The reason is that, according to 11.4.3 [class.mfct.non.static] paragraph 3, the reference to A::a is transformed into (*this).A::a, and there is no A subobject of B. It would seem reasonable to suppress this transformation in unevaluated operands, where a reference to a non-static member is permitted without an object expression.

(See also issue 1005.)

Notes from the November, 2010 meeting:

The CWG agreed that the resolution of issue 515 was ill-advised and should be reversed.


1207. Type of class member in trailing-return-type

Section: 11.4.3  [class.mfct.non.static]     Status: C++11     Submitter: Jason Merrill     Date: 2010-10-06

[Voted into the WP at the March, 2011 meeting as part of paper N3282.]

Consider the following example:

    struct vector {
        struct iterator { };
        struct const_iterator { };
        iterator begin();
        const_iterator begin() const;
    };
    class block {
        vector v;
        auto end() const -> decltype(v.begin()) { return v.begin(); }
    };

Because the transformation of a member name into a class member access expression (11.4.3 [class.mfct.non.static] paragraph 3) only occurs inside the body of a non-static member function, the type of v in the trailing-return-type is non-const but is const in the return expression, resulting in a type mismatch between the return expression and the return type of the function.

One possibility would be to include the trailing-return-type as being subject to the transformation in 11.4.3 [class.mfct.non.static]. Note, however, that this is currently not in scope at that point (see issue 945).

Notes from the November, 2010 meeting:

The CWG felt that, because this is effectively an implicit parameter, the best approach would be to model its usability on the visibility of parameters: it could be named wherever a parameter of the function is in scope.

Proposed resolution (February, 2011):

  1. Change _N4567_.5.1.1 [expr.prim.general] paragraph 2 as follows, adding three new paragraphs:

  2. The keyword this names a pointer to the object for which a non-static member function (_N4868_.11.4.3.2 [class.this]) is invoked or a non-static data member's initializer (11.4 [class.mem]) is evaluated. The keyword this shall be used only inside the body of a non-static member function (11.4.2 [class.mfct]) of the nearest enclosing class or in a brace-or-equal-initializer for a non-static data member (11.4 [class.mem]). The type of the expression is a pointer to the class of the function or non-static data member, possibly with cv-qualifiers on the class type. The expression is a prvalue.

    If a function-definition or member-declarator declares a member function of a class X, the expression this is a prvalue of type “pointer to cv-qualifier-seq X” between the optional cv-qualifier-seq and the end of the function-definition or member-declarator. It shall not appear before the optional cv-qualifier-seq and it shall not appear within the declaration of a static member function (although its type and value category is defined within a static member function as it is within a non-static member function). [Note: the type and value category is defined even for the case of a static member function because declaration matching does not occur until the complete declarator is known, and this may be used in the trailing-return-type of the declarator. —end note]

    Otherwise, if a member-declarator declares a non-static data member (11.4 [class.mem]) of a class X, the expression this is a prvalue of type “pointer to X” within the optional brace-or-equal-initializer. It shall not appear elsewhere in the member-declarator.

    The expression this shall not appear in any other context.

    [Example:...

  3. Change _N4567_.5.1.1 [expr.prim.general] paragraph 10 as follows:

  4. An id-expression that denotes a non-static data member or non-static member function of a class can only be used:

  5. Change 11.4.3 [class.mfct.non.static] paragraph 3 as follows:

  6. When an id-expression (7.5 [expr.prim]) that is not part of a class member access syntax (7.6.1.5 [expr.ref]) and not used to form a pointer to member (7.6.2.2 [expr.unary.op]) is used in the body declaration of a non-static member function of class X, if name lookup (6.5 [basic.lookup]) resolves the name...



1208. Explicit noexcept in defaulted definition

Section: 11.4.3  [class.mfct.non.static]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-10-07

[Voted into the WP at the March, 2011 meeting.]

It is currently not permitted to specify an exception-specification in a defaulted definition. It would be nice to be able to do so (providing the explicit specification matches the one that would be implicitly supplied) for documentation purposes.

Proposed resolution (November, 2010) [SUPERSEDED]:

This issue is resolved by the resolution of issue 1135.




738. constexpr not permitted by the syntax of constructor declarations

Section: 11.4.5  [class.ctor]     Status: C++11     Submitter: James Widman     Date: 27 October, 2008

[Voted into WP at August, 2010 meeting.]

According to 11.4.5 [class.ctor] paragraph 1, only function-specifiers are permitted in the declaration of a constructor, and constexpr is not a function-specifier. (See also issue 263, in which the resolution of a similar concern regarding the friend specifier did not change 11.4.5 [class.ctor] paragraph 1 but perhaps should have done so.)

Proposed resolution (February, 2010):

Change 11.4.5 [class.ctor] paragraph 1 as follows:

Constructors do not have names. A special declarator syntax using an optional sequence of function-specifiers (9.2.3 [dcl.fct.spec]) followed by the constructor's class name followed by a parameter list is used to declare or define the constructor. The syntax uses

in that order. In such a declaration, optional parentheses around the constructor class name are ignored. [Example:...




1145. Defaulting and triviality

Section: 11.4.5  [class.ctor]     Status: C++11     Submitter: FI     Date: 2010-08-03

[Voted into the WP at the March, 2011 meeting.]

N3092 comment FI 4

What effect does defaulting have on triviality? Related to issue 1135, non-public special members defaulted on their first declaration should retain triviality, because they shouldn't be considered user-provided. Related to issue 1137, defaulted member functions that are virtual should not be considered trivial, but there's no reason why non-virtuals could not be.

Furthermore, a class with a non-public explicitly-defaulted constructor isn't ever trivially constructible under the current rules. If such a class is used as a subobject, the constructor of the aggregating class should be trivial if it can access the non-public explicitly defaulted constructor of a subobject.

Suggested resolution: Change the triviality rules so that a class can have a trivial default constructor if the class has access to the default constructors of its subobjects and the default constructors of the subobjects are explicitly defaulted on first declaration, even if said defaulted constructors are non-public.

See also issue 1149.

Rationale (August, 2010):

The consensus of the CWG was that this change should not be made at this point in the standardization process, but that it might be considered at a later date.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1135.




1191. Deleted subobject destructors and implicitly-defined constructors

Section: 11.4.5  [class.ctor]     Status: C++11     Submitter: Jason Merrill     Date: 2010-09-02

[Voted into the WP at the March, 2011 meeting.]

Consider the following example:

    struct A {
       A();
       ~A() = delete;
    };

    struct B: A { };
    B* b = new B;

Under the current rules, B() is not deleted, but is ill-formed because it calls the deleted ~A::A() if it exits via an exception after the completion of the construction of A. A deleted subobject destructor should be added to the list of reasons for implicit deletion in 11.4.5 [class.ctor] and 11.4.5.3 [class.copy.ctor].

Notes from the November, 2010 meeting:

The CWG agreed that a change was needed, but only if one or more base and/or member constructors are non-trivial.

Proposed resolution (January, 2011):

  1. Add a new bullet to 11.4.5 [class.ctor] paragraph 5 as follows:

  2. ...A defaulted default constructor for class X is defined as deleted if:

  3. Add a new bullet to 11.4.5.3 [class.copy.ctor] paragraph 12 as follows:

  4. ...A defaulted copy/move constructor for a class X is defined as deleted (9.5.3 [dcl.fct.def.delete]) if X has:




1020. Implicitly-defined copy constructors and explicit base class constructors

Section: 11.4.5.3  [class.copy.ctor]     Status: C++11     Submitter: Niels Dekker     Date: 2010-01-12

[Voted into the WP at the November, 2010 meeting.]

N3092 comment FI 19

It is not clear whether the current specification allows a defaulted copy constructor to call an explicit constructor to copy a base or member subobject, and if so, whether that is desirable or not. See also issues 535 and 1118.

Proposed resolution (August, 2010):

This issue is resolved by the resolution of issue 1051.




1051. Reference members and generated copy constructors

Section: 11.4.5.3  [class.copy.ctor]     Status: C++11     Submitter: Steve Adamczyk     Date: 2010-03-11

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 62

The new wording describing generated copy constructors (11.4.5.3 [class.copy.ctor] paragraph 16) does not describe the initialization of members with reference type.

See also issue 992.

Proposed resolution (October, 2010):

  1. Change 11.4.5.3 [class.copy.ctor] paragraph 12 as follows:

  2. An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (9.5.3 [dcl.fct.def.delete]) if X has:

  3. Change 11.4.5.3 [class.copy.ctor] paragraph 16 as follows:

  4. The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its subobjects bases and members. [Note: brace-or-equal-initializers of non-static data members are ignored. See also the example in 11.9.3 [class.base.init]. —end note] The order of copying initialization is the same as the order of initialization of bases and members in a user-defined constructor (see 11.9.3 [class.base.init]). Let x be either the parameter of the constructor or, for the move constructor, an xvalue referring to the parameter. Each subobject base or non-static data member is copied/moved in the manner appropriate to its type:

    Virtual base class subobjects shall be copied initialized only once by the implicitly-defined copy/move constructor (see 11.9.3 [class.base.init]).

  5. Delete 11.4.5.3 [class.copy.ctor] paragraph 17:

  6. The implicitly-defined move constructor for a non-union class X performs a memberwise move of its subobjects. [Note: brace-or-equal-initializers of non-static data members are ignored. See also the example in 11.9.3 [class.base.init]. —end note] The order of moving is the same as the order of initialization of bases and members in a user-defined constructor (see 11.9.3 [class.base.init]). Given a parameter named x, each base or non-static data member is moved in the manner appropriate to its type:

    Virtual base class subobjects shall be moved only once by the implicitly-defined move constructor (see 11.9.3 [class.base.init]).

  7. Change 11.4.5.3 [class.copy.ctor] paragraph 18 as follows:

  8. The implicitly-defined copy/move constructor for a union X copies the object representation (6.8 [basic.types]) of X.
  9. Change 11.4.5.3 [class.copy.ctor] paragraph 28 as follows:

  10. A copy/move assignment operator that is defaulted and not defined as deleted is implicitly defined when is assigned a value of its class type or a value of a class type derived from its class type it is used (6.3 [basic.def.odr]) (e.g., when it is selected by overload resolution to assign to an object of its class type) or when it is explicitly defaulted after its first declaration.
  11. Change 11.4.5.3 [class.copy.ctor] paragraph 30 as follows:

  12. The implicitly-defined copy/move assignment operator for a non-union class X performs memberwise copy/move assignment of its subobjects. The direct base classes of X are assigned first, in the order of their declaration in the base-specifier-list, and then the immediate non-static data members of X are assigned, in the order in which they were declared in the class definition. Let x be either the parameter of the function or, for the move assignment operator, an xvalue referring to the parameter. Each subobject is assigned in the manner appropriate to its type:

    It is unspecified whether subobjects representing virtual base classes are assigned more than once by the implicitly-defined copy assignment operator. [Example:

      struct V { };
      struct A : virtual V { };
      struct B : virtual V { };
      struct C : B, A { };
    

    It is unspecified whether the virtual base class subobject V is assigned twice by the implicitly-defined copy assignment operator for C. —end example] [Note: This does not apply to move assignment, as a defaulted move assignment operator is deleted if the class has virtual bases. —end note]

  13. Delete 11.4.5.3 [class.copy.ctor] paragraph 31:

  14. The implicitly-defined move assignment operator for a non-union class X performs memberwise assignment of its subobjects. The direct base classes of X are assigned first, in the order of their declaration in the base-specifier-list, and then the immediate non-static data members of X are assigned, in the order in which they were declared in the class definition. Given a parameter named x, each subobject is assigned in the manner appropriate to its type:

This resolution also resolves issues 1020, 1064 and 1066.




1064. Defaulted move constructor for a union

Section: 11.4.5.3  [class.copy.ctor]     Status: C++11     Submitter: Mike Miller     Date: 2010-03-23

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 63

The new wording in 11.4.5.3 [class.copy.ctor] specifies the behavior of an implicitly-defined copy constructor for a non-union class (paragraph 16), an implicitly-defined move constructor for a non-union class (paragraph 17), and an implicitly-defined copy constructor for a union (paragraph 18), but not an implicitly-defined move constructor for a union.

Proposed resolution (August, 2010):

This issue is resolved by the resolution of issue 1051.




1080. Confusing relationship between templates and copy constructors

Section: 11.4.5.3  [class.copy.ctor]     Status: C++11     Submitter: Jason Merrill     Date: 2010-06-19

[Voted into the WP at the March, 2011 meeting.]

11.4.5.3 [class.copy.ctor] paragraphs 6-7 currently read,

A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-qualified) X and either there are no other parameters or else all other parameters have default arguments.

A member function template is never instantiated to perform the copy of a class object to an object of its class type. [Example:

    struct S {
        template<typename T> S(T);
        template<typename T> S(T&&);
        S();
    };

    S f();
    const S g;

    void h() {
        S a( f() ); // does not instantiate member template;
                    // uses the implicitly generated move constructor
        S a(g);     // does not instantiate the member template;
                    // uses the implicitly generated copy constructor
    }

These paragraphs were previously a single paragraph, and the second sentence was intended to mean that

    template <class T> A(T):

will never be instantiated to produce A(A). It should not have been split and the example should not have been amended to include move construction.

Lawrence Crowl: I suggest something along the lines of

A member function template is never instantiated to match the signature of an ill-formed constructor.

Proposed resolution (November, 2010):

Merge 11.4.5.3 [class.copy.ctor] paragraphs 6 and 7 and change the text as follows:

A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-qualified) X and either there are no other parameters or else all other parameters have default arguments. A member function template is never instantiated to perform the copy of a class object to an object of its class type produce such a constructor signature. [Example:

  struct S {
    template<typename T> S(T);
    template<typename T> S(T&&);
    S();
  };

  S f();
  const S g;

  void h() {
    S a( f() ); // does not instantiate member template;
                // uses the implicitly generated move constructor
    S a(g);     // does not instantiate the member template to produce S::S<S>(S);
                // uses the implicitly generated declared copy constructor
}



1082. Implicit copy function if subobject has none?

Section: 11.4.5.3  [class.copy.ctor]     Status: C++11     Submitter: Jason Merrill     Date: 2010-06-22

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

It seems odd to have an implicitly declared copy constructor (and the same for the copy assignment operator) if one of the subobjects does not have one. For example,

    struct A {
       A();
       A(A&&);
    };

    struct B: A { };

    B b;
    B b2(b); // error when implicitly defining B(B&), should not be declared

If we don't declare it in that case, we need to decide what happens if one base has only a move constructor and another has only a copy constructor.

Notes from the November, 2010 meeting:

The consensus of the CWG was to change the behavior so that all classes have a declaration of a copy constructor, but that it is defined as deleted in the cases where the declaration is omitted by the current rules.




1149. Trivial non-public copy operators in subobjects

Section: 11.4.5.3  [class.copy.ctor]     Status: C++11     Submitter: FI     Date: 2010-08-03

[Voted into the WP at the March, 2011 meeting.]

N3092 comment FI 5

A class with a non-public explicitly-defaulted copy constructor isn't ever trivially copyable under the current rules. If such a class is used as a subobject, the copy constructor of the aggregating class should be trivial if it can access the non-public explicitly defaulted copy constructor of a subobject.

See also issue 1145.

Rationale (August, 2010):

The consensus of the CWG was that this change should not be made at this point in the standardization process, but that it might be considered at a later date.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 1135.




1224. constexpr defaulted copy constructors

Section: 11.4.5.3  [class.copy.ctor]     Status: C++11     Submitter: Jason Merrill     Date: 2010-10-25

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

11.4.5 [class.ctor] allows for a defaulted default constructor to be constexpr, but 11.4.5.3 [class.copy.ctor] does not do the same for a defaulted copy constructor. This seems wrong.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 11.4.5.3 [class.copy.ctor] paragraph 14 as follows:

A copy/move constructor that is defaulted and not defined as deleted is implicitly defined if it is odr-used (6.3 [basic.def.odr]) to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type123 or when it is explicitly defaulted after its first declaration. [Note: the copy/move constructor is implicitly defined even if the implementation elided its odr-use (6.3 [basic.def.odr], 6.7.7 [class.temporary]). —end note] If the implicitly-defined constructor would satisfy the requirements of a constexpr constructor (9.2.6 [dcl.constexpr]), the implicitly-defined constructor is constexpr.



1066. When is a copy/move assignment operator implicitly defined?

Section: 11.4.6  [class.copy.assign]     Status: C++11     Submitter: Mike Miller     Date: 2010-03-24

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 64

According to 11.4.5.3 [class.copy.ctor] paragraph 28,

A copy/move assignment operator that is defaulted and not defined as deleted is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type or when it is explicitly defaulted after its first declaration.

This sounds as if any assignment to a class object, regardless of whether it is a copy or a move assignment, defines both the copy and move operators. Presumably an assignment should only define the assignment operator chosen by overload resolution for the operation. (Compare the corresponding wording in paragraph 14 for the copy/move constructors: “...implicitly defined if it is used to initialize an object of its class type...”)

Proposed resolution (August, 2010):

This issue is resolved by the resolution of issue 1051.




1029. Type of a destructor call

Section: 11.4.7  [class.dtor]     Status: C++11     Submitter: Johannes Schaub     Date: 2010-02-08

[Voted into the WP at the November, 2010 meeting.]

The Standard does not define the type of a destructor call. Although that is not of any practical importance, it should do so as a matter of completeness. (_N4778_.7.6.1.4 [expr.pseudo] paragraph 1 defines the type of a pseudo-destructor call as void.)

Proposed resolution (September, 2010):

Change 7.6.1.3 [expr.call] paragraph 3 as follows:

The If the postfix-expression designates a destructor (11.4.7 [class.dtor]), the type of the function call expression is void; otherwise, the type of the function call expression is the return type of the statically chosen function (i.e., ignoring the virtual keyword), even if the type of the function actually called is different. This type shall be a complete object type, a reference type or the type void.



1081. Defaulted destructor and unusable operator delete

Section: 11.4.7  [class.dtor]     Status: C++11     Submitter: Jason Merrill     Date: 2010-06-22

[Voted into the WP at the March, 2011 meeting.]

A defaulted destructor should be implicitly defined as deleted if operator delete is deleted or inaccessible.

Proposed resolution (November, 2010):

Change 11.4.7 [class.dtor] paragraph 3 as follows:

...A defaulted destructor for a class X is defined as deleted if:

A destructor is trivial if...




1146. exception-specifications of defaulted functions

Section: 11.4.7  [class.dtor]     Status: C++11     Submitter: GB     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment GB 39
N3092 comment GB 41

The note in 11.4.7 [class.dtor] paragraph 4 says,

An explictly defaulted definition has no implicit exception-specification.

There are similar notes in 11.4.5.3 [class.copy.ctor] paragraphs 15 and 29.

However, 9.5.2 [dcl.fct.def.default] bullet 2.4 says that a special member function that is explicitly defaulted on its first declaration

is implicitly considered to have the same exception-specification as if it had been implicitly declared (14.5 [except.spec])

The notes are incorrect.

Proposed resolution (August, 2010):

  1. Change 11.4.7 [class.dtor] paragraph 4 as follows:

  2. [Note: an implicitly- declared destructor has an exception-specification (15.4). An explictly defaulted definition has no implicit exception-specification.end note]
  3. Change 11.4.5.3 [class.copy.ctor] paragraph 15 as follows:

  4. [Note: an implicitly-declared copy/move constructor has an exception-specification (15.4). An explicitly-defaulted definition (8.4.2) has no implicit exception-specification.end note]
  5. Change 11.4.5.3 [class.copy.ctor] paragraph 29 as follows:

  6. [Note: An implicitly-declared copy/move assignment operator has an exception- specification (15.4). An explicitly-defaulted definition has no implicit exception-specification.end note]



1147. Destructors should be default nothrow

Section: 11.4.7  [class.dtor]     Status: C++11     Submitter: GB     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting as paper N3204.]

N3092 comment GB 40
N3092 comment CH 9

A user-declared destructor that does not supply an exception specification should be considered as if declared noexcept(true) rather than noexcept(false).

(Duplicate of issue 1123.)




1241. Which members does a destructor destroy?

Section: 11.4.7  [class.dtor]     Status: C++11     Submitter: Ryou Ezoe     Date: 2011-02-08

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The current wording of 11.4.7 [class.dtor] paragraph 7 says,

After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X's direct non-variant members...

This is incorrect; it is only the non-static members that are destroyed.




1101. Non-integral initialized static data members

Section: 11.4.9.3  [class.static.data]     Status: C++11     Submitter: Jason Merrill     Date: 2010-08-02

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

According to 11.4.9.3 [class.static.data] paragraph 3,

If a static data member is of const literal type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [Note: In both these cases, the member may appear in constant expressions. —end note]

However, 7.7 [expr.const] bullet 2.7 allows only integral and enumeration types in constant expressions for the const case; the other types require constexpr to be considered constant expressions.

Proposed resolution (November, 2010):

Change 11.4.9.3 [class.static.data] paragraph 3 as follows:

If a non-volatile const static data member is of const literal integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (7.7 [expr.const]). A static data member of literal type can be declared in the class definition with the constexpr specifier...



741. “plain” long long bit-fields

Section: 11.4.10  [class.bit]     Status: C++11     Submitter: Mike Miller     Date: 7 November, 2008

[Voted into WP at August, 2010 meeting.]

The type long long is missing from the list of bit-field types in 11.4.10 [class.bit] paragraph 3 for which the implementation can choose the signedness. This was presumably an oversight. (If that is the case, we may want to reconsider the handling of 7.3.7 [conv.prom] paragraph 3: a long long bit-field that the implementation treats as unsigned will — pending the outcome of issue 739 — still promote to signed long long, which can lead to unexpected results for bit-fields with the same number of bits as long long.)

Proposed resolution (February, 2010):

Change 11.4.10 [class.bit] paragraph 3 as follows:

...It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int or, long, or long long bit-field is signed or unsigned...



696. Use of block-scope constants in local classes

Section: 11.6  [class.local]     Status: C++11     Submitter: Steve Adamczyk     Date: 29 May, 2008

[Voted into the WP at the March, 2011 meeting.]

According to 11.6 [class.local] paragraph 1,

Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators from the enclosing scope.

This would presumably make both of the members of S2 below ill-formed:

    void test () {
      const int local_const = 7;
      struct S2 {
        int member:local_const;
        void f() { int j = local_const; }
      };
    }

Should there be an exception to this rule for constant values? Current implementations seem to accept the reference to local_const in the bit-field declaration but not in the member function definition. Should they be the same or different?

Notes from the September, 2008 meeting:

The CWG agreed that both uses of local_const in the example above should be accepted. The intent of the restriction was to avoid the need to pass a frame pointer into local class member functions, so uses of local const variables as values should be permitted.

Notes from the October, 2009 meeting:

There was interest in an approach that would allow explicitly-captured constants to appear in constant expressions but also to be “used.” Another suggestion was to have variables captured if they appear in either “use” or “non-use” contexts.

Proposed resolution (February, 2011):

  1. Change 7.5.5 [expr.prim.lambda] paragraph 17 as follows:

  2. Every id-expression that is an odr-use (6.3 [basic.def.odr]) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type. [Note: an id-expression that is not an odr-use refers to the original entity, never to a member of the closure type. Furthermore, such an id-expression does not cause the implicit capture of the entity. —end note] If this is captured, each odr-use of this is transformed into an access to the corresponding unnamed data member of the closure type, cast (7.6.3 [expr.cast]) to the type of this. [Note: the cast ensures that the transformed expression is a prvalue. —end note] [Example:

      void f(const int*);
      void g() {
        const int N = 10;
        [=] {
          int arr[N];    // OK: not an odr-use, refers to automatic variable
          f(&N);         // OK: causes N to be captured; &N points to the
                         // corresponding member of the closure type
        }
      }
    

    end example]

  3. Change 11.6 [class.local] paragraph 1 as follows:
  4. ...Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators from the shall not odr-use (6.3 [basic.def.odr]) a variable with automatic storage duration from an enclosing scope. [Example:

      int x;
      void f() {
        static int s ;
        int x;
        const int N = 5;
        extern int g q();
    
        struct local {
          int g() { return x; }     // error: odr-use of automatic variable x has automatic storage duration
          int h() { return s; }     // OK
          int k() { return ::x; }   // OK
          int l() { return g q(); } // OK
          int m() { return N; }     // OK: not an odr-use
          int* n() { return &N; }   // error: odr-use of automatic variable N
        };
      }
    
      local* p = 0;                 // error: local not in scope
    

    end example]




580. Access in template-parameters of member and friend definitions

Section: 11.8  [class.access]     Status: C++11     Submitter: John Spicer     Date: 16 May 2006

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The resolution of issue 372 leaves unclear whether the following are well-formed or not:

    class C {
        typedef int I;                // private
        template <int> struct X;
        template <int> friend struct Y;
    }

    template <C::I> struct C::X { };  // C::I accessible to member?

    template <C::I> struct Y { };     // C::I accessible to friend?

Presumably the answer to both questions is “yes,” but the new wording does not address template-parameters.

Proposed resolution (June, 2008) [SUPERSEDED]:

Change 11.8 [class.access] paragraph 6 as follows:

...For purposes of access control, the base-specifiers of a class, the template-parameters of a template-declaration, and the definitions of class members that appear outside of the class definition are considered to be within the scope of that class...

Notes from the September, 2008 meeting:

The proposed resolution preserves the word “scope” as a holdover from the original specification prior to issue 372, which intended to change access determination from a scope-based model to an entity-based model. The resolution should eliminate all references to scope and simply use the entity-based model.

(See also issue 718.)

Proposed resolution (February, 2010) [SUPERSEDED]:

Change 11.8 [class.access] paragraphs 6-7 as follows:

All access controls in 11.8 [class.access] affect the ability to access a class member name from a declaration of a particular scope entity, including references appearing in those parts of the declaration that precede the name of the entity being declared and implicit references to constructors, conversion functions, and destructors involved in the creation and destruction of a static data member. For purposes of access control, the base-specifiers of a class and the definitions of class members that appear outside of the class definition are considered to be within the scope of that class. In particular, access controls apply as usual to member names accessed as part of a function return type, even though it is not possible to determine the access privileges of that use without first parsing the rest of the function declarator. Similarly, access control for implicit calls to the constructors, the conversion functions, or the destructor called to create and destroy a static data member is performed as if these calls appeared in the scope of the member's class. [Example:

  class A {
    typedef int I;    // private member
    I f();
    friend I g(I);
    static I x;
    template<int> struct X;
    template<int> friend struct Y;
  protected:
    struct B { };
  };

  A::I A::f() { return 0; }
  A::I g(A::I p = A::x);
  A::I g(A::I p) { return 0; }
  A::I A::x = 0;
  template<A::I> struct A::X { };
  template<A::I> struct Y { };

  struct D: A::B, A { };

Here, all the uses of A::I are well-formed because A::f and, A::x, and A::X are members of class A and g is a friend and Y are friends of class A. This implies, for example, that access checking on the first use of A::I must be deferred until it is determined that this use of A::I is as the return type of a member of class A. Similarly, the use of A::B as a base-specifier is well-formed because D is derived from A, so checking of base-specifiers must be deferred until the entire base-specifier-list has been seen. —end example]




655. Initialization not specified for forwarding constructors

Section: 11.9.3  [class.base.init]     Status: C++11     Submitter: Alisdair Meredith     Date: 17 October 2007

[Voted into WP at August, 2010 meeting.]

The changes for delegating constructors overlooked the need to change 11.9.3 [class.base.init] paragraph 3:

The expression-list in a mem-initializer is used to initialize the base class or non-static data member subobject denoted by the mem-initializer-id. The semantics of a mem-initializer are as follows:

The initialization of each base and member constitutes a full-expression. Any expression in a mem-initializer is evaluated as part of the full-expression that performs the initialization.

This paragraph deals only with subobjects; it needs to be made more general to apply to the complete object as well when the mem-initializer-id designates the constructor's class.

Proposed resolution (June, 2008):

Change 11.9.3 [class.base.init] paragraph 3 as follows:

The expression-list in a mem-initializer is used to initialize the base class or non-static data member subobject denoted by the mem-initializer-id. The semantics of a mem-initializer are A mem-initializer in which the mem-initializer-id names the constructor's class initializes the object by invoking the selected target constructor with the mem-initializer's expression-list. A mem-initializer in which the mem-initializer-id names a base class or non-static data member initializes the designated subobject as follows:

...

The initialization of each base and member performed by each mem-initializer constitutes a full-expression. Any expression...

Notes from the September, 2008 meeting:

This text was significantly modified by N2756 (nonstatic data member initializers) and needs to be reworked in light of those changes.

Proposed resolution (February, 2010):

  1. Change 11.9.3 [class.base.init] paragraph 7 as follows:

  2. The expression-list or braced-init-list in a mem-initializer is used to initialize the base class or non-static data member subobject denoted by the mem-initializer-id designated subobject (or, in the case of a delegating constructor, the complete class object) according to the initialization rules of 9.4 [dcl.init] for direct-initialization.

    [Example: ...

    end example] The initialization of each base and member performed by each mem-initializer constitutes a full-expression...

  3. Change 11.9.3 [class.base.init] paragraph 8 as follows:

  4. If In a non-delegating constructor, if a given non-static data member or base class is not named by a mem-initializer-id...
  5. Change 11.9.3 [class.base.init] paragraph 10 as follows:

  6. Initialization In a non-delegating constructor, initialization proceeds in the following order:
  7. Change 11.9.3 [class.base.init] paragraph 12 as follows (this is an unrelated change correcting an error noticed while preparing the resolution of this issue):

  8. Names in the expression-list or braced-init-list of a mem-initializer are evaluated in the scope of the constructor...
  9. Change the next-to-last bullet of the note in 9.4.5 [dcl.init.list] paragraph 1 as follows:




838. Use of this in a brace-or-equal-initializer

Section: 11.9.3  [class.base.init]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 12 March, 2009

References to non-static data members inside the body of a non-static member function (which includes the mem-initializers of a constructor definition) are implicitly transformed to member access expressions using (*this) (11.4.3 [class.mfct.non.static] paragraph 3) . Although _N4567_.5.1.1 [expr.prim.general] paragraph 3 permits use of this in a brace-or-equal-initializer for a non-static data member, 11.9.3 [class.base.init] does not give details about the value of this in that context, and there is no parallel to the transformation of member references into class member access expressions. This leaves use of non-static data members in this context underspecified.

Proposed resolution (March, 2011):

This issue is resolved by the resolution of issues 1017 and 1207 in document N3282.




1242. Initializing variant class members

Section: 11.9.3  [class.base.init]     Status: C++11     Submitter: Ryou Ezoe     Date: 2011-02-08

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The current wording of 11.9.3 [class.base.init] paragraph 5 says,

A ctor-initializer may initialize the member of an anonymous union that is a member of the constructor's class.

The wording “the member” is strange; furthermore, this should be restricted to non-static data members. That could be accomplished by using the existing term “variant members,” which is defined in 11.5 [class.union] paragraph 8 to be “the non-static data members of all anonymous unions that are members of” the class (which by definition must be non-static data members, since a storage class specifier is not allowed on an anonymous union in class scope).




1202. Calling virtual functions during destruction

Section: 11.9.5  [class.cdtor]     Status: C++11     Submitter: Bjarne Stroustrup     Date: 2010-09-21

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

According to 11.9.5 [class.cdtor] paragraph 4,

Member functions, including virtual functions (11.7.3 [class.virtual]), can be called during construction or destruction (11.9.3 [class.base.init]). When a virtual function is called directly or indirectly from a constructor (including the mem-initializer or brace-or-equal-initializer for a non-static data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor's own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructor's class, or overriding it in one of the other base classes of the most derived object (6.7.2 [intro.object]).

This is clear regarding virtual functions called during the initialization of a class's members, but it does not specifically address the polymorphic behavior of the class during the destruction of the members. Presumably the behavior during destruction should be the exact inverse of that of the constructor, i.e., the class's virtual functions should still be called during member destruction.

In addition, the wording

If the virtual function call uses an explicit class member access (7.6.1.5 [expr.ref]) and the object-expression refers to the object under construction or destruction but its type is neither the constructor or destructor's own class or one of its bases, the result of the call is undefined.

should be clarified that “refers to the object under construction” does not include referring to member subobjects but only to base or more-derived classes of the class under construction or destruction.




1148. Copy elision and move construction of function parameters

Section: 11.9.6  [class.copy.elision]     Status: C++11     Submitter: DE     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment DE 11

It is unclear whether copy elision is permitted when returning a parameter of class type. If not, it should still be possible to move, rather than copy, the return value.

Suggested resolution: Amend paragraph 34 to explicitly exclude function parameters from copy elision. Amend paragraph 35 to include function parameters as eligible for move-construction.

Proposed resolution (September, 2010):

  1. Change 11.4.5.3 [class.copy.ctor] paragraph 34 bullets 1 and 2 as follows:

  2. Change 11.4.5.3 [class.copy.ctor] paragraph 35 as follows:

    When the criteria for elision of a copy operation are met, or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution...
[Drafting note: the change to paragraph 35 intentionally omits catch-clause parameters out of concern that a rethrow during the move would throw a stripped exception object. This should not be problematic in most cases, since exception objects are typically small.]


1016. Overloadable declarations, function templates, and references

Section: Clause 12  [over]     Status: C++11     Submitter: Johannes Schaub     Date: 2009-12-27

[Voted into the WP at the November, 2010 meeting.]

According to Clause 12 [over] paragraph 1,

Only function declarations can be overloaded; object and type declarations cannot be overloaded.

There are two problems with this statement. First, it does not allow for overloading function templates. (There may be other places in the Standard that refer to “functions” but should include function templates, as well.)

Second, the restriction on “object” declarations should presumably be on “variable” declarations instead, since one can also not overload reference declarations.

Proposed resolution (September, 2010):

Change Clause 12 [over] paragraph 1 as follows:

...Only function and function template declarations can be overloaded; object variable and type declarations cannot be overloaded.



1087. Additional applications of issue 899

Section: 12.2.2.5  [over.match.copy]     Status: C++11     Submitter: Jason Merrill     Date: 2010-06-25

[Voted into the WP at the November, 2010 meeting.]

The resolution of issue 899 needs to be extended to cover move constructors and template constructors as well.

Proposed resolution (August, 2010):

For example

    struct C {
       template <class T = int> C(C&, T = 0);
    };

    struct A {
       explicit operator C&() const;
    };

    int main() {
       A a;
       C c (a); // should use template constructor
    }



1151. Overload resolution with initializer-list and non-list constructors

Section: 12.2.2.8  [over.match.list]     Status: C++11     Submitter: US     Date: 2010-08-03

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

N3092 comment US 66

Overload resolution should first look for a viable list constructor, then look for a non-list constructor if no list constructor is viable.

Proposed resolution (August, 2010) [SUPERSEDED]:

  1. Change 9.4.5 [dcl.init.list] bullet 3.5 as follows:

  2. Change 12.2.2.8 [over.match.list] as follows:

  3. When objects of non-aggregate class type T are list-initialized (9.4.5 [dcl.init.list]), overload resolution selects the constructor in two phases as follows, where T is the cv-unqualified class type of the object being initialized:

    For In copy-list-initialization, the candidate functions are all the constructors of T. However, if an explicit constructor is chosen, the initialization is ill-formed. [Note: This differs from other situations (12.2.2.4 [over.match.ctor], 12.2.2.5 [over.match.copy]), where only converting constructors are considered for copy-initialization. This restriction only applies if this initialization is part of the final result of overload resolution.end note]




1229. Overload resolution with empty braced-init-list argument

Section: 12.2.2.8  [over.match.list]     Status: C++11     Submitter: Johannes Schaub     Date: 2010-12-09

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The changes for issue 990 did not address the description of overload resolution when an argument is an empty braced-init-list. For example:

    struct A {
      A();
      A(std::initializer_list<int>);
      A(std::initializer_list<double>);
    };

    A a{};       // OK

    void f(A);

    void g() {
      f({});     // ambiguous
    }



1152. Rules for determining existence of implicit conversion sequence

Section: 12.2.3  [over.match.viable]     Status: C++11     Submitter: US     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 67

To determine whether there is an implicit conversion sequence that converts the argument to the corresponding parameter, 12.2.3 [over.match.viable] paragraph 3 uses 12.2.4.2 [over.best.ics] instead of just saying “there is an ICS if-and-only-if a copy initialization would be well-formed.” Apparently this is intended, but to a casual reader or an implementor reading these rules for the first time for a new implementation, it's not clear why that's desirable. A note should be added to explain the rationale.

Proposed resolution (August, 2010):

Change 12.2.4.2.5 [over.ics.ref] paragraph 3 as follows:

Except for an implicit object parameter, for which see 12.2.2 [over.match.funcs], a standard conversion sequence cannot be formed if it requires binding an lvalue reference to non-const other than a reference to a non-volatile const type to an rvalue or binding an rvalue reference to an lvalue. [Note:...



1079. Overload resolution involving aggregate initialization

Section: 12.2.4.3  [over.ics.rank]     Status: C++11     Submitter: Jason Merrill     Date: 2010-06-15

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The current wording makes some calls involving aggregate initialization ambiguous that should not be. For example, the calls below to f and g should each prefer the second overload:

    struct A { int i; };

    void f (const A &);
    void f (A &&);

    void g (A, double);
    void g (A, int);

    int main() {
       f ( { 1 } );
       g ( { 1 }, 1 );
    }

Proposed resolution (August, 2010) [SUPERSEDED]:

Change 12.2.4.3 [over.ics.rank] bullet 3.2 as follows:




1238. Overloading ambiguity binding reference to function

Section: 12.2.4.3  [over.ics.rank]     Status: C++11     Submitter: Doug Gregor     Date: 2011-01-25

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

Currently overload resolution does not distinguish between binding an lvalue reference to a function lvalue and an rvalue reference to a function lvalue. The former should be preferred.

In a related point, the current wording of 12.2.4.2.5 [over.ics.ref] paragraph 3 forbids binding an rvalue reference to an lvalue; this should be changed to allow binding an rvalue reference to a function lvalue.




1153. Type matching in address of overloaded function

Section: 12.3  [over.over]     Status: C++11     Submitter: US     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 68

Overload resolution within the operand of a unary & operator is done by selecting the function “whose type matches the target type required in the context.” The criterion for determining whether the types match, however, is not defined. At least three possibilities suggest themselves:

  1. The types are identical.

  2. The source type can be implicitly converted to the target type.

  3. The expression would be well-formed if the function under consideration were not overloaded.

This question arises for pointer-to-member types, where there is an implicit conversion from a pointer-to-base-member to a pointer-to-derived-member, as well as when the context is an explicit type conversion (which allows, for static_cast, a conversion from pointer-to-derived-member to a pointer-to-base-member and, in the reinterpret_cast interpretation of functional and old-style casts, essentially any conversion).

Notes from the August, 2010 meeting:

CWG observed that the only case in which the types might not match exactly was for pointers to member functions. In this case, the approach should be to ignore the class of which the functions are members and just match (exactly) on the function type.

Proposed resolution (September, 2010):

  1. Change 12.3 [over.over] paragraph 1 as follows:

  2. ...The function selected is the one whose type matches is identical to the function type of the target type required in the context. [Note: That is, the class of which the function is a member is ignored when matching a pointer-to-member-function type. —end note] The target can be...
  3. Change 12.3 [over.over] paragraph 3 as follows:
  4. Non-member functions and static member functions match targets of type “pointer-to-function” or “reference- to-function.” Nonstatic member functions match targets of type “pointer-to-member-function;.the function type of the pointer to member is used to select the member function from the set of overloaded member functions. If a non-static member function is selected, the reference to the overloaded function name is required to have the form of a pointer to member as described in 7.6.2.2 [expr.unary.op].



1009. Missing cases in the declarator-id of a function template declaration

Section: Clause 13  [temp]     Status: C++11     Submitter: Johannes Schaub     Date: 2009-11-28

[Voted into the WP at the November, 2010 meeting.]

According to Clause 13 [temp] paragraph 2,

In a function template declaration, the last component of the declarator-id shall be a template-name or operator-function-id (i.e., not a template-id).

This is too restrictive; it should also allow conversion-function-ids and literal-operator-ids.

Proposed resolution (September, 2010):

Change Clause 13 [temp] paragraph 2 as follows:

A template-declaration can appear only as a namespace scope or class scope declaration. In a function template declaration, the last component of the declarator-id shall not be a template-id template-name or operator-function-id (i.e., not a template-id). [Note: in That last component may be an identifier, an operator-function-id, a conversion-function-id, or a literal-operator-id. In a class template declaration, if the class name is a simple-template-id, the declaration declares a class template partial specialization (13.7.6 [temp.spec.partial]). —end note]



1096. Missing requirement for template definitions

Section: Clause 13  [temp]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-07-28

[Voted into the WP at the March, 2011 meeting.]

The removal of the export keyword inadvertently deleted the text (previously found in Clause 13 [temp] paragraph 8 of the 2003 Standard),

A non-exported template must be defined in every translation unit in which it is implicitly instantiated (13.9.2 [temp.inst]), unless the corresponding specialization is explicitly instantiated (13.9.3 [temp.explicit]) in some translation unit; no diagnostic is required.

This requirement must be reinstated.

Proposed resolution (January, 2011):

Add the following as a new paragraph following Clause 13 [temp] paragraph 5:

A function template, member function of a class template, or static data member of a class template shall be defined in every translation unit in which it is implicitly instantiated (13.9.2 [temp.inst]), unless the corresponding specialization is explicitly instantiated (13.9.3 [temp.explicit]) in some translation unit; no diagnostic is required.



691. Template parameter packs in class template partial specializations

Section: 13.2  [temp.param]     Status: C++11     Submitter: Doug Gregor     Date: 9 April, 2008

[Voted into WP at August, 2010 meeting.]

13.2 [temp.param] paragraph 11 currently says,

If a template-parameter of a class template is a template parameter pack, it shall be the last template-parameter. [Note: These are not requirements for function templates because template arguments might be deduced (13.10.3 [temp.deduct])...

This restriction was only meant to apply to primary class templates, not partial specializations.

Suggested resolution:

If a template-parameter of a primary class template is a template parameter pack, it shall be the last template-parameter. [Note: These are not requirements for function templates or class template partial specializations because template arguments might be deduced (13.10.3 [temp.deduct])...

Proposed resolution (February, 2010):

Change 13.2 [temp.param] paragraph 11 as follows:

If a template-parameter of a class template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. If a template-parameter of a primary class template is a template parameter pack, it shall be the last template-parameter. [Note: These are not requirements for function templates or class template partial specializations because template arguments might can be deduced (13.10.3 [temp.deduct]). [Example:...



778. Template parameter packs in non-type template parameters

Section: 13.2  [temp.param]     Status: C++11     Submitter: Michael Wong     Date: 13 February, 2009

[Voted into the WP at the March, 2011 meeting as part of paper N3270.]

Consider an example like:

    template <typename T, T Value> struct bar { };
    template <typename... T, T ...Value> void foo(bar<T, Value>);

The current wording in 13.2 [temp.param] is unclear as to whether this is permitted or not. For comparison, 9.3.4.6 [dcl.fct] paragraph 13 says,

A declarator-id or abstract-declarator containing an ellipsis shall only be used in a parameter-declaration. Such a parameter-declaration is a parameter pack (13.7.4 [temp.variadic]). When it is part of a parameter-declaration-clause, the parameter pack is a function parameter pack (13.7.4 [temp.variadic]). [Note: Otherwise, the parameter-declaration is part of a template-parameter-list and the parameter pack is a template parameter pack; see 13.2 [temp.param]. —end note] A function parameter pack, if present, shall occur at the end of the parameter-declaration-list. The type T of the declarator-id of the function parameter pack shall contain a template parameter pack; each template parameter pack in T is expanded by the function parameter pack.

The requirement here that the type of a function parameter pack must contain a template parameter pack is not repeated for template non-type parameters in 13.2 [temp.param], nor is the statement that it expands the template parameter pack.

A related issue is that neither function nor template parameter packs are listed in 13.7.4 [temp.variadic] paragraph 4 among the contexts in which a pack expansion can appear.

Proposed resolution (November, 2010):

  1. Change 7.6.2.5 [expr.sizeof] paragraph 5 as follows:

  2. The identifier in a sizeof... expression shall name a parameter pack. The sizeof... operator yields the number of arguments provided for the parameter pack identifier. The parameter pack is expanded (13.7.4 [temp.variadic]) by the sizeof... operator A sizeof... expression is a pack expansion (13.7.4 [temp.variadic]). [Example:...
  3. Change 9.3.4.6 [dcl.fct] paragraph 13 as follows:

  4. A declarator-id or abstract-declarator containing an ellipsis shall only be used in a parameter-declaration. Such a parameter-declaration is a parameter pack (13.7.4 [temp.variadic]). When it is part of a parameter-declaration-clause, the parameter pack is a function parameter pack (13.7.4 [temp.variadic]). [Note: Otherwise, the parameter-declaration is part of a template-parameter-list and the parameter pack is a template parameter pack; see 13.2 [temp.param]. —end note] The type T of the declarator-id of the function parameter pack shall contain a template parameter pack; each template parameter pack in T is expanded by the function parameter pack A function parameter pack is a pack expansion (13.7.4 [temp.variadic]). [Example:...
  5. Change 13.2 [temp.param] paragraph 15 as follows:

  6. If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a parameter pack (9.3.4.6 [dcl.fct]), then the template-parameter is a template parameter pack (13.7.4 [temp.variadic]). A template parameter pack that is a parameter-declaration whose type contains one or more unexpanded parameter packs is a pack expansion. Similarly, a template parameter pack that is a type-parameter with a template-parameter-list containing one or more unexpanded parameter packs is a pack expansion. [Example:

      template <class... Types> class Tuple; // Types is a template type parameter pack and a pack expansion
      template <class T, int... Dims> struct multi_array; // Dims is a non-type template parameter pack but not a pack expansion
      template <class T, T... Values> struct static_array; // Values is a non-type template parameter pack and a pack expansion
    
  7. Change 13.7.4 [temp.variadic] paragraphs 4-6 and add a new paragraph 7 as follows:

  8. A pack expansion is a sequence of tokens that names one or more parameter packs, followed by an ellipsis. The sequence of tokens is called the pattern of the expansion; its syntax consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list (described below). The form of the pattern depends on the context in which the expansion occurs. Pack expansions can occur in the following contexts:

    [Example:...

    A parameter pack whose name appears within the pattern of a pack expansion is expanded by that pack expansion. An appearance of the name of a parameter pack is only expanded by the innermost enclosing pack expansion. The pattern of a pack expansion shall name one or more parameter packs that are not expanded by a nested pack expansion; such parameter packs are called unexpanded parameter packs in the pattern. All of the parameter packs expanded...

      ...
      void g(Args ... args) {  // OK: “Args” is expanded by the function parameter pack “args
      ...
    

    The instantiation of an a pack expansion that is not a sizeof... expression produces a list...

    The instantiation of a sizeof... expression (7.6.2.5 [expr.sizeof]) produces an integral constant containing the number of elements in the parameter pack it expands.

This resolution also resolves issues 1182 and 1183.

Additional note (February, 2011):

A problematic case is a function like

  template<typename... T, T... t> void f(T...) { }

where each element of the nontype pack actually has a different type. This causes problems for template argument deduction, since T and t are supposed to be deduced independently, but they're linked through their sizes. There doesn't appear to be any use case for this kind of example, so it should be ill-formed.

The rule should probably be to consider a non-type template parameter pack that expands any template parameter packs from the same template-parameter-list as ill-formed.




1006. std::nullptr_t as a non-type template parameter

Section: 13.2  [temp.param]     Status: C++11     Submitter: Mike Miller     Date: 2009-11-20

[Voted into the WP at the November, 2010 meeting.]

std::nullptr_t is not currently allowed by 13.2 [temp.param] paragraph 4 to be used as the type of a non-type template parameter. However, this could arise for a template with a non-type template parameter with a dependent type in a template intended for use with pointers, e.g.,

    template<typename T, T t> void f();
    ...
    f<std::nullptr_t, nullptr>();

or in a case of delegation.

Proposed resolution (September, 2010):

Change 13.2 [temp.param] paragraph 4 as follows:

A non-type template-parameter shall have one of the following (optionally cv-qualified) types:




1068. Template aliases with default arguments and template parameter packs

Section: 13.2  [temp.param]     Status: C++11     Submitter: Nikolay Ivchenkov     Date: 2010-03-27

[Voted into the WP at the March, 2011 meeting.]

Since there appear to be no restrictions against it, it would appear that default arguments and template parameter packs can be used with template aliases just as with other templates. If that is the case, then, the current wording in 13.2 [temp.param] paragraph 11 requires adjustment:

If a template-parameter of a class template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. If a template-parameter of a class template is a template parameter pack, it shall be the last template-parameter.

Presumably these restrictions should also apply to template aliases, but as written, they only apply to class templates.

Proposed resolution (January, 2011):

Change 13.2 [temp.param] paragraph 11 as follows:

If a template-parameter of a class template or alias template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. If a template-parameter of a primary class template or alias template is a template parameter pack, it shall be the last template-parameter. [Note:...



1246. Non-deduced non-final parameter packs

Section: 13.2  [temp.param]     Status: C++11     Submitter: John Spicer     Date: 2011-02-23

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

According to 13.2 [temp.param] paragraph 11,

If a template-parameter of a class template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. If a template-parameter of a primary class template is a template parameter pack, it shall be the last template-parameter. [Note: These are not requirements for function templates or class template partial specializations because template arguments can be deduced (13.10.3 [temp.deduct])...

Should the Standard forbid non-final parameter packs in cases where the declaration does not allow the template arguments to be deduced? For example,

    template<typename... T, typename... U> void f() { }
    template<typename... T, typename U> void g() { }

(See also issue 549.)




96. Syntactic disambiguation using the template keyword

Section: 13.3  [temp.names]     Status: C++11     Submitter: John Spicer     Date: 16 Feb 1999

[Voted into WP at August, 2010 meeting.]

The following is the wording from 13.3 [temp.names] paragraphs 4 and 5 that discusses the use of the "template" keyword following . or -> and in qualified names.

The whole point of this feature is to say that the "template" keyword is needed to indicate that a "<" begins a template parameter list in certain contexts. The constraints in paragraph 5 leave open to debate certain cases.

First, I think it should be made more clear that the template name must be followed by a template argument list when the "template" keyword is used in these contexts. If we don't make this clear, we would have to add several semantic clarifications instead. For example, if you say "p->template f()", and "f" is an overload set containing both templates and nontemplates: a) is this valid? b) are the nontemplates in the overload set ignored? If the user is forced to write "p->template f<>()" it is clear that this is valid, and it is equally clear that nontemplates in the overload set are ignored. As this feature was added purely to provide syntactic guidance, I think it is important that it otherwise have no semantic implications.

I propose that paragraph 5 be modified to:

(See also issue 30 and document J16/00-0008 = WG21 N1231.)

Notes from 04/00 meeting:

The discussion of this issue revived interest in issues 11 and 109.

Notes from the October 2003 meeting:

We reviewed John Spicer's paper N1528 and agreed with his recommendations therein.

Proposed resolution (February, 2010):

Change 13.3 [temp.names] paragraph 5 as follows:

If a A name prefixed by the keyword template is not the name of a template, shall be a template-id or the name shall refer to a class template the program is ill-formed. [Note: the keyword template may not be applied to non-template members of class templates. —end note] [Note: as is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the nested-name-specifier or the expression on the left of the -> or . is not dependent on a template-parameter, or the use does not appear in the scope of a template. —end note] [Example:

  template <class T> struct A {
    void f(int);
    template <class U> void f(U); };

  template <class T> void f(T t) {
    A<T> a;
    a.template f<>(t); // OK: calls template
    a.template f(t);   // error: not a template-id
  }

  template <class T> struct B {template <class T2> struct C {}; };
  // OK: T::template C names a class template:
  template <class T, template <class X> class TT = T::template C> struct D {};
  D<B<int>> db;

end example]




431. Defect in wording in 14.2

Section: 13.3  [temp.names]     Status: C++11     Submitter: Mat Marcus     Date: 10 August 2003

[Voted into WP at August, 2010 meeting.]

Consider this example:

   class Foo {
   public:
       template< typename T > T *get();
   };

   template< typename U >
   U *testFoo( Foo &foo ) {
       return foo.get< U >(); //#1
   }

I am under the impression that this should compile without requiring the insertion of the template keyword before get in the expression at //#1. This notion is supported by this note excerpted from 13.3 [temp.names]/5:

[Note: just as is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the expression on the left of the -> or ., or the nested-name-specifier is not dependent on a template parameter.]

But 13.3 [temp.names]/4 contains this text:

When the name of a member template specialization appears after . or -> in a postfix-expression, or after nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.

The only way that I can read this to support my assumption above is if I assume that the phrase postfix-expression is used twice above with different meaning. That is I read the first use as referring to the full expression while the second use refers to the subexpression preceding the operator. Is this the correct determination of intent? I find this text confusing. Would it be an improvement if the second occurrence of "postfix-expression" should be replaced by "the subexpression preceding the operator". Of course that begs the question "where is subexpression actually defined in the standard?"

John Spicer: I agree that the code should work, and that we should tweak the wording.

Proposed resolution (March, 2010):

Change 13.3 [temp.names] paragraph 4 as follows:

When the name of a member template specialization appears after . or -> in a postfix-expression, or after a nested-name-specifier in a qualified-id, and the object or pointer expression of the postfix-expression or the nested-name-specifier in the qualified-id explicitly depends on a template-parameter template parameter (13.8.3 [temp.dep]) but does not refer to a member of the current instantiation (13.8.3.2 [temp.dep.type]), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template. [Example:...



773. Parentheses in address non-type template arguments

Section: 13.4.3  [temp.arg.nontype]     Status: C++11     Submitter: Doug Gregor     Date: 11 February, 2009

[Voted into WP at August, 2010 meeting.]

According to 13.4.3 [temp.arg.nontype] paragraph 1, bullet 3, one of the acceptable forms of a non-type, non-template template argument is:

the address of an object or function... expressed as & id-expression where the & is optional if the name refers to a function or array, or if the corresponding template-parameter is a reference

It is not clear from this whether a template argument like (&i) satisfies the requirement or not.

Notes from the March, 2009 meeting:

The consensus of the CWG was that the parentheses should be allowed.

Proposed resolution (February, 2010):

Change 13.4.3 [temp.arg.nontype] bullet 1.3 as follows:

[Drafting note: The change requiring the omission of the & in the reference case fixes an existing problem that is not related to this issue.]




1025. Use of a reference as a non-type template argument

Section: 13.4.3  [temp.arg.nontype]     Status: C++11     Submitter: Mike Miller     Date: 2010-01-31

[Voted into the WP at the November, 2010 meeting.]

The current wording of 13.4.3 [temp.arg.nontype] paragraph 1 does not prevent the use a reference as a non-type template argument. It simply requires

the address of an object or function with external linkage... expressed as & id-expression...

This would presumably (but unintentionally?) allow an example like the following:

    struct S { };
    template<S*> struct X { };
    S s;
    S& ref = s;
    X<&ref> xr;   // well-formed?

The expression &ref is not a constant expression, but the current wording of 13.4.3 [temp.arg.nontype] does not require a constant expression.

Proposed resolution (September, 2010):

Change 13.4.3 [temp.arg.nontype] bullet 1.3 as follows:




1154. Address of thread_local variable as non-type template argument

Section: 13.4.3  [temp.arg.nontype]     Status: C++11     Submitter: US     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 69

The standard permits the address of a thread_local object as a non-type template argument. The addresses of these objects are not constant, however. Such template arguments should require objects of static storage duration.

Proposed resolution (August, 2010):

This issue is resolved by the resolution of issue 1155.




1155. Internal-linkage non-type template arguments

Section: 13.4.3  [temp.arg.nontype]     Status: C++11     Submitter: US     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment DE 12

Now that local classes can be used as template arguments, it seems odd that there are “external linkage” restrictions on non-type template parameters. The addresses of objects and functions with internal linkage should be permitted as well.

Proposed resolution (August, 2010):

Change 13.4.3 [temp.arg.nontype] bullet 1.3 as follows:

This resolution also resolves issue 1154.




1244. Equivalence of alias templates and class templates

Section: 13.6  [temp.type]     Status: C++11     Submitter: Johannes Schaub     Date: 2011-02-22

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The example in 13.6 [temp.type] paragraph 1 reads in significant part,

    template<template<class> class TT> struct X { };
    template<class> struct Y { };
    template<class T> using Z = Y<T>;
    X<Y> y;
    X<Z> z;

and says that y and z have the same type.

This would only be true if alias template Z were considered to be equivalent to class template Y. However, 13.7.8 [temp.alias] describes equivalence only for specializations of alias templates, not for the alias templates themselves. Either such rules should be specified, which could be tricky, or the example should be deleted.




1206. Defining opaque enumeration members of class templates

Section: 13.7.2  [temp.class]     Status: C++11     Submitter: Jason Merrill     Date: 2010-10-06

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

Presumably an out-of-class definition for an opaque enumeration member of a class template is intended to be allowed; however, the current wording of 13.7.2 [temp.class] provides only for out-of-class definitions of member functions, member classes, static data members, and member templates, not for opaque enumerations.

Proposed resolution (November, 2010) [SUPERSEDED]:

  1. Change Clause 13 [temp] paragraph 1 as follows:

  2. ...The declaration in a template-declaration shall

  3. Change 13.7.2 [temp.class] paragraph 3 as follows:

  4. When a member function, a member class, a member enumeration, a static data member or a member template of a class template is defined outside of the class template definition...
  5. Add a new section following 13.7.2.5 [temp.static]:

  6. 14.5.1.4 Enumeration members of class templates [temp.mem.enum]

    An enumeration member of a class template may be defined outside the class template definition. [Example:

      template<class T> struct A {
        enum E: T;
      };
      A<int> a;
      template<class T> enum A<T>::E: T { e1, e2 };
      A<int>::E e = A<int>::e1;
    

    end example]

  7. Change 13.9 [temp.spec] paragraph 2 as follows:

  8. A function instantiated from a function template is called an instantiated function. A class instantiated from a class template is called an instantiated class. A member function, a member class, a member enumeration, or a static data member of a class template instantiated from the member definition of the class template is called, respectively, an instantiated member function, member class, member enumeration, or static data member. A member function...
  9. Change 13.9.2 [temp.inst] paragraph 1 as follows:

  10. ...The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, scoped member enumerations, static data members and member templates; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions. Unless a member...
  11. Change 13.9.4 [temp.expl.spec] paragraph 1 as follows:

  12. An explicit specialization of any of the following:

  13. Change 13.9.4 [temp.expl.spec] paragraph 4 as follows:

  14. A member function, a member class, a member enumeration, or a static data member of a class template may be explicitly specialized for a class specialization that is implicitly instantiated...
  15. Add the indicated text to the example in 13.9 [temp.spec] paragraph 6:

  16.   template<> void sort<>(Array(<char*>& v);        // OK: sort<char*> not yet used
      template<class T> struct A {
        enum E: T;
        enum class S: T;
      };
      template<> enum A<int>::E: int { eint };         // OK
      template<> enum class A<int>::S: int { sint };   // OK
      template<class T> enum A<T>::E: T { eT };
      template<class T> enum class A<T>::S: T { sT };
      template<> enum A<char>::E: int { echar };       // ill-formed, A<char>::E was instantiated when A<char> was instantiated
      template<> enum class A<char>::S: int { schar }; // OK
    
  17. Change 13.9.4 [temp.expl.spec] paragraph 7 as follows:

  18. The placement of explicit specialization declarations for function templates, class templates, member functions of class templates, static data members of class templates, member classes of class templates, member enumerations of class templates, member class templates of class templates, member function templates...



1245. Matching declarations involving decltype

Section: 13.7.2.2  [temp.mem.func]     Status: C++11     Submitter: Jason Merrill     Date: 2011-02-22

Type matching rules aren't well-specified in the current Standard, but it seems reasonable to say that if a declaration uses decltype, its definition must do so as well. For example, the following should be ill-formed:

    template<class T, T* u> struct S {
      decltype(u) foo(T);
    };

    template<class T, T *u> T* S<T, u>::foo(T) {
       return nullptr;
    }

Proposed resolution (March, 2011):

This issue is resolved by the resolution of issue 1057 in document N3262.




1032. Empty pack expansions

Section: 13.7.4  [temp.variadic]     Status: C++11     Submitter: James Widman     Date: 2010-02-16

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

It was intended for empty pack expansions to be useful in contexts like base-specifiers, e.g.,

    template<class... T> struct A : T... {};
    A<> x; // ok?

However, the current wording provides no description of how that might work. (More generally, the problem arises in any context where the pack expansion follows a token that should only be present when the pack expansion is non-empty: following another argument in a function call, etc.)




1182. Incorrect description of pack expansion syntax

Section: 13.7.4  [temp.variadic]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 2010-08-26

[Voted into the WP at the March, 2011 meeting as part of paper N3270.]

According to 13.7.4 [temp.variadic] paragraph 4,

A pack expansion is a sequence of tokens that names one or more parameter packs, followed by an ellipsis.

This is contradicted by 7.6.2.5 [expr.sizeof] paragraph 5, which describes sizeof...(Types) as an expansion, as well as the case where the expansion appears in a declarator like the example given in 9.3.4.6 [dcl.fct] paragraph 13:

    template<typename... T> void f(T (* ...t)(int, int));

This is also described as a pack expansion, although it does not fit the syntactic summary.

Proposed resolution (November, 2010):

This issue is resolved by the resolution of issue 778.




1231. Variadic templates requiring an empty pack expansion

Section: 13.7.4  [temp.variadic]     Status: C++11     Submitter: John Spicer     Date: 2010-12-20

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

Should the Standard allow declarations of variadic templates or member functions of class templates where only an empty expansion would be well-formed? For example,

    template<typename ... T> struct A {
      void operator++(int, T... t);
    };
    template<typename ... T> union X: T... { };
    template<typename ... T> struct A: T..., T... { };



674. “matching specialization” for a friend declaration

Section: 13.7.5  [temp.friend]     Status: C++11     Submitter: James Widman     Date: 7 February, 2008

[Voted into WP at August, 2010 meeting.]

13.7.5 [temp.friend] bullet 1.3 says:

I'm not sure this says what it's supposed to say. For example:

    namespace N {
        template<class T> int f(T);
    }

    class A {
        friend int N::f(int);
        int m;
        A();
    };

    namespace N {
        template< class T > int f(T) {
            A a;            // ok for T=int?
            return a.m;     // ok for T=int?
        }
    }

    int m = N::f(42);       // ok?
    char c = N::f('a');     // Clearly ill-formed.

The key is that the wording talks about a “matching specialization,” which to me means that N::f<int> is befriended only if that specialization existed in N before the friend declaration. So it's ill-formed as written, but if we move the call to N::f<int> up to a point before the definition of A, it's well-formed.

That seems surprising, especially given that the first bullet does not require a pre-existing specialization. So I suggest replacing bullet 3 with something like:

Proposed resolution (June, 2010):

Change 13.7.5 [temp.friend] bullet 1.3 as follows:

...For a friend function declaration that is not a template declaration:

(This resolution depends on that of issue 873; in particular, the cross-reference to 14.8.2.6 [temp.deduct.decl] refers to a new section added by the resolution of that issue.)




996. Ambiguous partial specializations of member class templates

Section: 13.7.6  [temp.spec.partial]     Status: C++11     Submitter: Doug Gregor     Date: 28 Oct, 2009

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

Given an example like

  template<typename T, typename U>
  struct Outer {
    template<typename X, typename Y> struct Inner;
    template<typename Y> struct Inner<T, Y> {};
    template<typename Y> struct Inner<U, Y> {};
  };
  Outer<int, int> outer;                      // #1
  Outer<int, int>::Inner<int, float> inner;   // #2

Is #1 ill-formed because of the identical partial specializations? If not, presumably #2 is ill-formed because of the resulting ambiguity (13.7.6.2 [temp.spec.partial.match] paragraph 1).

Notes from the November, 2010 meeting:

The instantiation of Outer<int,int> results in duplicate declarations of the partial specialization, which are ill-formed by 11.4 [class.mem] paragraph 1. No normative change is required, but it might be helpful to add an example like this somewhere.




532. Member/nonmember operator template partial ordering

Section: 13.7.7.3  [temp.func.order]     Status: C++11     Submitter: Nathan Sidwell     Date: 16 September 2005

[Voted into WP at August, 2010 meeting.]

The Standard does not specify how member and nonmember function templates are to be ordered. This question arises with an example like the following:

    struct A {
        template<class T> void operator<<(T&);
    };

    template<class T> struct B { };
    template<class T> void operator<<(A&, B<T>&);

    int main() {
        A a;
        B<A> b;
        a << b;
    }

The two candidates for “a << b” are:

  1. A::operator<< <B<A> >(B<A>&)
  2. ::operator<< <A>(A&, B<A>&)

How should we treat the implicit this parameter of #1 and the explicit first parameter of #2?

The difference between option 1 and option 2 can be seen in the following example:

    struct A { };

    template<class T> struct B {
        template<typename R> int operator*(R&);   // #1
    };

    template <typename T> int operator*(T&, A&);  // #2

    int main() {
        A a;
        B<A> b;
        b * a;
    }

Should this select #1, select #2, or be ambiguous? Option 1 will select #2, because “A&” is more specialized than “T&”. Option 2 will make this example ambiguous, because “B<A>&” is more specialized than “T&”.

If one were considering two non-member templates,

    template <typename T> int operator*(T&, A&);                 // #2
    template <typename T, typename R> int operator*(B<A>&, R&);  // #3

the current rules would make these unordered. Option 2 thus seems more consistent with this existing behavior.

Notes from the April, 2006 meeting:

The group favored option 2.

Proposed resolution (February, 2010):

Change 13.7.7.3 [temp.func.order] paragraph 3 as follows:

...and substitute it for each occurrence of that parameter in the function type of the template. If only one of the function templates is a non-static member, that function template is considered to have a new first parameter inserted in its function parameter list. The new parameter is of type “reference to cv A,” where cv are the cv-qualifiers of the function template (if any) and A is the class of which the function template is a member. [Note: This allows a non-static member to be ordered with respect to a nonmember function and for the results to be equivalent to the ordering of two equivalent nonmembers. —end note] [Example:

  struct A { };
  template<class T> struct B {
    template<typename R> int operator*(R&); // #1
  };

  template<typename T, typename R> int operator*(T&, R&); // #2

  // The declaration of B::operator* is transformed into the equivalent of
  // template<typename R> int operator*(B<A>&, R&);  // #1a

  int main() {
    A a;
    B<A> b;
    b * a;   // calls #1a
  }

end example]




1156. Partial ordering in a non-call context

Section: 13.7.7.3  [temp.func.order]     Status: C++11     Submitter: US     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 70

13.10.3.5 [temp.deduct.partial] paragraph 3 specifies that the deduction used in partial ordering in a non-call context is based on the complete function type of the function templates. The wording in 13.7.7.3 [temp.func.order] paragraph 2 (and echoed in paragraph 4) reflects an earlier specification, however, saying that the deduction uses only “the function parameter types, or in the case of a conversion function the return type.” This is a contradiction. The wording in 13.7.7.3 [temp.func.order] should be changed.

Proposed resolution (September, 2010):

  1. Change 13.7.7.3 [temp.func.order] paragraph 2 as follows:

  2. Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function parameter types, or in the case of a conversion function the return type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.
  3. Change 13.7.7.3 [temp.func.order] paragraph 4 as follows:

  4. Using the transformed function template's function parameter list, or in the case of a conversion function its transformed return type, perform type deduction against the function parameter list (or return type) type of the other function template. The mechanism for performing these deductions is given in 13.10.3.5 [temp.deduct.partial].



1235. “Unused” ellipsis and default arguments in partial ordering

Section: 13.7.7.3  [temp.func.order]     Status: C++11     Submitter: James Widman     Date: 2011-01-18

The specification for how to handle default arguments and ellipsis in partial ordering of function templates is confusing. 13.7.7.3 [temp.func.order] paragraph 5 currently reads,

The presence of unused ellipsis and default arguments has no effect on the partial ordering of function templates.

It is not clear what “unused” means in this context. According to the original issue resolution that resulted in this wording (N1053, item 6.55), the intent was that “When partial ordering of function templates containing a different number of parameters is done, only the common parameters are considered.” Presumably this would include parameters with default arguments if each function had such parameters in corresponding positions.

The wording needs to be revised to make this intent clear.

Proposed resolution (March, 2011):

This issue is resolved by the resolution of issue 692 in document N3281.




1056. Template aliases, member definitions, and the current instantiation

Section: 13.7.8  [temp.alias]     Status: C++11     Submitter: John Spicer     Date: 2010-03-17

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

According to 13.8.3.2 [temp.dep.type] paragraph 1, in a primary class template a name refers to the current instantiation if it is the injected-class-name or the name of the class template followed by the template argument list of the template. Although a template-id referring to a specialization of a template alias is described as “equivalent to” the associated type, a specialization of a template alias is neither of the things that qualifies as naming the current instantiation, so presumably the typename keyword in the following example is required:

    template <class T> struct A;
    template <class T> using B = A<T>;

    template <class T> struct A {
        struct C {};
        typename B<T>::C bc;  // typename needed
    };

(However, the list in 13.8.3.2 [temp.dep.type] may not be exactly what we want; it doesn't allow use of a typedef denoting the type of the current instantiation, either, but that presumably should be accepted.)

For analogous reasons, it should not be permitted to use a template alias as a nested-name-specifier when defining the members of a class template:

    template <class T> struct A {
        void g();
    };
    template <class T> using B = A<T>;
    template <class T> void B<T>::g() {} // error

Notes from the November, 2010 meeting:

The CWG disagreed with the suggested direction, feeling that aliases should work like typedefs and that the examples should be accepted.

Proposed resolution (November, 2010) [SUPERSEDED]:

  1. Change 13.8.3.2 [temp.dep.type] paragraph 1 as follows:

  2. In the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, a name refers to the current instantiation if it is

    Change 13.8.3.2 [temp.dep.type] paragraph 3 as follows:

    A template argument that is equivalent to a template parameter (i.e., has the same constant value or the same type as the template parameter) can be used in place of that template parameter in a reference to the current instantiation, except that a decltype-specifier that denotes a dependent type is always considered non-equivalent. In the case of a non-type template argument, the argument must have been given the value of the template parameter and not an expression in which the template parameter appears as a subexpression. [Example:...

This resolution also resolves issue 1057.




1158. Recursive instantiation via alias template

Section: 13.7.8  [temp.alias]     Status: C++11     Submitter: US     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 73

The current wording of 9.2.4 [dcl.typedef] paragraph 2 requires that the identifier in an alias-declaration “...shall not appear in the type-id.” With template aliases, however, the name of the alias can be used indirectly:

    template<typename T> struct A;
    template<typename T> using B=typename A<T>::U;
    template<typename T> struct A {
      typedef B<T> U;
    };
    B<short> b;

Here the instantiation of B<short> causes the instantiation of A<short>, and the typedef in A<short> ends up attempting to use B<short>, which is still in the process of being instantiated.

There should be an explicit prohibition of such uses.

Proposed resolution (August, 2010):

Add the following as a new paragraph at the end of 13.7.8 [temp.alias]:

The type-id in an alias template declaration shall not refer to the alias template being declared. The type produced by an alias template specialization shall not directly or indirectly make use of that specialization. [Example:

  template <class T> struct A;
  template <class T> using B = typename A<T>::U;
  template <class T> struct A {
    typedef B<T> U;
  };
  B<short> b; // Error: instantiation of B<short> uses own type via A<short>::U.

end example]

[Note: this wording assumes the change from “template alias” to “alias template” requested by comment FI 11 on FCD N3092.]


1159. Class and enumeration definitions in template aliases

Section: 13.7.8  [temp.alias]     Status: C++11     Submitter: US     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 74

An alias-declaration allows a class or enumeration type to be defined in its type-id (9.2.9 [dcl.type] paragraph 3). 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 (August, 2010):

Change 9.2.9 [dcl.type] paragraph 3 as follows:

...A type-specifier-seq shall not define a class or enumeration unless it appears in the type-id of an alias-declaration (9.2.4 [dcl.typedef]) that is not the declaration of a template-declaration.



1161. Dependent nested-name-specifier in a pointer-to-member declarator

Section: 13.8  [temp.res]     Status: C++11     Submitter: GB     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment GB 44

It is not clear from the current wording of 13.8 [temp.res] whether the following is well-formed or not:

    template<typename T> struct id { typedef T type; };
    template<typename T> void f() { int id<T>::type::*p = 0; }
    struct A { };
    int main() { f<A>(); }

If typename is required before the use of id<T>::type, it is not permitted by the current syntax.

Proposed resolution (August, 2010):

Change 13.8 [temp.res] paragraph 5 as follows:

A qualified name used as the name in a mem-initializer-id, a base-specifier, 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. —end note]



448. Set of template functions in call with dependent explicit argument

Section: 13.8.2  [temp.local]     Status: C++11     Submitter: Mark Mitchell     Date: 4 Jan 2004

[Voted into WP at August, 2010 meeting.]

Is this program valid?

  template <typename T> int g(int);
  class h{};
  template <typename T> int l(){h j; return g<T>(j);}
  template <typename T> int g(const h&);
  class j{};
  int jj(){return l<j>();}

The key issue is when "g" is looked up, i.e., whether both overloaded template "g" functions are available at the call site or only the first. Clearly, the entire postfix-expression "g<T>(j)" is dependent, but when is the set of available template functions determined?

For consistency with the rules about when the set of available overloads is determined when calling a function given by an unqualified-id, I would think that we should postpone determining the set of template functions if (and only if) any of the explicit template arguments are dependent.

John Spicer: I agree that there should be a core issue for this. The definition of "dependent name" (13.8.3 [temp.dep] paragraph 1) should probably be modified to cover this case. It currently only handles cases where the function name is a simple identifier.

Notes from the March 2004 meeting:

A related issue is a call with a qualified name and dependent arguments, e.g., x::y(depa, depb).

Proposed resolution (June, 2010):

  1. Change 13.8.3 [temp.dep] paragraph 1 as follows:

  2. ...In an expression of the form:

    where the postfix-expression is an unqualified-id id-expression, the unqualified-id id-expression denotes a dependent name if and only if any of the expressions in the expression-list is a type-dependent expression (13.8.3.3 [temp.dep.expr]) or if the unqualified-id of the id-expression is a template-id in which any of the template arguments depends on a template parameter. If an operand of an operator is a type-dependent expression, the operator also denotes a dependent name. Such names are unbound and are looked up at the point of the template instantiation (13.8.4.1 [temp.point]) in both the context of the template definition and the context of the point of instantiation.

  3. Change 13.8.4.2 [temp.dep.candidate] paragraph 1 as follows:

  4. For a function call that depends on a template parameter, if the function name is an unqualified-id or if the function is called using operator notation, the candidate functions are found using the usual lookup rules (6.5.3 [basic.lookup.unqual], 6.5.4 [basic.lookup.argdep], 6.5.5 [basic.lookup.qual]) except that:

    If the function name is an unqualified-id and the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior.




458. Hiding of member template parameters by other members

Section: 13.8.2  [temp.local]     Status: C++11     Submitter: Gabriel Dos Reis     Date: 2 Feb 2004

[Voted into WP at August, 2010 meeting.]

The list of cases in 13.8.2 [temp.local] about when a template parameter is hidden seems to be incomplete.

Consider

      // example-1
    struct S {
       int C;
       template<class> void f();
    };

    template<class C>
      void S::f()
      {
         C c;           // #1
      }

Someone asked whether line #1 is well-formed and I responded "no" based on my understanding of the rules in 14.6.1. After a second looking, I've realized that the above case is currently missing from the list.

The list in 14.6.1 covers cases like

     // example-2
   template<class T>
     struct S {
        int C;
        void f();
     };

   template<class C>
     void S<C>::f()
     {
       C c;     // ERROR: 'C' is 'S::C' not the template parameter
     }
or
     // example-3
   struct A { int C; }

   template<class C>
      struct S : A {
        C c;    // ERROR: 'C' is 'A::C', not the template parameter
      };
But the case of a 'member template' is missing. I believe it should follow the same rule as above. The reason is this.

In the case listed in 14.6.1 (having to do with members of classes), the "algorithm" seems to be this:

  1. put the "template parameter scope"[1] on the top of active scope stack. That will make the template parameter declarations the innermost bindings.
  2. Enter the class scope. That will push more scopes on the stack. In particular, any bindings from non-dependent base classes or from the class definition will hide any previous bindings, especially the template parameter declarations.
The above formulation uniformly covers paragraphs 5 and 7 of section 14.6.1 and gives a general view of how name lookup is supposed to happen.

I believe that any rule, coherent with 14.6.1/5 and 14.6.1/7, for covering the cases of member templates (example-1) will be described by the above "algorithm".

Am I missing something?

[1] of course, the standard text does not formally speak of "template parameter scope", but we all know that the template parameters "live" somewhere. I'm using that terminology to designate the declarative region of the template parameters.

Mike Miller: I have a somewhat different perspective on this question. I think your example-1 is fundamentally different from your example-2 and example-3. Looking, for instance, at your example-2, I see four nested scopes:

     namespace scope
       template scope (where the parameter is)
         class S scope
           S::f() block scope

Naturally, S::C hides the template parameter C. The same is true of your example-3, with three scopes:

     namespace scope
       template scope
         class S scope (includes 10.2 base class lookup)

Again, it's clear that the C inherited from A hides the template parameter in the containing scope.

The scopes I see in your example-1, however, are different:

     namespace scope
       struct S scope
         template scope (where the parameter is)
           S::f() block scope

Here it seems clear to me that the template parameter hides the class member.

It might help to look at the case where the function template is defined inline in the class:

     struct S {
        int C;
        template<class C> int f() {
            C c;   // #1
        }
     };

It would be pretty strange, I think, if the #1 C were the member and not the template parameter. It would also be odd if the name lookup were different between an inline definition and an out-of-line definition.

See also issue 459.

Notes from the March 2004 meeting:

Basically, the standard is okay. We think Gaby's desired cases like #1 should be ill-formed.

There is a wording problem in 13.8.2 [temp.local] paragraph 7. It says:

In the definition of a member of a class template that appears outside of the class template definition, the name of a member of this template hides the name of a template-parameter.

It should say "hides the name of a template-parameter of the class template (but not a template-parameter of the member, if the member is itself a template)" or words to that effect.

Proposed resolution (February, 2010):

Change 13.8.2 [temp.local] paragraph 8 as follows:

In the definition of a member of a class template that appears outside of the class template definition, the name of a member of this the class template hides the name of a template-parameter of any enclosing class templates (but not a template-parameter of the member, if the member is a class or function template). [Example:

  template<class T> struct A {
    struct B { /* ... */ };
    typedef void C;
    void f();
    template<class U> void g(U);
  };

  template<class B> void A<B>::f() {
    B b;   // A's B, not the template parameter
  }

  template<class B> template<class C> void A<B>::g(C) {
    B b;   // A's B, not the template parameter
    C c;   // the template parameter C, not A's C
  }



602. When is the injected-class-name of a class template a template?

Section: 13.8.2  [temp.local]     Status: C++11     Submitter: Daveed Vandevoorde     Date: 23 October 2006

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

Consider the following example:

    template<class T>
    struct A {
         template<class U>
             friend struct A; // Which A?
    };

Presumably the lookup for A in the friend declaration finds the injected-class-name of the template. However, according to 13.8.2 [temp.local] paragraph 1,

The injected-class-name can be used with or without a template-argument-list. When it is used without a template-argument-list, it is equivalent to the injected-class-name followed by the template-parameters of the class template enclosed in <>. When it is used with a template-argument-list, it refers to the specified class template specialization, which could be the current specialization or another specialization.

If that rule applies, then this example is ill-formed (because you can't have a template-argument-list in a class template declaration that is not a partial specialization).

Mike Miller: The injected-class-name has a dual nature, as described in 13.8.2 [temp.local], acting as either a template name or a class name, depending on the context; a template argument list forces the name to be interpreted as a template. It seems reasonable that in this example the injected-class-name has to be understood as referring to the class template; a template header is at least as strong a contextual indicator as a template argument list. However, the current wording doesn't say that.

(See also issue 1004.)

Proposed resolution (November, 2010) [SUPERSEDED]:

This issue is resolved by the resolution of issue 1004.




1004. Injected-class-names as arguments for template template parameters

Section: 13.8.2  [temp.local]     Status: C++11     Submitter: Jason Merrill     Date: 2009-11-19

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The injected-class-name of a class template can be used either by itself, in which case it is a type denoting the current instantiation, or followed by a template argument list, in which case it is a template-name. It would be helpful to extend this treatment so that the injected-class-name could be used as an argument for a template template parameter:

    template <class T> struct A { };

    template <template <class> class TTP> struct B { };

    struct C: A<int> {
       B<A> b;
    };

(This is accepted by g++.)

James Widman:

It would not be so helpful when used with overloaded function templates, for example:

    template <template <class> class TTP>  void f(); // #1
    template <                 class T  >  void f(); // #2

    template <class T> struct A { };

    struct C: A<int> {
        void h(  ) {
            f<A>(); //  #1?  #2?  Substitution failure?
        }
    };

(See also issue 602.)

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 13.8.2 [temp.local] paragraphs 1-5 as follows:

Like normal (non-template) classes, class templates have an injected-class-name (Clause 11 [class]). The injected-class-name can be used with or without a template-argument-list as a template-name or a type-name. When it is used without a template-argument-list, it is equivalent to the injected-class-name followed by the template-parameters of the class template enclosed in <>. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier of a friend class template declaration it refers to the specified class template specialization, which could be the current specialization or another specialization. class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.

Within the scope of a class template specialization or partial specialization, when the injected-class-name is not followed by a < used as a type-name, it is equivalent to the injected-class-name template-name followed by the template-arguments of the class template specialization or partial specialization enclosed in <>. [Example:

  template<template<class> class T> class A { };
  template<class T> class Y;
  template<> class Y<int> {
    Y* p;           // meaning Y<int>
    Y<char>* q;     // meaning Y<char>
    A<Y>* a;        // meaning A<::Y>
    class B {
      template<class> friend class Y;  // meaning ::Y
    };
  };

end example]

The injected-class-name of a class template or class template specialization can be used either with or without a template-argument-list as a template-name or a type-name wherever it is in scope. [Example:

  template <class T> struct Base {
    Base* p;
  };

  template <class T> struct Derived: public Base<T> {
    typename Derived::Base* p;    // meaning Derived::Base<T>
  };

  template<class T, template<class> class U = T::template Base> struct Third { };
  Third<Base<int>> t; // OK, default argument uses injected-class-name as a template

end example]

A lookup that finds an injected-class-name (6.5.2 [class.member.lookup]) can result in an ambiguity in certain cases (for example, if it is found in more than one base class). If all of the injected-class-names that are found refer to specializations of the same class template, and if the name is followed by a template-argument-list used as a template-name, the reference refers to the class template itself and not a specialization thereof, and is not ambiguous. [Example:

  template <class T> struct Base { };
  template <class T> struct Derived: Base<int>, Base<char> {
    typename Derived::Base b;             // error: ambiguous
    typename Derived::Base<double> d;     // OK
  };

end example]

When the normal name of the template (i.e., the name from the enclosing scope, not the injected-class-name) is used without a template-argument-list, it always refers to the class template itself and not a specialization of the template. [Example:...

This resolution also resolves issue 602.




1233. Pack expansions and dependent calls

Section: 13.8.3  [temp.dep]     Status: C++11     Submitter: John Spicer     Date: 2011-01-11

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

In an example like

  void f(int, int, int);
  template<int ...N> void g() {
    f((N+N)...);
  }
  void h() {
    g<1, 2, 3>();
  }

the call to f needs to be dependent; however, the arguments are not type-dependent, so the criteria of 13.8.3 [temp.dep] paragraph 1 are not met. Presumably the specification needs to be updated so that an argument list containing a type-level pack expansion is dependent.




502. Dependency of nested enumerations and enumerators

Section: 13.8.3.2  [temp.dep.type]     Status: C++11     Submitter: Mark Mitchell     Date: 05 Feb 2005

[Voted into WP at August, 2010 meeting.]

The Standard is currently silent on the dependency status of enumerations and enumerators that are members of class templates. There are three questions that must be answered in this regard:

  1. Are enumeration members of class templates dependent types?

    It seems clear that nested enumerations must be dependent. For example:

        void f(int);
    
        template<typename T> struct S {
            enum E { e0 };
            void g() {
                f(e0);
            }
        };
    
        void f(S<int>::E);
    
        void x() {
            S<int> si;
            si->g();       // Should call f(S<int>::E)
        }
    
  2. Is sizeof applied to a nested enumeration a value-dependent expression (13.8.3.4 [temp.dep.constexpr])?

    There are three distinct cases that might have different answers to this question:

    13.8.3.4 [temp.dep.constexpr] paragraph 2 says that a sizeof expression is value-dependent if the type of the operand is type-dependent. Unless enumerations are given special treatment, all three of these examples will have value-dependent sizes. This could be surprising for the first case, at least, if not the second as well.

  3. Are nested enumerators value-dependent expressions?

    Again the question of dependent initializers comes into play. As an example, consider:

        template<short I> struct S {
            enum E { e0, e1 = I, e2 };
        };
    

    There seem to be three possible approaches as to whether the enumerators of E are value-dependent:

    1. The enumerators of a nested enumeration are all value-dependent, regardless of whether they have a value-dependent initializer or not. This is the current position of 13.8.3.4 [temp.dep.constexpr] paragraph 2, which says that an identifier is value-dependent if it is a name declared with a dependent type.

    2. The enumerators of a nested enumeration are all value-dependent if any of the enumeration's enumerators has a value-dependent initializer. In this approach, e0 would be value-dependent, even though it is clear that it has the value 0.

    3. An enumerator of a nested enumeration is value-dependent only if it has a value-dependent initializer (explict or implicit). This approach would make e1 and e2 value-dependent, but not e0.

    An example that bears on the third approach is the following:

        template<typename T> struct S {
            enum E { N = UINT_MAX, O = T::O };
            int a[N + 2];
        };
    

    With the normal treatment of enumerations, the type of a might be either int[UINT_MAX+2] or int[1], depending on whether the value of T::O was such that the underlying type of E is unsigned int or long.

    One possibility for addressing this problem under the third approach would be to treat a given enumerator as having the type of its initializer in such cases, rather than the enumeration type. This would be similar to the way enumerators are treated within the enumerator list, before the enumeration declaration is complete (9.7.1 [dcl.enum] paragraph 5). The argument against this is that it makes arithmetic using enumerators behave differently when the enumeration is a member of a class template and when it is not.

Notes from the April, 2005 meeting:

The CWG agreed on the following positions:

  1. Nested enumerations are dependent types.

  2. The result of the sizeof operator applied to a nested enumeration is value-dependent unless there are no dependent initializers in its definition; the first case above is not dependent, while the second and third are dependent.

  3. The approach described in 3.C above is correct. This is similar to the treatment of static const integral data members, which are dependent only if their initializer is dependent.

Notes from the October, 2005 meeting:

There was no consensus among the CWG regarding question #3 (which enumerators should be considered value-dependent). The argument in favor of 3.C is principally that the values of enumerators with non-dependent initializers are known at definition time, so there is no need to treat them as dependent.

One objection to 3.C is that, according to the consensus of the CWG, the enumeration type is dependent and thus even the known values of the enumeration would have a dependent type, which could affect the results when such enumerations are used in expressions. A possible response to this concern would be to treat non-dependent initializers as having the type of the initializer rather than the enumeration type, similar to the treatment of enumerators within the enumerator-list (9.7.1 [dcl.enum] paragraph 5). However, this approach would be inconsistent with the treatment of other enumeration types. It would also interfere with overload resolution (e.g., the call in the example under question #1 above would resolve to f(int) with this approach rather than f(S<int>::E)).

Those in favor of option 3.A also suggested that it would be simpler and require less drafting: if all the enumerators have the (dependent) type of the enumeration, 13.8.3.4 [temp.dep.constexpr] paragraph 2 already says that a name with a dependent type is value-dependent, so nothing further would need to be said. Option 3.C would require additional caveats to exempt some enumerators.

The proponents of 3.A also pointed out that there are many other cases where a known value with a dependent type is treated as dependent:

    static const T t = 0;
    ... A<t> ...

or

    template <int I> void f() {
        g(I-I);
    }

With regard to current practice, g++ and MSVC++ implement 3.A, while EDG implements 3.C.

Notes from the July, 2009 meeting:

The consensus of the CWG was that all the types and values are dependent.

Proposed resolution (June, 2010):

Change 13.8.3.2 [temp.dep.type] paragraph 6 as follows:

A type is dependent if it is




590. Nested classes and the “current instantiation”

Section: 13.8.3.2  [temp.dep.type]     Status: C++11     Submitter: James Widman     Date: 23 August 2006

In 13.8.3.2 [temp.dep.type] paragraph 5 we have:

A name is a member of an unknown specialization if the name is a qualified-id in which the nested-name-specifier names a dependent type that is not the current instantiation.

So given:

    template<class T> struct A {
        struct B {
            struct C {
                A<T>::B::C f();
            };
        };
    };

it appears that the name A<T>::B::C should be taken as a member of an unknown specialization, because the WP refers to “the” current instantiation, implying that there can be at most one at any given time. At the declaration of f(), the current instantiation is C, so A<T>::B is not the current instantiation.

Would it be better to refer to “a known instantiation” instead of “the current instantiation?”

Mike Miller:

I agree that there is a problem here, but I don't think the “current instantiation” terminology needs to be replaced. By way of background, paragraph 1 makes it clear that A<T>::B “refers to” the current instantiation:

In the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, a name refers to the current instantiation if it is

A<T>::B satisfies bullet 3. Paragraph 4 says,

A name is a member of the current instantiation if it is

So clearly by paragraphs 1 and 4, A<T>::B::C is a member of the current instantiation. The problem is in the phrasing of paragraph 5, which incorrectly requires that the nested-name-specifier “be” the current instantiation rather than simply “referring to” the current instantiation, which would be the correct complement to paragraph 4. Perhaps paragraph 5 could simply be rephrased as, “...a dependent type and it is not a member of the current instantiation.”

(Paragraph 1 may require a bit more wordsmithing to make it truly recursive across multiple levels of nested classes; as it stands, it's not clear whether the name of a nested class of a nested class of a class template is covered or not.)

Additional note (April, 2011):

It appears that these concerns are addressed by the resolution of issue 1043 in document N3283.

Proposed resolution (December, 2011):

This issue is resolved by the resolution of issue 1043.




1043. Qualified name lookup in the current instantiation

Section: 13.8.3.2  [temp.dep.type]     Status: C++11     Submitter: Doug Gregor     Date: 2010-03-05

[Voted into the WP at the March, 2011 meeting as paper N3283.]

13.8.3.2 [temp.dep.type] paragraph 4 treats unqualified-ids and qualified-ids in which the nested-name-specifier refers to the current instantiation as equivalent. However, the lookups associated with these two id-expressions are different in the presence of dependent base classes (13.8.3 [temp.dep] paragraph 3) : with an unqualified-id, a dependent base class scope is never examined, while with a qualified-id it is. The current wording does not specify how an example like the following is to be handled:

  template<typename T> struct B {};
  struct C { typedef int type; };

  template<typename T>
  struct A : B<T>, C {
    template<typename U> type a(); // #1
    template<typename U> typename A<T>::type a(); // #2: different from #1?
  };

  template<typename T> template<typename U> typename A<T>::type
    A<T>::a() { ... } // defines #1 or #2?

There seem to be two possible strategies for the handling of typename A<T>::type:

  1. It is handled like the unqualified-id case, looking only in non-dependent base classes and not being a dependent type.

  2. Since the current instantiation has dependent base classes, it is handled as a dependent type.

EDG seems to be doing the former, g++ the latter.

Notes from the November, 2010 meeting:

The CWG agreed that if a name is found in a non-dependent base, the type should be treated as non-dependent also.

Additional note (November, 2010):

The overall treatment of dependent base classes in handling a qualified-id in which the nested-name-specifier names the current instantiation or in a member access expression where the object expression is *this is not very clear. It would be helpful if the resolution of this issue could clarify the overall treatment while dealing with the mixed dependent/non-dependent case given in the example.




1057. decltype and the current instantiation

Section: 13.8.3.2  [temp.dep.type]     Status: C++11     Submitter: Mike Miller     Date: 2010-03-18

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

According to 13.8.3.2 [temp.dep.type] paragraph 3,

A template argument that is equivalent to a template parameter (i.e., has the same constant value or the same type as the template parameter) can be used in place of that template parameter in a reference to the current instantiation.

This would presumably include something like

    template<typename T> struct A {
        struct B { };
        A<decltype(T())>::B b;    // no typename
    };

However, this example is rejected by current implementations. Does this need to be clarified in the existing wording?

Notes from the November, 2010 meeting:

The example is not well-formed; if T is an rvalue reference type, for example, decltype(T()) is not equivalent to T.

Proposed resolution (November, 2010) [SUPERSEDED]:

This issue is resolved by the resolution of issue 1056.




1160. Definitions of template members and the current instantiation

Section: 13.8.3.2  [temp.dep.type]     Status: C++11     Submitter: GB     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment GB 43

The current rules for determining whether a name refers to the current instantiation, given in 13.8.3.2 [temp.dep.type] paragraph 1, do not cover the case when a template-id matching a primary template or partial specialization appears in the definition of a member of the template.

Proposed resolution (August, 2010):

Change 13.8.3.2 [temp.dep.type] paragraph 1 as follows:

In the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, a A name refers to the current instantiation if it is



1047. When is typeid value-dependent?

Section: 13.8.3.4  [temp.dep.constexpr]     Status: C++11     Submitter: Steve Adamczyk     Date: 2010-03-08

[Voted into the WP at the November, 2010 meeting.]

The Standard should, but does not currently, say that typeid is value-dependent if its expression or type is type-dependent.

Proposed resolution (September, 2010):

Change 13.8.3.4 [temp.dep.constexpr] paragraph 2 as follows:

...Expressions of the following form are value-dependent if the unary-expression or expression is type-dependent or the type-id is dependent:




1074. Value-dependent noexcept-expressions

Section: 13.8.3.4  [temp.dep.constexpr]     Status: C++11     Submitter: Jason Merrill     Date: 2010-06-04

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

It is not clear why a noexcept-expression is value-dependent if its operand is value-dependent. It would seem that the value of the expression depends only on the type of the operand, not its value.

Proposed resolution (November, 2010) [SUPERSEDED]:

Delete “noexcept( expression )” from the list in 13.8.3.4 [temp.dep.constexpr] paragraph 3.




1088. Dependent non-type template arguments

Section: 13.8.3.4  [temp.dep.constexpr]     Status: C++11     Submitter: Jason Merrill     Date: 2010-06-28

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

Given

    template <const char *N> struct A { static const char *p; };
    template <class T>
       struct B { static const char c[1]; typedef A<B<T>::c> C; };
    template <class T>
       struct D { static const char c[1]; typedef A<c> C; };

13.8.3.5 [temp.dep.temp] says that B<T>::c is dependent because it is used as a non-integral non-type template argument and it is a qualified-id with a nested-name-specifier that names a dependent type.

There doesn't seem to be anything to say that c in the definition of D<T>::C is dependent, which suggests that D<T>::C is the same type for all T, which is clearly false.

Instead of this special rule in 13.8.3.5 [temp.dep.temp], 13.8.3.4 [temp.dep.constexpr] should say that the address of a member of a dependent type is value-dependent, regardless of whether the address is written with a qualified-id.

Proposed resolution (November, 2010) [SUPERSEDED]:

  1. Add a new paragraph at the end of 13.8.3.4 [temp.dep.constexpr]:

  2. An id-expression is value-dependent if it names a member of an unknown specialization.
  3. Change 13.8.3.5 [temp.dep.temp] paragraphs 2-3 as follows:

  4. An integral A non-type template-argument is dependent if its type is dependent or the constant expression it specifies is value-dependent.

    A non-integral Furthermore, a non-type template-argument is dependent if its type is dependent or it has either of the following forms

    and contains a nested-name-specifier which specifies a class-name that names a dependent type the corresponding non-type template-parameter is of reference or pointer type and the template-argument designates or points to a member of the current instantiation or a member of a dependent type.




993. Freedom to perform instantiation at the end of the translation unit

Section: 13.8.4.1  [temp.point]     Status: C++11     Submitter: John Spicer     Date: 6 March, 2009

[Voted into the WP at the March, 2011 meeting.]

The intent is that it is a permissible implementation technique to do template instantiation at the end of a translation unit rather than at an actual point of instantiation. This idea is not reflected in the current rules, however.

Proposed resolution (January, 2011):

Change 13.8.4.1 [temp.point] paragraph 7 as follows:

A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. A specialization for a class template...



546. Explicit instantiation of class template members

Section: 13.9.3  [temp.explicit]     Status: C++11     Submitter: Martin Sebor     Date: 29 October 2005

[Voted into WP at August, 2010 meeting.]

Issue 470 specified the explicit instantiation of members of explicitly-instantiated class templates. In restricting the affected members to those “whose definition is visible at the point of instantiation,” however, this resolution introduced an incompatibility between explicitly instantiating a member function or static data member and explicitly instantiating the class template of which it is a member (13.9.3 [temp.explicit] paragraph 3 requires only that the class template definition, not that of the member function or static data member, be visible at the point of the explicit instantiation). It would be better to treat the member instantiations the same, regardless of whether they are directly or indirectly explicitly instantiated.

Notes from the April, 2006 meeting:

In forwarding document J16/06-0057 = WG21 N1987 to be approved by the full Committee, the CWG reaffirmed its position that explicitly instantiating a class template only explicitly instantiates those of its members that have been defined before the point of the explicit instantiation. The effect of the position advocated above would be to require all non-exported member functions to be defined in the translation unit in which the class template is explicitly instantiated (cf paragraph 4), and we did not want to require that. We did agree that the “visible” terminology should be replaced by wording along the lines of “has been defined.”

Proposed resolution (February, 2010):

Change 13.9.3 [temp.explicit] paragraph 8 as follows:

An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is only an explicit instantiation definition of members whose definition is visible that have been defined at the point of instantiation.



1196. Definition required for explicit instantiation after explicit specialization?

Section: 13.9.3  [temp.explicit]     Status: C++11     Submitter: Mike Miller     Date: 2010-09-08

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

According to 13.9.3 [temp.explicit] paragraph 4,

For a given set of template parameters, if an explicit instantiation of a template appears after a declaration of an explicit specialization for that template, the explicit instantiation has no effect.

However, that does not appear to negate the requirements of paragraph 3 that a definition of the entity being instantiated must be in scope. Consequently, the following would appear to be ill-formed, even though there is no real need for the definition of C:

    template<typename T> class C;
    template<> class C<int> { };
    template class C<int>;

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 13.9.3 [temp.explicit] paragraphs 3-4 as follows:

A declaration of a function template shall be in scope at the point of the explicit instantiation of the function template. A definition of the class or class template containing a member function template shall be in scope at the point of the explicit instantiation of the member function template. A definition of a class template or class member template shall be in scope at the point of the explicit instantiation of the class template or class member template. A definition of a class template shall be in scope at the point of an explicit instantiation of a member function or a static data member of the class template. A definition of a member class of a class template shall be in scope at the point of an explicit instantiation of the member class. A declaration of a function template, a member function or static data member of a class template, or a member function template of a class or class template shall precede an explicit instantiation of that entity. A definition of a class template, a member class of a class template, or a member class template of a class or class template shall precede an explicit instantiation of that entity, unless the explicit instantiation is preceded by an explicit specialization of the entity with the same template arguments. If the declaration of the explicit instantiation names an implicitly-declared special member function (11.4.4 [special]), the program is ill-formed.

For a given set of template parameters arguments, if an explicit instantiation of a template appears after a declaration of an explicit specialization for that template, the explicit instantiation has no effect. Otherwise...




531. Defining members of explicit specializations

Section: 13.9.4  [temp.expl.spec]     Status: C++11     Submitter: Mike Miller     Date: 1 October 2005

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The Standard does not fully describe the syntax to be used when a member of an explicitly-specialized member class or member class template is defined in namespace scope. 13.9.4 [temp.expl.spec] paragraph 4 says that the “explicit specialization syntax” (presumably referring to “template<>”) is not used in defining a member of an explicit specialization when a class template is explicitly specialized as a class. However, nothing is said anywhere about how to define a member of a specialization when:

  1. the entity being specialized is a class (member of a template class) rather than a class template.

  2. the result of the specialization is a class template rather than a class (cf 13.9.4 [temp.expl.spec] paragraph 18, which describes this case as a “member template that... remain[s] unspecialized”).

(See paper J16/05-0148 = WG21 N1888 for further details, including a survey of existing implementation practice.)

Notes from the October, 2005 meeting:

The CWG felt that the best approach, balancing consistency with implementation issues and existing practice, would be to require that template<> be used when defining members of all explicit specializations, including those currently covered by 13.9.4 [temp.expl.spec] paragraph 4.

Proposed resolution (February, 2010) [SUPERSEDED]:

Change 13.9.4 [temp.expl.spec] paragraph 5 as follows:

...The definition of an explicitly specialized class is unrelated to the definition of a generated specialization. That is, its members need not have the same names, types, etc. as the members of a generated specialization. Definitions of members of an explicitly specialized class are defined in the same manner as members of normal classes, and not using the syntax for explicit specialization using the same template<> prefix(es) as the explicitly specialized class. [Example:

    template<class T> struct A {
      void f(T) { /* ... */ }
      struct B { /* ... */ };
      template<class U> struct C { /* ... */ };
    };
    template<> struct A<int> {
      void f(int);
      struct B;
      template<class U> struct C;
    };
    void h() {
      A<int> a;
      a.f(16); // A<int>::f must be defined somewhere
    }
    // explicit specialization syntax not used for a member of
    // explicitly specialized class template specialization
    // members of explicitly specialized classes are defined using
    //the same syntax as the explicitly specialized class:
    template<> void A<int>::f(int) { /* ... */ }
    template<> struct A<int>::B { /* ... */ };
    template<> template<class T> struct A<int>::C { /* ... */ };

end example]

Note (June, 2010):

Because the survey of implementations on which the CWG relied in reaching this resolution is quite old, a new survey of current practice is needed.




605. Linkage of explicit specializations

Section: 13.9.4  [temp.expl.spec]     Status: C++11     Submitter: Steve Clamage     Date: 30 November 2006

[Voted into WP at August, 2010 meeting.]

Given

    template <class T> static T f(T   t) { ... }
    template <>             int f(int t) { ... }

what is the linkage of f(int)?

Section Clause 13 [temp] paragraph 4 says,

Entities generated from a template with internal linkage are distinct from all entities generated in other translation units.

But is the explicit specialization “generated from” the primary template? Does it inherit the local linkage? If so, where do I find a reference saying so explicitly?

James Widman: Data points: EDG 3.8 inherits, GCC 4.0 does not.

Mike Miller: There's a pretty strong presumption that the linkage of an explicit specialization cannot be different from that of its primary template, given that storage class specifiers cannot appear in an explicit specialization (9.2.2 [dcl.stc] paragraph 1).

Notes from the April, 2007 meeting:

The CWG agreed that the linkage of an explicit specialization must be that of the template. Gabriel Dos Reis will investigate the reason for the different behavior of g++.

Proposed resolution (March, 2010):

Change Clause 13 [temp] paragraph 4 as follows:

...Entities generated from Specializations (explicit or implicit) of a template with that has internal linkage are distinct from all entities generated specializations in other translation units...



621. Template argument deduction from function return types

Section: 13.9.4  [temp.expl.spec]     Status: C++11     Submitter: Richard Corden     Date: 16 February 2007

[Voted into WP at August, 2010 meeting.]

It does not appear that the following example is well-formed, although most compilers accept it:

    template <typename T> T foo();
    template <> int foo();

The reason is that 13.9.4 [temp.expl.spec] paragraph 11 only allows trailing template-arguments to be omitted if they “can be deduced from the function argument type,” and there are no function arguments in this example.

13.9.4 [temp.expl.spec] should probably say “function type” instead of “function argument type.” Also, a subsection should probably be added to 13.10.3 [temp.deduct] to cover “Deducing template arguments from declarative contexts” or some such. It would be essentially the same as 13.10.3.3 [temp.deduct.funcaddr] except that the function type from the declaration would be used as the type of P.

Proposed resolution (March, 2008):

  1. Insert the following as a new subsection after 13.10.3.6 [temp.deduct.type]:

  2. 14.9.2.6 Deducing template arguments in a declaration that names a specialization of a function template [temp.deduct.funcdecl]

    Template arguments can be deduced from the function type specified when declaring a specialization of a function template. [Note: this can occur in the context of an explicit specialization, an explicit instantiation, or a friend declaration. —end note] The function template's function type and the declared type are used as the types of P and A, and the deduction is done as described in 13.10.3.6 [temp.deduct.type].

  3. Change 13.9.4 [temp.expl.spec] paragraph 11 as follows:

  4. A trailing template-argument can be left unspecified in the template-id naming an explicit function template specialization provided it can be deduced from the function argument type (14.9.2.6 [temp.deduct.funcdecl])...

Notes from the September, 2008 meeting:

The proposed resolution is probably more than is needed. Instead of a complete new section, the material could become a paragraph in 13.7.7 [temp.fct].

Proposed resolution (February, 2010):

Add the following paragraph at the end of 13.7.7 [temp.fct]:

In a declaration that names a specialization of a function template, template arguments can be deduced from the function type. [Note: this can occur in the context of an explicit specialization, an explicit instantiation, or a friend declaration. —end note] The function template's function type and the declared type are used as the types of P and A and the deduction is done as described in 13.10.3.6 [temp.deduct.type].

Proposed resolution (March, 2010):

This issue is resolved by the resolution of issue 873.




941. Explicit specialization of deleted function template

Section: 13.9.4  [temp.expl.spec]     Status: C++11     Submitter: Spicer     Date: 14 July, 2009

[Voted into WP at August, 2010 meeting.]

According to 13.9.4 [temp.expl.spec] paragraph 1, only non-deleted function templates may be explicitly specialized. There doesn't appear to be a compelling need for this restriction, however, and it could be useful to forbid use of implicitly-instantiated specializations while still allowing use of explicitly-specialized versions.

Proposed resolution (February, 2010):

Change 13.9.4 [temp.expl.spec] paragraph 1 as follows:

An explicit specialization of any of the following:

can be declared...




575. Criteria for deduction failure

Section: 13.10.3  [temp.deduct]     Status: C++11     Submitter: James Widman     Date: 19 April 2006

[Voted into WP at August, 2010 meeting.]

The last two sentences of 13.10.3 [temp.deduct] paragraph 5 read:

When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in non-deduced contexts are replaced with the corresponding deduced or default argument values. If the substitution results in an invalid type, as described above, type deduction fails.

Shouldn't the substitution occur for all uses of the parameters, so that any of them could result in deduction failure?

Proposed resolution (October, 2006):

Change 13.10.3 [temp.deduct] paragraph 5 as follows:

...When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in non-deduced contexts the function type are replaced with the corresponding deduced or default argument values. If the substitution results in an invalid type, as described above, type deduction fails.

Notes from the September, 2008 meeting:

This issue was returned to "drafting" status in order to coordinate the wording with the concepts proposal.

Proposed resolution (March, 2010):

Change 13.10.3 [temp.deduct] paragraph 5 as follows:

When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in non-deduced contexts the template parameter list of the template and the function type are replaced with the corresponding deduced or default argument values. If the substitution results in an invalid type, as described above, type deduction fails.



709. Enumeration names as nested-name-specifiers in deduction failure

Section: 13.10.3  [temp.deduct]     Status: C++11     Submitter: Doug Gregor     Date: 23 Aug, 2008

[Voted into WP at August, 2010 meeting.]

The current rules in 13.10.3 [temp.deduct] say that type deduction fails as a result of attempting to use a type that is not a class type in a qualified name. However, it is now possible to use enumeration names as nested-name-specifiers, so this rule needs to be updated accordingly.

Proposed resolution (February, 2010):

Change the third bullet of the note in 13.10.3 [temp.deduct] paragraph 8 as follows:

[Note: Type deduction may fail for the following reasons:




1170. Access checking during template argument deduction

Section: 13.10.3  [temp.deduct]     Status: C++11     Submitter: Adamczyk     Date: 2010-08-03

[Voted into the WP at the March, 2011 meeting.]

According to 13.10.3 [temp.deduct] paragraph 8,

Access checking is not done as part of the substitution process. Consequently, when deduction succeeds, an access error could still result when the function is instantiated.

This mimics the way access checking is done in overload resolution. However, experience has shown that this exemption of access errors from deduction failure significantly complicates the Standard library, so this rule should be changed.

Proposed resolution (January, 2011):

Change 13.10.3 [temp.deduct] paragraph 8 as follows:

If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. [Note: Access checking is not done as part of the substitution process. end note] Consequently, when deduction succeeds, an access error could still result when the function is instantiated. Only invalid types...



1164. Partial ordering of f(T&) and f(T&&)

Section: 13.10.3.2  [temp.deduct.call]     Status: C++11     Submitter: US     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 77

The following example is ambiguous:

    template<typename T> int f(T&);
    template<typename T> int f(T&&);
    int i;
    int j = f(i);

Because of the special deduction rule for lvalues passed to rvalue-reference parameters, deduction produces f(int&) for both templates, and they are indistinguishable.

Because f(T&) accepts a strict subset of the things that f(T&&) does, it should be considered more specialized by the partial ordering rules.

Proposed resolution (August, 2010):

Change 13.10.3.5 [temp.deduct.partial] paragraph 9 as follows:

If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):

[Editing note: this change transforms the running text at the end of the paragraph into a bulleted list.]




1184. Argument conversions to nondeduced parameter types

Section: 13.10.3.2  [temp.deduct.call]     Status: C++11     Submitter: Daniel Krügler     Date: 2010-08-29

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

It is not clear whether the following example is well-formed or not:

    template<class T> struct identity { typedef T type; };

    template<class T, class C>
    void f(T C::*, typename identity<T>::type*){}

    struct X { void f() {}; };

    int main() { f(&X::f, 0); }

The null pointer conversion required for the second parameter of f is not one of the ones permitted by 13.10.3.2 [temp.deduct.call] paragraph 4, but it's unclear whether that list should apply to parameters with nondeduced types or not. 13.10.2 [temp.arg.explicit] paragraph 6 is explicit that

Implicit conversions (7.3 [conv]) will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter type contains no template-parameters that participate in template argument deduction.

However, this statement appears in a section dealing with explicitly-specified template arguments, so its applicability to nondeduced contexts in general is not clear.

Implementations disagree on the handling of this example.




692. Partial ordering of variadic class template partial specializations

Section: 13.10.3.6  [temp.deduct.type]     Status: C++11     Submitter: Doug Gregor     Date: 16 April, 2008

[Voted into the WP at the March, 2011 meeting as paper N3281.]

13.10.3.6 [temp.deduct.type] paragraph 22 describes how we cope with partial ordering between two function templates that differ because one has a function parameter pack while the other has a normal function parameter. However, this paragraph was meant to apply to template parameter packs as well, e.g., to help with partial ordering of class template partial specializations:

   template <class T1, class ...Z> class S; // #1
   template <class T1, class ...Z> class S<T1, const Z&...> {}; // #2
   template <class T1, class T2> class S<T1, const T2&> {};; // #3
   S<int, const int&> s; // both #2 and #3 match; #3 is more specialized

(See also issue 818.)

Proposed resolution (March, 2009):

Change 13.10.3.6 [temp.deduct.type] paragraphs 9-10 as follows (and add the example above to paragraph 9):

If P has a form that contains <T> or <i>, then each argument Pi of the respective template argument list of P is compared with the corresponding argument Ai of the corresponding template argument list of A. If the template argument list of P contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context. If Pi is a pack expansion, then the pattern of Pi is compared with each remaining argument in the template argument list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by Pi. During partial ordering (13.10.3.5 [temp.deduct.partial]), if Ai was originally a pack expansion and Pi is not a pack expansion, or if P does not contain a template argument corresponding to Ai, argument deduction fails.

Similarly, if P has a form that contains (T), then each parameter type Pi of the respective parameter-type-list of P is compared with the corresponding parameter type Ai of the corresponding parameter-type-list of A. If the parameter-declaration corresponding to Pi is a function parameter pack, then the type of its declarator-id is compared with each remaining parameter type in the parameter-type-list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. During partial ordering (13.10.3.5 [temp.deduct.partial]), if Ai was originally a function parameter pack and Pi is not a function parameter pack, or if P does not contain a function parameter type corresponding to Ai, argument deduction fails. [Note: A function parameter pack can only occur at the end of a parameter-declaration-list (9.3.4.6 [dcl.fct]). —end note]




873. Deducing rvalue references in declarative contexts

Section: 13.10.3.6  [temp.deduct.type]     Status: C++11     Submitter: John Spicer     Date: 16 April, 2009

[Voted into WP at August, 2010 meeting.]

13.10.3.2 [temp.deduct.call] paragraph 3 gives the deduction of rvalue references special treatment in the context of a function call:

If P is of the form T&&, where T is a template parameter, and the argument is an lvalue, the type A& is used in place of A for type deduction.

A similar provision is needed, but is not present, in declarative contexts. For example:

    template<typename T> void f(T&&);
    template<> void f(int&) { }    // #1
    template<> void f(int&&) { }   // #2
    void g(int i) {
        f(i);    // calls f<int&>(int&), i.e., #1
        f(0);    // calls f<int>(int&&), i.e., #2
    }

There need to be rules that deduce the template arguments for the specializations in the same way that the arguments are deduced in the calls.

Proposed resolution (February, 2010):

  1. Change 13.10.3.6 [temp.deduct.type] paragraph 10 as follows:

  2. Similarly, if P has a form that contains (T), then each parameter type Pi of the respective parameter-type-list of P is compared with the corresponding parameter type Ai of the corresponding parameter-type-list of A. If P and A are function types that originated from deduction when taking the address of a function template (13.10.3.3 [temp.deduct.funcaddr]) or when deducing template arguments from a function declaration ([temp.deduct.decl]) and Pi and Ai are parameters of the top-level parameter-type-list of P and A, respectively, Pi is adjusted if it is an rvalue reference to a cv-unqualified template parameter and Ai is an lvalue reference, in which case the type of Pi is changed to be the template parameter type (i.e., T&& is changed to simply T). [Note: As a result, when Pi is T&& and Ai is X&, the adjusted Pi will be T, causing T to be deduced as X&. —end note] [Example:

      template<typename T> void f(T&&);
      template<> void f(int&) { }      // #1
      template<> void f(int&&) { }     // #2
      void g(int i) {
        f(i);   // calls f<int&>(int&), i.e., #1
        f(0);   // calls f<int>(int&&), i.e., #2
      }
    

    end example]

    If the parameter-declaration corresponding to Pi is a function parameter pack...

  3. Add a new section under 13.10.3 [temp.deduct], either before or after 13.10.3.6 [temp.deduct.type], as follows:

  4. 14.8.2.x Deducing template arguments from a function declaration [temp.deduct.decl]

    In a declaration whose declarator-id refers to a specialization of a function template, template argument deduction is performed to identify the specialization to which the declaration refers. Specifically, this is done for explicit instantiations (13.9.3 [temp.explicit]), explicit specializations (13.9.4 [temp.expl.spec]), and certain friend declarations (13.7.5 [temp.friend]). This is also done to determine whether a function template specialization matches a placement operator new (6.7.5.5.3 [basic.stc.dynamic.deallocation], 7.6.2.8 [expr.new]). In all these cases, P is the type of the function template being considered as a potential match and A is the function type from the declaration. The deduction is done as described in 13.10.3.6 [temp.deduct.type].

    If, for the set of function templates so considered, there is either no match or more than one match after partial ordering has been considered (13.7.7.3 [temp.func.order]), deduction fails and the declaration is ill-formed.

(Note that the resolution of issue 674 depends on this resolution.)




1178. Deduction failure matching placement new

Section: 13.10.3.7  [temp.deduct.decl]     Status: C++11     Submitter: Mike Miller     Date: 2010-08-19

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

The new wording added by issue 873 says,

...This is also done to determine whether a function template specialization matches a placement operator new (6.7.5.5.3 [basic.stc.dynamic.deallocation], 7.6.2.8 [expr.new])... If, for the set of function templates so considered, there is either no match or more than one match after partial ordering has been considered (13.7.7.3 [temp.func.order]), deduction fails and the declaration is ill-formed.

The statement describing the consequence of deduction failure (“the declaration is ill-formed”) does not apply to the case when deduction is being performed for placement operator delete, as there is no declaration involved. It may not be necessary to describe what happens when deduction fails in that case, but at least the wording should be tweaked to limit the conclusion to declarative contexts.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 13.10.3.7 [temp.deduct.decl] paragraphs 1-2 as follows:

In a declaration whose declarator-id refers to a specialization of a function template, template argument deduction is performed to identify the specialization to which the declaration refers. Specifically, this is done for explicit instantiations (13.9.3 [temp.explicit]), explicit specializations (13.9.4 [temp.expl.spec]), and certain friend declarations (13.7.5 [temp.friend]). This is also done to determine whether a deallocation function template specialization matches a placement operator new (6.7.5.5.3 [basic.stc.dynamic.deallocation], 7.6.2.8 [expr.new]). In all these cases, P is the type of the function template being considered as a potential match and A is either the function type from the declaration or the type of the deallocation function that would match the placement operator new as described in 7.6.2.8 [expr.new]. The deduction is done as described in 13.10.3.6 [temp.deduct.type].

If, for the set of function templates so considered, there is either no match or more than one match after partial ordering has been considered (13.7.7.3 [temp.func.order]), deduction fails and, in the declaration cases, the declaration program is ill-formed.




1165. Exceptions when destroying array elements

Section: 14.3  [except.ctor]     Status: C++11     Submitter: US     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 78

According to 14.3 [except.ctor] paragraph 2,

An object that is partially constructed or partially destroyed will have destructors executed for all of its fully constructed base classes and non-variant members, that is, for subobjects for which the principal constructor (11.9.3 [class.base.init]) has completed execution and the destructor has not yet begun execution.

This wording leaves unclear whether the remaining elements of an array will be destroyed if the destructor for one of the elements exits via an exception: an array element is a subobject (6.7.2 [intro.object] paragraph 2), but it is not a base class or non-variant member.

Proposed resolution (September, 2010):

Change 14.3 [except.ctor] paragraph 2 as follows:

An object that is partially constructed or partially destroyed of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed base classes and non-variant members subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (11.9.3 [class.base.init]) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor...



971. Incorrect treatment of exception-declarations

Section: 14.4  [except.handle]     Status: C++11     Submitter: Mike Miller     Date: 28 September, 2009

The current wording of 14.4 [except.handle] paragraph 16 is:

The object declared in an exception-declaration or, if the exception-declaration does not specify a name, a temporary (6.7.7 [class.temporary]) is copy-initialized (9.4 [dcl.init]) from the exception object. The object shall not have an abstract class type. The object is destroyed when the handler exits, after the destruction of any automatic objects initialized within the handler.

There are two problems with this. First, it's not clear what it means for the handler's “parameter” to be a temporary. This possibility is briefly mentioned in 6.7.7 [class.temporary], but the lifetime of such a temporary is not defined there; the discussion of lifetime is restricted to those temporaries that arise during the evaluation of an expression, and this is not such a case.

Second, this wording assumes that there will be an object to be destroyed and thus ignores the possibility that the exception-declaration declares a reference.

Proposed resolution (March, 2011):

This issue is resolved by the resolution of issue 1166 in paper N3262.




1166. exception-declarations that do not declare objects

Section: 14.4  [except.handle]     Status: C++11     Submitter: US     Date: 2010-08-03

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

N3092 comment GB 45

According to 14.4 [except.handle] paragraph 16,

The object declared in an exception-declaration or, if the exception-declaration does not specify a name, a temporary (6.7.7 [class.temporary]) is copy-initialized (9.4 [dcl.init]) from the exception object. The object shall not have an abstract class type. The object is destroyed when the handler exits, after the destruction of any automatic objects initialized within the handler.

This wording leaves unspecified how an exception-declaration that is a reference should be treated. For example, presumably a reference to an abstract class type should be permitted, but that is not specified. The treatment of ellipsis is also not clearly addressed.




1218. What is the “currently-handled exception” in a multi-threaded program?

Section: 14.4  [except.handle]     Status: C++11     Submitter: CA     Date: 2010-11-12

[Voted into the WP at the March, 2011 meeting.]

N3092 comment CA 5

14.4 [except.handle] paragraph 8 defines the “currently handled exception” as

The exception with the most recently activated handler that is still active

This definition ignores the possibility that an exception might be thrown and caught in another thread during the execution of a handler. Since throw; rethrows the “currently handled exception,” one might conclude that it would be the other thread's exception that would be rethrown instead of the one that activated that handler.

Proposed resolution (January, 2011):

  1. Change Clause 14 [except] paragraph 1 as follows:

  2. Exception handling provides a way of transferring control and information from a point in the execution of a program thread to an exception handler associated with a point previously passed by the execution...
  3. Change 14.2 [except.throw] paragraph 4 as follows:

  4. ...The implementation may then deallocate the memory for the exception object; any such deallocation is done in an unspecified way. [Note: An exception thrown by a throw-expression does not propagate to other threads unless caught, stored, and rethrown using appropriate library functions; see 17.9.7 [propagation] and 33.10 [futures]. —end note]
  5. Change 14.4 [except.handle] paragraph 6 as follows:

  6. If no match is found among the handlers for a try block, the search for a matching handler continues in a dynamically surrounding try block of the same thread.



1073. Merging dynamic-exception-specifications and noexcept-specifications

Section: 14.5  [except.spec]     Status: C++11     Submitter: Jason Merrill     Date: 2010-06-02

[Voted into the WP at the March, 2011 meeting.]

It is not clear how to handle compatible dynamic-exception-specifications and noexcept-specifications. For example, given

    void f() throw();
    void f() noexcept {
       throw 1;
    }

should we call terminate() or unexpected()? And for

    void g() throw (int);
    void g() noexcept (false) {
       throw 1.0;
    }

should this call unexpected or propagate the exception? Does the order of the declarations (and which is the definition) matter?

Alisdair Meredith:

And what about something like

    struct A { ~A() throw() { } };
    struct B { ~B() noexcept { } };
    struct C: A, B { };

What is the exception specification for C's destructor?

Proposed resolution (November, 2010):

  1. Change 14.5 [except.spec] paragraph 3 as follows:

  2. Two exception-specifications are compatible if:

  3. Add the following note to the end of 14.5 [except.spec] paragraph 9:

  4. Whenever an exception is thrown and the search...

    end example]

    [Note: A function can have multiple declarations with different non-throwing exception-specifications; for this purpose, the one on the function definition is used. —end note]




1167. function-try-blocks for destructors

Section: 14.5  [except.spec]     Status: C++11     Submitter: GB     Date: 2010-08-03

[Voted into the WP at the March, 2011 meeting.]

N3092 comment GB 46

It is not entirely clear that a function-try-block on a destructor will catch exceptions from a base or member destructor; whether such exceptions might be swallowed with a simple return statement rather than being rethrown; and whether such a clause might be entered multiple times if multiple bases/members throw, or if that is an automatic terminate call.

Proposed resolution (August, 2010):

Change Clause 14 [except] paragraph 4 as follows:

...An exception thrown during the execution of the initializer expressions in the ctor-initializer or during the execution of the compound-statement, or — in the case of a destructor — during the destruction of a subobject, transfers control to a handler in a function-try-block in the same way as an exception thrown during the execution of a try-block transfers control to other handlers. [Example:...

Additional note (October, 2010):

There is a related problem with this wording: it covers only “the execution of the initializer expressions in the ctor-initializer,” when it should also cover execution of base and member constructors, regardless of whether they have initializer expressions in the ctor-initializer or not.

The issue has been moved back to "review" status to allow consideration of amending the proposed resolution to something like

...during the execution of the compound-statement or, if the function is a constructor or destructor, during the initialization or destruction of the class's subobjects, transfers control...

Proposed resolution (November, 2010):

Change Clause 14 [except] paragraph 4 as follows:

A function-try-block associates a handler-seq with the ctor-initializer, if present, and the compound-statement. An exception thrown during the execution of the initializer expressions in the ctor-initializer or during the execution of the compound-statement or, for constructors and destructors, during the initialization or destruction, respectively, of the class's subobjects, transfers control to a handler in a function-try-block in the same way as an exception thrown during the execution of a try-block transfers control to other handlers.



1216. Exceptions “allowed” by a noexcept-specification

Section: 14.5  [except.spec]     Status: C++11     Submitter: Jens Maurer     Date: 2010-11-02

[Voted into the WP at the March, 2011 meeting as part of paper N3262.]

According to 14.5 [except.spec] paragraph2 8 and 9,

A function is said to allow an exception of type E if its dynamic-exception-specification contains a type T for which a handler of type T would be a match (14.4 [except.handle]) for an exception of type E.

Whenever an exception is thrown and the search for a handler (14.4 [except.handle]) encounters the outermost block of a function with an exception-specification that does not allow the exception, then,

This does not define what it means for a noexcept-specification to allow an exception.

Proposed resolution (November, 2010) [SUPERSEDED]:

Change 14.5 [except.spec] paragraph 8 as follows:

A function is said to allow an exception of type E if the constant-expression in its noexcept-specification evaluates to false or its dynamic-exception-specification contains a type T for which a handler of type T would be a match (14.4 [except.handle]) for an exception of type E.



1168. Additional reasons to call std::terminate

Section: 14.6.2  [except.terminate]     Status: C++11     Submitter: GB     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment GB 47

The list of reasons for which std::terminate is called needs to be extended to cover several additional cases in C++0x:

Proposed resolution (August, 2010):

  1. Change 14.6.2 [except.terminate] paragraph 1 as follows:

  2. In the following some situations exception handling must be abandoned for less subtle error handling techniques:. [Note: These situations are:

    end note]

  3. Insert the following as a new paragraph following 14.2 [except.throw] paragraph 6:

  4. An exception is considered caught...

    If the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught, calls a function that exits via an exception, std::terminate is called (14.6.2 [except.terminate]). [Example:

        struct C {
          C() { }
          C(const C&) { throw 0; }
        };
    
        int main() {
          try {
            throw C();   // calls std::terminate()
          } catch(C) { }
        }
    

    end example]

  5. Change 14.3 [except.ctor] paragraph 3 as follows:

  6. The process of calling destructors for automatic objects constructed on the path from a try block to a throw-expression is called “stack unwinding.” [Note: If a destructor called during stack unwinding exits with an exception, std::terminate is called (14.6.2 [except.terminate]). [Note: So destructors should generally catch exceptions and not let them propagate out of the destructor. —end note]
  7. Change 6.9.3.2 [basic.start.static] paragraph 6 as follows:

  8. [Note: If the initialization of a non-local variable with static or thread storage duration terminates by throwing exits via an exception, std::terminate is called (see 14.6.2 [except.terminate]). end note]
  9. Change 6.9.3.3 [basic.start.dynamic] paragraph 1 as follows:

  10. ...[Note: If the destruction of a non-local an object with static or thread storage duration terminates by throwing exits via an exception, std::terminate is called (see 14.6.2 [except.terminate]). end note]
  11. Change 17.5 [support.start.term] bullet 8.1 as follows:




1171. Partial stack unwinding with noexcept violation

Section: 14.6.2  [except.terminate]     Status: C++11     Submitter: Merrill     Date: 2010-08-04

[Voted into the WP at the November, 2010 meeting.]

The current wording of 14.6.2 [except.terminate] paragraph 2 makes it sound as if stack unwinding in the case of a noexcept violation is an all-or-nothing proposition. It would be useful to be able to partially unwind the stack, in particular, not to call destructors for the function containing the noexcept-specification.

Proposed resolution (August, 2010):

Change 14.6.2 [except.terminate] paragraph 2 as follows:

...In the situation where the search for a handler (14.4 [except.handle]) encounters the outermost block of a function with a noexcept-specification that does not allow the exception (14.5 [except.spec]), it is implementation-defined whether the stack is unwound, unwound partially, or not unwound at all before std::terminate() is called. In all other situations, the stack shall not be unwound...



475. When is std::uncaught_exception() true? (take 2)

Section: 14.6.3  [except.uncaught]     Status: C++11     Submitter: Martin Sebor     Date: 27 Sep 2004

[Voted into WP at August, 2010 meeting.]

See also issue 37.

Given this piece of code and S having a user-defined ctor, at precisely which point must std::uncaught_exception() return true and where false?

    try { S s0; throw s0; } catch (S s2) { }

My understanding of the semantics of the code is as follows:

  1. The throw expression creates a temporary for a copy of s0, say s1, using the copy ctor of S. In this invocation of the copy ctor uncaught_exception() must return true.
  2. s0 is destroyed during stack unwinding. In the invocation of S dtor uncaught_exception() must still return true.
  3. The variable s2 is initialized from s1 by invoking the copy ctor of S. In this invocation uncaught_exception() must also return true.
  4. s2 and s1 are destroyed. In the invocations of S dtor uncaught_exception() must return false.

Is my understanding correct?

14.2 [except.throw] paragraph 3 talks about “the exception object” when describing the semantics of the throw-expression:

a throw-expression initializes a temporary object, called the exception object...

However, 14.6.2 [except.terminate] paragraph 1 talks about “the expression to be thrown” when enumerating the conditions under which terminate() is called:

when the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught (14.2 [except.throw]), calls a user function that exits via an uncaught exception...

And, 14.6.3 [except.uncaught] paragraph 1 refers to “the object to be thrown” in the description of uncaught_exception():

The function std::uncaught_exception() returns true after completing evaluation of the object to be thrown...

Are all these objects one and the same? I believe the answer is important in case the construction of the temporary exception object throws another exception.

Suppose they are the same. Then uncaught_exception() invoked from the copy ctor for s1 (from the example [above]) must return false and a new exception (e.g., bad_alloc) may be thrown and caught by a matching handler (i.e., without calling terminate()).

But if they are not the same, then uncaught_exception() invoked from the copy ctor for s1 must return true and throwing another exception would end up calling terminate(). This would, IMO, have pretty severe consequences on writing exception safe exception classes.

As in the first case, different compilers behave differently, with most compilers not calling terminate() when the ctor for the temporary exception object throws. Unfortunately, the two compilers that I trust the most do call terminate().

FWIW, my feeling is that it should be possible for the copy ctor invoked to initialize the temporary exception object to safely exit by throwing another exception, and that the new exception should be allowed to be caught without calling terminate.

Mike Miller: The way I see this, a throw-expression has an assignment-expression as an operand. This expression is “the expression to be thrown.” Evaluation of this expression yields an object; this object is “the object to be thrown.” This object is then copied to the exception object.

Martin Sebor: Here's a survey of the return value from uncaught_exception() in the various stages of exception handling, implemented by current compilers:

expr temp unwind handlr 2nd ex
HP aCC 6 0 0 1 0 OK
Compaq C++ 6.5 0 0 1 1 ABRT
EDG eccp 3.4 0 1 1 1 ABRT
g++ 3.4.2 0 0 1 0 OK
Intel C++ 7.0 0 0 1 0 OK
MIPSpro 7.4.1 0 0 1 1 ABRT
MSVC 7.0 0 0 1 0 OK
SunPro 5.5 1 1 1 0 OK
VisualAge 6.0 0 1 1 1 OK

In the table above:

Proposed resolution (October, 2004):

  1. Change 14.2 [except.throw] paragraph 3 as follows:

    A throw-expression initializes a temporary object, called the exception object, the by copying the thrown object (i.e., the result of evaluating its assignment-expression operand) to it. The type of which the exception object is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T,” respectively. [Note: the temporary object created for by a throw-expression that whose operand is a string literal is never of type char* or wchar_t*; that is, the special conversions for string literals from the types “array of const char” and “array of const wchar_t” to the types “pointer to char” and “pointer to wchar_t,” respectively (7.3.3 [conv.array]), are never applied to the operand of a throw-expression. —end note] The temporary is an lvalue and is used to initialize the variable named in the matching handler (14.4 [except.handle]). The type of the operand of a throw-expression shall not be an incomplete type, or a pointer to an incomplete type other than (possibly cv-qualified) void. [...]
  2. Change the note in 14.4 [except.handle] paragraph 3 as follows:

    [Note: a throw-expression operand that which is an integral constant expression of integer type that evaluates to zero does not match a handler of pointer type; that is, the null pointer constant conversions (7.3.12 [conv.ptr], 7.3.13 [conv.mem]) do not apply. —end note]
  3. Change 14.6.2 [except.terminate] bullet 1.1 as follows:

    when the exception handling mechanism, after completing evaluation of the expression to be thrown operand of throw but before the exception is caught (14.2 [except.throw]), calls a user function that exits via an uncaught exception,
  4. Change 14.6.3 [except.uncaught] paragraph 1 as follows:

    The function std::uncaught_exception() returns true after completing evaluation of the object to be thrown operand of throw until completing the initialization of the exception-declaration in the matching handler (_N4140_.18.8.4 [uncaught]).
  5. Change _N4140_.18.8.4 [uncaught] paragraph 1 by adding the indicated words:

    Returns: true after completing evaluation of the operand of a throw-expression until either completing initialization of the exception-declaration in the matching handler or entering unexpected() due to the throw; or after entering terminate() for any reason other than an explicit call to terminate(). [Note: This includes stack unwinding (14.3 [except.ctor]). —end note]

Notes from the April, 2005 meeting:

The CWG discussed this resolution both within the group and with other interested parties. Among the points that were made:

The CWG felt that more input from a wider audience was necessary before a decision could be made on the appropriate resolution.

Notes from the April, 2006 meeting:

The CWG agreed with the position that std::uncaught_exception() should return false during the copy to the exception object and that std::terminate() should not be called if that constructor exits with an exception. The issue was returned to “drafting” status for rewording to reflect this position.

Additional notes (September, 2007):

Although this issue deals primarily with when std::uncaught_exception() begins to return true, the specification of when it begins to return false is also problematic. There are two parallel sections that define the meaning of std::uncaught_exception() and each has a different problem. 14.6.3 [except.uncaught] reads,

The function std::uncaught_exception() returns true after completing evaluation of the object to be thrown until completing the initialization of the exception-declaration in the matching handler (_N4140_.18.8.4 [uncaught]).

The problem here is that whether an exception is considered caught (the underlying condition tested by the function) is here presented in terms of having initialized the exception-declaration, while in other places it is specified by having an active handler for the exception, e.g., 14.2 [except.throw] paragraph 6:

An exception is considered caught when a handler for that exception becomes active (14.4 [except.handle]).

This distinction is important because of 14.4 [except.handle] paragraph 3:

A handler is considered active when initialization is complete for the formal parameter (if any) of the catch clause. [Note: the stack will have been unwound at that point. —end note] Also, an implicit handler is considered active when std::terminate() or std::unexpected() is entered due to a throw.

Note that there is no exception-declaration to be initialized for the std::terminate() and std::unexpected() cases; nevertheless, according to _N4140_.18.8.4 [uncaught], std::uncaught_exception() is supposed to return false when one of those two functions is entered.

The specification in _N4140_.18.8.4 [uncaught] is not well phrased, however, and is open to misinterpretation. It reads,

Returns: true after completing evaluation of a throw-expression until either completing initialization of the exception-declaration in the matching handler or entering unexpected() due to the throw; or after entering terminate() for any reason other than an explicit call to terminate().

The problem here is lack of parallelism: does “after entering terminate” refer to the condition for returning true or false? This would be better phrased along the lines of

Returns: true after completing evaluation of a throw-expression until a handler for the exception becomes active (14.4 [except.handle]).

Proposed resolution (March, 2010):

  1. Change 14.6.2 [except.terminate] bullet 1.1 as follows:

  2. In the following situations exception handling must be abandoned for less subtle error handling techniques:

  3. Change 14.6.3 [except.uncaught] paragraph 1 as follows:

  4. The function std::uncaught_exception() returns true after completing evaluation of the object to be thrown the initialization of the exception object (14.2 [except.throw]) until completing the initialization of the exception-declaration in the matching handler activation of a handler for the exception (14.4 [except.handle], _N4140_.18.8.4 [uncaught])...
  5. Change _N4140_.18.8.4 [uncaught] paragraph 1 as follows:

  6. Returns: true after completing evaluation of a throw-expression initializing an exception object 14.2 [except.throw] until either completing initialization of the exception-declaration in the matching handler or entering unexpected() due to the throw; or after entering terminate() for any reason other than an explicit call to terminate() a handler for the exception (including unexpected() or terminate()) is activated (14.4 [except.handle]). [Note: This includes stack unwinding (14.3 [except.ctor]). —end note]



1169. Missing feature macro for strict pointer safety

Section: 15.11  [cpp.predefined]     Status: C++11     Submitter: GB     Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment DE 13

The recommendations of document N2693 included a feature macro to enable a program to determine whether the implementation enforces strict pointer safety or not, but this macro is not specified in 15.11 [cpp.predefined].

Proposed resolution (August, 2010):

Add the following to the end of 15.11 [cpp.predefined] paragraph 2:






Issues with "C++14" Status


1567. Inheriting constructors and copy/move constructors

Section: _N4527_.12.9  [class.inhctor]     Status: C++14     Submitter: Jason Merrill     Date: 2012-10-10

[Moved to DR at the September, 2013 meeting.]

According to _N4527_.12.9 [class.inhctor] paragraph 3,

For each non-template constructor in the candidate set of inherited constructors other than a constructor having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly declared with the same constructor characteristics unless there is a user-declared constructor with the same signature in the class where the using-declaration appears.

It seems that this should be suppressing constructors that would be copy/move constructors in the derived class rather than copy/move constructors in the base class. For example:

  struct B;
  struct A {
    A(const A&);
    A(const B&);
    A(int);
  };

  struct B: A {
    using A::A;
  };

If B::B(const B&) is an inheriting constructor, other subobjects of B will not be copied. Also, if A::A(const A&) is not inherited, B objects cannot be constructed from an A object.

Proposed resolution (April, 2013):

Change _N4527_.12.9 [class.inhctor] paragraph 3 as follows:

For each non-template constructor in the candidate set of inherited constructors other than a constructor having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly declared with the same constructor characteristics unless there is a user-declared constructor with the same signature in the class where the using-declaration appears or the constructor would be a default, copy, or move constructor for that class.



1738. Explicit instantiation/specialization of inheriting constructor templates

Section: _N4527_.12.9  [class.inhctor]     Status: C++14     Submitter: Daveed Vandevoorde     Date: 2013-08-16

[Moved to DR at the February, 2014 meeting.]

It is not clear whether it is permitted to explicitly instantiate or explicitly specialize specializations of inheriting constructor templates. Since inheriting constructors are considered to be implicitly declared (_N4527_.12.9 [class.inhctor] paragraph 1), it might be inferred that 13.9.3 [temp.explicit] paragraph 4 forbids their explicit instantiation:

If the declaration of the explicit instantiation names an implicitly-declared special member function ( 11.4.4 [special]), the program is ill-formed.

Similarly, an explicit specialization provides a definition for the specialized member, and 11.4.4 [special] paragraph 1 forbids defining an implicitly-declared special member function.

These inferences do not seem conclusive, however, so an explicit statement in _N4527_.12.9 [class.inhctor] would be helpful.

(See also issue 1780.)

Proposed resolution (January, 2014):

Change _N4527_.12.9 [class.inhctor] paragraph 4 as follows:

A constructor so declared has the same access as the corresponding constructor in X. It is deleted if the corresponding constructor in X is deleted (9.5 [dcl.fct.def]). An inheriting constructor shall not be explicitly instantiated (13.9.3 [temp.explicit]) or explicitly specialized (13.9.4 [temp.expl.spec]).



1575. Incorrect definition of “strict pointer safety”

Section: _N4885_6.7.5.5.4  [basic.stc.dynamic.safety]     Status: C++14     Submitter: Matt Austern     Date: 2012-10-17

[Moved to DR at the September, 2013 meeting.]

According to _N4885_6.7.5.5.4 [basic.stc.dynamic.safety] paragraph 4,

an implementation may have strict pointer safety, in which case a pointer value that is not a safely-derived pointer value is an invalid pointer value unless the referenced complete object is of dynamic storage duration and has previously been declared reachable (_N4885_.20.10.5 [util.dynamic.safety]).

“Safely-derived pointer” is defined only with respect to dynamically-allocated storage. Presumably pointers to objects with automatic and static storage duration should also be considered valid.

Proposed resolution (April, 2013):

Change _N4885_6.7.5.5.4 [basic.stc.dynamic.safety] paragraph 4 as follows:

Alternatively, an implementation may have strict pointer safety, in which case a pointer value referring to an object with dynamic storage duration that is not a safely-derived pointer value is an invalid pointer value unless the referenced complete object is of dynamic storage duration and has previously been declared reachable (_N4885_.20.10.5 [util.dynamic.safety]). [Note:...



1509. Definition of “non-template function”

Section: Clause 3  [intro.defs]     Status: C++14     Submitter: Johannes Schaub     Date: 2012-06-08

[Moved to DR at the September, 2013 meeting.]

The term “non-template function” is used in various places but never defined.

Proposed resolution (June, 2013):

  1. Change 7.6.1.3 [expr.call] paragraph 1 as follows:

  2. There are two kinds of function call: ordinary function call and member function [Footnote: A static member function (11.4.9 [class.static]) is an ordinary function. —end foot note] (11.4.2 [class.mfct]) call. A function call is a postfix expression followed by parentheses containing a possibly empty, comma-separated list of initializer-clauses which constitute the arguments to the function. The postfix expression shall have function type or pointer to function type. For an ordinary function a call to a non-member function or to a static member function, the postfix expression shall be either... For a member function call to a non-static member function, the postfix expression shall be...
  3. Add a new paragraph before 9.3.4.6 [dcl.fct] paragraph 13:

  4. A non-template function is a function that is not a function template specialization. [Note: A function template is not a function. —end note]

    A declarator-id or abstract-declarator containing an ellipsis shall only be used...

  5. Change the footnote in 12.2.2 [over.match.funcs] paragraph 7 as follows:

  6. The process of argument deduction fully determines the parameter types of the function template specializations, i.e., the parameters of function template specializations contain no template parameter types. Therefore the, except where specified otherwise, function template specializations can be treated as normal (non-template) functions and non-template functions (9.3.4.6 [dcl.fct]) are treated equivalently for the remainder of overload resolution.
  7. Change the final sub-bullet of 12.2.2.3 [over.match.oper] paragraph 3 as follows:

  8. For a unary operator @...

  9. Change the indicated bullet of the second bulleted list of 12.2.4 [over.match.best] paragraph 1 as follows:

  10. Change 12.3 [over.over] paragraph 4 as follows:

  11. If more than one function is selected, any function template specializations in the set are eliminated if the set also contains a non-template function that is not a function template specialization, and any given function template specialization F1 is eliminated
  12. Change Clause 13 [temp] paragraph 5 as follows:

  13. A class template shall not have the same name as any other template, class, function, variable, enumeration, enumerator, namespace, or type in the same scope (6.4 [basic.scope]), except as specified in (13.7.6 [temp.spec.partial]). Except that a function template can be overloaded either by (non-template) functions (9.3.4.6 [dcl.fct]) with the same name or by other function templates with the same name (13.10.4 [temp.over]), a template name declared in namespace scope or in class scope shall be unique in that scope.
  14. Change 13.7.3 [temp.mem] paragraph 2 as follows:

  15. A local class of non-closure type shall not have member templates. Access control rules (11.8 [class.access]) apply to member template names. A destructor shall not be a member template. A normal (non-template) member function (9.3.4.6 [dcl.fct]) with a given name and type and a member function template of the same name, which could be used to generate a specialization of the same type, can both be declared in a class. When both exist, a use of that name and type refers to the non-template member unless an explicit template argument list is supplied. [Example:

      template <class T> struct A {
        void f(int);
        template <class T2> void f(T2);
      };
    
      template <> void A<int>::f(int) { }               // non-template member function
      template <> template <> void A<int>::f<>(int) { } // template member function template specialization
    
      int main() {
        A<char> ac;
        ac.f(1);       // non-template
        ac.f('c');     // template
        ac.f<>(1);     // template
      }
    

    end example]

  16. Change 13.7.5 [temp.friend] paragraph 1 as follows:

  17. A friend of a class or class template can be a function template or class template, a specialization of a function template or class template, or an ordinary ( a non-template) function or class. For a friend function declaration that is not a template declaration:

  18. Change 13.7.5 [temp.friend] paragraph 4 as follows:

  19. When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used (6.3 [basic.def.odr]). The same restrictions...
  20. Change 13.7.7 [temp.fct] paragraph 2 as follows:

  21. A function template can be overloaded with other function templates and with normal (non-template) functions (9.3.4.6 [dcl.fct]). A normal non-template function is not related to a function template (i.e., it is never considered to be a specialization), even if it has the same name and type as a potentially generated function template specialization.144
  22. Change 13.10.2 [temp.arg.explicit] paragraph 4 as follows:

  23. [Note: An empty template argument list can be used to indicate that a given use refers to a specialization of a function template even when a normal (i.e., non-template) function (9.3.4.6 [dcl.fct]) is visible that would otherwise be used. For example:...



1775. Undefined behavior of line splice in raw string literal

Section: 5.2  [lex.phases]     Status: C++14     Submitter: Canada     Date: 2013-09-24

N3690 comment CA 26

[Moved to DR at the February, 2014 meeting.]

According to phase 2 of 5.2 [lex.phases] paragraph 1,

Each instance of a backslash character (\) immediately followed by a new-line character is deleted, splicing physical source lines to form logical source lines. Only the last backslash on any physical source line shall be eligible for being part of such a splice. If, as a result, a character sequence that matches the syntax of a universal-character-name is produced, the behavior is undefined.

There does not appear to be a good reason for the behavior to be undefined when the line splice occurs within a raw string literal, since the splicing will be reverted (5.4 [lex.pptoken] paragraph 3).

Proposed resolution (September, 2013):

Change 5.2 [lex.phases] paragraph 1 phase 2 as follows:

  1. Each instance of a backslash character (\) immediately followed by a new-line character is deleted, splicing physical source lines to form logical source lines. Only the last backslash on any physical source line shall be eligible for being part of such a splice. If, as a result, Except for splices reverted in a raw string literal, if a splice results in a character sequence that matches the syntax of a universal-character-name is produced, the behavior is undefined. A source file that is not empty and that does not end in a new-line character, or that ends in a new-line character immediately preceded by a backslash character before any such splicing takes place, shall be processed as if an additional new-line character were appended to the file.




1717. Missing specification of type of binary literal

Section: 5.13.2  [lex.icon]     Status: C++14     Submitter: Richard Smith     Date: 2013-07-23

N3690 comment ES 7

[Applied to WP at the February, 2014 meeting.]

Table 6 of 5.13.2 [lex.icon] paragraph 2 covers only decimal, octal, and hexadecimal literals. Binary literals should be treated like the latter two. (It would also be more consistent to refer to these as “literals” instead of “constants.”)

Proposed resolution (September, 2013):

Change the caption and header row of Table 6 in 5.13.2 [lex.icon] paragraph 2 as follows:

Table 6 — Types of integer constants literals

Suffix Decimal constants literal Binary, Octal octal, or hexadecimal constant literal




1759. UTF-8 code units in plain char

Section: 5.13.5  [lex.string]     Status: C++14     Submitter: United Kingdom     Date: 2013-09-23

N3690 comment GB 1

[Moved to DR at the February, 2014 meeting as part of document N3914.]

A UTF-8 string literal might result in a code unit with the value 0x80. However, plain char is not guaranteed to be able to represent 0x80.




1741. odr-use of class object in lvalue-to-rvalue conversion

Section: 6.3  [basic.def.odr]     Status: C++14     Submitter: Richard Smith     Date: 2013-08-21

N3690 comment CA 28

[Moved to DR at the February, 2014 meeting.]

According to 7.3.2 [conv.lval] paragraph 2,

if the glvalue has a class type, the [lvalue-to-rvalue] conversion copy-initializes a temporary of type T from the glvalue and the result of the conversion is a prvalue for the temporary.

The implications of such a conversion for odr-use do not appear to have been factored into 6.3 [basic.def.odr] paragraph 3, which exempts constant objects that are immediately lvalue-to-rvalue converted. For example, given

  struct S { int n; };
  struct T {
    static constexpr S s = {};
  };
  void f(...);
  void g() { f(T::s); }

Does this odr-use T::s, requiring it to have a definition, because of binding it to the reference parameter of S's copy constructor? How about

  struct S { int n; };
  void f(...);
  void g() {
    constexpr S s = {};
    [] { f(s); };
  }

Does s need to be captured? There is implementation variance on both these examples.

Proposed resolution (September, 2013):

Change 6.3 [basic.def.odr] paragraph 3 as follows:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used unless x satisfies the requirements for appearing in a constant expression (7.7 [expr.const]) applying the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) to x yields a constant expression (7.7 [expr.const]) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is applied to e, or e is a discarded-value expression ( Clause 7 [expr]). this is odr-used...



1764. Hiding of function from using-declaration by signature

Section: 6.5.2  [class.member.lookup]     Status: C++14     Submitter: Canada     Date: 2013-09-23

N3690 comment CA 6

[Moved to DR at the February, 2014 meeting.]

According to 9.9 [namespace.udecl] paragraph 15,

When a using-declaration brings names from a base class into a derived class scope, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list (9.3.4.6 [dcl.fct]), cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting).

The algorithm for class-scope name lookup given in 6.5.2 [class.member.lookup], however, does not implement this requirement; there is nothing that removes a hidden base class member (replacing the using-declaration, per paragraph 3) from the result set.

Proposed resolution (September, 2013):

Change 6.5.2 [class.member.lookup] paragraph 3 as follows:

The lookup set for f in C, called S(f, C), consists of two component sets: the declaration set, a set of members named f; and the subobject set, a set of subobjects where declarations of these members (possibly including using-declarations) were found. In the declaration set, using-declarations are replaced by the members they designate set of designated members that are not hidden or overridden by members of the derived class (9.9 [namespace.udecl]), and type declarations (including injected-class-names) are replaced by the types they designate...



1690. Associated namespace for local type

Section: 6.5.4  [basic.lookup.argdep]     Status: C++14     Submitter: Ville Voutilainen     Date: 2013-05-27

[Moved to DR at the February, 2014 meeting.]

We can return a lambda or an object of a local class type from a lambda in c++11, and we can return them from normal functions in c++14. If those lambdas and normal functions are in a namespace, the returned lambdas/local-classes apparently aren't in that namespace, or even if they are, ADL won't find them. Is this intended?

(See also issue 1664.)

Proposed resolution (September, 2013):

This issue is resolved by the resolution of issue 1691.




1691. Argument-dependent lookup and opaque enumerations

Section: 6.5.4  [basic.lookup.argdep]     Status: C++14     Submitter: David Krauss     Date: 2013-05-27

[Moved to DR at the February, 2014 meeting.]

According to 6.5.4 [basic.lookup.argdep] paragraph 2,

If T is an enumeration type, its associated namespace is the namespace in which it is defined. If it is class member, its associated class is the member's class; else it has no associated class.

This does not take into account opaque enumerations, which can be defined in an enclosing namespace of the one of which is a member.

Proposed resolution (September, 2013):

Change 6.5.4 [basic.lookup.argdep] paragraph 2 as follows:

...The sets of namespaces and classes are determined in the following way:

This resolution also resolves issues 1690 and 1692.




1692. Associated namespaces of doubly-nested classes

Section: 6.5.4  [basic.lookup.argdep]     Status: C++14     Submitter: David Krauss     Date: 2013-05-29

[Moved to DR at the February, 2014 meeting.]

According to 6.5.4 [basic.lookup.argdep] paragraph 2,

If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces of which its associated classes are members.

Consider an example like

  struct A {
    struct B {
      struct C { };
    };
  };

A has one associated class, itself, and has the global namespace as its associated namespace. A::B has two associated classes, A and itself, and by virtue of its association with A, has the global namespace as its associated namespace. A::B::C has two associated classes, A::B and itself. However, because neither A::B nor A::B::C is a member of a namespace, A::B::C has no associated namespaces.

This seems like a defect.

Proposed resolution (September, 2013):

This issue is resolved by the resolution of issue 1691.




1746. Are volatile scalar types trivially copyable?

Section: 6.8  [basic.types]     Status: C++14     Submitter: Walter Brown     Date: 2013-09-06

[Moved to DR at the February, 2014 meeting.]

According to 6.8 [basic.types] paragraph 9,

Arithmetic types (6.8.2 [basic.fundamental]), enumeration types, pointer types, pointer to member types (6.8.4 [basic.compound]), std::nullptr_t, and cv-qualified versions of these types (6.8.5 [basic.type.qualifier]) are collectively called scalar types... Scalar types, trivially copyable class types ( Clause 11 [class]), arrays of such types, and non-volatile const-qualified versions of these types (6.8.5 [basic.type.qualifier]) are collectively called trivially copyable types.

This is confusing, because “scalar types” include volatile-qualified types, but the intent of the definition of “trivially copyable type” appears to be to exclude volatile-qualified types. Perhaps the second quoted sentence should read something like,

A non-volatile type T or an array of such T is called a trivially copyable type if T is either a scalar type or a trivially copyable class type.

(Note that the following sentence, defining “trivial type,” has a similar formal issue, although it has no actual significance because all cv-qualifiers are permitted.)

(See also issue 496.)

Proposed resolution (January, 2014):

Change 6.8 [basic.types] paragraph 10 as follows:

...Scalar Cv-unqualified scalar types, trivially copyable class types ( Clause 11 [class]), arrays of such types, and non-volatile const-qualified versions of these types (6.8.5 [basic.type.qualifier]) are collectively called trivially copyable types...



1441. Unclear wording for signal handler restrictions

Section: 6.9.1  [intro.execution]     Status: C++14     Submitter: Alisdair Meredith     Date: 2012-01-06

N3690 comment US 3

[Moved to DR at the February, 2014 meeting as paper N3910.]

The wording of 6.9.1 [intro.execution] paragraph 6 is intended to describe the values of objects upon entry to and exit from the handler — i.e., that signal handler cannot rely on non-atomic objects being in a consistent state upon entry, nor can it reliably set the value of non-atomic objects and expect that they will continue to have those values after the handler exits. However, the wording could be read as saying even during the execution of the handler it cannot set and use non-atomic objects. The wording should be clarified.




1583. Incorrect example of unspecified behavior

Section: 6.9.1  [intro.execution]     Status: C++14     Submitter: Alisdair Meredith     Date: 2012-11-03

[Moved to DR at the September, 2013 meeting.]

Currently, 6.9.1 [intro.execution] paragraph 3 says,

Certain other aspects and operations of the abstract machine are described in this International Standard as unspecified (for example, order of evaluation of arguments to a function).

However, the order of evaluation of function arguments is no longer “unspecified;” instead, their value computations are unsequenced. A better example of unspecified behavior is needed.

Proposed resolution (April, 2013):

Change 6.9.1 [intro.execution] paragraph 3 as follows:

Certain other aspects and operations of the abstract machine are described in this International Standard as unspecified (for example, order of evaluation of arguments to a function evaluation of expressions in a new-initializer if the allocation function fails to allocate memory (7.6.2.8 [expr.new])). Where possible...



1466. Visible sequences of side effects are redundant

Section: 6.9.2  [intro.multithread]     Status: C++14     Submitter: Mark Batty     Date: 2012-02-21

[Moved to DR at the February, 2014 meeting as part of document N3914.]

[The following is reproduced verbatim from WG14 DR406 as a C-liaison issue.]

It has been mathematically proved that a simplification can be made to the memory model as it is specified in the final draft of the C++11 standard. Essentially, the restriction defining visible sequence of side effects (vsse) is redundant and can be removed with no ill effects. The main motivation for doing this is that the current restriction is misleading. 5.1.2.4p22 defines vsse's:

The visible sequence of side effects on an atomic object M, with respect to a value computation B of M, is a maximal contiguous sub-sequence of side effects in the modification order of M, where the first side effect is visible with respect to B, and for every subsequent side effect, it is not the case that B happens before it. The value of an atomic object M, as determined by evaluation B, shall be the value stored by some operation in the visible sequence of M with respect to B.

The wording of this paragraph makes it seem as if the vsse identifies the writes that an atomic read is allowed to read from, but this is not the case. There can be writes in the vsse that cannot be read due to the coherence requirements (to be included in C, 1.10p15 through 1.10p18 in C++ N3291). Consequently this is even more confusing than it at first appears.

Also propose changing 5.1.2.4p22 to the following:

The value of an atomic object M, as determined by evaluation B, shall be the value stored by some side effect A that modifies M, where B does not happen before A.

With a note to remind the reader of the coherence requirements:

NOTE: The set of side effects that a given evaluation might take its value from is also restricted by the rest of the rules described here, and in particular, by the coherence requirements below

If the committee is concerned about allowing a differing text from C++11, then a note could be added to assure the reader:

NOTE: Although the rules for multi-threaded executions differ here from those of C++11, the executions they allow are precisely the same. Visible sequences of side effects are a redundant restriction.

Proposed resolution (January, 2014) [SSUPERSEDED]:

  1. Change 6.9.2 [intro.multithread] paragraph 14 as follows:

  2. The visible sequence of side effects on an atomic object M, with respect to a value computation B of M, is a maximal contiguous sub-sequence of side effects in the modification order of M, where the first side effect is visible with respect to B, and for every side effect, it is not the case that B happens before it. The value of an atomic object M, as determined by evaluation B, shall be the value stored by some operation in the visible sequence of M with respect to B side effect A that modifies M, where B does not happen before A. [Note: It can be shown that the visible sequence of side effects of a value computation is unique given The set of side effects that a given evaluation might take its value from is also restricted by the rest of the rules described here, and in particular, by the coherence requirements below. —end note]
  3. Change 6.9.2 [intro.multithread] paragraph 20 as follows:

  4. [Note: The visible sequence of side effects value observed by a load of an atomic depends on the “happens before” relation, which depends on the values observed by loads of atomics, which we are restricting here. The intended reading is that there must exist an association of atomic loads with modifications they observe that, together with suitably chosen modification orders and the “happens before” relation derived as described above, satisfy the resulting constraints as imposed here. —end note]
  5. Change 6.9.2 [intro.multithread] paragraph 22 as follows:

  6. [Note: Compiler transformations that introduce assignments to a potentially shared memory location that would not be modified by the abstract machine are generally precluded by this standard, since such an assignment might overwrite another assignment by a different thread in cases in which an abstract machine execution would not have encountered a data race. This includes implementations of data member assignment that overwrite adjacent members in separate memory locations. Reordering of atomic loads in cases in which the atomics in question may alias is also generally precluded, since this may violate the “visible sequence” coherence rules. —end note]
  7. Change 33.5.4 [atomics.order] paragraph 3 as follows:

  8. There shall be a single total order S on all memory_order_seq_cst operations, consistent with the “happens before” order and modification orders for all affected locations, such that each memory_order_seq_cst operation B that loads a value from an atomic object M observes one of the following values:

    [Note:...




1669. auto return type for main

Section: 6.9.3.1  [basic.start.main]     Status: C++14     Submitter: Richard Smith     Date: 2013-04-26

[Applied to WP at the February, 2014 meeting.]

Should it be permitted for main to have a deduced return type?

(See also issue 1676.)

Proposed resolution (November, 2013):

Change 6.9.3.1 [basic.start.main] paragraph 2 as follows:

An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a declared return type of type int, but otherwise its type is implementation-defined. All implementations An implementation shall allow both

as the type...




1747. Constant initialization of reference to function

Section: 6.9.3.2  [basic.start.static]     Status: C++14     Submitter: Daniel Krügler     Date: 2013-09-10

[Moved to DR at the February, 2014 meeting.]

According to 6.9.3.2 [basic.start.static] paragraph 2,

...Constant initialization is performed:

This wording, presumably inadvertently, excludes a reference to a function from being constant initialization.

Proposed resolution (January, 2014):

Change 6.9.3.2 [basic.start.static] paragraph 2 as follows:

...Constant initialization is performed:




1576. Discarded-value volatile xvalues

Section: Clause 7  [expr]     Status: C++14     Submitter: Michael Wong     Date: 2012-10-18

[Moved to DR at the September, 2013 meeting.]

According to the current wording of Clause 7 [expr] paragraph 11, the lvalue-to-rvalue conversion applies only to volatile lvalues in the listed contexts. Presumably it should apply to volatile xvalues as well.

Proposed resolution (June, 2013):

Change Clause 7 [expr] paragraph 11 as follows:

...The lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is applied if and only if the expression is an lvalue a glvalue of volatile-qualified type and it is one of the following:...



1773. Out-of-lifetime lvalue-to-rvalue conversion

Section: 7.3.2  [conv.lval]     Status: C++14     Submitter: Canada     Date: 2013-09-24

N3690 comment CA 14

[Moved to DR at the February, 2014 meeting.]

Consider an example like the following:

  struct Base {
    virtual int call() = 0;
  };
  Base *foo() {
    constexpr int x = 0;
    struct Local : Base {
      virtual int call() { return x; }
    };
    static Local local;
    return &local;
  }

  int main() {
    return foo()->call();
  }

While the likely intention is that the lvalue-to-rvalue conversion of the block-scope constant is implemented by using the value of the constant expression in place of reading from storage, it seems that the wording of 7.3.2 [conv.lval] paragraph 2 does not prevent this program from being subject to undefined behaviour caused by lifetime violation. In particular, it seems that a name expression that appears in a potentially-evaluated expression such that the object named is not odr-used (by that instance of the name) may still be evaluated, in theory, as an lvalue through which the object named or a subobject thereof is accessed.

Proposed resolution (September, 2013):

Change 7.3.2 [conv.lval] paragraph 2 as follows:

When an lvalue-to-rvalue conversion occurs in an unevaluated operand or a subexpression thereof ( Clause 7 [expr]) is applied to an expression e, and either

the value contained in the referenced object is not accessed. [Example:

  struct S { int n; };
  auto f() {
    S x { 1 };
    constexpr S y { 2 };
    return [&](bool b) { return (b ? y : x).n; };
  }
  auto g = f();
  int m = g(false); // undefined behavior due to access of x.n outside its lifetime
  int n = g(true);  // OK, does not access y.n

end example] In all other cases, the result of the conversion...




1787. Uninitialized unsigned char values

Section: 7.3.2  [conv.lval]     Status: C++14     Submitter: United Kingdom     Date: 2013-09-28

N3690 comment GB 2

[Moved to DR at the February, 2014 meeting as part of document N3914.]

The current wording of 7.3.2 [conv.lval] gives the result of fetching an uninitialized unsigned character an unspecified value, which is then stable: assigned to a different variable, it will be the same throughout the lifetime of that variable. It would be more helpful to optimizers for the unspecified value to be viral, so that fetching from the second variable would also yield an unspecified result, not necessarily the same each time.




1601. Promotion of enumeration with fixed underlying type

Section: 7.3.7  [conv.prom]     Status: C++14     Submitter: Ville Voutilainen     Date: 2013-01-09

[Moved to DR at the September, 2013 meeting.]

According to 7.3.7 [conv.prom] paragraph 4,

A prvalue of an unscoped enumeration type whose underlying type is fixed (9.7.1 [dcl.enum]) can be converted to a prvalue of its underlying type. Moreover, if integral promotion can be applied to its underlying type, a prvalue of an unscoped enumeration type whose underlying type is fixed can also be converted to a prvalue of the promoted underlying type.

Because both of these conversions have the same rank, a call like the following is ambiguous, even though conversion to the underlying type might seem better than conversion to int:

  enum E : char { e };
  void f(char);
  void f(int);
  void g() {
    f(e);      // ambiguous
  }

On the other hand, character types often have non-numeric semantics in programs, and programmers might use a character type just to set the size of the enumeration's object representation, not to imply character semantics for the enumeration. It might be better to leave the ambiguity in place in order to require programmers to make their intent explicit.

Proposed resolution (June, 2013):

Change 12.2.4.3 [over.ics.rank] paragraph 4 as follows:

...Two conversion sequences with the same rank are indistinguishable unless one of the following rules applies:




1607. Lambdas in template parameters

Section: 7.5.5  [expr.prim.lambda]     Status: C++14     Submitter: Daniel Krügler     Date: 2013-01-19

N3690 comment CA 3

[Moved to DR at the February, 2014 meeting.]

Lambda expressions cannot appear in unevaluated operands nor in evaluated portions of constant expressions. However, the following example appears to circumvent those restrictions:

  template <bool> struct BoolSink { typedef void type; };

  template <typename T, typename U>
  struct AddRvalueReferenceImpl { typedef T type; };

  template <typename T>
  struct AddRvalueReferenceImpl<T, typename BoolSink<false &&
     [] {
       extern T &&tref;
     }>::type> {
    typedef T &&type;
  };

  template <typename T>
  struct AddRvalueReference : AddRvalueReferenceImpl<T, void> { };

  namespace ImplHelpers {
    template <typename T>
    typename AddRvalueReference<T>::type create(void) { }
  }

  template <typename T, typename U, typename ...Args>
  struct IsConstructibleImpl { enum { value = 0 }; };

  template <typename T, typename ...Args>
  struct IsConstructibleImpl<T, typename BoolSink<false &&
     [] {
       T t( ::ImplHelpers::create<Args>() ...);
     }>::type, Args ...> {
    enum { value = 1 };
  };

  template <typename T, typename ...Args>
  struct IsConstructible : IsConstructibleImpl<T, void, Args ...> { };

  struct DestroyMe {
    ~DestroyMe() = delete;
  };

  static_assert(+IsConstructible<int>::value, "error");
  static_assert(!IsConstructible<void>::value, "error");
  static_assert(+IsConstructible<int [1]>::value, "error");
  static_assert(!IsConstructible<DestroyMe>::value, "error");
  static_assert(!IsConstructible<int *, char *>::value, "error");

  static_assert(+IsConstructible<int &&, int>::value, "error");
  static_assert(!IsConstructible<int &&, int &>::value, "error");
  static_assert(+IsConstructible<int &&, int &&>::value, "error");

Is this intended?

Additional notes, April, 2013:

Further discussion has arisen regarding lambda-expressions in function template signatures. Although the restriction that lambda-expressions cannot appear as unevaluated operands (7.5.5 [expr.prim.lambda] paragraph 2) was intended to avert the need to deal with them in function template signatures, the fact that 7.7 [expr.const] treats unevaluated subexpressions separately from unevaluated operands opens another avenue for lambda-expressions in template signatures, e.g.,

  template<typename T>
  void f(int [(0 && [] { for (auto x : T()) {} }, 1)]);

Four possible approaches for dealing with this issue have been suggested:

  1. Allow lambda-expressions in function template signatures. This would be costly in some implementations.

  2. Give a function template internal linkage if its signature includes a lambda-expression. This would allow SFINAE and redeclaration to work without requiring that lambda-expressions be mangled.

  3. Specify that a function signature containing a lambda-expression is not a redeclaration of any other function template, which would allow SFINAE to work but not require declaration matching and mangling.

  4. Do not allow lambda-expressions in function template signatures.

If any of these approaches were adopted, the rationale for disallowing lambda-expressions in unevaluated operands would be removed, so it might make sense to remove the restriction at the same time.

Proposed resolution (September, 2013):

Change 7.5.5 [expr.prim.lambda] paragraph 2 as follows:

...A lambda-expression shall not appear in an unevaluated operand (Clause 7 [expr]), in a template-argument, in an alias-declaration, in a typedef declaration, or in the declaration of a function or function template outside its function body and default arguments. [Note: The intention is to prevent lambdas from appearing in a signature —end note]. [Note: A closure object behaves like a function object (22.10 [function.objects]). —end note]



1664. Argument-dependent lookup of lambdas used in default arguments

Section: 7.5.5  [expr.prim.lambda]     Status: C++14     Submitter: Michael Wong     Date: 2013-04-15

N3690 comment CA 18

[Moved to DR at the February, 2014 meeting.]

According to 7.5.5 [expr.prim.lambda] paragraph 3,

The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression. [Note: This determines the set of namespaces and classes associated with the closure type (6.5.4 [basic.lookup.argdep]). The parameter types of a lambda-declarator do not affect these associated namespaces and classes. —end note]

However, 13.9.2 [temp.inst] paragraph 13 says,

If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point.

A possibility, then, is that the closure type for a lambda expression in a default argument for a template function (or, presumably, a member function of a class template) is to be considered as having been declared in some block scope in the body of the fictional function template specialization.

Consider the following example:

  namespace J {
    inline namespace K {
      template <typename T> int zap(const T &t) { foo(t); return 0; }
      template <typename T> void zip(int = zap([] { })) { }
    }
    template <typename T> void foo(const T &) { }
  }
  void bar() { J::K::zip<long>(); }

If zip were not a template, argument-dependent lookup successfully resolves the lookup for foo in all implementations tested; however, there is implementation variance in the handling of the example as written.

(See also issue 1690.)

Proposed resolution (September, 2013):

Change 13.9.2 [temp.inst] paragraph 13 as follows:

If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point, except that the scope in which a closure type is declared (7.5.5 [expr.prim.lambda]) — and therefore its associated namespaces — remain as determined from the context of the definition for the default argument. This analysis is called default argument instantiation. The instantiated default argument is then used as the argument of f.



1772. __func__ in a lambda body

Section: 7.5.5  [expr.prim.lambda]     Status: C++14     Submitter: Switzerland/Canada     Date: 2013-09-24

N3690 comment CH 5
N3690 comment CA 22

[Moved to DR at the February, 2014 meeting.]

It is not clear whether __func__ in the body of a lambda refers to the operator() of the closure class or to the containing function (if any). Since lambdas can appear in non-function scope, it would be preferable for them to refer to the closure class's operator().

Proposed resolution (September, 2013):

Change 7.5.5 [expr.prim.lambda] paragraph 7 as follows:

The lambda-expression's compound-statement yields the function-body... —end example] Further, a variable __func__ is implicitly defined at the beginning of the compound-statement of the lambda-expression, with semantics as described in 9.5.1 [dcl.fct.def.general].



1629. Can a closure class be a literal type?

Section: 7.5.5.2  [expr.prim.lambda.closure]     Status: C++14     Submitter: John Spicer     Date: 2012-02-22

N3690 comment ES 9

[Moved to DR at the February, 2014 meeting.]

The description of the characteristics of a closure class in 7.5.5 [expr.prim.lambda] leaves open the possibility that such a class might be a literal type, although it could also be a non-literal type. It would probably be good to specify that a closure class is not a literal type, in the interests of portability.

On a related note, it might be wise to remove the implementation freedom to make a closure class either a POD or not a POD; it seems only to be an invitation for portability problems with no real advantage.

Additional note, April, 2013:

Some have expressed the opinion that such portability issues are just “par for the course,” and that specifying these characteristics at this point might unnecessarily limit the ability to extend the language in desirable directions at some point in the future.

Proposed resolution (April, 2013) [superseded]:

Change 7.5.5 [expr.prim.lambda] paragraph 3 as follows:

...An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:

Notes from the September, 2013 meeting:

At the suggestion of EWG, it was decided that closure types should be specified as not literal, rather than being unspecified as in the April, 2013 proposed resolution. The issue has thus been returned to "drafting" status.

Proposed resolution (January, 2014):

Change 7.5.5 [expr.prim.lambda] paragraph 3 as follows:

The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type — called the closure type — whose properties are described below. This class type is not neither an aggregate (9.4.2 [dcl.init.aggr]) nor a literal type (6.8 [basic.types]). The closure type is declared...



1612. Implicit lambda capture and anonymous unions

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: C++14     Submitter: Michael Wong     Date: 2013-01-31

N3690 comment CA 17

[Moved to DR at the February, 2014 meeting.]

It is not clear from the description of capturing in 7.5.5 [expr.prim.lambda] whether an implicit capture resulting from the odr-use of a member of an anonymous union captures that member or the anonymous union, and there is implementation divergence. For example,

  int main() {
    static int result;
    struct A { int x; };
    struct B { int y; };
    union {
      A a; B b;
    };
    a.x = 1;
    [=]() mutable { a.x = 2; result = b.y; }();
    if (result == 1) return 0;
    throw 0;
  }

Notes from the April, 2013 meeting:

CWG decided that an attempt to capture a member of an anonymous union should be ill-formed.

Proposed resolution (September, 2013):

  1. Change 7.5.5 [expr.prim.lambda] paragraph 15 as follows:

  2. ...An array of runtime bound (9.3.4.5 [dcl.array]) or a member of an anonymous union shall not be captured by copy.
  3. Change 7.5.5 [expr.prim.lambda] paragraph 16 as follows:

  4. ...It is unspecified whether additional unnamed non-static data members are declared in the closure type for entities captured by reference. A member of an anonymous union shall not be captured by reference. [Note:...



1613. Constant expressions and lambda capture

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: C++14     Submitter: Michael Wong     Date: 2013-01-31

N3690 comment CA 16

[Moved to DR at the February, 2014 meeting.]

Since instances of a variable in constant expressions may be odr-uses, the ordering of:

may affect the semantics of a program such as the one below.

The transformation under 7.5.5 [expr.prim.lambda] paragraph 17 introduces uses of the this pointer of the operator() in its function-body. These instances of this are invalid under issue 1369 if the transformation is applied before the evaluation of the constant expressions. Without the resolution of issue 1369, another situation occurs where instances of this in the compound-statement are transformed into class member access expressions (see the initializations of addrEqA and addrEqB below).

Also, for the initialization of nonZero below, the expression fails to be a constant expression if the transformation is applied before constant expression evaluation.

Finally, the answer to the static assertion changes depending on whether the constant expression evaluation is performed before the transformation as opposed to after and whether the proposed resolution issue 1472 is enabled.

There appears to be implementation divergence regarding

Using explicit value captures is not a panacea, since the paragraph 17 transformations only apply to odr-uses. As a result of the resolution of issue 1472, if the reference r below happened to have been initialized with a constant expression, the value of its (modifiable) target is not captured; if the same target were specified in the initialization of the reference with a non-constant expression, its value would be captured.

  struct A {
    void foo();
  };

  struct LitType { int val; };
  constexpr int ceFunc(const LitType &x) { return x.val; }

  void A::foo() {
    constexpr LitType y = { 0 };
    static int z;
    int x, &r = z;
    [=] {
     constexpr bool addrEqA = &x == &x;         // ill-formed under issue 1369 after transformation
                                                // under paragraph 17
     constexpr bool addrEqB = &*this == &*this; // well-formed after transformation under N3290
                                                // paragraph 17
     constexpr bool nonZero = ceFunc(y);        // lvalue-to-rvalue conversion occurs only after
                                                // function invocation substitution; the closure member,
                                                // being not a variable, cannot be constexpr
     static_assert(&r != &z,
       "reference which could be captured by value found to alias target");
                                                // affected by issue 1472
    };
  }

Proposed resolution (September, 2013):

Add the following bullet to 7.7 [expr.const] paragraph 2:

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.9.1 [intro.execution]), would evaluate one of the following expressions:




1662. Capturing function parameter packs

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: C++14     Submitter: Michael Wong     Date: 2013-04-15

N3690 comment CA 20

[Moved to DR at the February, 2014 meeting.]

The example in 7.5.5 [expr.prim.lambda] paragraph 24 reads,

  template<class... Args>
  void f(Args... args) {
    auto lm = [&, args...] { return g(args...); };
    lm();
  }

However, it's not clear how this example squares with the requirements in paragraph 10,

The identifier in a simple-capture is looked up using the usual rules for unqualified name lookup (6.5.3 [basic.lookup.unqual]); each such lookup shall find a variable with automatic storage duration declared in the reaching scope of the local lambda expression.

since a function parameter pack is not a variable. Although the requirement might be met in a given specialization of the function template, is there a rule that relaxes it in the context of the function template definition?

Proposed resolution (September, 2013):

  1. Change 7.5.5 [expr.prim.lambda] paragraph 10 as follows:

  2. The identifier in a simple-capture is looked up using the usual rules for unqualified name lookup (6.5.3 [basic.lookup.unqual]); each such lookup shall find an entity. An entity that is designated by a simple-capture is said to be explicitly captured, and shall be this or a variable with automatic storage duration declared in the reaching scope of the local lambda expression. An entity (i.e. a variable or this) is said to be explicitly captured if it is found by this process.
  3. Change 13.7.4 [temp.variadic] paragraph 4 as follows:

  4. ...Pack expansions can occur in the following contexts:

    For the purpose of determining whether a parameter pack satisfies a rule regarding entities other than parameter packs, the parameter pack is considered to be the entity that would result from an instantiation of the pattern in which it appears.

    [Example:

    ...
  5. Change 13.7.4 [temp.variadic] paragraph 6 as follows:

  6. ...Each Ei is generated by instantiating the pattern and replacing each pack expansion parameter with its ith element. Such an element, in the context of the instantiation, is interpreted as follows:

    All of the Ei become elements in the enclosing list. [Note:...




1681. init-captures and nested lambdas

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: C++14     Submitter: Richard Smith     Date: 2013-05-14

[Applied to WP at the February, 2014 meeting.]

According to 7.5.5 [expr.prim.lambda] paragraph 10,

The identifier in a simple-capture is looked up using the usual rules for unqualified name lookup (6.5.3 [basic.lookup.unqual]); each such lookup shall find a variable with automatic storage duration declared in the reaching scope of the local lambda expression.

This does not permit a nested lambda to capture the non-static data member of the closure type introduced by an init-capture. A similar restriction applies to implicit capture in paragraph 12.

Presumably such captures should be allowed, capturing the non-static data member directly rather than the this pointer from the enclosing lambda's operator().

Proposed resolution (September, 2013):

This issue is resolved by the resolution of issue 1760.




1760. Access of member corresponding to init-capture

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: C++14     Submitter: United Kingdom     Date: 2013-09-23

N3690 comment GB 3

[Applied to WP at the February, 2014 meeting.]

The access of the non-static data member corresponding to an init-capture is not specified. The question would be moot if the member were unnamed like the other non-static data members of the closure class.

Proposed resolution (September, 2013):

  1. Change 7.5.5 [expr.prim.lambda] paragraph 11 as follows:
  2. For every init-capture a non-static data member named by the identifier of the init-capture is declared in the closure type. This member is not a bit-field and not mutable. The type of that member corresponds to the type of a hypothetical An init-capture behaves as if it declares and explicitly captures a variable declaration of the form “auto init-capture ;” whose declarative region is the lambda-expression's compound-statement, except that the variable name (i.e., the identifier of the init-capture) is replaced by a unique identifier.:

    [Note: This enables an init-capture like “x = std::move(x)”; the second “x” must bind to a declaration in the surrounding context. —end note] No entity is captured by an init-capture. Within the lambda-expressions lambda-declarator and compound-statement, the identifier in the init-capture hides any declaration of the same name in scopes enclosing the lambda-expression. [Example:...

  3. Change 7.5.5 [expr.prim.lambda] paragraph 15 as follows:

  4. An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that does not include an & is not of the form & identifier or & identifier initializer. For each entity...
  5. Change 7.5.5 [expr.prim.lambda] paragraph 18 as follows:

  6. Every id-expression within the compound-statement of a lambda-expression that is an odr-use (6.3 [basic.def.odr]) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type. [Note:...

This resolution also resolves issue 1681.




1376. static_cast of temporary to rvalue reference

Section: 7.6.1.9  [expr.static.cast]     Status: C++14     Submitter: Michael Wong     Date: 2011-08-17

N3690 comment CA 1

[Moved to DR at the February, 2014 meeting.]

In a declaration like

    T&& r = static_cast<T&&>(T());

it is not clear what the lifetime of the T temporary should be. According to 7.6.1.9 [expr.static.cast] paragraph 4, the static_cast is equivalent to a declaration of an invented temporary variable t. The lifetime of the temporary is extended to that of t, but it is not clear what that lifetime should be, nor if the subsequent binding of t to r would affect the lifetime of the original temporary. (See also issue 1568.)

Notes from the February, 2012 meeting:

The reference is bound to the xvalue result of the static_cast, so the lifetime of the temporary is not extended and this example results in a dangling reference.

Proposed resolution (February, 2012) [SUPERSEDED]:

Change 7.6.1.9 [expr.static.cast] paragraph 4 as follows:

Otherwise, an expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t (9.4 [dcl.init]). The effect...

Proposed resolution (September, 2013):

Change 7.6.1.9 [expr.static.cast] paragraph 3 as follows:

A glvalue, class prvalue, or array prvalue of type “cv1 T1” can be cast to type “rvalue reference to cv2 T2” if “cv2 T2” is reference-compatible with “cv1 T1” (9.4.4 [dcl.init.ref]). If the glvalue value is not a bit-field, the result refers to the object or the specified base class subobject thereof; otherwise, the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is applied to the bit-field and the resulting prvalue is used as the expression of the static_cast for the remainder of this section. If T2 is an inaccessible (11.8 [class.access]) or ambiguous (6.5.2 [class.member.lookup]) base class of T1, a program that necessitates such a cast is ill-formed.



1739. Conversion of floating point to enumeration

Section: 7.6.1.9  [expr.static.cast]     Status: C++14     Submitter: Ville Voutilainen     Date: 2013-08-18

[Moved to DR at the February, 2014 meeting.]

The specification of conversion from integral or enumeration type in 7.6.1.9 [expr.static.cast] paragraph 10 uses the phrase “explicitly converted,” while the description of conversion from floating point to enumeration omits the word “explicitly.” Presumably these should be harmonized.

Proposed resolution (January, 2014):

Change 7.6.1.9 [expr.static.cast] paragraph 10 as follows:

...A value of floating-point type can also be explicitly converted to an enumeration type...



1786. Effect of merging allocations on memory leakage

Section: 7.6.2.8  [expr.new]     Status: C++14     Submitter: United Kingdom     Date: 2013-09-28

N3690 comment GB 4

[Applied to WP at the February, 2014 meeting as part of document N3914.]

The strategy of merging allocations could turn a small memory leak into a big one. For example,

  class P {
    int x;
  };
  class Q {
  public:
    Q(){ throw 42; }
  private:
    int x[LARGE_NUMBER];
  };

  {
    P* p1 = new P();
    Q* q1 = new Q(); // bang :-( 
    // don't get here 
    delete q1;
    delete p1;
  }

Instead of just leaking the first allocation, this could result in leaking the combined allocation.

Notes from the September, 2013 meeting:

EWG was not so concerned with memory leaks, but there are problems with the current wording dealing with lifetime issues. In particular, the existing wording did not guarantee that all of the merged allocations would, in fact, be executed. The intended direction is to relax the strict lifetime containment requirement from the current wording but to ensure that all of the allocations and frees will be executed, which requires that the allocation and initialization of later objects be non-throwing.




1598. Criterion for equality of pointers to members

Section: 7.6.10  [expr.eq]     Status: C++14     Submitter: Richard Smith     Date: 2012-12-21

[Moved to DR at the February, 2014 meeting.]

According to 7.6.10 [expr.eq] paragraph 2, pointers to data members compare equal

if and only if they would refer to the same member of the same most derived object (6.7.2 [intro.object]) or the same subobject if indirection with a hypothetical object of the associated class type were performed.

This specification is overly constrained. For data members, most implementations simply compare the offsets of the members involved, violating the “only if” part of the specification. For example:

  struct A {};
  struct B : A { int x; };
  struct C : A { int x; };

  int A::*bx = (int(A::*))&B::x;
  int A::*cx = (int(A::*))&C::x;

  bool b1 = bx == cx;

The existing wording requires b1 to have the value false, even though the offsets of the members are the same. It would be better if the result of the comparison were unspecified unless the class containing the original member for the LHS is a base or derived class of the class containing the original member for the RHS.

Proposed resolution (January, 2014):

Change 7.6.10 [expr.eq] paragraph 3 as follows:

...Comparing pointers to members is defined as follows:




1732. Defining types in conditions and range-based for statements

Section: 8.5  [stmt.select]     Status: C++14     Submitter: Richard Smith     Date: 2013-08-08

[Moved to DR at the February, 2014 meeting.]

The interaction of various issue resolutions has inadvertently resulted in the loss of the prohibition of defining types in conditions (8.5 [stmt.select] paragraph 1) and in a range-based for (8.6 [stmt.iter] paragraph 1). Issue 686 addressed a patchwork of restrictions by banning type definitions in type-specifier-seqs. Issue 948 then changed the definition of condition to use a decl-specifier-seq instead of a type-specifier-seq in order to permit the constexpr specifier. A similar change was made for range-based for statements by issue 1204.

The restrictions against type definitions in these contexts should be restored.

Proposed resolution (January, 2014):

  1. Change 8.5 [stmt.select] paragraph 2 as follows:

  2. The rules for conditions apply both to selection-statements and to the for and while statements (8.6 [stmt.iter]). The declarator shall not specify a function or an array. The decl-specifier-seq shall not define a class or enumeration. If the auto type-specifier appears in the decl-specifier-seq, the type of the identifier being declared is deduced from the initializer as described in 9.2.9.7 [dcl.spec.auto].
  3. Change 8.6.5 [stmt.ranged] paragraph 2 as follows:

  4. In the decl-specifier-seq of a for-range-declaration, each decl-specifier shall be either a type-specifier or constexpr. The decl-specifier-seq shall not define a class or enumeration.



1767. Scoped enumeration in a switch statement

Section: 8.5.3  [stmt.switch]     Status: C++14     Submitter: Canada     Date: 2013-09-23

N3690 comment CA 8

[Moved to DR at the February, 2014 meeting.]

The description of the switch statement and case labels in 8.5.3 [stmt.switch] paragraph 2 apply integral promotions to the condition value and refer to the “promoted type” of the condition. However, the integral promotions (7.3.7 [conv.prom]) do not describe the result when they are applied to a scoped enumeration value.

Proposed resolution (September, 2013):

Change 8.5.3 [stmt.switch] paragraph 2 as follows:

The condition shall be of integral type, enumeration type, or class type. If of class type, the condition is contextually implicitly converted (7.3 [conv]) to an integral or enumeration type. Integral promotions are performed. If the (possibly converted) type is subject to integral promotions (7.3.7 [conv.prom]), the condition is converted to the promoted type. Any statement within the switch statement can be labeled with one or more case labels as follows:

where the constant-expression shall be a converted constant expression (7.7 [expr.const]) of the promoted adjusted type of the switch condition. No two of the case constants in the same switch shall have the same value after conversion to the promoted type of the switch condition.




1648. thread_local vs block extern declarations

Section: 9.2.2  [dcl.stc]     Status: C++14     Submitter: Richard Smith     Date: 2013-04-12

[Moved to DR at the September, 2013 meeting.]

According to 9.2.2 [dcl.stc] paragraph 4,

When thread_local is applied to a variable of block scope the storage-class-specifier static is implied if it does not appear explicitly.

This, presumably unintentionally, prohibits a declaration like

  void f() {
    extern thread_local int n;
  }

Proposed resolution (April, 2013):

When thread_local is applied to a variable of block scope the storage-class-specifier static is implied if it does not appear explicitly no other storage-class-specifier appears in the decl-specifier-seq.



1587. constexpr initialization and nested anonymous unions

Section: 9.2.6  [dcl.constexpr]     Status: C++14     Submitter: Daveed Vandvoorde     Date: 2012-11-16

[Moved to DR at the September, 2013 meeting.]

After the resolution of issue 1359, one of the requirements for constexpr constructors is:

if the class is a non-empty union, or for each non-empty anonymous union member of a non-union class, exactly one non-static data member shall be initialized;

This wording does not appear to handle nested anonymous unions. For example:

  struct S {
    union {
      union {
        int x = 1;
        float f;
      };
      void *p = nullptr;
    };
  };

Clearly here both S::x and S::p are initialized, but that does not appear to violate the new constraint.

Additional note (March, 2013):

It is not clear whether this example violates 11.5 [class.union] paragraph 5:

The member-specification of an anonymous union shall only define non-static data members. [Note: Nested types and functions cannot be declared within an anonymous union. —end note]

Is a nested anonymous union a “non-static data member” or a “nested type?”

See also issues 57, 1460, 1562, 1587, 1621, and 1623.

Proposed resolution (June, 2013):

Change 11.5 [class.union] paragraph 5 as follows:

...The member-specification of an anonymous union shall only define non-static data members. [Note: Nested types, anonymous unions, and functions cannot be declared within an anonymous union. —end note] The names of the members...



1595. Constructors “involved in” subobject initialization

Section: 9.2.6  [dcl.constexpr]     Status: C++14     Submitter: Daveed Vandevoorde     Date: 2012-12-19

[Moved to DR at the September, 2013 meeting.]

According to 9.2.6 [dcl.constexpr] paragraph 5

For a constexpr constructor, if no argument values exist such that after function invocation substitution, every constructor call and full-expression in the mem-initializers would be a constant expression (including conversions), the program is ill-formed; no diagnostic required.

However, paragraph 4 also says,

every constructor involved in initializing non-static data members and base class sub-objects shall be a constexpr constructor;

violation of which requires a diagnostic. The question is whether a constructor call appearing in a mem-initializer expression is “involved in” the initialization of X::m. Given the “no diagnostic required” status of constructor calls in paragraph 5, the intent of the “involved in” phrasing would appear to be referring to constructors of members with class types and of base-class subobjects, but the wording should be clarified. For example, in a constructor definition like

  constexpr X(): m(f(S())) { }

if S::S() is not constexpr, is a diagnostic required? For another example,

  struct S {
    constexpr S() {}
    S(int);
  };

  struct A { S s; };

  struct C {
    A x;
    constexpr C(): x{ 1 } {}
  };

Is S::S(int) “involved?”

Proposed resolution (August, 2013):

Change 9.2.6 [dcl.constexpr] paragraph 4 as follows:

...In addition, either its function-body shall be = delete, or it shall satisfy the following constraints:




1684. Static constexpr member functions for non-literal classes

Section: 9.2.6  [dcl.constexpr]     Status: C++14     Submitter: Richard Smith     Date: 2013-05-15

[Applied to WP at the February, 2014 meeting.]

The current wording of 9.2.6 [dcl.constexpr] paragraph 8 is:

The constexpr specifier has no effect on the type of a constexpr function or a constexpr constructor. The class of which a constexpr function is a member shall be a literal type (6.8 [basic.types]).

The previous version of this wording made clear that the restriction on the class type applied only to non-static member functions; consequently, the new formulation has inadvertently banned static constexpr member functions of non-literal classes.

Proposed resolution (November, 2013):

Change 9.2.6 [dcl.constexpr] paragraph 8 as follows:

The constexpr specifier has no effect on the type of a constexpr function or a constexpr constructor. The class of which a constexpr function is a member shall be a literal type (6.8 [basic.types]). [Example:...



1707. template in elaborated-type-specifier without nested-name-specifier

Section: 9.2.9.5  [dcl.type.elab]     Status: C++14     Submitter: Richard Smith     Date: 2013-06-27

[Moved to DR at the February, 2014 meeting.]

The grammar for elaborated-type-specifier in 9.2.9.5 [dcl.type.elab] reads, in part,

This allows use of the template keyword without a nested-name-specifier, e.g., struct template S<int>. This is inconsistent with other uses of the template keyword. It might be better to split the production in two and only allow the keyword following a nested-name-specifier, i.e.,

Proposed resolution (January, 2014):

Change the grammar in 9.2.9.5 [dcl.type.elab] as follows:




1674. Return type deduction for address of function

Section: 9.2.9.7  [dcl.spec.auto]     Status: C++14     Submitter: Richard Smith     Date: 2013-05-02

[Applied to WP at the February, 2014 meeting.]

The following example appears in 9.2.9.7 [dcl.spec.auto] paragraph 12:

  template <class T> auto f(T t) { return t; } // return type deduced at instantiation time
  typedef decltype(f(1)) fint_t;               // instantiates f<int> to deduce return type
  template<class T> auto f(T* t) { return *t; }
  void g() { int (*p)(int*) = &f; }            // instantiates both fs to determine return types,
                                               // chooses second

This is the desired behavior, but the current wording does not achieve that effect. One possible way this could work would be:

Proposed resolution (November, 2013):

Add the following as a new paragraph at the end of 13.10.3.3 [temp.deduct.funcaddr]:

A placeholder type (9.2.9.7 [dcl.spec.auto]) in the return type of a function template is a non-deduced context. If template argument deduction succeeds for such a function, the return type is determined from instantiation of the function body.



1417. Pointers/references to functions with cv-qualifiers or ref-qualifier

Section: 9.3.4.6  [dcl.fct]     Status: C++14     Submitter: Daniel Krügler     Date: 2011-11-17

[Moved to DR at the September, 2013 meeting.]

It is not sufficiently clear from the existing wording that pointers and references to function types containing cv-qualifiers or a ref-qualifier are not permitted and thus would result in a deduction failure if created during template argument substitution. Normative wording to that effect should be added to, e.g., 9.3.4.6 [dcl.fct].

Proposed resolution (October, 2012) [SUPERSEDED]:

  1. Change 9.3.4.2 [dcl.ptr] paragraph 4 as follows:

  2. [Note: There are no pointers to references Forming a pointer to reference type is ill-formed; see 9.3.4.3 [dcl.ref]. Forming a pointer to a function type that has cv-qualifiers or a ref-qualifier is ill-formed; see 9.3.4.6 [dcl.fct]. Since the address of a bit-field (11.4.10 [class.bit]) cannot be taken, a pointer can never point to a bit-field. —end note]
  3. Change 9.3.4.3 [dcl.ref] paragraph 6 as follows:

  4. If a typedef typedef-name (9.2.4 [dcl.typedef]), a type template-parameter (13.4.2 [temp.arg.type]), 13.2 [temp.param]) or a decltype-specifier (9.2.9.3 [dcl.type.simple]) denotes a type TR that is a reference to a type T, an attempt to create...
  5. Add the following as a new paragraph at the end of 9.3.4.3 [dcl.ref]:

  6. [Note: Forming a reference to a function type that has cv-qualifiers or a ref-qualifier is ill-formed; see 9.3.4.6 [dcl.fct]. —end note]
  7. Change 9.3.4.6 [dcl.fct] paragraph 6 as follows:

  8. A function type with a cv-qualifier-seq or a ref-qualifier (including by typedef-name (9.2.4 [dcl.typedef], 13.2 [temp.param]) or decltype-specifier (9.2.9.3 [dcl.type.simple])) shall only be part of appear as:

    [Example:

       typedef int FIC(int) const;
       FIC f;       // ill-formed: does not declare a member function
       struct S {
        FIC f;      // OK
       };
       FIC S::*pm = &S::f; // OK
    

    end example]

    The effect of a cv-qualifier-seq in a function declarator...

  9. Delete the following text from 9.3.4.6 [dcl.fct] paragraph 10 (the example is moved to paragraph 6, as indicated in the preceding change):

  10. A typedef of a function type whose declarator includes a cv-qualifier-seq shall be used only to declare the function type for a non-static member function, to declare the function type to which a pointer to member refers, or to declare the top-level function type of another function typedef declaration. [Example: ... —end example]

Additional note (March, 2013):

The issue is being returned to "review" status out of concern that 12.5 [over.built] paragraph 11 requires forming a reference to a function type that might have a cv-qualifier or ref-qualifier.

Proposed resolution (April, 2013):

  1. Change 9.3.4.2 [dcl.ptr] paragraph 4 as follows:

  2. [Note: There are no pointers to references Forming a pointer to reference type is ill-formed; see 9.3.4.3 [dcl.ref]. Forming a pointer to function type is ill-formed if the function type has cv-qualifiers or a ref-qualifier; see 9.3.4.6 [dcl.fct]. Since the address of a bit-field (11.4.10 [class.bit]) cannot be taken, a pointer can never point to a bit-field. —end note]
  3. Change 9.3.4.3 [dcl.ref] paragraph 6:

  4. If a typedef typedef-name (9.2.4 [dcl.typedef]), a type template-parameter (13.4.2 [temp.arg.type]), 13.2 [temp.param]) or a decltype-specifier (9.2.9.3 [dcl.type.simple]) denotes a type TR that is a reference to a type T, an attempt to create the type “lvalue reference to cv TR” creates the type “lvalue reference to T”, while an attempt to create the type “rvalue reference to cv TR" creates the type TR. [Example:...
  5. Add the following as a new paragraph at the end of 9.3.4.3 [dcl.ref]:

  6. [Note: Forming a reference to function type is ill-formed if the function type has cv-qualifiers or a ref-qualifier; see 9.3.4.6 [dcl.fct]. —end note]
  7. Change 9.3.4.6 [dcl.fct] paragraph 6 as follows:

  8. A function type with a cv-qualifier-seq or a ref-qualifier (including a type named by typedef-name (9.2.4 [dcl.typedef], 13.2 [temp.param])) shall appear only be part of as:

    [Example:

       typedef int FIC(int) const;
       FIC f;              // ill-formed: does not declare a member function
       struct S {
         FIC f;            // OK
       };
       FIC S::*pm = &S::f; // OK
    

    end example] The effect of a cv-qualifier-seq...

  9. Change 9.3.4.6 [dcl.fct] paragraph 10 as follows, moving the example to paragraph 6 as shown above:

  10. ...—end example] A typedef of a function type whose declarator includes a cv-qualifier-seq shall be used only to declare the function type for a non-static member function, to declare the function type to which a pointer to member refers, or to declare the top-level function type of another function typedef declaration. [Example: ... —end example]
  11. Change 12.5 [over.built] paragraph 11 as follows:

  12. For every quintuple (C1, C2, T, CV1, CV2), where C2 is a class type, C1 is the same type as C2 or is a derived class of C2, T is an object type or a function type, and CV1 and CV2 are cv-qualifier-seqs, there exist candidate operator functions of the form

    where CV12 is the union of CV1 and CV2. The return type is shown for exposition only; see 7.6.4 [expr.mptr.oper] for the determination of the operator's result type.




1716. When are default arguments evaluated?

Section: 9.3.4.7  [dcl.fct.default]     Status: C++14     Submitter: David Krauss     Date: 2013-07-17

[Moved to DR at the February, 2014 meeting.]

According to 9.3.4.7 [dcl.fct.default] paragraph 9,

Default arguments are evaluated each time the function is called.

Obviously, what was intended by this is that the default argument is evaluated only if no corresponding actual argument is provided, but this could be read as indicating that the default argument is evaluated and discarded by every function call.

Proposed resolution (January, 2014):

Change 9.3.4.7 [dcl.fct.default] paragraph 9 as follows:

Default arguments are A default argument is evaluated each time the function is called with no argument for the corresponding parameter. The order of evaluation...



1287. Direct initialization vs “implicit” conversion in reference binding

Section: 9.4.4  [dcl.init.ref]     Status: C++14     Submitter: Daniel Krügler     Date: 2011-04-06

[Moved to DR at the September, 2013 meeting.]

In 9.4.5 [dcl.init.list] paragraph 5, both the cases in which a reference can be bound to the result of a conversion function use the phrase “can be implicitly converted to...” This is confusing, as it could be read as excluding explicit conversion functions. However, that appears not to be the intent, as 12.2.2.7 [over.match.ref], which is cited in these cases, allows explicit conversion functions for direct-initialization.

Proposed resolution (August, 2011) [SUPERSEDED]:

Change the two indicated (not contiguous) sub-bullets of 9.4.4 [dcl.init.ref] paragraph 5 as follows:

Additional note, January, 2012:

Questions have been raised regarding the consistency of the treatment of class prvalues in this resolution with other types . The issue is thus being returned to "review" status for additional discussion.

Proposed resolution (February, 2012) [SUPERSEDED]:

  1. Change 9.4.4 [dcl.init.ref] paragraph 5 as follows:

  2. A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

  3. Change 12.2.2.7 [over.match.ref] paragraph 1 as follows:

  4. Under the conditions specified in 9.4.4 [dcl.init.ref], a reference can be bound directly to a glvalue or class prvalue that is the result of applying a conversion function...

Note from the April, 2013 meeting:

Because of concerns about slicing and performance in the February, 2012 proposed resolution, CWG decided to return to the August, 2011 proposed resolution and split off the discussion about class prvalues into issue 1650.

Proposed resolution (April, 2013):

Change the two indicated (not contiguous) sub-bullets of 9.4.4 [dcl.init.ref] paragraph 5 as follows:




1604. Double temporaries in reference initialization

Section: 9.4.4  [dcl.init.ref]     Status: C++14     Submitter: Nikolay Ivchenkov     Date: 2013-01-10

N3690 comment CA 30

[Moved to DR at the February, 2014 meeting.]

Bullet 2 sub-bullet 2 of 9.4.4 [dcl.init.ref] paragraph 5 says,

Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (9.4 [dcl.init]). The reference is then bound to the temporary.

It is not clear what “using the rules for a non-reference copy-initialization” means. If it means that the temporary is initialized as if it were a standalone variable, the last bullet of 9.4 [dcl.init] paragraph 17 could apply:

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 12.2.2.5 [over.match.copy], and the best one is chosen through overload resolution (12.2 [over.match]). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type. The temporary is a prvalue. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization.

That is, for an example like

  struct X {
    X(int) {}
      X(X const &) = delete;
    };

  void f() {
    X const &x = 0;
  }

the specification requires creation of a temporary X(0), copying that tempoary to another temporary (subject to copy elision), and binding the reference to that second temporary. In the example above, because the copy constructor is deleted, the example is ill-formed, although current implementations fail to diagnose it as an error.

Is this intended?

Proposed resolution (September, 2013):

  1. Change the last bullet of 9.4.4 [dcl.init.ref] paragraph 5, breaking it into sub-bullets, and the subsequent example as follows:

  2. [Example:

      struct Banana { };
      struct Enigma { operator const Banana(); };
      void enigmatic() {
        typedef const Banana ConstBanana;
        Banana &&banana1 = ConstBanana(); // ill-formed
        Banana &&banana2 = Enigma();      // ill-formed
      }
    
      const double& rcd2 = 2;             // rcd2 refers to temporary with value 2.0
      ...
    
  3. Change 12.2.2.5 [over.match.copy] paragraph 1 as follows:

  4. Under the conditions specified in 9.4 [dcl.init], as part of a copy-initialization of an object of class type, a user-defined conversion can be invoked to convert an initializer expression to the type of the object being initialized. Overload resolution is used to select the user-defined conversion to be invoked. [Note: The conversion performed for indirect binding to a reference to a possibly cv-qualified class type is determined in terms of a corresponding non-reference copy-initialization. —end note] Assuming that



1508. Template initializer-list constructors

Section: 9.4.5  [dcl.init.list]     Status: C++14     Submitter: Daveed Vandevoorde     Date: 2012-06-06

[Moved to DR at the September, 2013 meeting.]

Can a constructor template ever be an initializer-list constructor without std::initializer_list (or an alias template specialization for it) appearing in the template signature? E.g., is there any way that the constructor in:

  struct S {
    template<typename T> S(T);
  };

can be an initializer-list constructor?

Proposed resolution (October, 2012) [superseded]:

Modify 9.4.5 [dcl.init.list] paragraph 2 as follows:

A constructor is an initializer-list constructor if its first parameter is of type std::initializer_list<E> or reference to possibly cv-qualified std::initializer_list<E> for some type E, and either there are no other parameters or else all other parameters have default arguments (9.3.4.7 [dcl.fct.default]). [Note: Initializer-list constructors are favored over other constructors in list-initialization (12.2.2.8 [over.match.list]). Given a class C, a constructor template such as template<class T> C(T) is never instantiated to produce an initializer-list constructor, because an initializer list argument causes the corresponding parameter to be a non-deduced context (13.10.3.2 [temp.deduct.call]).end note] The template std::initializer_list is not predefined;...

Additional note (January, 2013):

The wording of the new note needs to be adjusted, because such a constructor template might have a default template argument that is a specialization of std::initializer_list. For example:

  struct D {
    template<typename T = std::initializer_list<int>> D(T);
  };
  D d{{1, 2, 3}};

Proposed resolution (June, 2013):

Change the note in 9.4.5 [dcl.init.list] paragraph 2 as follows:

[Note: Initializer-list constructors are favored over other constructors in list-initialization (12.2.2.8 [over.match.list]). Passing an initializer list as the argument to the constructor template template<class T> C(T) of a class C does not create an initializer-list constructor, because an initializer list argument causes the corresponding parameter to be a non-deduced context (13.10.3.2 [temp.deduct.call]).end note]



1778. exception-specification in explicitly-defaulted functions

Section: 9.5.2  [dcl.fct.def.default]     Status: C++14     Submitter: USA     Date: 2013-09-25

N3690 comment US 23

[Moved to DR at the February, 2014 meeting.]

According to 9.5.2 [dcl.fct.def.default] paragraph 2,

An explicitly-defaulted function may be declared constexpr only if it would have been implicitly declared as constexpr, and may have an explicit exception-specification only if it is compatible (14.5 [except.spec]) with the exception-specification on the implicit declaration.

The requirement for exception-specifications has unfortunate consequences for the standard library component atomic, as described in LWG issue 2165: the component cannot be used with a T unless T is nothrow default constructible, even if the std::atomic<T> variable is never default initialized.

Proposed resolution (September, 2013):

Change 9.5.2 [dcl.fct.def.default] paragraphs 2 and 3 as follows:

An explicitly-defaulted function may be declared constexpr only if it would have been implicitly declared as constexpr, and may have an explicit exception-specification only if it is compatible (14.5 [except.spec]) with the exception-specification on the implicit declaration. If a function is explicitly defaulted on its first declaration,

If a function that is explicitly defaulted has an explicit exception-specification that is not compatible (14.5 [except.spec]) with the exception-specification on the implicit declaration, then

[Example:

  struct S {
    constexpr S() = default;            // ill-formed: implicit S() is not constexpr
    S(int a = 0) = default;             // ill-formed: default argument
    void operator=(const S&) = default; // ill-formed: non-matching return type
    ~S() throw(int) = default;          // ill-formed deleted: exception specification does not match
  private:
    int i;
    S(S&);                              // OK: private copy constructor
  };
  S::S(S&) = default;                   // OK: defines copy constructor

end example]

Additional note, January, 2014:

The proposed resolution appears to have the undesirable implication that a special member function could become deleted after the class is complete. For example, given

  struct S {
    S() noexcept(false) = default;
  };

we need to check that the explicit exception specification is compatible with the one on the implicit declaration. After the resolution of issue 1330, the class is regarded as complete within exception-specifications, per 11.4 [class.mem] paragraph 2. This implies that the explicit exception-specification can only be checked once the class is complete.

The issue has been returned to "review" status to allow discussion of this concern.




1618. Gratuitously-unsigned underlying enum type

Section: 9.7.1  [dcl.enum]     Status: C++14     Submitter: Daniel Krügler     Date: 2013-02-03

[Moved to DR at the September, 2013 meeting.]

In an enumeration whose underlying type is not fixed, the type of the first enumerator is unspecified if it has no initializer, meaning that an implementation could choose either a signed or an unsigned type. As a result, the values of one and two in this example could be either -1 and 0 or very large unsigned numbers:

  enum { zero, one = zero -1, two };

It would be better if 9.7.1 [dcl.enum] paragraph 5 specified the type of the first enumerator as a signed type.

Proposed resolution (August, 2013):

Change 9.7.1 [dcl.enum] paragraph 5 as follows:

...If the underlying type is not fixed, the type of each enumerator is the type of its initializing value:




1765. Overflow of enumeration used as enumerator value

Section: 9.7.1  [dcl.enum]     Status: C++14     Submitter: Canada     Date: 2013-09-23

N3690 comment CA 7

[Moved to DR at the February, 2014 meeting.]

Regarding the value of an enumerator whose enumeration's underlying type is not fixed, 9.7.1 [dcl.enum] paragraph 5 says,

the type of the initializing value is the same as the type of the initializing value of the preceding enumerator unless the incremented value is not representable in that type, in which case the type is an unspecified integral type sufficient to contain the incremented value.

It is not clear how this is to be applied when the preceding enumerator value is given by an enumerator whose value is the largest of its enumeration's values, and there is implementation variance on this point.

Proposed resolution (September, 2013):

Change 9.7.1 [dcl.enum] paragraph 5 as follows:

...If the underlying type is fixed, the type of each enumerator enumerator prior to the closing brace is the underlying type and the constant-expression in the enumerator-definition shall be a converted constant expression of the underlying type (7.7 [expr.const]); if the initializing value of an enumerator cannot be represented by the underlying type, the program is ill-formed. If the underlying type is not fixed, the type of each enumerator prior to the closing brace is the type of its initializing value determined as follows:




1551. Wording problems in using-declaration specification

Section: 9.9  [namespace.udecl]     Status: C++14     Submitter: Richard Smith     Date: 2012-09-06

[Moved to DR at the September, 2013 meeting.]

There are at least a couple of problems with the current wording of 9.9 [namespace.udecl]. First is the use of the word “entity” to describe what the using-declaration represents. For example, in paragraph 1,

If a using-declaration names a constructor (6.5.5.2 [class.qual]), it implicitly declares a set of constructors in the class in which the using-declaration appears (_N4527_.12.9 [class.inhctor]); otherwise the name specified in a using-declaration is a synonym for the name of some entity declared elsewhere.

An overload set of functions and function templates is not an “entity,” according to 6.1 [basic.pre] paragraph 3. A better phrasing might be something like, “...a synonym for a set of declarations in a different declarative region.”

The other problem is in paragraph 11:

The entity declared by a using-declaration shall be known in the context using it according to its definition at the point of the using-declaration. Definitions added to the namespace after the using-declaration are not considered when a use of the name is made.

This has the same problem with use of the term “entity,” and it also refers to “definitions” when presumably it means “declarations.”

Proposed resolution (October, 2012) [superseded]:

  1. Add the following as a new paragraph after 6.4.2 [basic.scope.pdecl] paragraph 3:

  2. The point of declaration for a class or class template first declared by a class-specifier is immediately after the identifier or simple-template-id (if any) in its class-head ( Clause 11 [class]). The point of declaration for an enumeration is immediately after the identifier (if any) in either its enum-specifier (9.7.1 [dcl.enum]) or its first opaque-enum-declaration (9.7.1 [dcl.enum]), whichever comes first. The point of declaration of an alias or alias template immediately follows the type-id to which the alias refers.

    The point of declaration of a using-declaration that does not name a constructor is immediately after the using-declaration (9.9 [namespace.udecl]).

  3. Change 9.9 [namespace.udecl] paragraph 1 as follows:

  4. ...If a using-declaration names a constructor (6.5.5.2 [class.qual]), it implicitly declares a set of constructors in the class in which the using-declaration appears (_N4527_.12.9 [class.inhctor]); otherwise the name specified in a using-declaration is a synonym for the name of some entity declared elsewhere a set of declarations in another namespace or class.
  5. Change 9.9 [namespace.udecl] paragraph 11 as follows:

  6. The entity declared by a using-declaration shall be known in the context using it according to its definition at the point of the using-declaration. Definitions Declarations added to the namespace after the using-declaration are not considered when a use of the name is made. [Note: Thus, additional overloads and default arguments (9.3.4.7 [dcl.fct.default]) added after the using-declaration are ignored, but template specializations (13.7.6 [temp.spec.partial], 13.9.4 [temp.expl.spec]) are considered. —end note] [Example:...

Additional note (October, 2012):

The note added by this resolution to 9.9 [namespace.udecl] paragraph 11 regarding the treatment of default arguments directly contradicts the normative statement of 9.3.4.7 [dcl.fct.default] paragraph 9:

When a declaration of a function is introduced by way of a using-declaration (9.9 [namespace.udecl]), any default argument information associated with the declaration is made known as well. If the function is redeclared thereafter in the namespace with additional default arguments, the additional arguments are also known at any point following the redeclaration where the using-declaration is in scope.

The issue has been returned to "review" status for reconciliation of these two passages.

Proposed resolution (June, 2013):

  1. Insert a new paragraph following 6.4.2 [basic.scope.pdecl] paragraph 3, as follows

  2. ...whichever comes first. The point of declaration of an alias or alias template immediately follows the type-id to which the alias refers.

    The point of declaration of a using-declaration that does not name a constructor is immediately after the using-declaration (9.9 [namespace.udecl]).

    The point of declaration for an enumerator...

  3. Change 9.9 [namespace.udecl] paragraph 1 as follows:

  4. ...If a using-declaration names a constructor (6.5.5.2 [class.qual]), it implicitly declares a set of constructors in the class in which the using-declaration appears (_N4527_.12.9 [class.inhctor]); otherwise the name specified in a using-declaration is a synonym for the name of some entity declared elsewhere a set of declarations in another namespace or class.
  5. Change 9.9 [namespace.udecl] paragraph 11 as follows:

  6. The entity declared by a using-declaration shall be known in the context using it according to its definition at the point of the using-declaration. Definitions Members added to the namespace after the using-declaration are not considered when a use of the name is made. [Note: Thus, additional overloads added after the using-declaration are ignored, but default function arguments (9.3.4.7 [dcl.fct.default]), default template arguments (13.2 [temp.param]), and template specializations (13.7.6 [temp.spec.partial], 13.9.4 [temp.expl.spec]) are considered. —end note] [Example:



1689. Syntactic nonterminal for operand of alignas

Section: 9.12.1  [dcl.attr.grammar]     Status: C++14     Submitter: Richard Smith     Date: 2013-05-26

[Moved to DR at the February, 2014 meeting.]

Issue 1323 dealt with correcting the specification of the operand of alignas, which was originally given as the nonexistent term alignment-expression. It was corrected editorially to match the use of assignment-expression in 9.12.2 [dcl.align].

In 9.12.2 [dcl.align] paragraph 2, the expression is semantically constrained to be an integral constant expression. Since a constant-expression is syntactically a conditional-expression rather than an assignment-expression, it would probably make sense to change the syntactic nonterminal for the operand of alignas to be either a constant-expression or a conditional-expression.

Proposed resolution (November, 2013):

  1. Change 9.12.1 [dcl.attr.grammar] paragraph 1 as follows:

  2. Change 9.12.2 [dcl.align] paragraph 2 as follows:

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




1660. member-declaration requirements and unnamed bit-fields

Section: 11.4  [class.mem]     Status: C++14     Submitter: Richard Smith     Date: 2013-04-15

[Moved to DR at the February, 2014 meeting.]

According to 11.4 [class.mem] paragraph 1,

Except when used to declare friends (11.8.4 [class.friend]) or to introduce the name of a member of a base class into a derived class (9.9 [namespace.udecl]), member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class.

Unnamed bit-fields (11.4.10 [class.bit] paragraph 2) are described as not being members, and they obviously do not declare a member name; presumably they should therefore be included among the exceptions to this rule.

Additional note (October, 2013):

Curiously, the exemption for an unnamed bit-field not introducing names is in 9.1 [dcl.pre] paragraph 3, referring to a simple-declaration. However, a simple-declaration is not a member-declaration and thus does not apply.

Proposed resolution (November, 2013):

  1. Change 9.1 [dcl.pre] paragraph 3 as follows:

  2. ...In such cases, and except for the declaration of an unnamed bit-field (11.4.10 [class.bit]), the decl-specifier-seq shall introduce one or more names into the program, or shall redeclare a name introduced by a previous declaration. [Example:...
  3. Change 11.4 [class.mem] paragraph 1 as follows:

  4. ...Except when used to declare friends (11.8.4 [class.friend]), to declare an unamed bit-field (11.4.10 [class.bit]), or to introduce the name of a member of a base class into a derived class (9.9 [namespace.udecl]), member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class. A member shall not...



1693. Superfluous semicolons in class definitions

Section: 11.4  [class.mem]     Status: C++14     Submitter: Richard Smith     Date: 2013-05-29

[Moved to DR at the February, 2014 meeting.]

The grammar in 11.4 [class.mem] and 9.5.1 [dcl.fct.def.general] paragraph 1 are (in part):

This leads to the following curiosity:

  struct A {
    void f1() = delete;   // #1, ok
    void f2() = delete;;  // #2, ok
    void f3() = delete;;; // #3, error, extraneous semicolon
  };

This could be addressed by moving the semicolon into the productions for function-body for the non-default/delete forms or by adding empty-declaration to the list of member-declaration productions, as is done with namespace-scope declarations.

Proposed resolution (November, 2013):

  1. Change the grammar in 11.4 [class.mem] as follows:

  2. Change 11.4 [class.mem] paragraph 1 as follows:

  3. ...Except when used to declare friends (11.8.4 [class.friend]) or to introduce the name of a member of a base class into a derived class (9.9 [namespace.udecl]), or when the declaration is an empty-declaration, member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class.



1611. Deleted default constructor for abstract class

Section: 11.4.5  [class.ctor]     Status: C++14     Submitter: Ville Voutilainen     Date: 2013-01-31

[Moved to DR at the February, 2014 meeting.]

Bullet 6 of 11.4.5 [class.ctor] paragraph 5 gives an abstract class a deleted default constructor when the virtual base has no default constructor, even though the abstract class's default constructor can never construct the virtual base class subobject. This seems parallel to the case described in issue 257 for mem-initializers. Should a similar accommodation be made to avoid deleted default constructors in abstract classes?

Notes from the April, 2013 meeting:

CWG agreed that a virtual base class should not cause an abstract class's default constructor to be defined as deleted.

Proposed resolution (August, 2013) [superseded]:

Change 11.4.5 [class.ctor] paragraph 4 as follows:

...A defaulted default constructor for class X is defined as deleted if:

Proposed resolution (November, 2013):

This issue is resolved by the resolution of issue 1658.




1658. Deleted default constructor for abstract class via destructor

Section: 11.4.5  [class.ctor]     Status: C++14     Submitter: Richard Smith     Date: 2013-08-26

[Moved to DR at the February, 2014 meeting.]

While reviewing the resolution of issue 1611, it was noticed that the final bullet of 11.4.5 [class.ctor] paragraph 4 has a similar issue:

...A defaulted default constructor for class X is defined as deleted if:

Presumably destructors for virtual bases of abstract classes should not be considered in making this determination.

A question was also raised regarding whether odr-use is correctly defined for destructors of virtual bases of abstract classes. 6.3 [basic.def.odr] paragraph 3 simply refers to 11.4.7 [class.dtor], where the relevant passage (paragraph 8) reads,

After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X's direct non-variant non-static data members, the destructors for X's direct base classes and, if X is the type of the most derived class (11.9.3 [class.base.init]), its destructor calls the destructors for X's virtual base classes.

It could be argued, particularly in light of the reference to 11.9.3 [class.base.init], that this is clear enough that the destructor for an abstract class does not invoke destructors for its virtual bases, but a note to that effect might be helpful.

Proposed resolution (November, 2013):

  1. Add the following as a new paragraph at the end of 11.4.4 [special]:

  2. For a class, its non-static data members, its non-virtual direct base classes, and, if the class is not abstract (11.7.4 [class.abstract]), its virtual base classes are called its potentially constructed subobjects.
  3. Change 11.4.5 [class.ctor] paragraph 4 as follows:

  4. ...A defaulted default constructor for class X is defined as deleted if:

  5. Change 11.4.7 [class.dtor] paragraph 5 as follows:

  6. A defaulted destructor for a class X is defined as deleted if:

  7. Change 11.9.3 [class.base.init] paragraph 8 as follows:

  8. In a non-delegating constructor, if a given non-static data member or base class potentially constructed subobject is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (11.7.4 [class.abstract]), then...
  9. Change 11.9.3 [class.base.init] paragraph 10 as follows:

  10. In a non-delegating constructor, the destructor for each direct or virtual base class and for each non-static data member potentially constructed subobject of class type is potentially invoked (11.4.7 [class.dtor]). [Note: This provision ensures that destructors can be called for fully-constructed sub-objects in case an exception is thrown (14.3 [except.ctor]). —end note]
  11. Change 11.4.5.3 [class.copy.ctor] paragraph 8, replacing the bulleted list with a single sentence, as follows:

  12. The implicitly-declared copy constructor for a class X will have the form

    if each potentially constructed subobject

    Otherwise...

  13. Change 11.4.5.3 [class.copy.ctor] paragraph 11 as follows:

  14. An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (9.5.3 [dcl.fct.def.delete]) if X has:

  15. Change 11.4.5.3 [class.copy.ctor] paragraph 14 as follows:

  16. Before the defaulted copy/move constructor for a class is implicitly defined, all non-user-provided copy/move constructors for its direct and virtual base classes and its non-static data members potentially constructed subobjects shall have been implicitly defined. [Note: An implicitly-declared copy/move constructor has an exception-specification (14.5 [except.spec]). —end note]
  17. Change 11.4.5.3 [class.copy.ctor] paragraph 23 as follows:

  18. A defaulted copy/move assignment operator for class X is defined as deleted if X has:

This resolution also resolves issue 1611.




1344. Adding new special member functions to a class via default arguments

Section: 11.4.5.3  [class.copy.ctor]     Status: C++14     Submitter: Sean Hunt     Date: 2011-08-16

N3690 comment CA 29

[Moved to DR at the February, 2014 meeting.]

If default arguments added in the out-of-class definition of a constructor make it a special member function, this can affect the overload resolution and thus the implicit exception specification and the triviality of an implicitly-declared special member function in a derived class.

See also issue 1496, which should also be addressed by the resolution of this issue.

Notes from the September, 2013 meeting:

It was decided to resolve this issue separately from issue 1496, which is now decoupled from this issue.

Proposed resolution (September, 2013):

  1. Change 9.3.4.7 [dcl.fct.default] paragraph 6 as follows:

  2. Except for member functions of class templates, the default arguments in a member function definition that appears outside of the class definition are added to the set of default arguments provided by the member function declaration in the class definition; the program is ill-formed if a default constructor (11.4.5 [class.ctor]), copy or move constructor, or copy or move assignment operator (11.4.5.3 [class.copy.ctor]) is so declared. Default arguments for a member function of a class template shall be specified on the initial declaration of the member function within the class template. [Example:...
  3. Delete the following from 11.4.5.3 [class.copy.ctor] paragraph 7:

  4. ...The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor. Thus, for the class definition

      struct X {
        X(const X&, int);
      };
    

    a copy constructor is implicitly-declared. If the user-declared constructor is later defined as

      X::X(const X& x, int i =0) { /* ... */ }
    

    then any use of X's copy constructor is ill-formed because of the ambiguity; no diagnostic is required.




1493. Criteria for move-construction

Section: 11.4.5.3  [class.copy.ctor]     Status: C++14     Submitter: John Spicer     Date: 2012-04-02

N3690 comment CA 23

[Moved to DR at the February, 2014 meeting.]

The decision in 11.4.5.3 [class.copy.ctor] paragraph 32 on whether or not a copy must be replaced by a move is phrased largely in terms of when copy elision can be performed, as described in the preceding paragraph. In particular, the fourth bullet of paragraph 31 applies

when the exception-declaration of an exception handler ( Clause 14 [except]) declares an object of the same type (except for cv-qualification) as the exception object (14.2 [except.throw]), the copy/move operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration.

The determination of “when the meaning of the program will be unchanged” is, in the general case, not practical. Copy elision is optional, meaning that the compiler can simply choose not to perform it if the determination is too difficult. Choosing whether to do a copy or a move, however, is mandatory, even if the copy is elided, and such a broad criterion is not appropriate. Probably the best way to address this problem would be to eliminate exception-declarations as a possible target for move-construction.

(See also issue 1579.)

Proposed resolution (September, 2013):

Change 11.4.5.3 [class.copy.ctor] paragraphs 31 and 32 as follows:

...This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

[Example:...

When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If the first overload resolution fails or was not performed, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [Note:...




1579. Return by converting move constructor

Section: 11.4.5.3  [class.copy.ctor]     Status: C++14     Submitter: Jeffrey Yasskin     Date: 2012-10-23

N3690 comment US 13

[Moved to DR at the February, 2014 meeting.]

Currently the conditions for moving from an object returned from a function are tied closely to the criteria for copy elision, which requires that the type of the object being returned be the same as the return type of the function. Another possibility that should be considered is to allow something like

  optional<T> foo() {
    T t;
    ...
    return t;
  }

and allow optional<T>::optional(T&&) to be used for the initialization of the return type. Currently this can be achieved explicitly by use of std::move, but it would be nice not to have to remember to do so.

Similarly, the current rules apply only to complete objects; it could make sense to allow moving from subobjects of local objects.

(See also issue 1493 for other questions about the criteria for moving from a returned object.)

Rationale (April, 2013):

CWG felt that this suggestion should be considered in a broader context and was thus more appropriate for EWG.

Additional note (September, 2013):

Returned to "open" status in light of CD National Body comment.

Proposed resolution (September, 2013):

Change 11.4.5.3 [class.copy.ctor] paragraph 32 as follows:

When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails...



1593. “Parameter type” of special member functions

Section: 11.4.5.3  [class.copy.ctor]     Status: C++14     Submitter: Richard Smith     Date: 2012-12-03

[Moved to DR at the September, 2013 meeting.]

Paragraphs 12 and 25 of 11.4.5.3 [class.copy.ctor] both say that the function

is trivial if it is not user-provided, its declared parameter type is the same as if it had been implicitly declared, and...

However, a non-user-provided function might have more than one parameter if default arguments are used. The phrasing would be better as something like “its parameter-type-list is equivalent to the parameter-type-list of an implicit declaration.” (For consistency, the same phrasing should be used in 11.4.5 [class.ctor] paragraph 5. )

Proposed resolution (June, 2013):

  1. Change 11.4.5.3 [class.copy.ctor] paragraph 12 as follows:

  2. A copy/move constructor for class X is trivial if it is not user-provided, its declared parameter type is the same as if it had been implicitly declared parameter-type-list is equivalent to the parameter-type-list of an implicit declaration, and if...
  3. Change 11.4.5.3 [class.copy.ctor] paragraph 25 as follows:

  4. A copy/move assignment operator for class X is trivial if it is not user-provided, its declared parameter type is the same as if it had been implicitly declared parameter-type-list is equivalent to the parameter-type-list of an implicit declaration, and if...



1514. Ambiguity between enumeration definition and zero-length bit-field

Section: 11.4.10  [class.bit]     Status: C++14     Submitter: John Spicer     Date: 2012-07-03

[Moved to DR at the September, 2013 meeting.]

Consider an example like:

  struct S {
    typedef int T;
    enum E : T {};
    enum E : T {};    // #1
  };

The declaration at #1 is ambiguous: it could either be an invalid redefinition of enum E or a zero-length bit-field of type enum E (since T{} is zero-valued constant expression). The current Standard does not appear to have a rule to disambiguate the two.

Proposed resolution (October, 2012) [superseded]:

Change 9.7.1 [dcl.enum] paragraph 1 as follows:

...the attributes in that attribute-specifier-seq are thereafter considered attributes of the enumeration whenever it is named. In a member-specification, an ambiguity can arise from the similarity between the declaration of an enumeration with an enum-base and the declaration of a zero-length unnamed bit-field of enumeration type. The ambiguity appears as a choice between an enum-specifier and a member-declaration for a bit-field. The resolution is that a : following enum identifier is parsed as part of an enum-base. [Example:

  struct S {
    enum E : int {};
    enum E : int {};  // error: redeclaration of enumeration
  };

end example]

Notes from the April, 2013 meeting:

The ambiguity does not occur only with an empty set of braces but also when there is a single identifier that could be taken as the name of a constant in a containing scope or the declaration of an enumerator.

The resolution above sounds as if it is to be applied only if an ambiguity occurs; it should instead be always applied.

Proposed resolution (June, 2013):

Change 9.7.1 [dcl.enum] paragraph 1 as follows:

...the attributes in that attribute-specifier-seq are thereafter considered attributes of the enumeration whenever it is named. A : following “enum identifier” is parsed as part of an enum-base. [Note: This resolves a potential ambiguity between the declaration of an enumeration with an enum-base and the declaration of an unnamed bit-field of enumeration type. [Example:

   struct S {
     enum E : int {};
     enum E : int {};  // error: redeclaration of enumeration
   };

end example] —end note]




1460. What is an empty union?

Section: 11.5  [class.union]     Status: C++14     Submitter: Jason Merrill     Date: 2012-02-08

[Moved to DR at the September, 2013 meeting.]

When a similar question was raised in issue 413, the resolution was to remove the use of the term. The resolution of issue 1359 has now reintroduced the concept of an “empty” union, so there is once again the need to define it.

(See also issues 1562 and 1622.)

Proposed resolution (February, 2013) [superseded]:

Change 11.5 [class.union] paragraph 2 as follows:

...At most one non-static data member of a union may have a brace-or-equal-initializer. A union is an empty union if it has no non-static data members. [Note: If any...

Additional note (March, 2013):

The question was raised as to whether an example like

  union A {
    union {};
    union {};
    constexpr A() {}
  };
  A a = A();

is well-formed, which hinges on the question of whether A is an “empty union,” per 9.2.6 [dcl.constexpr] paragraph 4 bullet 5:

Must one of the empty anonymous union members be initialized for A's constructor to be constexpr?

The issue is being returned to "review" status for discussion of this point.

See also issues 1562, 1587, 1621, and 1623.

Proposed resolution (August, 2013):

  1. Change 9.2.6 [dcl.constexpr] paragraph 4 as follows:

  2. ...In addition, either its function-body shall be = delete, or it shall satisfy the following constraints:

  3. Change 11.5 [class.union] paragraph 2 as follows:

  4. A union can have member functions (including constructors and destructors), but not virtual (11.7.3 [class.virtual]) functions. A union shall not have base classes. A union shall not be used as a base class. If a union contains a non-static data member of reference type the program is ill-formed. At most one non-static data member of a union may have a brace-or-equal-initializer. [Note: If any non-static data member...
  5. Change 11.5 [class.union] paragraph 8 as follows:

  6. A union-like class is a union or a class that has an anonymous union as a direct member. A union-like class X has a set of variant members. If X is a union its variant members are the non-static data members; otherwise, its variant members are the non-static data members of all anonymous unions that are members of X. If X is a union, a non-static data member of X that is not an anonymous union is a variant member of X. In addition, a non-static data member of an anonymous union that is a member of X is also a variant member of X. At most one variant member of a union may have a brace-or-equal-initializer. [Example:

      union U {
        int x = 0;
        union { };
        union {
          int z;
          int y = 1; // error: initialization for second variant member of U
        };
      };
    

    end example]

  7. Change 11.9.3 [class.base.init] paragraph 8 as follows:

  8. In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (11.7.4 [class.abstract]), then

(This resolution also resolves issue 1562.)




1562. Non-static data member initializers and union ctor-initializer

Section: 11.9.3  [class.base.init]     Status: C++14     Submitter: Richard Smith     Date: 2012-10-01

[Moved to DR at the September, 2013 meeting.]

The following example is ill-formed:

  union U {
    int a = 0;
    float f;
    U() {}               // ok, a initialized to 0
    U(float f) : f(f) {} // error, constructor initializes both a and f
  };

because of 11.9.3 [class.base.init] paragraph 8:

An attempt to initialize more than one non-static data member of a union renders the program ill-formed.

In non-union classes, a mem-initializer takes precedence over a non-static data member initializer, per paragraph 9:

If a given non-static data member has both a brace-or-equal-initializer and a mem-initializer, the initialization specified by the mem-initializer is performed, and the non-static data member's brace-or-equal-initializer is ignored.

It would be reasonable to treat union mem-initializers in an analogous fashion.

(See also issue 1460.)

Proposed resolution (March, 2013) [superseded]:

Change 11.9.3 [class.base.init] paragraph 8 as follows:

In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (11.7.4 [class.abstract]), then

[Note: this wording was reviewed during the 2013-03-25 teleconference.]

See also issues 1460, 1587, 1621, and 1623.

Proposed resolution (August, 2013):

This issue is resolved by the resolution of issue 1460.




1649. Error in the syntax of mem-initializer-list

Section: 11.9.3  [class.base.init]     Status: C++14     Submitter: Daniel Krügler     Date: 2013-04-13

[Moved to DR at the September, 2013 meeting.]

The grammar in 11.9.3 [class.base.init] paragraph 1 for mem-initializer-list is incorrect:

The ellipsis in the second production should be on mem-initializer, not on mem-initializer-list.

Proposed resolution (August, 2013):

Change the grammar in 11.9.3 [class.base.init] paragraph 1 as follows:




1608. Operator lookup in trailing return type

Section: 12.2.2.3  [over.match.oper]     Status: C++14     Submitter: Johannes Schaub     Date: 2013-01-23

[Moved to DR at the September, 2013 meeting.]

There is an unfortunate disparity between the treatment of an example like

  struct S {
    int operator[](int);
    auto f() -> decltype(this->operator[](0));
  };

(which is permitted, cf _N4567_.5.1.1 [expr.prim.general] paragraph 3), and

  struct S {
    int operator[](int);
    auto f() -> decltype((*this)[0]);
  };

which is not. The reason for rejecting the latter is 12.2.2.3 [over.match.oper] paragraph 3:

For a unary operator @ with an operand of a type whose cv-unqualified version is T1, and for a binary operator @ with a left operand of a type whose cv-unqualified version is T1 and a right operand of a type whose cv-unqualified version is T2, three sets of candidate functions, designated member candidates, non-member candidates and built-in candidates, are constructed as follows:

It would be desirable to update the latter specification to allow lookup of preceding declarations in a class currently being defined, analogously with the lookup performed in the function-notation case.

Proposed resolution (April, 2013):

Change 12.2.2.3 [over.match.oper] bullet 3.1 as follows:




1687. Conversions of operands of built-in operators

Section: 12.2.2.3  [over.match.oper]     Status: C++14     Submitter: Richard Smith     Date: 2013-05-17

[Moved to DR at the February, 2014 meeting.]

Consider an example like:

  struct Y {
    operator int*();
  };

  extern int *p;
  int *a = p + 100.0;   // #1
  int *b = Y() + 100.0; // #2

#1 is ill-formed because it violates the requirement of 7.6.6 [expr.add] that the non-pointer operand have integral or enumeration type. It appears that #2 is well-formed, however, because 12.2.2.3 [over.match.oper] paragraph 7 says,

If a built-in candidate is selected by overload resolution, the operands are converted to the types of the corresponding parameters of the selected operation function. Then the operator is treated as the corresponding built-in operator and interpreted according to Clause 7 [expr].

In this case, the selected operation function is

  int *operator+(int *, std::ptrdiff_t)

100.0 is thus converted to std::ptrdiff_t before reaching 7.6.6 [expr.add].

This problem could be addressed by restricting the conversion to the class or enumeration operand rather than both operands.

Proposed resolution (January, 2014):

Change 12.2.2.3 [over.match.oper] paragraph 7 as follows:

If a built-in candidate is selected by overload resolution, the operands of class type are converted to the types of the corresponding parameters of the selected operation function, except that the second standard conversion sequence of a user-defined conversion sequence (12.2.4.2.3 [over.ics.user]) is not applied. Then the operator is treated as the corresponding built-in operator and interpreted according to Clause 7 [expr]. [Example:

  struct X {
    operator double();
  };

  struct Y {
    operator int*();
  };

  int *a = Y() + 100.0; // error: pointer arithmetic requires integral operand
  int *b = Y() + X();   // error: pointer arithmetic requires integral operand

end example]




1673. Clarifying overload resolution for the second step of copy-initialization

Section: 12.2.4.2  [over.best.ics]     Status: C++14     Submitter: Vinny Romano     Date: 2013-04-29

N3690 comment CA 5

[Moved to DR at the February, 2014 meeting as part of document N3914.]

Currently, 12.2.4.2 [over.best.ics] paragraph 4 reads,

However, when considering the argument of a constructor or user-defined conversion function that is a candidate by 12.2.2.4 [over.match.ctor] when invoked for the copying/moving of the temporary in the second step of a class copy-initialization, by 12.2.2.8 [over.match.list] when passing the initializer list as a single argument or when the initializer list has exactly one element and a conversion to some class X or reference to (possibly cv-qualified) X is considered for the first parameter of a constructor of X, or by 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv], or 12.2.2.7 [over.match.ref] in all cases, only standard conversion sequences and ellipsis conversion sequences are considered.

This is cumbersome and hard to understand. A possible improvement might be:

However, only standard conversion sequences and ellipsis conversion sequences are considered if:

(Note that this rewording removes the restriction that applies during phase one of 12.2.2.8 [over.match.list], as there is no longer any way to trigger it due to the fact that only initializer-list constructors are candidates. See this bug report for details.)

Proposed resolution (September, 2013) [SUPERSEDED]:

Change 12.2.4.2 [over.best.ics] paragraph 4 as follows:

However, when considering the argument of a constructor or user-defined conversion function that is a candidate by 12.2.2.4 [over.match.ctor] when invoked for the copying/moving of the temporary in the second step of a class copy-initialization, by 12.2.2.8 [over.match.list] when passing the initializer list as a single argument or when the initializer list has exactly one element and a conversion to some class X or reference to (possibly cv-qualified) X is considered for the first parameter of a constructor of X, or by 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv], or 12.2.2.7 [over.match.ref] in all cases, only standard conversion sequences and ellipsis conversion sequences are considered. if the target is

and the constructor or user-defined conversion function is a candidate by

user-defined conversion sequences are not considered. [Example:

  struct X { X(); };
  struct B { operator X&(); };
  B b;
  X x({b}); // error: B::operator X&() is not a candidate

end example]

Additional note (October, 2013):

Questions have been raised about several of the bullets in the September, 2013 proposed resolution and whether a note would be preferable instead of or in addition to the example . The issue has been returned to "review" status to allow consideration of these questions.

Additional note (January, 2014):

It has also been observed that the proposed resolution would make the following example ill-formed by preventing the consideration of B's conversion function when initializing the first parameter of A's copy constructor:

  struct A {
    A() {}
    A(const A &) {}
  };

  struct B {
    operator A() { return A(); }
  } b;
  A a{b};



1307. Overload resolution based on size of array initializer-list

Section: 12.2.4.2.6  [over.ics.list]     Status: C++14     Submitter: Johannes Schaub     Date: 2011-04-30

[Moved to DR at the September, 2013 meeting.]

In an example like

  void f(int const(&)[2]);
  void f(int const(&)[3]);

  int main() {
   f({1, 2, 3});
  }

the current overload resolution rules make no provision for different array sizes and thus treats the call as ambiguous, even though it seems obvious that the second f should be chosen in this case.

Rationale (August, 2011):

The implications of array temporaries for the language should be considered by the Evolution Working Group in a comprehensive fashion rather than on a case-by-case basis. See also issues 1300, 1326, and 1525.

Notes from the October, 2012 meeting:

CWG determined that this issue is unrelated to array temporaries and that a tiebreaker should be added for this case in overload resolution.

Proposed resolution, April, 2013:

  1. Change 12.2.4.2.6 [over.ics.list] paragraph 2 as follows, adding a new paragraph (and moving the footnote to the new paragraph, as indicated):

  2. If the parameter type is std::initializer_list<X> or “array of X” [Footnote: Since there are no parameters of array type, this will only occur as the underlying type of a reference parameter. —end footnote] and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X. This conversion can be a user-defined conversion even in the context of a call to an initializer-list constructor. [Example: ... —end example]

    Otherwise, if the parameter type is “array of N X” [Footnote: Since there are no parameters of array type, this will only occur as the underlying type of a reference parameter. —end footnote], if the initializer list has exactly N elements or if it has fewer than N elements and X is default-constructible, and if all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X.

    [Drafting note: no other case in the remainder of the paragraph applies when the initializer list has more elements than the parameter array.]
  3. Change 12.2.4.3 [over.ics.rank] paragraph 3 as follows:

  4. Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of the following rules applies:




1762. Reserved identifier used in literal-operator-id example

Section: 12.6  [over.literal]     Status: C++14     Submitter: Switzerland     Date: 2013-09-23

N3690 comment CH 6

[Moved to DR at the February, 2014 meeting.]

The example in 12.6 [over.literal] paragraph 8 contains the line

  float operator ""E(const char*); // OK

E does not begin with an underscore and thus is a reserved name.

Proposed resolution (September, 2013):

Change the example in 12.6 [over.literal] paragraph 8 as follows:

  void operator "" _km(long double);                     // OK
  string operator "" _i18n(const char*, std::size_t);    // OK
  template <char...> double operator "" _\u03C0();       // OK: UCN for lowercase pi
  float operator ""_E(const char*);                      // OK
  float operator ""E(const char*);                       // error: reserved identifier
  float operator " " B(const char*);                     // error: non-empty string-literal
  string operator "" 5X(const char*, std::size_t);       // error: invalid literal suffix identifier
  double operator "" _miles(double);                     // error: invalid parameter-declaration-clause
  template <char...> int operator "" _j(const char*);    // error: invalid parameter-declaration-clause

Rationale (February, 2014):

The specification was removed from the WP and moved into a Technical Specification.




1570. Address of subobject as non-type template argument

Section: 13.4.3  [temp.arg.nontype]     Status: C++14     Submitter: Michael Wong     Date: 2012-02-06

[Moved to DR at the September, 2013 meeting.]

According to 13.4.3 [temp.arg.nontype] paragraph 1, the argument for a non-type template parameter of pointer or reference type must be

a constant expression (7.7 [expr.const]) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference

In C++03, the requirement for an id-expression eliminated the use of “addresses of array elements and names or addresses of non-static class members,” as noted in paragraph 3 of that section. With the advent of generalized constant expressions, however, it is possible to satisfy the requirements and still address these subobjects. For example:

  extern constexpr int x[] = { 0, 1 };
  constexpr const int *p1 = x + 1;
  const int &r = *p1;

  template <const int *> struct A;
  template <> struct A<&r> { };

If this is intentional, the note in 13.4.3 [temp.arg.nontype] paragraph 3 should be revised or removed; if not, the normative wording of paragraph 1 must be revised.

Notes from the April, 2013 meeting:

CWG did not favor extending the range of non-type template arguments to include subobjects, feeling that they should continue to be restricted to the address of a complete object.

Proposed resolution (June, 2013):

Change 13.4.3 [temp.arg.nontype] paragraph 1 as follows:

A template-argument for a non-type, non-template template-parameter shall be one of:




1666. Address constant expressions

Section: 13.4.3  [temp.arg.nontype]     Status: C++14     Submitter: Daniel Krügler     Date: 2013-04-24

[Moved to DR at the February, 2014 meeting.]

The term “address constant expression” was removed by paper N3652, but it is still referenced in the list of permissible nontype template arguments in 13.4.3 [temp.arg.nontype] paragraph 1:

Proposed resolution (November, 2013):

Change 13.4.3 [temp.arg.nontype] paragraph 1 as follows:

A template-argument for a non-type, non-template template-parameter shall be one of:




1592. When do template parameters match?

Section: 13.4.4  [temp.arg.template]     Status: C++14     Submitter: Richard Smith     Date: 2012-12-03

[Moved to DR at the September, 2013 meeting.]

According to 13.4.4 [temp.arg.template] paragraph 3,

A template-argument matches a template template-parameter (call it P) when each of the template parameters in the template-parameter-list of the template-argument's corresponding class template or alias template (call it A) matches the corresponding template parameter in the template-parameter-list of P.

There does not appear to be a formal definition of the criteria for whether two template parameters “match,” however, and there is implementation variance in the treatment of an example like

  struct A {
    typedef int T1;
    typedef int T2;
  };
  template<template<typename T, typename T::T1 N> class U>
  struct B {
    U<A, 0> u;
  };
  template<typename T, typename T::T2 N>
  struct C {
  };
  B<C> b; // ok?

Proposed resolution (June, 2013):

Change 13.4.4 [temp.arg.template] paragraph 3 as follows:

A template-argument matches a template template-parameter (call it P) when each of the template parameters in the template-parameter-list of the template-argument's corresponding class template or alias template (call it A) matches the corresponding template parameter in the template-parameter-list of P. Two template parameters match if they are of the same kind (type, non-type, template), for non-type template-parameters, their types are equivalent (13.7.7.2 [temp.over.link]), and for template template-parameters, each of their corresponding template-parameters matches, recursively. When P's template-parameter-list contains a template parameter pack...



1737. Type dependence of call to a member of the current instantiation

Section: 13.8.3.2  [temp.dep.type]     Status: C++14     Submitter: Richard Smith     Date: 2013-08-14

[Moved to DR at the February, 2014 meeting.]

Since we don't deduce the return type of a function temploid until it is instantiated, it seems that a call to such a function as a member of the current instantiation must have a dependent type, but 13.8.3.3 [temp.dep.expr] doesn't appear to say that. For example:

   template<typename T> struct X { typedef int type; };
   template<typename T> struct S {
     auto f() { return 0; }
     void g() {
       X<decltype(f())>::type x; // typename presumably needed here
     }
   }

Presumably there should be a bullet in 13.8.3.2 [temp.dep.type] paragraph 8 for this case.

Proposed resolution (November, 2013):

Change 13.8.3.3 [temp.dep.expr] paragraph 3 as follows:

An id-expression is type-dependent if it contains

or if it names a dependent member...




1569. Deducing a function parameter pack before ellipsis

Section: 13.10.3.6  [temp.deduct.type]     Status: C++14     Submitter: Michael Wong     Date: 2012-02-06

[Moved to DR at the September, 2013 meeting.]

According to 13.10.3.6 [temp.deduct.type] paragraph 5, one of the non-deduced contexts is

A function parameter pack that does not occur at the end of the parameter-declaration-clause.

This would make the following example ill-formed:

  template <typename R, typename ...P>
  void foo(R (&)(P ..., ...)) { }

  void bar(int, ...) { }

  void zip() {
   foo(bar);
  }

It is not clear whether this is intentional; if the wording referred to parameter-declaration-list instead of parameter-declaration-clause, the example would be accepted.

Proposed resolution (June, 2013):

Change 13.10.3.6 [temp.deduct.type] paragraph 5 as follows

The non-deduced contexts are:




1770. Type matching of non-type template parameters and arguments

Section: 13.10.3.6  [temp.deduct.type]     Status: C++14     Submitter: Canada     Date: 2013-09-24

N3690 comment CA 15

[Moved to DR at the February, 2014 meeting.]

According to 13.10.3.6 [temp.deduct.type] paragraph 17,

If, in the declaration of a function template with a non-type template-parameter, the non-type template-parameter is used in an expression in the function parameter-list and, if the corresponding template-argument is deduced, the template-argument type shall match the type of the template-parameter exactly, except that a template-argument deduced from an array bound may be of any integral type.

This does not cover return types, leaving the outcome of an example like the following unclear:

  template <int N> struct A;

  template <short N>
  A<N> *foo();

  void bar() {
    A<1> *(*fp)(void) = &foo;
  }

Proposed resolution (September, 2013):

Change 13.10.3.6 [temp.deduct.type] paragraph 17 as follows:

If, in the declaration of a function template with a non-type template-parameter, the non-type template-parameter is used in an expression in the function parameter-list and, if the corresponding template-argument is deduced, the template-argument type shall match the type of the template-parameter exactly, except that a template-argument deduced from an array bound may be of any integral type P has a form that contains <i>, and if the type of the corresponding value of A differs from the type of i, deduction fails. If P has a form that contains [i], and if the type of i is not an integral type, deduction fails.147 [Example:...



1424. When must sub-object destructors be accessible?

Section: 14.3  [except.ctor]     Status: C++14     Submitter: Daniel Krügler     Date: 2011-12-07

[Moved to DR at the September, 2013 meeting.]

The current specification does not appear to say whether an implementation is permitted/required/forbidden to complain when a sub-object's destructor is inaccessible. In particular, if there is no possibility for an exception to be thrown following a given sub-object's construction, should an implementation issue an error if that sub-object's destructor is inaccessible?

Proposed resolution (February, 2013):

  1. Change 6.3 [basic.def.odr] paragraph 2 as follows:

  2. ...A destructor for a class is odr-used as specified in if it is potentially invoked (11.4.7 [class.dtor]).
  3. Change 7.6.2.8 [expr.new] paragraph 17 as follows:

  4. If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function (11.4.11 [class.free]), and the constructor (11.4.5 [class.ctor]). If the new expression new-expression creates an array of objects of class type, access and ambiguity control are done for the destructor is potentially invoked (11.4.7 [class.dtor]).
  5. Change 11.4.7 [class.dtor] paragraph 11 as follows:

  6. Destructors are A destructor is invoked implicitly

    In each case, the context of the invocation is the context of the construction of the object. A destructor is also invoked implicitly through use of a delete-expression (7.6.2.9 [expr.delete]) for a constructed object allocated by a new-expression (7.6.2.8 [expr.new]); the context of the invocation is the delete-expression. [Note: An array of class type contains several subobjects for each of which the destructor is invoked. —end note] A destructor can also be invoked explicitly. A destructor is potentially invoked if it is invoked or as specified in 7.6.2.8 [expr.new] and 11.9.3 [class.base.init]. A program is ill-formed if an object of class type or array thereof is declared and the destructor for the class is not accessible at the point of the declaration a destructor that is potentially invoked is deleted or not accessible from the context of the invocation. Destructors can also be invoked explicitly.

  7. Add the following as a new paragraph following 11.9.3 [class.base.init] paragraph 9:

  8. If a given non-static data member has both...

    In a non-delegating constructor, the destructor for each direct or virtual base class and for each non-static data member of class type is potentially invoked (11.4.7 [class.dtor]). [Note: This provision ensures that destructors can be called for fully-constructed sub-objects in case an exception is thrown (14.3 [except.ctor]). —end note]

    In a non-delegating constructor, initialization proceeds...




1769. Catching a base class of the exception object

Section: 14.4  [except.handle]     Status: C++14     Submitter: Canada     Date: 2013-09-24

N3690 comment CA 13

[Moved to DR at the February, 2014 meeting.]

In saying that the catch-clause parameter is copy-initialized from the exception object, 14.4 [except.handle] paragraph 16 leaves open the possibility that a converting constructor might be used for the initialization when the type of the exception-declaration is a base of the type of the exception object. It should be specified that the parameter is copy-constructed from the corresponding base class subobject in such cases.

Proposed resolution (September, 2013) [superseded]:

Change 14.4 [except.handle] paragraph 16 as follows:

If the exception-declaration specifies introduces a name, it declares a variable which is copy-initialized (9.4 [dcl.init]) from the exception object. If the exception-declaration denotes an object type but does not specify a name, a temporary (6.7.7 [class.temporary]) is copy-initialized (9.4 [dcl.init]) from the exception object.; otherwise, an unnamed variable is created. That variable, of type cv T or cv T&, is initialized from the exception object, of type E, as follows:

Additional note (October, 2013):

Additional discussion has pointed out that, although the unnamed handler parameter is no longer called a “temporary” in the proposed resolution, 6.7.7 [class.temporary] paragraph 1 still lists “entering a handler (14.4 [except.handle])” as one of the contexts in which a temporary is created. A question was also raised as to whether it is necessary to deal with named and unnamed handler parameters separately, since both are now referred to as “variables” in the revised wording. This issue has therefore been returned to "review" status to allow consideration of these points.

Proposed resolution (November, 2013):

  1. Change 6.1 [basic.pre] paragraph 6 as follows:

  2. A variable is introduced by the declaration of a reference other than a non-static data member or of an object. The variable's name, if any, denotes the reference or object.
  3. Change 6.7.7 [class.temporary] paragraph 1 as follows:

  4. Temporaries of class type are created in various contexts: binding a reference to a prvalue (9.4.4 [dcl.init.ref]), returning a prvalue (8.7.4 [stmt.return]), a conversion that creates a prvalue (7.3.2 [conv.lval], 7.6.1.9 [expr.static.cast], 7.6.1.11 [expr.const.cast], 7.6.3 [expr.cast]), throwing an exception (14.2 [except.throw]), entering a handler (14.4 [except.handle]), and in some initializations (9.4 [dcl.init]). [Note:...
  5. Change 14.2 [except.throw] paragraph 3 as follows:

  6. Throwing an exception copy-initializes (9.4 [dcl.init], 11.4.5.3 [class.copy.ctor]) a temporary object, called the exception object. The temporary is an lvalue and is used to initialize the variable named declared in the matching handler (14.4 [except.handle]). If the type...
  7. Change 14.4 [except.handle] paragraph 16 as follows:

  8. If the exception-declaration specifies a name, it declares a variable which is copy-initialized (9.4 [dcl.init]) from the exception object. If the exception-declaration denotes an object type but does not specify a name, a temporary (6.7.7 [class.temporary]) is copy-initialized (9.4 [dcl.init]) from the exception object. The variable declared by the exception-declaration, of type cv T or cv T&, is initialized from the exception object, of type E, as follows:

    The lifetime of the variable or temporary ends when the handler exits, after the destruction of any automatic objects initialized within the handler.




1740. Disambiguation of noexcept

Section: 14.5  [except.spec]     Status: C++14     Submitter: Richard Smith     Date: 2013-08-13

N3690 comment CA 27

[Moved to DR at the February, 2014 meeting.]

There is an ambiguity between a noexcept specifier's optional parenthesized constant-expression and an initializer:

  void f() noexcept;
  void (*p)() noexcept (&f);

Here, we can just about make 9.3.3 [dcl.ambig.res] paragraph 1's rule fit, and say that the (&f) is part of the exception-specification rather than being an initializer. However, this case is much more problematic:

  void (*fp2)() noexcept, (*fp)() noexcept (fp2 = 0);

The (fp = 0) here is unambiguously an initializer, because an assignment-expression cannot syntactically be a constant-expression, although current implementations treat it as an ill-formed part of the exception-specification.

Probably the best approach would be to change 14.5 [except.spec] to say that a ( following noexcept is always treated as being part of the noexcept-specification.

Proposed resolution (September, 2013):

Change 14.5 [except.spec] paragraph 1 as follows:

...In a noexcept-specification, the constant-expression, if supplied, shall be a constant expression (7.7 [expr.const]) that is contextually converted to bool (7.3 [conv]). A noexcept-specification noexcept is equivalent to noexcept( true). A ( token that follows noexcept is part of the noexcept-specification (and does not commence an initializer (9.4 [dcl.init]).





Issues with "C++17" Status


2155. Defining classes and enumerations via using-declarations

Section: _N4868_.9.8.2.3  [namespace.memdef]     Status: C++17     Submitter: Richard Smith     Date: 2015-07-05

[Moved to DR at the November, 2016 meeting.]

The resolution of issue 1838 was intended to resolve issue 1021 but does not actually do so, as it applies only to declarations with a declarator-id, which is not true of classes and enumerations.

Proposed resolution (October, 2015):

  1. Change _N4868_.9.8.2.3 [namespace.memdef] paragraph 1 as follows:

  2. A declaration in a namespace N (excluding declarations in nested scopes) whose declarator-id is an unqualified-id (9.3.4 [dcl.meaning]), whose class-head-name (Clause 11 [class]) or enum-head-name (9.7.1 [dcl.enum]) is an identifier, or whose elaborated-type-specifier is of the form class-key attribute-specifier-seqopt identifier (9.2.9.5 [dcl.type.elab]), or that is an opaque-enum-declaration, declares (or redeclares) its unqualified-id or identifier as a member of N, and may be a definition. [Note:...
  3. Change the grammar of 9.7.1 [dcl.enum] as follows:




2218. Ambiguity and namespace aliases

Section: 6.5  [basic.lookup]     Status: C++17     Submitter: Richard Smith     Date: 2015-12-29

[Adopted at the February/March, 2017 meeting.]

There is implementation divergence on the status of the following example:

  namespace A { namespace B { int x; } }
  namespace C { namespace B = A::B; }
  using namespace A;
  using namespace C;
  int x = B::x;

This should presumably be valid: the lookup of B finds A::B and C::B, but it is not ambiguous because they denote the same entity. A similar example with a using-declaration or alias-declaration seems to be universally accepted. Perhaps the lookup rules need to be clarified regarding the status of this example.

Proposed resolution (November, 2016):

Change 6.5 [basic.lookup] paragraph 1 as follows:

The name lookup rules apply uniformly to all names (including typedef-names (9.2.4 [dcl.typedef]), namespace-names (9.8 [basic.namespace]), and class-names (11.3 [class.name])) wherever the grammar allows such names in the context discussed by a particular rule. Name lookup associates the use of a name with a declaration set of declarations (6.2 [basic.def]) of that name. Name lookup shall find an unambiguous declaration for the name (see 6.5.2 [class.member.lookup]). Name lookup may associate more than one declaration with a name if it finds the name to be a function name; The declarations found by name lookup shall either all declare the same entity or shall all declare functions; in the latter case, the declarations are said to form a set of overloaded functions (_N4868_.12.2 [over.load]). Overload resolution...



426. Identically-named variables, one internally and one externally linked, allowed?

Section: 6.6  [basic.link]     Status: C++17     Submitter: Steve Adamczyk     Date: 2 July 2003

[Adopted at the February/March, 2017 meeting.]

An example in 6.6 [basic.link] paragraph 6 creates two file-scope variables with the same name, one with internal linkage and one with external.

  static void f();
  static int i = 0;                       //1
  void g() {
          extern void f();                // internal linkage
          int i;                          //2: i has no linkage
          {
                  extern void f();        // internal linkage
                  extern int i;           //3: external linkage
          }
  }

Is this really what we want? C99 has 6.2.2.7/7, which gives undefined behavior for having an identifier appear with internal and external linkage in the same translation unit. C++ doesn't seem to have an equivalent.

Notes from October 2003 meeting:

We agree that this is an error. We propose to leave the example but change the comment to indicate that line //3 has undefined behavior, and elsewhere add a normative rule giving such a case undefined behavior.

Proposed resolution (October, 2005) [SUPERSEDED]:

Change 6.6 [basic.link] paragraph 6 as indicated:

...Otherwise, if no matching entity is found, the block scope entity receives external linkage. If, within a translation unit, the same entity is declared with both internal and external linkage, the behavior is undefined.

[Example:

    static void f();
    static int i = 0;            // 1
    void g () {
        extern void f ();        // internal linkage
        int i;                   // 2: i has no linkage
        {
            extern void f ();    // internal linkage
            extern int i;        // 3: external linkage
        }
    }

There are three objects named i in this program. The object with internal linkage introduced by the declaration in global scope (line //1 ), the object with automatic storage duration and no linkage introduced by the declaration on line //2, and the object with static storage duration and external linkage introduced by the declaration on line //3. Without the declaration at line //2, the declaration at line //3 would link with the declaration at line //1. But because the declaration with internal linkage is hidden, //3 is given external linkage, resulting in a linkage conflict.end example]

Notes from the April 2006 meeting:

According to 6.6 [basic.link] paragraph 9, the two variables with linkage in the proposed example are not “the same entity” because they do not have the same linkage. Some other formulation will be needed to describe the relationship between those two variables.

Notes from the October 2006 meeting:

The CWG decided that it would be better to make a program with this kind of linkage mismatch ill-formed instead of having undefined behavior.

Proposed resolution (November, 2016):

Change 6.6 [basic.link] paragraph 6 as follows:

...Otherwise, if no matching entity is found, the block scope entity receives external linkage. If, within a translation unit, the same entity is declared with both internal and external linkage, the program is ill-formed. [Example:

  static void f();
  static int i = 0;     // #1
  void g() {
    extern void f();    // internal linkage
    int i;              // #2: i has no linkage
    {
      extern void f();  // internal linkage
      extern int i;     // #3 external linkage, ill-formed
    }
  }

There are three objects named i in this program. The object with internal linkage introduced by the declaration in global scope (line #1 ), the object with automatic storage duration and no linkage introduced by the declaration on line #2, and the object with static storage duration and external linkage introduced by the declaration on line #3. Without the declaration at line #2, the declaration at line #3 would link with the declaration at line #1. Because the declaration with internal linkage is hidden, however, #3 is given external linkage, making the program ill-formed.end example]




2198. Linkage of enumerators

Section: 6.6  [basic.link]     Status: C++17     Submitter: Richard Smith     Date: 2015-11-12

[Adopted at the February/March, 2017 meeting.]

According to the rules in 6.6 [basic.link] paragraph 4, the enumerators of an enumeration type with linkage also have linkage. Having same-named enumerators in different translation units would seem to be innocuous. Is there a rationale for this rule?

Proposed resolution (March, 2017):

  1. Delete 6.6 [basic.link] bullet 4.5:

  2. An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage. All other namespaces have external linkage. A name having namespace scope that has not been given internal linkage above has the same linkage as the enclosing namespace if it is the name of

  3. Change 6.6 [basic.link] paragraph 9 as follows:

  4. Two names that are the same (6.1 [basic.pre]) and that are declared in different scopes shall denote the same variable, function, type, enumerator, template or namespace if...



2214. Missing requirement on representation of integer values

Section: 6.8.2  [basic.fundamental]     Status: C++17     Submitter: Richard Smith     Date: 2015-12-15

[Adopted at the February/March, 2017 meeting.]

According to 6.8.2 [basic.fundamental] paragraph 3,

The range of non-negative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the value representation of each corresponding signed/unsigned type shall be the same.

The corresponding wording from C11 is,

The range of nonnegative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the representation of the same value in each type is the same.

The C wording is arguably clearer, but it loses the implication of the C++ wording that the sign bit of a signed type is part of the value representation of the corresponding unsigned type.

Proposed resolution (January, 2017):

Change 6.8.2 [basic.fundamental] paragraph 3 as follows:

...The standard and extended unsigned integer types are collectively called unsigned integer types. The range of non-negative values of a signed integer type is a subrange of the corresponding unsigned integer type, the representation of the same value in each of the two types is the same, and the value representation of each corresponding signed/unsigned type shall be the same. The standard signed integer types...



2201. Cv-qualification of array types

Section: 6.8.5  [basic.type.qualifier]     Status: C++17     Submitter: Robert Haberlach     Date: 2015-11-15

[Adopted at the February/March, 2017 meeting.]

Issue 1059 changed 6.8.5 [basic.type.qualifier] paragraph 6 to read,

An array type whose elements are cv-qualified is also considered to have the same cv-qualifications as its elements.

However, that change overlooked the earlier statement in paragraph 2,

A compound type (6.8.4 [basic.compound]) is not cv-qualified by the cv-qualifiers (if any) of the types from which it is compounded. Any cv-qualifiers applied to an array type affect the array element type, not the array type (9.3.4.5 [dcl.array]).

Proposed resolution (January, 2017):

Change 6.8.5 [basic.type.qualifier] paragraph 2 as follows:

A compound type (6.8.4 [basic.compound]) is not cv-qualified by the cv-qualifiers (if any) of the types from which it is compounded. Any cv-qualifiers applied to an array type affect the array element type, not the array type (9.3.4.5 [dcl.array]).



1343. Sequencing of non-class initialization

Section: 6.9.1  [intro.execution]     Status: C++17     Submitter: Johannes Schaub     Date: 2011-08-12

[Moved to DR at the November, 2016 meeting as paper P0507R0.]

The current wording does not indicate that initialization of a non-class object is a full-expression, but presumably should do so.

Additional note, April, 2013:

There is implementation variance in the treatment of the following example:

  struct A {
    A() { puts("ctor"); }
    A(const A&) { puts("copy"); }
    const A&get() const { return *this; }
    ~A() { puts("dtor"); }
  };
  struct B { B(A, A) {} };

  typedef A A2[2];
  A2 a = { A().get(), A().get() };
  B b = { A().get(), A().get() };
  int c = (A2{ A().get(), A().get() }, 0);
  int d = (B{ A().get(), A().get() }, 0);

  int main() {}

Additional note (February, 2014):

Aggregate initialization could also involve more than one full-expression, so the limitation above to “initialization of a non-class object” is not correct.

Additional note (October, 2015):

For additional examples, consider:

   int i = i++;
   int j = j;

The current sequencing rules do not cover these initializations.

Also, in

  const int& f(const int& x) { return x; }
  int y = f(5);

it doesn't appear that 6.9.1 [intro.execution] paragraph 10 requires the temporary for 5 to persist until the initialization of y is complete.

Proposed resolution (June, 2016):

The resolution is given in paper P0507R0.




1961. Potentially-concurrent actions within a signal handler

Section: 6.9.2  [intro.multithread]     Status: C++17     Submitter: Faisal Vali     Date: 2014-07-04

[Moved to DR at the November, 2016 meeting.]

According to 6.9.2 [intro.multithread] paragraph 23,

Two actions are potentially concurrent if

This definition should exclude the case when both actions are performed by a signal handler.

Notes from the October, 2015 meeting:

SG1 agrees that the existing wording should be amended to say something like “and they are not both performed by the same signal handler invocation.”

Proposed resolution (November, 2015):

Change 6.9.2 [intro.multithread] paragraph 23 as follows:

Two actions are potentially concurrent if




2046. Incomplete thread specifications

Section: 6.9.2  [intro.multithread]     Status: C++17     Submitter: Dinka Ranns     Date: 2014-11-17

[Adopted at the February/March, 2017 meeting as document P0250R3.]

Given the example:

    auto id1 = std::this_thread::get_id();
    thread_local auto id2 = std::this_thread::get_id();
    int main()
    {

      auto id3 = std::this_thread::get_id();
      auto id4 = std::this_thread::get_id();
      std::cout << id1 << std::endl << id2 << std::endl << id3 <<
          std::endl << id4 << std::endl;
    }
  1. What is the thread of execution that initializes id1? Is it the same as, different from, or unspecified in relation to id3?

  2. I believe this is unspecified by omission. (I can find no wording in the standard saying otherwise.) It looks like the standard attempts to require that these happen in the same thread, but fails to do so. We say that in a program that does not start a thread, all ordered dynamic initializations are sequenced, which implies they happen in the same thread — because “sequenced before” is an intra-thread notion — but we do not say that these initializations are sequenced before entering main (we just say they may be “done before” entering main). This seems like a defect to me.

  3. What is the thread of execution that initializes id2? Is it the same as, different from, or unspecified in relation to id1 and id3?

  4. This also seems to be unspecified; this seems like a defect. We say in 6.9.3.2 [basic.start.static] paragraph 5:

    It is implementation-defined whether the dynamic initialization of a non-local variable with static or thread storage duration is done before the first statement of the initial function of the thread. If the initialization is deferred to some point in time after the first statement of the initial function of the thread, it shall occur before the first odr-use (6.3 [basic.def.odr]) of any variable with thread storage duration defined in the same translation unit as the variable to be initialized.

    but “done before” and “occurs before” are meaningless noise here. I think what is intended is that these are a “sequenced before” relation for initialization of thread storage duration entities (implying initialization in the same thread) and “happens before” for initialization of static storage duration entities (implying initialization in some thread but visible to this thread).

  5. Am i guaranteed to have only one copy of id2, or could there be more?

  6. You are guaranteed to have one per thread, per 6.7.5.3 [basic.stc.thread] paragraph 1.

    I cannot find any explicit guarantee that the implementation will not create additional threads behind your back, so there appears to be no guarantee that you have only one id2 variable. That may be a defect.

I'm also concerned by 6.9.3.2 [basic.start.static] paragraph 2:

...Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definitions in the translation unit...

This is obviously wrong if a translation unit contains both static and thread storage duration variables.

Notes from the October, 2015 meeting:

A paper is required to address these issues fully. A lot of the existing text talks about sequencing when it should refer to “the transitive subset of happens before,” which could be, but is not currently, defined. “Happens before” would do if we didn't have memory_order_consume. The SG1 consensus on what should be said includes the following:

Notes from the February, 2016 meeting:

SG1 is included to introduce the “strongly happens before” relation from P0250, although possibly in a differnt way. It seems to be needed to fix all sorts of wording that's currently imperfect because it doesn't work in the presence of memory_order_consume.




1677. Constant initialization via aggregate initialization

Section: 6.9.3.2  [basic.start.static]     Status: C++17     Submitter: Daniel Krügler     Date: 2013-05-05

[Adopted at the February/March, 2017 meeting.]

The resolution of issue 1489 added wording regarding value initialization to 6.9.3.2 [basic.start.static] paragraph 2 in an attempt to clarify the status of an example like

  int a[1000]{};

However, this example is aggregate initialization, not value initialization. Also, now that we allow brace-or-equal-initializers in aggregates, this wording also needs to be updated to allow an aggregate with constant non-static data member initializers to qualify for constant initialization.

Proposed resolution (November, 2016):

  1. Change 6.9.3.2 [basic.start.static] paragraph 2 as follows, converting the bulleted list into running text as indicated:

  2. A constant initializer for an a variable or temporary object o is an expression that initializer whose full-expression is a constant expression, except that it if o is an object, such an initializer may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types. [Note: Such a class may have a non-trivial destructor —end note] Constant initialization is performed:

    If constant initialization is not performed...

  3. Change 7.7 [expr.const] paragraph 2 as follows:

    A conditional-expression An expression e is a core constant expression unless...



2206. Composite type of object and function pointers

Section: Clause 7  [expr]     Status: C++17     Submitter: Mike Miller     Date: 2015-12-01

[Adopted at the February/March, 2017 meeting.]

Consider an example like

  void *p;
  void (*pf)();
  auto x = true ? p : pf;

The rules in Clause 7 [expr] paragraph 13 say that the composite type between a void* and a function pointer type is void*. This is surprising, since a function pointer type cannot be implicitly converted to void*.

Proposed resolution (January, 2017):

Change Clause 7 [expr] bullet 14.5 as follows:

The cv-combined type of two types T1 and T2 is a type T3 similar to T1 whose cv-qualification signature (7.3.6 [conv.qual]) is:




2011. Unclear effect of reference capture of reference

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: C++17     Submitter: Ville Voutilainen     Date: 2014-09-28

[Adopted at the February/March, 2017 meeting as document P0613R0.]

The Standard refers to capturing “entities,” and a reference is an entity. However, it is not clear what capturing a reference by reference would mean. In particular, 7.5.5 [expr.prim.lambda] paragraph 16 says,

It is unspecified whether additional unnamed non-static data members are declared in the closure type for entities captured by reference.

If a reference captured by reference is not represented by a member, it is hard to see how something like the following example could work:

  #include <functional>
  #include <iostream>

  std::function<void()> make_function(int& x) {
    return [&]{ std::cout << x << std::endl; };
  }

  int main() {
    int i = 3;
    auto f = make_function(i);
    i = 5;
    f();
  }

Should this be undefined behavior or should it print 5?

Proposed resolution (November, 2014) [SUPERSEDED]:

  1. Change 7.5.5 [expr.prim.lambda] paragraph 18 as follows:

  2. Every id-expression within the compound-statement of a lambda-expression that is an odr-use (6.3 [basic.def.odr]) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type. [Note: An id-expression that is not an odr-use refers to the original entity, never to a member of the closure type. Furthermore, such an id-expression does not cause the implicit capture of the entity. —end note] If this is captured, each odr-use of this is transformed into an access to the corresponding unnamed data member of the closure type, cast (7.6.3 [expr.cast]) to the type of this. [Note: The cast ensures that the transformed expression is a prvalue. —end note] An id-expression within the compound-statement of a lambda-expression that is an odr-use of a reference captured by reference refers to the entity to which the captured reference is bound and not to the captured reference. [Note: Such odr-uses are not invalidated by the end of the captured reference's lifetime. —end note] [Example:

      void f(const int*);
      void g() {
        const int N = 10;
        [=] {
        int arr[N]; // OK: not an odr-use, refers to automatic variable
        f(&N);      // OK: causes N to be captured; &N points to the
                    // corresponding member of the closure type
        };
      }
      auto h(int &r) {
        return [&]() {
          ++r;      // Valid after h returns if the lifetime of the
                    // object to which r is bound has not ended
        };
      }
    

    end example]

  3. Change 7.5.5 [expr.prim.lambda] paragraph 23 as follows:

  4. [Note: If an a non-reference entity is implicitly or explicitly captured by reference, invoking the function call operator of the corresponding lambda-expression after the lifetime of the entity has ended is likely to result in undefined behavior. —end note]

Proposed resolution (February, 2017):

  1. Change 7.5.5 [expr.prim.lambda] paragraph 17 as follows:

  2. Every id-expression within the compound-statement of a lambda-expression that is an odr-use (6.3 [basic.def.odr]) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type. [Note: An id-expression that is not an odr-use refers to the original entity, never to a member of the closure type. Furthermore, such an id-expression does not cause the implicit capture of the entity. —end note] If *this is captured by copy, each odr-use of this is transformed into a pointer to the corresponding unnamed data member of the closure type, cast (7.6.3 [expr.cast]) to the type of this. [Note: The cast ensures that the transformed expression is a prvalue. —end note] An id-expression within the compound-statement of a lambda-expression that is an odr-use of a reference captured by reference refers to the entity to which the captured reference is bound and not to the captured reference. [Note: The validity of such captures is determined by the lifetime of the object to which the reference refers, not by the lifetime of the reference itself. —end note] [Example:

      void f(const int*);
      void g() {
        const int N = 10;
        [=] {
        int arr[N]; // OK: not an odr-use, refers to automatic variable
        f(&N);      // OK: causes N to be captured; &N points to the
                    // corresponding member of the closure type
        };
      }
      auto h(int &r) {
        return [&] {
          ++r;      // Valid after h returns if the lifetime of the
                    // object to which r is bound has not ended
        };
      }
    

    end example]

  3. Change 7.5.5 [expr.prim.lambda] paragraph 25 as follows:

  4. [Note: If an a non-reference entity is implicitly or explicitly captured by reference, invoking the function call operator of the corresponding lambda-expression after the lifetime of the entity has ended is likely to result in undefined behavior. —end note]



2211. Hiding by lambda captures and parameters

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: C++17     Submitter: Ville Voutilainen     Date: 2015-12-07

[Adopted at the February/March, 2017 meeting.]

Consider:

  #include <iostream>

  int main()
  {
    [x=2](int x) { std::cout << x << std::endl; }(3);
  }

What is the code supposed to print? There is implementation divergence.

Proposed resolution (February, 2017):

  1. Add the following as a new paragraph after 7.5.5 [expr.prim.lambda] paragraph 11:

  2. The identifier in a simple-capture is looked up using the usual rules for unqualified name lookup (6.5.3 [basic.lookup.unqual]); each such lookup shall find an entity. An entity that is designated by a simple-capture is said to be explicitly captured, and shall be *this (when the simple-capture is “this ” or “* this ”) or a variable with automatic storage duration declared in the reaching scope of the local lambda expression.

    If an identifier in a simple-capture appears as the declarator-id of a parameter of the lambda-declarator's parameter-declaration-clause, the program is ill-formed. [Example:

      void f() {
        int x = 0;
        auto g = [x](int x) { return 0; }  // error: parameter and simple-capture have the same name
      }
    

    end example]

  3. Change the example of 7.5.5 [expr.prim.lambda] paragraph 12 as follows:

  4.   int x = 4;
      auto y = [&r = x, x = x+1]()->int {
                      r += 2;
                      return x+2;
                   }(); // Updates ::x to 6, and initializes y to 7.
      auto z = [a = 42](int a) { return 1; } // error: parameter and local variable have the same name
    



2247. Lambda capture and variable argument list

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: C++17     Submitter: Aaron Ballman     Date: 2016-03-11

[Adopted at the February/March, 2017 meeting.]

Consider:

  #include <cstdarg>
  #include <iostream>

  auto f(int i, ...) {
   return [&]() {
    va_list l;
    va_start(l, i);
    std::cout << "i = " << i << ", " << va_arg(l, int) << std::endl;
    va_end(l);
   };
  }

  int main() {
   auto l = f(100, 42);
   l();
  }

Is this defined behavior, accessing the variadic arguments passed to f?

Notes from the December, 2016 teleconference:

Such examples should have undefined behavior; va_start should only be permitted to access the arguments for the current function.

Proposed resolution (February, 2017):

Change 17.13.2 [cstdarg.syn] paragraph 1 as follows:

The contents of the header <cstdarg> are the same as the C standard library header <stdarg.h>, with the following changes: The restrictions that ISO C places on the second parameter to the va_start() macro in header <stdarg.h> are different in this International Standard. The parameter parmN is the identifier of the rightmost parameter in the variable parameter list of the function definition (the one just before the ... ).223 If the parameter parmN is a pack expansion (13.7.4 [temp.variadic]) or an entity resulting from a lambda capture (7.5.5 [expr.prim.lambda]), the program is ill-formed, no diagnostic required. If the parameter parmN is of a reference type, or of a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.



2215. Redundant description of language linkage in function call

Section: 7.6.1.3  [expr.call]     Status: C++17     Submitter: Richard Smith     Date: 2015-12-17

Calling a function through an expression whose language linkage is different from that of the called function is explicitly called out as undefined behavior, even though there is a general provision that makes any difference in the types of the expression and function undefined. The special treatment for language linkage should be removed.

Notes from the November, 2016 meeting:

This issue will be resolved editorially and is being placed in "review" status until the corresponding change appears in a working draft.

Additional note, February, 2021:

This issue was resolved editorially.




2224. Member subobjects and base-class casts

Section: 7.6.1.9  [expr.static.cast]     Status: C++17     Submitter: Aaron Ballman     Date: 2016-01-13

[Adopted at the February/March, 2017 meeting.]

The current wording of 7.6.1.9 [expr.static.cast] paragraph 2 appears to permit the following example:

  struct B {
    int i;
  };

  struct D : B {
    int j;
    B b;
  };

  int main() {
    D d;

    B &br = d.b;
    D &dr = static_cast<D&>(br);  // Okay?
  }

Presumably such casts should only be supported if the operand object is a base class subobject, not a member subobject.

Proposed resolution (January, 2017):

Change 7.6.1.9 [expr.static.cast] paragraph 2 as follows:

...If the object of type “cv1 B” is actually a base class subobject of an object of type D, the result refers to the enclosing object of type D. Otherwise, the behavior is undefined. [Example:...



2248. Problems with sized delete

Section: 7.6.2.9  [expr.delete]     Status: C++17     Submitter: Richard Smith     Date: 2016-03-14

[Adopted at the February/March, 2017 meeting.]

Consider:

   T *p = new (::operator new(sizeof(T) + 100)) T;
   delete p;

It is infeasible for the implementation to infer the size of the block of storage, yet the standard does not permit undefined behavior for this case.

Proposed resolution (December, 2016):

Change 7.6.2.9 [expr.delete] paragraph 11 as follows:

When a delete-expression is executed, the selected deallocation function shall be called with the address of the block of storage to be reclaimed most-derived object in the delete object case, or the address of the object suitably adjusted for the array allocation overhead (7.6.2.8 [expr.new]) in the delete array case, as its first argument. If a deallocation function with a parameter of type std::align_val_t is used, the alignment of the type of the object to be deleted is passed as the corresponding argument. If a deallocation function with a parameter of type std::size_t is used, the size of the block most-derived type, or of the array plus allocation overhead, respectively, is passed as the corresponding argument. [Note: If this results in a call to a usual deallocation function, and either the first argument was not the result of a prior call to a usual allocation function or the second argument was not the corresponding argument in said call, the behavior is undefined (17.6.3.2 [new.delete.single], 17.6.3.3 [new.delete.array]). —end note]



2220. Hiding index variable in range-based for

Section: 8.6.5  [stmt.ranged]     Status: C++17     Submitter: Daveed Vandevoorde     Date: 2016-01-08

[Adopted at the February/March, 2017 meeting.]

Given an example like

  void f() {
    int arr[] = { 1, 2, 3 };
    for (int val : arr) {
      int val;   // Redeclares index variable
    }
  }

one might expect that the redeclaration of the index variable would be an error, as it is in the corresponding classic for statement. However, the restriction that makes the latter an error is phrased in terms of the condition nonterminal in 8.5 [stmt.select] paragraph 3, and the range-based for does not refer to condition. Should there be an explicit prohibition of such a redeclaration?

Proposed resolution (January, 2017):

Add the following as a new paragraph after 8.6 [stmt.iter] paragraph 3:

Thus after the while statement, i is no longer in scope. —end example]

If a name introduced in an init-statement or for-range-declaration is redeclared in the outermost block of the substatement, the program is ill-formed. [Example:

  void f() {
    for (int i = 0; i < 10; ++i)
      int i = 0;          // error: redeclaration
    for (int i : { 1, 2, 3 })
      int i = 1;          // error: redeclaration
  }

end example]




1784. Concurrent execution during static local initialization

Section: 8.8  [stmt.dcl]     Status: C++17     Submitter: Jens Maurer     Date: 2013-09-27

[Adopted at the February/March, 2017 meeting as document P0250R3.]

Regarding initialization of a block-scope static variable, 8.8 [stmt.dcl] paragraph 4 says,

If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

This specification does not use the terminology of 6.9.2 [intro.multithread], so the meaning of “wait” is not clear. For example, will a concurrent thread that “waited” see (in the sense of happens-before) the result of the initialization (including side effects caused during the initialization)?

Perhaps the “synchronizes-with” terminology could be used here.

Notes from the February, 2016 meeting:

SG1 concluded that wording similar to the following should be added:

For any action A such that the declaration is sequenced before A, initialization shall happen before A. The concurrent execution shall block for completion of the initialization.



2268. Unions with mutable members in constant expressions revisited

Section: 9.2.6  [dcl.constexpr]     Status: C++17     Submitter: Richard Smith     Date: 2016-05-26

[Adopted at the February/March, 2017 meeting.]

Issue 2004 concerns this example:

  union U { int a; mutable int b; };
  constexpr U u1 = {1};
  int k = (u1.b = 2);
  constexpr U u2 = u1; 

Clearly this must be ill-formed. But issue 2004 goes too far by making the copy and move operations of U non-constexpr. This breaks reasonable code such as:

  constexpr int f() {
    U u = {1};
    U v = u;
    return v.a;
  } 

Proposed resolution (February, 2017):

  1. Add the following as a new bullet following 7.7 [expr.const] bullet 2.8

  2. A conditional-expression e is a core constant expression unless the evaluation of e , following the rules of the abstract machine (6.9.1 [intro.execution]), would evaluate one of the following expressions:

  3. Delete bullet 3.2 in 9.2.6 [dcl.constexpr]:

  4. Delete bullet 4.2 in 9.2.6 [dcl.constexpr]:




2259. Unclear context describing ambiguity

Section: 9.3.3  [dcl.ambig.res]     Status: C++17     Submitter: Richard Smith     Date: 2016-04-12

[Adopted at the February/March, 2017 meeting.]

According to 9.3.3 [dcl.ambig.res] paragraph 3,

Another ambiguity arises in a parameter-declaration-clause of a function declaration, or in a type-id that is the operand of a sizeof or typeid operator, when a type-name is nested in parentheses.

There are two problems here: first, a parameter-declaration-clause appears in a lambda-expression, not just in a function declaration. Second, the ambiguity can arise in a type-id appearing in any context, not just in a sizeof or typeid expression.

Proposed resolution (January, 2017):

Change 9.3.3 [dcl.ambig.res] paragraph 3 as follows:

Another ambiguity arises in a parameter-declaration-clause of a function declaration, or in a type-id that is the operand of a sizeof or typeid operator, when a type-name is nested in parentheses. In this case, the choice is between the declaration of a parameter of type pointer to function and the declaration of a parameter with redundant parentheses around the declarator-id. The resolution is to consider the type-name as a simple-type-specifier rather than a declarator-id. [Example:...



253. Why must empty or fully-initialized const objects be initialized?

Section: 9.4  [dcl.init]     Status: C++17     Submitter: Mike Miller     Date: 11 Jul 2000

[Adopted at the November, 2016 meeting as part of paper P0490R0.]

Paragraph 9 of 9.4 [dcl.init] says:

If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for an object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.

What if a const POD object has no non-static data members? This wording requires an empty initializer for such cases:

    struct Z {
        // no data members
        operator int() const { return 0; }
    };

    void f() {
        const Z z1;         // ill-formed: no initializer
        const Z z2 = { };   // well-formed
    }

Similar comments apply to a non-POD const object, all of whose non-static data members and base class subobjects have default constructors. Why should the class of such an object be required to have a user-declared default constructor?

(See also issue 78.)

Additional note (February, 2011):

This issue should be brought up again in light of constexpr constructors and non-static data member initializers.

Notes from the August, 2011 meeting:

If the implicit default constructor initializes all subobjects, no initializer should be required.




2196. Zero-initialization with virtual base classes

Section: 9.4  [dcl.init]     Status: C++17     Submitter: Richard Smith     Date: 2015-11-06

[Adopted at the February/March, 2017 meeting.]

Consider:

  struct V { int n; };
  struct A : virtual V {};
  struct B : A {
   B() : V{123}, A() {}
  } b;

Initialization of b first performs aggregate initialization of the V virtual base subobject. Then it performs value initialization of the A base subobject. Per 9.4.1 [dcl.init.general] bullet 9.1.2, the A base subobject is zero-initialized. Per 9.4.1 [dcl.init.general] bullet 6.2, this zero-initializes the V virtual base subobject.

Result: b.n is required to be 0, not 123.

Proposed resolution (November, 2016):

Change 9.4 [dcl.init] bullet 6.2 as follows:

To zero-initialize an object or reference of type T means:




1622. Empty aggregate initializer for union

Section: 9.4.2  [dcl.init.aggr]     Status: C++17     Submitter: Daveed Vandevoorde     Date: 2013-02-14

[Adopted at the February/March, 2017 meeting.]

According to 9.4.2 [dcl.init.aggr] paragraph 15,

When a union is initialized with a brace-enclosed initializer, the braces shall only contain an initializer-clause for the first non-static data member of the union.

This would appear to preclude using {} as the initializer for a union, which would otherwise have reasonable semantics. Is there a reason for this restriction?

Also, paragraph 7 reads,

If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from an empty initializer list (9.4.5 [dcl.init.list]).

There should presumably be special treatment for unions, so that only a single member is initialized in such cases.

(See also issue 1460.)

Proposed resolution (November, 2016) [SUPERSEDED]:

Change 9.4.2 [dcl.init.aggr] paragraph 8 as follows:

If there are fewer initializer-clauses in the list than there are elements in the aggregate, then each Each non-variant element of the aggregate that is not explicitly initialized shall be is initialized from its default member initializer (11.4 [class.mem]) or, if there is no default member initializer, copy-initialized from an empty initializer list (9.4.5 [dcl.init.list]). If the aggregate is a union and the initializer list is empty, then

[Example:...

Proposed resolution (February, 2017):

The resolution of issue 2272 also resolves this issue.




2116. Direct or copy initialization for omitted aggregate initializers

Section: 9.4.2  [dcl.init.aggr]     Status: C++17     Submitter: Richard Smith     Date: 2015-04-22

[Adopted at the February/March, 2017 meeting.]

The Standard does not specify whether the initialization from {} that is done for omitted initializers in aggregate initialization is direct or copy initialization. There is divergence among implementations.

Proposed resolution (May, 2015) [SUPERSEDED]:

This issue is resolved by the resolution of issue 1630.

Notes from the October, 2015 meeting:

CWG agreed that copy initialization should be used; paragraph 7 should have wording similar to paragraph 2. See also issue 1518.

Additional notes (July, 2022):

The resolution of issue 2272 also resolves this issue.




2272. Implicit initialization of aggregate members of reference type

Section: 9.4.2  [dcl.init.aggr]     Status: C++17     Submitter: Vinny Romano     Date: 2016-06-10

[Adopted at the February/March, 2017 meeting.]

Consider:

  struct S {
    const int &i;
  } s{}; 

This example ought to be ill-formed, but 9.4.2 [dcl.init.aggr] paragraph 8 states that i is instead initialized from an empty initializer list, which causes i to bind to a value-initialized temporary of type int.

Proposed resolution (February, 2017):

  1. Change 9.4.2 [dcl.init.aggr] paragraph 8 as follows:

  2. If there are fewer initializer-clauses in the list than there are elements in the a non-union aggregate, then each element not explicitly initialized shall be initialized from its default member initializer (11.4 [class.mem]) or, if there is no default member initializer, from an empty initializer list (9.4.5 [dcl.init.list]). is initialized as follows:

    If the aggregate is a union and the initializer list is empty, then

    [Example:...

  3. Delete 9.4.2 [dcl.init.aggr] paragraph 11:

  4. If an incomplete or empty initializer-list leaves a member of reference type uninitialized, the program is ill-formed.

This resolution also resolves issues 1622 and 2116.




2251. Unreachable enumeration list-initialization

Section: 9.4.5  [dcl.init.list]     Status: C++17     Submitter: Richard Smith     Date: 2016-03-22

[Adopted at the February/March, 2017 meeting.]

P0138R2 adds a new bullet for enum initialization after bullet 8 of 9.4.5 [dcl.init.list] paragraph 3. However, paragraph 7 already dealt with all the cases where the initializer list contains a single element and the target type is a non-reference type, so the new paragraph 9 rule is unreachable.

Proposed resolution (December, 2016):

Reorder the bullets in 9.4.5 [dcl.init.list] paragraph 3 as follows:

List-initialization of an object or reference of type T is defined as follows:




2262. Attributes for asm-definition

Section: 9.10  [dcl.asm]     Status: C++17     Submitter: Richard Smith     Date: 2016-05-04

[Adopted at the February/March, 2017 meeting.]

There does not seem to be a good reason not to permit attributes on an asm declaration. This would be handy for things like:

  [[vendor::asm_syntax("intel")]] asm(...);

Notes from the December, 2016 teleconference:

The omission seems to have been an oversight that should be corrected.

Proposed resolution (January, 2017):

Change 9.10 [dcl.asm] paragraph 1 as follows:

An asm declaration has the form

The asm declaration is conditionally-supported; its meaning is implementation-defined. The optional attribute-specifier-seq in an asm-definition appertains to the asm declaration. [Note: Typically it is used to pass information through the implementation to an assembler. —end note]




2205. Restrictions on use of alignas

Section: 9.12.1  [dcl.attr.grammar]     Status: C++17     Submitter: Richard Smith     Date: 2015-11-30

[Adopted at the February/March, 2017 meeting.]

According to 9.12.1 [dcl.attr.grammar] paragraph 5, a program is ill-formed if an attribute appertains to an entity or statement to which it is not allowed to apply. Presumably an alignment-specifier should have the same restriction.

Proposed resolution (November, 2016):

Change 9.12.1 [dcl.attr.grammar] paragraph 5 as follows:

Each attribute-specifier-seq is said to appertain to some entity or statement, identified by the syntactic context where it appears (Clause 8 [stmt.stmt], 9.1 [dcl.pre], 9.3 [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 (11.8.4 [class.friend]), that declaration shall be a definition. No attribute-specifier-seq shall appertain to an explicit instantiation (13.9.3 [temp.explicit]).



2271. Aliasing this

Section: 11.4.5  [class.ctor]     Status: C++17     Submitter: Richard Smith     Date: 2016-06-20

[Moved to DR at the November, 2016 meeting.]

The restrictions against aliasing this inside a constructor should apply to all objects, not just to const objects.

Proposed resolution (June, 2016):

Change 11.4.5 [class.ctor] paragraph 12 as follows:

During the construction of a const an object, if the value of the object or any of its subobjects is accessed through a glvalue that is not obtained, directly or indirectly, from the constructor's this pointer, the value of the object or subobject thus obtained is unspecified. [Example:

  struct C;
  void no_opt(C*);

  struct C {
    int c;
    C() : c(0) { no_opt(this); }
  };

  const C cobj;

  void no_opt(C* cptr) {
    int i = cobj.c * 100; // value of cobj.c is unspecified
    cptr->c = 1;
    cout << cobj.c * 100  // value of cobj.c is unspecified
         << '\n';
  }

  extern struct D d;
  struct D {
    D(int a) : a(a), b(d.a) {}
    int a, b;
  };
  D d = D(1);             // value of d.b is unspecified

end example]




2094. Trivial copy/move constructor for class with volatile member

Section: 11.4.5.3  [class.copy.ctor]     Status: C++17     Submitter: Daveed Vandevoorde     Date: 2015-03-06

[Moved to DR at the November, 2016 meeting.]

The resolution of issue 496 included the addition of 11.4.5.3 [class.copy.ctor] paragraph 25. 2, making a class's copy/move constructor non-trivial if it has a non-static data member of volatile-qualified type. This change breaks the IA-64 ABI, so it has been requested that CWG reconsider this aspect of the resolution.

On a related note, the resolution of issue 496 also changed 6.8 [basic.types] paragraph 9, which makes volatile-qualified scalar types “trivial” but not “trivially copyable.” It is not clear why there is a distinction made here; the only actual use of “trivial type” in the Standard appears to be in the description of qsort, which should probably use “trivially copyable.” (See also issue 1746.)

Notes from the February, 2016 meeting:

CWG agreed with the suggested direction for the changes in 11.4.5.3 [class.copy.ctor]; the use of “trivial” will be dealt with separately and not as part of the resolution of this issue.

Proposed resolution (June, 2016):

  1. Change 6.8 [basic.types] paragraph 9 as follows:

  2. ...called POD types. Cv-unqualified scalar types, trivially copyable class types (Clause 11 [class]), arrays of such types, and non-volatile const-qualified cv-qualified versions of these types (6.8.5 [basic.type.qualifier]) are collectively called trivially copyable types. Scalar types...
  3. Delete bullet 12.2 of 11.4.5.3 [class.copy.ctor]:

  4. A copy/move constructor for class X is trivial if it is not user-provided, its parameter-type-list is equivalent to the parameter-type-list of an implicit declaration, and if

  5. Delete bullet 25.2 of 11.4.5.3 [class.copy.ctor]:

  6. A copy/move assignment operator for class X is trivial if it is not user-provided, its parameter-type-list is equivalent to the parameter-type-list of an implicit declaration, and if




2197. Overload resolution and deleted special member functions

Section: 11.4.5.3  [class.copy.ctor]     Status: C++17     Submitter: Maxim Kartashev     Date: 2015-11-11

The resolution to issue 535 replaced references to "copy constructor" with "constructor selected by overload resolution". 11.4.5.3 [class.copy.ctor] paragraph 10 was missed.

Notes from the November, 2016 meeting:

This issue is to be handled editorially and is in "review" status to check that the change has been applied.

Additional notes (February, 2022):

The change has been applied editorially with commit 6953b2.




1860. What is a “direct member?”

Section: 11.5  [class.union]     Status: C++17     Submitter: Dawn Perchik     Date: 2014-02-13

[Adopted at the February/March, 2017 meeting.]

The term “direct member” is used in 11.5 [class.union] paragraph 8 but is not defined. It might be better to refer to the class's member-specification instead.

Additional note, October, 2015:

This issue is expected to be addressed by the wording of N4532 or a successor thereof (“Default Comparisons”).

Proposed resolution (November, 2016):

  1. Change 9.4.2 [dcl.init.aggr] paragraph 2 as follows:

  2. The elements of an aggregate are:

  3. Change 11.4 [class.mem] paragraph 1 as follows:

  4. The member-specification in a class definition declares the full set of members of the class; no member can be added elsewhere. A direct member of a class X is a member of X that was first declared within the member-specification of X, including anonymous union objects and direct members thereof. Members of a class are...
  5. Change 11.5.2 [class.union.anon] paragraph 1 as follows:

  6. A union of the form

    is called an anonymous union; it defines an unnamed type and an unnamed object of unnamed that type called an anonymous union object. Each member-declaration in the member-specification of an anonymous union

    ...



1710. Missing template keyword in class-or-decltype

Section: 11.7  [class.derived]     Status: C++17     Submitter: Richard Smith     Date: 2013-07-03

[Adopted at the February/March, 2017 meeting.]

A class-or-decltype is used as a base-specifier and as a mem-initializer-id that names a base class. It is specified in 11.7 [class.derived] paragraph 1 as:

Consequently, a declaration like

  template<typename T> struct D : T::template B<int>::template C<int> {};

is ill-formed, although most implementations accept it; some actually require the use of the template keyword, although the relevant wording in 13.3 [temp.names] paragraph 4 only requires it in a qualified-id, not in a class-or-decltype. It would probably be good to add a production like

to the definition of class-or-decltype and explicitly mention those contexts in 13.3 [temp.names] as not requiring use of the template keyword.

Additional note (January, 2014):

This is effectively issues 314 and 343.

See also issue 1812.

Proposed resolution (February, 2014) [SUPERSEDED]:

  1. Change Clause 11 [class] paragraph 3 as follows:

  2. If a class is marked with the class-virt-specifier final and it appears as a base-type-specifier class-or-decltype in a base-clause (11.7 [class.derived]), the program is ill-formed. Whenever a class-key is followed...
  3. Change the grammar in 11.7 [class.derived] paragraph 1 as follows:

  4. Delete paragraph 4 and change paragraph 5 of 13.3 [temp.names] as follows, splitting paragraph 5 into two paragraphs and moving the example from paragraph 4 into paragraph 5:

  5. When the name of a member template specialization appears after . or -> in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (13.8.3.2 [temp.dep.type]), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template. [Example: ... —end example]

    A name prefixed by the keyword template shall be a template-id or the name shall refer to a class template or alias template. [Note: The keyword template may not be applied to non-template members of class templates. —end note] The nested-name-specifier (_N4567_.5.1.1 [expr.prim.general]) of

    or a nested-name-specifier directly contained in such a nested-name-specifier (recursively), shall not be of the form

    [Note: That is, a simple-template-id shall not be prefixed by the keyword template in these cases. —end note]

    The keyword template is optional in a typename-specifier (13.8 [temp.res]), elaborated-type-specifier (9.2.9.5 [dcl.type.elab]), using-declaration (9.9 [namespace.udecl]), or class-or-decltype (11.7 [class.derived]), and in recursively directly-contained nested-name-specifiers thereof. In these contexts, a < token is always assumed to introduce a template-argument-list. [Note: Thus, if the preceding name is not a template-name, the program is ill-formed. —end note] In other contexts, when the name of a member template specialization appears after a nested-name-specifier that denotes a dependent type, but the name is not a member of the current instantiation, the member template name shall be prefixed by the keyword template. Similarly, when the name of a member template specialization appears after . or -> in a postfix-expression (7.6.1 [expr.post]) and the object expression of the postfix-expression is type-dependent, but the name is not a member of the current instantiation (13.8.3.2 [temp.dep.type]), the member template name shall be prefixed by the keyword template. Otherwise, the name is assumed to name a non-template. [Example:

        <From original paragraph 4>
    

    end example] [Note: As is the case with the typename prefix...

This resolution also resolves issues 314, 343, 1794, and 1812.

Additional note, November, 2014:

Concerns have been expressed over the clarity and organization of the proposed resolution, so the issue has been moved back to "review" status to allow CWG to address these concerns.

Proposed resolution, March, 2017:

  1. Change Clause 11 [class] paragraph 3 as follows:

  2. If a class is marked with the class-virt-specifier final and it appears as a base-type-specifier class-or-decltype in a base-clause ( 11.7 [class.derived]), the program is ill-formed. Whenever a class-key is followed by a class-head-name...
  3. Change the grammar in 11.7 [class.derived] paragraph 1 as follows:

  4. Change 11.7 [class.derived] paragraph 2 as followx:

  5. The type denoted by a base-type-specifier A class-or-decltype shall be denote a class type that is not an incompletely defined class (Clause 11 [class]); this. The class denoted by the class-or-decltype of a base-specifier is called a direct base class for the class being defined. During the lookup for a base class name...
  6. Change 13.3 [temp.names] paragraphs 4 and 5 as follows:

  7. When the name of a member template specialization appears after . or -> in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation The keyword template is said to appear at the top level in a qualified-id if it appears outside of a template-argument-list or decltype-specifier. In a qualified-id of a declarator-id or in a qualified-id formed by a class-head-name ( Clause 11 [class]) or enum-head-name (9.7.1 [dcl.enum]), the keyword template shall not appear at the top level. In a qualified-id used as the name in a typename-specifier (13.8 [temp.res]), elaborated-type-specifier (9.2.9.5 [dcl.type.elab]), using-declaration (9.9 [namespace.udecl]), or class-or-decltype ( 11.7 [class.derived]), an optional keyword template appearing at the top level is ignored. In these contexts, a < token is always assumed to introduce a template-argument-list. In all other contexts, when naming a template specialization of a member of an unknown specialization (13.8.3.2 [temp.dep.type]), the member template name must shall be prefixed by the keyword template. Otherwise the name is assumed to name a non-template. [Example:...

    A name prefixed by the keyword template shall be a template-id or the name shall refer to a class template or an alias template. [Note: The keyword template may not be applied to non-template members of class templates. —end note]...

  8. Change 13.8 [temp.res] paragraph 5 as follows:

  9. A qualified name used as the name in a mem-initializer-id, a base-specifier, class-or-decltype ( 11.7 [class.derived]) 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. —end note]



314. template in base class specifier

Section: 13.3  [temp.names]     Status: C++17     Submitter: Mark Mitchell     Date: 23 Aug 2001

[Adopted at the February/March, 2017 meeting.]

The EDG front-end accepts:

template <typename T>
struct A {
  template <typename U>
  struct B {};
};

template <typename T>
struct C : public A<T>::template B<T> {
};

It rejects this code if the base-specifier is spelled A<T>::B<T>.

However, the grammar for a base-specifier does not allow the template keyword.

Suggested resolution:

It seems to me that a consistent approach to the solution that looks like it will be adopted for issue 180 (which deals with the typename keyword in similar contexts) would be to assume that B is a template if it is followed by a "<". After all, an expression cannot appear in this context.

Notes from the 4/02 meeting:

We agreed that template must be allowed in this context. The syntax needs to be changed. We also opened the related issue 343.

Additional note (August, 2010):

The same considerations apply to mem-initializer-ids, as noted in issue 1019.

Additional note (January, 2014):

See also issue 1710.

Proposed resolution (March, 2017):

This issue is resolved by the resolution of issue 1710.




343. Make template optional in contexts that require a type

Section: 13.3  [temp.names]     Status: C++17     Submitter: Steve Adamczyk     Date: 23 April 2002

[Adopted at the February/March, 2017 meeting.]

By analogy with typename, the keyword template used to indicate that a dependent name will be a template name should be optional in contexts where a type is required, e.g., base class lists. We could also consider member and parameter declarations.

This was suggested by issue 314.

Additional note (January, 2014):

See also issue 1710.

Proposed resolution (March, 2017):

This issue is resolved by the resolution of issue 1710.




1794. template keyword and alias templates

Section: 13.3  [temp.names]     Status: C++17     Submitter: Jonathan Caves     Date: 2013-10-04

[Adopted at the February/March, 2017 meeting.]

The current wording of 13.3 [temp.names] paragraph 5 is:

A name prefixed by the keyword template shall be a template-id or the name shall refer to a class template.

Presumably this should also allow template before alias templates. For example,

  template<template<typename> class Template>
  struct Internal {
          template<typename Arg>
          using Bind = Template<Arg>;
  };

  template<template<typename> class Template, typename Arg>
  using Instantiate = Template<Arg>;

  template<template<typename> class Template, typename Argument>
  using Bind = Instantiate<Internal<Template>::template Bind, Argument>;

Proposed resolution (March, 2017):

This issue is resolved by the resolution of issue 1710.




1812. Omission of template in a typename-specifier

Section: 13.3  [temp.names]     Status: C++17     Submitter: Daveed Vandevoorde     Date: 2013-11-18

[Adopted at the February/March, 2017 meeting.]

According to 13.3 [temp.names] paragraph 4,

When the name of a member template specialization appears after . or -> in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (13.8.3.2 [temp.dep.type]), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.

This does not seem necessary in a typename-specifier; a < following a qualified-id in a typename-specifier could safely be assumed to begin a template argument list, so the template keyword should be optional in this case. Some implementations already do not enforce this requirement.

See also issue 1710.

Proposed resolution (March, 2017):

This issue is resolved by the resolution of issue 1710.




150. Template template parameters and default arguments

Section: 13.4.4  [temp.arg.template]     Status: C++17     Submitter: Mike Miller     Date: 3 Aug 1999

[Moved to DR at the November, 2016 meeting as paper P0522R0.]

[Picked up by evolution group at October 2002 meeting.]

How are default template arguments handled with respect to template template parameters? Two separate questions have been raised:

  1. Do default template arguments allow a template argument to match a template parameter with fewer template parameters, and can the template template parameter be specialized using the smaller number of template arguments? For example,
        template <class T, class U = int>
        class ARG { };
    
        template <class X, template <class Y> class PARM>
        void f(PARM<X>) { }    // specialization permitted?
    
        void g() {
            ARG<int> x;        // actually ARG<int, int>
            f(x);              // does ARG (2 parms, 1 with default)
                               // match PARM (1 parm)?
    
    Template template parameters are deducible (13.10.3.6 [temp.deduct.type] paragraph 9) , but 13.4.4 [temp.arg.template] does not specify how matching is done.

    Jack Rouse: I implemented template template parameters assuming template signature matching is analogous to function type matching. This seems like the minimum reasonable implementation. The code in the example would not be accepted by this compiler. However, template default arguments are compile time entities so it seems reasonable to relax the matching rules to allow cases like the one in the example. But I would consider this to be an extension to the language.

    Herb Sutter: An open issue in the LWG is that the standard doesn't explicitly permit or forbid implementations' adding additional template-parameters to those specified by the standard, and the LWG may be leaning toward explicitly permitting this. [Under this interpretation,] if the standard is ever modified to allow additional template-parameters, then writing "a template that takes a standard library template as a template template parameter" won't be just ugly because you have to mention the defaulted parameters; it would not be (portably) possible at all except possibly by defining entire families of overloaded templates to account for all the possible numbers of parameters vector<> (or anything else) might actually have. That seems unfortunate.

  2. Are default arguments permitted in the template parameter list of a template template parameter? For example,
        template <template <class T, class U = int> class PARM>
        class C {
            PARM<int> pi;
        };
    

    Jack Rouse: I decided they could not in the compiler I support. This continues the analogy with function type matching. Also, I did not see a strong need to allow default arguments in this context.

    A class template used as a template template argument can have default template arguments from its declarations. How are the two sources of default arguments to be reconciled? The default arguments from the template template formal could override. But it could be cofusing if a template-id using the argument template, ARG<int>, behaves differently from a template-id using the template formal name, FORMAL<int>.

Rationale (10/99): Template template parameters are intended to be handled analogously to function function parameters. Thus the number of parameters in a template template argument must match the number of parameters in a template template parameter, regardless of whether any of those paramaters have default arguments or not. Default arguments are allowed for the parameters of a template template parameter, and those default arguments alone will be considered in a specialization of the template template parameter within a template definition; any default arguments for the parameters of a template template argument are ignored.

Note (Mark Mitchell, February, 2006):

Perhaps it is already obvious to all, but it seems worth noting that this extension would change the meaning of conforming programs:

    struct Dense { static const unsigned int dim = 1; };

    template <template <typename> class View,
              typename Block>
    void operator+(float, View<Block> const&);

    template <typename Block,
              unsigned int Dim = Block::dim>
    struct Lvalue_proxy { operator float() const; };

    void test_1d (void) {
        Lvalue_proxy<Dense> p;
        float b;
        b + p;
    }

If Lvalue_proxy is allowed to bind to View, then the template operator+ will be used to perform addition; otherwise, Lvalue_proxy's implicit conversion to float, followed by the built-in addition on floats will be used.

Note (March, 2008):

The Evolution Working Group has accepted the intent of this issue and referred it to CWG for action (not for C++0x). See paper J16/07-0033 = WG21 N2173.

Notes from the June, 2008 meeting:

The CWG decided to take no action on this issue until an interested party produces a paper with analysis and a proposal.




1393. Pack expansions in using-declarations

Section: 13.7.4  [temp.variadic]     Status: C++17     Submitter: Daniel Krügler     Date: 2011-09-10

[Accepted at the November, 2016 meeting as part of paper P0195R2.]

It would be handy if a pack expansion could appear in a using-declaration:

    template <class... Mixins>
    class X : Mixins... {
      using Mixins::foo ...; // Currently not supported
    };

Rationale (February, 2012):

This is not a defect but a request for an extension and thus more appropriately addressed by EWG.




1432. Newly-ambiguous variadic template expansions

Section: 13.7.4  [temp.variadic]     Status: C++17     Submitter: Daniel Krügler     Date: 2011-12-17

[Resolved by issue 1395, which was moved to DR at the November, 2016 meeting.]

With the new core rules in regard to variadic pack expansions the library specification of the traits template common_type is now broken, the reason is that it is defined as a series of specializations of the primary template

    template <class ...T>
    struct common_type;

The broken one is this pair:

  template <class T, class U>
  struct common_type<T, U> {
   typedef decltype(true ? declval<T>() : declval<U>()) type;
  };

  template <class T, class U, class... V>
  struct common_type<T, U, V...> {
   typedef typename common_type<typename common_type<T, U>::type, V...>::type type;
  };

With the new rules both specializations would now be ambiguous for an instantiation like common_type<X, Y>.

(See also issue 1395.)

Notes from the October, 2012 meeting:

It is possible that 13.7.6.3 [temp.spec.partial.order] may resolve this problem.

CWG 2022-11-11

Resolved by issue 1395; see the special rule about trailing packs in 13.10.3.5 [temp.deduct.partial] paragraph 11.




2174. Unclear rules for friend definitions in templates

Section: 13.7.5  [temp.friend]     Status: C++17     Submitter: Richard Smith     Date: 2015-09-17

[Adopted at the February/March, 2017 meeting.]

According to 13.7.5 [temp.friend] paragraph 4,

When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used (6.3 [basic.def.odr]). The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.

This seems to imply that:

  1. Instantiating a class template that contains a friend function definition instantiates the declaration, but not the definition, of that friend function, as usual (but see below).

  2. If the function is odr-used, a definition is instantiated for each such class template specialization whose template had a definition.

  3. If that results in multiple definitions, the program is ill-formed as usual.

The intent appears to be that the instantiated friend function declarations should be treated as if they were definitions, but that's not clear from the wording. This wording is also missing similar provisions for friend function template definitions; there is implementation divergence on the treatment of such cases.

There also does not appear to be wording that says that instantiating a class template specialization results in the instantiation of friend functions declared/defined therein (the relevant wording was removed from this section by issue 329). Presumably this should be covered in 13.9.2 [temp.inst] paragraph 1, which also includes the following wording that could be reused for the friend case:

However, for the purpose of determining whether an instantiated redeclaration of a member is valid according to 11.4 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition.

Also, the reliance on odr-use to trigger friend instantiation is out of date, as there are other contexts that can require an instantiation when there is no odr-use (a constexpr function invoked within an unevaluated operand).

Proposed resolution (October, 2015) [SUPERSEDED]:

  1. Delete 13.7.5 [temp.friend] paragraph 4:

  2. When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used (6.3 [basic.def.odr]). The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.
  3. Change 13.9.2 [temp.inst] paragraph 1 as follows:

  4. ...The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or exception-specifications of the class member functions, member classes, scoped member enumerations, static data members, and member templates, and friends; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions. However, for the purpose of determining whether an instantiated redeclaration of a member is valid according to 6.3 [basic.def.odr] and 11.4 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition. [Example:...
  5. Change 13.9.2 [temp.inst] paragraph 3 as follows:

  6. Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. A function whose declaration was instantiated from a friend function definition is implicitly instantiated when it is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.

Proposed resolution (November, 2016):

  1. Delete 13.7.5 [temp.friend] paragraph 4:

  2. When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used (6.3 [basic.def.odr]). The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.
  3. Change 13.9.2 [temp.inst] paragraph 1 as follows, splitting it into two paragraphs as indicated:

  4. ... [Note: Within a template declaration, a local class (11.6 [class.local]) or enumeration and the members of a local class are never considered to be entities that can be separately instantiated (this includes their default arguments, exception-specifications, and non-static data member initializers, if any). As a result, the dependent names are looked up, the semantic constraints are checked, and any templates used are instantiated as part of the instantiation of the entity within which the local class or enumeration is declared. —end note]

    The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or exception-specifications of the class member functions, member classes, scoped member enumerations, static data members, and member templates, and friends; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions. However, for the purpose of determining whether an instantiated redeclaration of a member is valid according to 6.3 [basic.def.odr] and 11.4 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition. [Example:

      template<class T, class U>
      struct Outer {
        template<class X, class Y> struct Inner;
        template<class Y> struct Inner<T, Y>;     // #1a
        template<class Y> struct Inner<T, Y> { }; // #1b; OK: valid redeclaration of #1a
        template<class Y> struct Inner<U, Y> { }; // #2
      };
    
      Outer<int, int> outer; // error at #2
    

    Outer<int, int>::Inner<int, Y> is redeclared at #1b. (It is not defined but noted as being associated with a definition in Outer<T, U>.) #2 is also a redeclaration of #1a. It is noted as associated with a definition, so it is an invalid redeclaration of the same partial specialization.

      template<typename T> struct Friendly {
        template<typename U> friend int f(U) { return sizeof(T); }
      };
      Friendly<char> fc;
      Friendly<float> ff; // ill-formed: produces second definition of f(U)
    

    end example]

  5. Change 13.9.2 [temp.inst] paragraph 3 as follows:

    Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. A function whose declaration was instantiated from a friend function definition is implicitly instantiated when it is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.



2143. Value-dependency via injected-class-name

Section: 13.8.3.2  [temp.dep.type]     Status: C++17     Submitter: Maxim Kartashev     Date: 2015-06-16

[Moved to DR at the November, 2016 meeting.]

There does not appear to be a rule that causes p or this to be dependent in the following example:

  template <typename T> struct A {
     void foo() {
        A* p = 0;
        bar(p);    // will be found by ADL at the point of instantiation
        bar(this); // same here
     }
  };

  void bar(...);

  int main() {
     A<int> a;
     a.foo();
  }

Proposed resolution (February, 2016) [SUPERSEDED]:

Change 13.8.3.2 [temp.dep.type] bullet 9.7 as follows:

A type is dependent if it is

Proposed resolution (March, 2016):

Change 13.8.3.2 [temp.dep.type] bullet 9.7 as follows:

A type is dependent if it is




2100. Value-dependent address of static data member of class template

Section: 13.8.3.4  [temp.dep.constexpr]     Status: C++17     Submitter: Richard Smith     Date: 2015-03-16

[Moved to DR at the November, 2016 meeting.]

According to bullet 2.4 of 13.8.3.4 [temp.dep.constexpr], an id-expression is value-dependent if

This implies that the address of an initialized static data member is not value-dependent, which is incorrect.

Proposed resolution (June, 2016):

Change 13.8.3.4 [temp.dep.constexpr] paragraph 5 as follows:

An expression of the form &qualified-id where the qualified-id names a dependent member of the current instantiation is value-dependent. An expression of the form

is also value-dependent if evaluating cast-expression as a core constant expression (7.7 [expr.const]) succeeds and the result of the evaluation refers to a templated entity that is an object with static or thread storage duration or a member function.




2276. Dependent noexcept and function type-dependence

Section: 13.8.3.4  [temp.dep.constexpr]     Status: C++17     Submitter: Maxim Kartashev     Date: 2016-06-23

[Adopted at the February/March, 2017 meeting.]

Consider:

  template <bool B> struct A
  {
    void static foo() noexcept(B);// only dependent on B

    void bar(struct X* x) {
      buz<noexcept(foo)>(x); // dependent call through explicit template arguments?
    }
  }; 

A value-dependent exception specification ought to make the type of the corresponding function type-dependent.

Proposed resolution (February, 2017):

Add the following as a new bullet following 13.8.3.2 [temp.dep.type] bullet 9.6:




727. In-class explicit specializations

Section: 13.9.4  [temp.expl.spec]     Status: C++17     Submitter: Faisal Vali     Date: 5 October, 2008

[Adopted as a DR at the February/March, 2017 meeting.]

13.9.4 [temp.expl.spec] paragraph 2 requires that explicit specializations of member templates be declared in namespace scope, not in the class definition. This restriction does not apply to partial specializations of member templates; that is,

    struct A {
      template<class T> struct B;
      template <class T> struct B<T*> { }; // well-formed
      template <> struct B<int*> { }; // ill-formed
    };

There does not seem to be a good reason for this inconsistency.

Additional note (October, 2013):

EWG has requested CWG to consider resolving this issue. See EWG issue 41.

Additional note, November, 2014:

See also paper N4090.

Proposed resolution (March, 2017):

  1. Change 13.7.6.1 [temp.spec.partial.general] paragraph 6 as follows:

  2. A class template partial specialization may be declared or redeclared in any namespace scope in which the corresponding primary template may be defined (_N4868_.9.8.2.3 [namespace.memdef] and , 11.4 [class.mem], 13.7.3 [temp.mem]). [Example:

      template<class T> struct A {
        struct C {
          template<class T2> struct B { };
          template<class T2> struct B<T2**> { };  // partial specialization #1
        };
      };
    
      // partial specialization of A<T>::C::B<T2>
      template<class T> template<class T2>
        struct A<T>::C::B<T2*> { };    // #2
    
    
      A<short>::C::B<int*> absip; // uses partial specialization #2
    
    

    end example]

  3. Change 13.9.4 [temp.expl.spec] paragraph 2 as follows:

  4. An explicit specialization shall be declared in a namespace enclosing the specialized template. An explicit specialization whose declarator-id or class-head-name is not qualified shall be declared in the nearest enclosing namespace of the template, or, if the namespace is inline (9.8.2 [namespace.def]), any namespace from its enclosing namespace set. Such a declaration may also be a definition may be declared in any scope in which the corresponding primary template may be defined (_N4868_.9.8.2.3 [namespace.memdef], 11.4 [class.mem], 13.7.3 [temp.mem]). If the declaration is not a definition, the specialization may be defined later (_N4868_.9.8.2.3 [namespace.memdef]).



1825. Partial ordering between variadic and non-variadic function templates

Section: 13.10.3.5  [temp.deduct.partial]     Status: C++17     Submitter: Steve Clamage     Date: 2013-12-30

[Moved to DR at the November, 2016 meeting.]

Given the following example,

  template <class ...T> int f(T*...)  { return 1; }
  template <class T>  int f(const T&) { return 2; }
  void g() {
    f((int*)0);
  }

the current specification makes the call ambiguous because deduction fails in both directions: with A being T and P being T* in one direction and A being T* and P being T, because 13.10.3.5 [temp.deduct.partial] paragraph 8 says,

If A was transformed from a function parameter pack and P is not a parameter pack, type deduction fails.

It is not clear whether this is the best outcome, however; it might be better to consider the first template more specialized, with the variadic/non-variadic test being a tie-breaker if there is no other reason to prefer one over the other based on the parameter types.

Notes from the February, 2014 meeting:

CWG felt that the best approach would be, when comparing P and A, if A is a pack and P is not, A should be repeated for each remaining instance of P and then use the variadic/nonvariadic criterion as a late tiebreaker if the result is still ambiguous. This would apply in the general case (including 13.10.3.5 [temp.deduct.partial]), not just in function calls.

Proposed resolution (June, 2016):

This issue is resolved by the resolution of issue 1395.




1395. Partial ordering of variadic templates reconsidered

Section: 13.10.3.6  [temp.deduct.type]     Status: C++17     Submitter: John Spicer     Date: 2011-09-21

[Moved to DR at the November, 2016 meeting.]

The resolution of issue 692 (found in document N3281) made the following example ambiguous and thus ill-formed:

    template<class T>
    void print(ostream &os, const T &t) {
        os << t;
    }

    template <class T, class... Args>
    void print(ostream &os, const T &t, const Args&... rest) {
        os << t << ", ";
        print(os, rest...);
    }

    int main() {
        print(cout, 42);
        print(cout, 42, 1.23);
    }

This pattern seems fairly intuitive; is it reason to reconsider or modify the outcome of issue 692?

(See also issue 1432.)

Notes from the October, 2012 meeting:

CWG agreed that the example should be accepted, handling this case as a late tiebreaker, preferring an omitted parameter over a parameter pack.

Additional note (March, 2013):

For another example:

  template<typename ...T> int f(T*...) {
    return 1;
  }
  template<typename T> int f(const T&) {
    return 2;
  }
  int main() {
    if (f((int*)0) != 1) {
       return 1;
    }
    return 0;
  }

This worked as expected prior to the resolution of issue 692.

Proposed resolution (June, 2016):

  1. Change 13.10.3.5 [temp.deduct.partial] paragraph 8 as follows:

  2. If A was transformed from a function parameter pack and P is not a parameter pack, type deduction fails. Otherwise, using Using the resulting types P and A, the deduction is then done as described in 13.10.3.6 [temp.deduct.type]. If P is a function parameter pack, the type A of each remaining parameter type of the argument template is compared with the type P of the declarator-id of the function parameter pack. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. Similarly, if A was transformed from a function parameter pack, it is compared with each remaining parameter type of the parameter template. If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template. [Example:...
  3. Add the following as a new paragraph following 13.10.3.5 [temp.deduct.partial] paragraph 10:

  4. Function template F is at least as specialized as function template G if, for each pair of types used to determine the ordering, the type from F is at least as specialized as the type from G. F is more specialized than G if F is at least as specialized as G and G is not at least as specialized as F.

    If, after considering the above, function template F is at least as specialized as function template G and vice-versa, and if G has a trailing paramter pack for which F does not have a corresponding parameter, and if F does not have a trailing parameter pack, then F is more specialized than G.

This resolution also resolves issue 1825.




2191. Incorrect result for noexcept(typeid(v))

Section: 14.5  [except.spec]     Status: C++17     Submitter: Mike Miller     Date: 2015-10-26

[Adopted at the November, 2016 meeting as part of paper P0003R5.]

Consider the following example:

    struct B { virtual void f() { } };
    struct D : B { } d;
    bool b = noexcept(typeid(d));

According to 7.6.2.7 [expr.unary.noexcept] paragraph 3, the value of b should be false, because 14.5 [except.spec] bullet 14.6 says,

and d is such an expression. This is clearly bogus, as the expression cannot possibly throw; according to 7.6.1.8 [expr.typeid] paragraph 2, the condition under which the exception might be thrown is:

If the glvalue expression is obtained by applying the unary * operator to a pointer69 and the pointer is a null pointer value (7.3.12 [conv.ptr]), the typeid expression throws an exception (14.2 [except.throw]) of a type that would match a handler of type std::bad_typeid exception (17.7.5 [bad.typeid]).

Proposed resolution (November, 2016):

Change 14.5 [except.spec] bullet 13.6 as follows:

The set of potential exceptions of an expression e is empty if e is a core constant expression (7.7 [expr.const]). Otherwise, it is the union of the sets of potential exceptions of the immediate subexpressions of e, including default argument expressions used in a function call, combined with a set S defined by the form of e, as follows:






Issues with "C++20" Status


2433. Variable templates in the ODR

Section: 6.3  [basic.def.odr]     Status: C++20     Submitter: Richard Smith     Date: 2019-10-10

[Adopted as a DR at the November, 2019 meeting.]

The list of entities in 6.3 [basic.def.odr] paragraph 12 that can have multiple definitions across translation units does not, but should, include variable templates.

Proposed resolution (October, 2019):

There can be more than one definition of a

in a program provided that...

[Drafting note: “with external linkage” is not needed for the inline entities because the other cases - entities attached to a named module and multiple definitions in the same translation unit - are ruled out later in that paragraph.]


2323. Expunge POD

Section: 6.8  [basic.types]     Status: C++20     Submitter: US     Date: 2017-02-27

[Adopted at the October, 2017 meeting as paper P0767R1.]

P0488R0 comment US 101

The term POD no longer serves a purpose in the standard, it is merely defined, and restrictions apply for when a few other types preserve this vestigial property. The is_pod trait should be deprecated, moving the definition of a POD type alongside the trait in Annex Clause Annex D [depr], and any remaining wording referring to POD should be struck, or revised to clearly state intent (usually triviality) without mentioning PODs.




2431. Full-expressions and temporaries bound to references

Section: 6.9  [basic.exec]     Status: C++20     Submitter: Andrey Erokhin     Date: 2019-02-07

[Adopted as a DR at the November, 2019 meeting.]

According to 6.9 [basic.exec] paragraph 5,

A full-expression is

This definition excludes the destruction of temporaries that are bound to references from being treated as full-expressions. It is not clear whether this omission has observable effects or not. See editorial issue 2664.

Proposed resolution (October, 2019):

Change 6.9 [basic.exec] bullet 5.5 as follows:

A full-expression is




2378. Inconsistent grammar for reference init-capture of pack

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: C++20     Submitter: Barry Revzin     Date: 2018-06-14

[Accepted (as paper P2095R0) at the February, 2020 (Prague) meeting.]

The grammar for init-capture is:

As a consequence a reference init-capture pack is written as

  [...&x = init]

instead of the way packs of references are commonly written:

  [&...x = init]

Notes from the November, 2018 meeting:

CWG agreed with the suggested direction.




2347. Passing short scoped enumerations to ellipsis

Section: 7.6.1.3  [expr.call]     Status: C++20     Submitter: Mike Miller     Date: 2017-04-28

[Adopted as a DR at the November, 2019 meeting.]

According to 7.6.1.3 [expr.call] paragraph 9,

If the argument has integral or enumeration type that is subject to the integral promotions (7.3.7 [conv.prom]), or a floating-point type that is subject to the floating-point promotion (7.3.8 [conv.fpprom]), the value of the argument is converted to the promoted type before the call. These promotions are referred to as the default argument promotions.

A scoped enumeration with an underlying type that is shorter than int will not be widened when passed to an ellipsis. Should it be?

Notes from the June, 2018 meeting:

The consensus of CWG was that the value passed ougnt to be widened to match the promoted type of the underlying type.

Proposed resolution (May, 2019): [SUPERSEDED]

Change 7.6.1.3 [expr.call] paragraph 12 as follows:

...If the argument has an integral or enumeration type that is subject to the integral promotions (7.3.7 [conv.prom]), a scoped enumeration type whose underlying type is subject to the integral promotions, or a floating-point type that is subject to the floating-point promotion (7.3.8 [conv.fpprom]), the value of the argument is converted to the promoted type before the call. These promotions are referred to as the default argument promotions.

Notes from the September, 2019 teleconference:

The consensus was that passing scoped enumerations to ellipsis should be conditionally-supported behavior, similar to the treatment of class types with nontrivial copy semantics.

Proposed resolution (October, 2019):

Change 7.6.1.3 [expr.call] paragraph 12 as follows:

...Passing a potentially-evaluated argument of a scoped enumeration type or of a class type ( Clause 11 [class]) having an eligible non-trivial copy constructor, an eligible non-trivial move constructor, or a non-trivial destructor (11.4.4 [special]), with no corresponding parameter, is conditionally-supported with implementation-defined semantics. If the argument...



2280. Matching a usual deallocation function with placement new

Section: 7.6.2.8  [expr.new]     Status: C++20     Submitter: Richard Smith     Date: 2016-06-27

[Accepted as a DR at the November, 2019 meeting.]

The restrictions in 7.6.2.8 [expr.new] against a placement deallocation function signature matching a usual deallocation function signature consider only std::size_t parameters, omitting std::align_val_t parameters.

Proposed resolution (February, 2017):

Change 7.6.2.8 [expr.new] paragraph 27 as follows:

A declaration of a placement deallocation function matches the declaration of a placement allocation function if it has the same number of parameters and, after parameter transformations (9.3.4.6 [dcl.fct]), all parameter types except the first are identical. If the lookup finds a single matching deallocation function, that function will be called; otherwise, no deallocation function will be called. If the lookup finds a usual deallocation function with a parameter of type std::size_t (6.7.5.5.3 [basic.stc.dynamic.deallocation]) and that function, considered as a placement deallocation function, would have been selected as a match for the allocation function, the program is ill-formed. For a non-placement allocation function, the normal deallocation function lookup is used to find the matching deallocation function (7.6.2.9 [expr.delete]). [Example:...
>


2282. Consistency with mismatched aligned/non-over-aligned allocation/deallocation functions

Section: 7.6.2.8  [expr.new]     Status: C++20     Submitter: Richard Smith     Date: 2016-06-27

[Adopted as a DR at the November, 2019 meeting.]

The fallback treatment for alignment and non-alignment allocation and deallocation functions is asymmetric. While a deletion of a non-overaligned class object will match a class-specific alignment deallocation function if no class-specific non-alignment deallocation function is provided, the same is not true for allocation: a new-expression for a non-overaligned class type will fail if an alignment allocation function is provided with no non-alignment allocation function. The allocation behavior should be changed to match the deallocation behavior.

Proposed resolution (September, 2019):

Change 7.6.2.8 [expr.new] paragraph 18 as follows, splitting the running text into bullets:

Overload resolution is performed on a function call created by assembling an argument list. The first argument is the amount of space requested, and has type std::size_t. If the type of the allocated object has new-extended alignment, the next argument is the type's alignment, and has type std::align_val_t. If the new-placement syntax is used, the initializer-clauses in its expression-list are the succeeding arguments. If no matching function is found then

and then overload resolution is performed again.




2419. Loss of generality treating pointers to objects as one-element arrays

Section: 7.6.6  [expr.add]     Status: C++20     Submitter: Andrey Erokhin     Date: 2019-07-15

[Adopted as a DR at the November, 2019 meeting.]

CCCC,

Before the resolution of issue 1596, 7.6.6 [expr.add] specified that:

For the purposes of these operators, a pointer to a nonarray object behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

where “these operators” refers to the additive operators. This provision thus applied to any pointer, regardless of its provenance. In its place, the normative provision for this treatment was restricted to the & operator only, in 7.6.2.2 [expr.unary.op] paragraph 3:

For purposes of pointer arithmetic (7.6.6 [expr.add]) and comparison (7.6.9 [expr.rel], 7.6.10 [expr.eq]), an object that is not an array element whose address is taken in this way is considered to belong to an array with one element of type T.

Thus, for example:

  int *p1 = new int;
  int *p2 = &*p1;
  bool b1 = p1 < p1+1;  // undefined behavior
  bool b2 = p2 < p2+1;  // well-defined

This restriction does not seem desirable.

Proposed resolution (October, 2019):

  1. Change 7.6.2.2 [expr.unary.op] bullet 3.2 as follows:

  2. The result of the unary & operator is a pointer to its operand.

  3. Change 6.8.4 [basic.compound] paragraph 3 as follows:

  4. ...A value of a pointer type that is a pointer to or past the end of an object represents the address of the first byte in memory (6.7.1 [intro.memory]) occupied by the object43 or the first byte in memory after the end of the storage occupied by the object, respectively. [Note: A pointer past the end of an object (7.6.6 [expr.add]) is not considered to point to an unrelated object of the object's type that might be located at that address. A pointer value becomes invalid when the storage it denotes reaches the end of its storage duration; see 6.7.5 [basic.stc]. —end note] For purposes of pointer arithmetic (7.6.6 [expr.add]) and comparison (7.6.9 [expr.rel], 7.6.10 [expr.eq]), a pointer past the end of the last element of an array x of n elements is considered to be equivalent to a pointer to a hypothetical element x[n] and an object of type T that is not an array element is considered to belong to an array with one element of type T. The value representation of pointer types...
  5. Change the footnote in 7.6.6 [expr.add] bullet 4.2 as follows:

  6. When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.

  7. Change the footnote in 7.6.9 [expr.rel] paragraph 4 aa follows:

  8. The result of comparing unequal pointers to objects [Footnote: An As specified in 6.8.4 [basic.compound], an object that is not an array element is considered to belong to a single-element array for this purpose; see 7.6.2.2 [expr.unary.op]. A and a pointer past the last element of an array x of n elements is considered to be equivalent to a pointer to a hypothetical array element x [ n ] n for this purpose; see 6.8.4 [basic.compound]. —end footnote] is defined in terms of a partial order consistent with the following rules:
  9. Change 27.10.16 [numeric.ops.midpoint] paragraph 5 as follows:

  10. Expects: a and b point to, respectively, elements x[i] and x[j] of the same array object x. [Note: An As specified in 6.8.4 [basic.compound], an object that is not an array element is considered to belong to a single-element array for this purpose; see 7.6.2.2 [expr.unary.op]. A and a pointer past the last element of an array x of n elements is considered to be equivalent to a pointer to a hypothetical array element x[n] n for this purpose; see 6.8.4 [basic.compound]. —end note]



2427. Deprecation of volatile operands and unevaluated contexts

Section: 7.6.19  [expr.ass]     Status: C++20     Submitter: Mike Miller     Date: 2019-08-14

[Adopted as a DR at the November, 2019 meeting.]

According to 7.6.19 [expr.ass] paragraph 7,

A simple assignment whose left operand is of a volatile -qualified non-class type is deprecated (D.4 [depr.volatile.type]) unless the assignment is either a discarded-value expression or appears in an unevaluated context.

The deprecations of increment, decrement, and compound assignment operators do not, but presumably should, mention unevaluated contexts.

Notes from the August, 2019 teleconference:

The omission of those operators was intentional; the deprecation is intended only to affect cases where using the result of the operation would result in a subsequent fetch of the value. However, some shortcomings of the existing wording were noted and will be addressed in the resolution.

Proposed resolution (October, 2019):

Change 7.6.19 [expr.ass] paragraph 5 as follows:

A simple assignment whose left operand is of a volatile-qualified type is deprecated (D.4 [depr.volatile.type]) unless the (possibly parenthesized) assignment is either a discarded-value expression or appears in an unevaluated context operand.



2126. Lifetime-extended temporaries in constant expressions

Section: 7.7  [expr.const]     Status: C++20     Submitter: Richard Smith     Date: 2015-05-20

[Adopted as a DR at the November, 2019 meeting.]

Consider an example like the following:

  typedef const int CI[3];
  constexpr CI &ci = CI{11, 22, 33};
  static_assert(ci[1] == 22, "");

This is ill-formed because the lifetime of the array temporary did not start within the current evaluation. Perhaps we should treat all lifetime-extended temporaries of const-qualified literal type that are initialized by constant expressions as if they are constexpr objects?

Proposed resolution (October, 2019):

Change 7.7 [expr.const] paragraph 3 as follows:

A variable is usable in constant expressions after its initializing declaration is encountered if it is a constexpr variable, or it is a constant-initialized variable of reference type or of const-qualified integral or enumeration type. An object or reference is usable in constant expressions if it is

This resolution also resolves issue 2439.




2439. Undefined term in definition of “usable in constant expressions”

Section: 7.7  [expr.const]     Status: C++20     Submitter: Davis Herring     Date: 2019-08-28

[Adopted as a DR at the November, 2019 meeting.]

According to 7.7 [expr.const] paragraph 3,

An object or reference is usable in constant expressions if it is

The phrase “initialized with a constant expression” is not defined (which causes problems with std::is_constant_evaluated). It would be better to use “is constant-initialized” instead.

Proposed resolution (October, 2019):

This issue is resolved by the resolution of issue 2126.




2429. Initialization of thread_local variables referenced by lambdas

Section: 8.8  [stmt.dcl]     Status: C++20     Submitter: Princeton Ferro     Date: 2019-08-22

[Adopted as a DR at the November, 2019 meeting.]

According to 6.7.5.3 [basic.stc.thread] paragraph 2,

A variable with thread storage duration shall be initialized before its first odr-use (6.3 [basic.def.odr]) and, if constructed, shall be destroyed on thread exit.

According to 8.8 [stmt.dcl] paragraph 4, for block-scope variables this initialization is peformed when the declaration statement is executed:

Dynamic initialization of a block-scope variable with static storage duration (6.7.5.2 [basic.stc.static]) or thread storage duration (6.7.5.3 [basic.stc.thread]) is performed the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization.

However, there are cases in which the flow of control does not pass through a variable's declaration, leaving it uninitialized in spite of it being odr-used. For example:

  #include <thread>
  #include <iostream>

  struct Object {
    int i;
    Object() : i(3) {}
  };

  int main(void) {
    static thread_local Object o;

    std::cout << "[main] o.i = " << o.i << std::endl;
    std::thread([] {
      std::cout << "[new thread] o.i = " << o.i << std::endl;
    }).join();
  }

o is a block-scope variable with thread storage and a dynamic initializer. The lambda passed into std::thread's constructor refers to o but does not capture it, which should be fine since o is not a variable with automatic storage. However, when control passes through the lambda, it will do so on a new thread. When that happens, it will refer to o for the first time. Because o is a thread-local variable, it should be initialized, but because it is declared in block scope, it will only be initialized when control passes through its declaration, which will never happen on the new thread.

This example is straightforward, but others are more ambiguous:

  #include <thread>
  #include <iostream>

  struct Object {
    int i;
    Object(int v) : i(3 + v) {}
  };

  int main(void) {
    int w = 4;
    static thread_local Object o(w);

    std::cout << "[main] o.i = " << o.i << std::endl;
    std::thread([] {
      std::cout << "[new thread] o.i = " << o.i << std::endl;
    }).join();
  }

Here the initialization of o uses the value of w, which is not captured. Perhaps it should be ill-formed for a lambda to refer to a block-scope thread-local variable.

Proposed resolution (October, 2019):

Change 6.7.5.3 [basic.stc.thread] paragraphs 1 and 2 as follows:

All variables declared with the thread_local keyword have thread storage duration. The storage for these entities shall last lasts for the duration of the thread in which they are created. There is a distinct object or reference per thread, and use of the declared name refers to the entity associated with the current thread.

[Note: A variable with thread storage duration shall be is initialized before its first odr-use (6.3 [basic.def.odr]) as specified in 6.9.3.2 [basic.start.static], 6.9.3.3 [basic.start.dynamic], and 8.8 [stmt.dcl] and, if constructed, shall be is destroyed on thread exit (6.9.3.4 [basic.start.term]). end note]




2424. constexpr initialization requirements for variant members

Section: 9.2.6  [dcl.constexpr]     Status: C++20     Submitter: Richard Smith     Date: 2019-08-03

[Adopted as a DR at the November, 2019 meeting.]

Paper P1331R2 removed the requirement that a constexpr constructor initialize every non-variant non-static data member, but it left untouched the corresponding requirements for variant members. That is, the modified text in 9.2.6 [dcl.constexpr] paragraph 4 still contains:

The definition of a constexpr constructor whose function-body is not = delete shall additionally satisfy the following requirements:

Presumably this was an oversight and these two bullets should be changed from “exactly” to “at most” or something similar.

Proposed resolution (September, 2019):

Delete the indcated text from 9.2.6 [dcl.constexpr] paragraph 4:

The definition of a constexpr constructor whose function-body is not = delete shall additionally satisfy the following requirements:




2441. Inline function parameters

Section: 9.2.8  [dcl.inline]     Status: C++20     Submitter: Kristian Stasiowski     Date: 2019-11-04

[Accepted as a DR at the November, 2019 meeting.]

According to 9.2.8 [dcl.inline] paragraph 5,

The inline specifier shall not appear on a block scope declaration.

Function parameters, however, are not block scope declarations, but they should also not be declared inline.

Proposed resolution (November, 2019):

Change 9.2.8 [dcl.inline] paragraph 5 as follows:

The inline specifier shall not appear on a block scope declaration.87 or on the declaration of a function parameter.



2053. auto in non-generic lambdas

Section: 9.2.9.7  [dcl.spec.auto]     Status: C++20     Submitter: Faisal Vali     Date: 2014-12-07

[Accepted as a DR at the February, 2020 (Prague) meeting.]

According to 9.2.9.7 [dcl.spec.auto] paragraph 3,

If the auto type-specifier appears as one of the decl-specifiers in the decl-specifier-seq of a parameter-declaration of a lambda-expression, the lambda is a generic lambda (7.5.5 [expr.prim.lambda]).

and 7.5.5 [expr.prim.lambda] paragraph 5 says,

For a generic lambda, the closure type has a public inline function call operator member template (13.7.3 [temp.mem]) whose template-parameter-list consists of one invented type template-parameter for each occurrence of auto in the lambda's parameter-declaration-clause, in order of appearance.

However, an auto that signals a trailing-return-type should be excluded from these descriptions.

Proposed resolution (February, 2020):

This issue is resolved by the resolution of issue 2447.




2447. Unintended description of abbreviated function templates

Section: 9.2.9.7  [dcl.spec.auto]     Status: C++20     Submitter: Hubert Tong     Date: 2019-10-15

[Accepted at the February, 2020 (Prague) meeting.]

The current wording for abbreviated function templates could lead to the incorrect conclusion that f and g in the example below are well-formed abbreviated function templates. The g case is the abbreviated function template analog of issue 2053 regarding generic lambdas.

9.2.9.7 [dcl.spec.auto] paragraph 4 clearly disallows the ap case. The inconsistency between the paragraph 2 wording for abbreviated function templates and the paragraph 4 wording is unintentional.

  template <typename> struct A;
  void f(A<auto> x);
  void g(auto f() -> int);
  A<auto> *ap = static_cast<A<int> *>(0);

Proposed resolution (February, 2020):

  1. Change 9.2.9.7 [dcl.spec.auto] paragraph 2 as follows:

  2. A placeholder-type-specifier of the form type-constraintopt auto can be used in as a decl-specifier of the decl-specifier-seq of a parameter-declaration of a function declaration or lambda-expression and, if it is not the auto type-specifier introducing a trailing-return-type (see below), is a generic parameter type placeholder of the function declaration or lambda-expression. [Note: Having a generic parameter type placeholder signifies that the function is an abbreviated function template (9.3.4.6 [dcl.fct]) or the lambda is a generic lambda (7.5.5 [expr.prim.lambda]). end note]
  3. Change 9.3.4.6 [dcl.fct] paragraph 18 as follows:

  4. An abbreviated function template is a function declaration whose parameter-type-list includes that has one or more generic parameter type placeholders (9.2.9.7 [dcl.spec.auto]). An abbreviated function template is equivalent to a function template (13.7.7.2 [temp.over.link]) whose template-parameter-list includes one invented type template-parameter for each occurrence of a generic parameter type placeholder type in the decl-specifier-seq of a parameter-declaration in the function's parameter-type-list of the function declaration, in order of appearance. For a placeholder-type-specifier of the form...
  5. Change 7.5.5 [expr.prim.lambda] paragraph 5 as follows:

  6. A lambda is a generic lambda if there is a decl-specifier that is a placeholder-type-specifier in the decl-specifier-seq of a parameter-declaration of the lambda-expression has any generic parameter type placeholders (9.2.9.7 [dcl.spec.auto]), or if the lambda has a template-parameter-list. [Example:...

This resolution also resolves issue 2053.




2374. Overly permissive specification of enum direct-list-initialization

Section: 9.4.5  [dcl.init.list]     Status: C++20     Submitter: Shafik Yaghmour     Date: 2018-02-18

[Adopted as a DR at the November, 2019 meeting.]

According to 9.4.5 [dcl.init.list] bullet 3.8,

The conversion T(v) is too broad, allowing, e.g., conversion from a different scoped enumeration type. The intent presumably was only to allow v to be a value of T's underlying type.

Notes from the October, 2018 teleconference:

CWG agreed with the suggested direction, along the lines of “...can be implicitly converted to the underlying type of T...”

Proposed resolution (May, 2019): [SUPERSEDED]

Change bullet 3.8 of 9.4.5 [dcl.init.list] as follows:

Proposed resolution (October, 2019):

Change bullet 3.8 of 9.4.5 [dcl.init.list] as follows:




2436. Copy semantics of coroutine parameters

Section: 9.5.4  [dcl.fct.def.coroutine]     Status: C++20     Submitter: Mathias Stearn     Date: 2018-06-09

[Accepted (as paper P2107R0) at the February, 2020 (Prague) meeting.]

(This was previously issue 33 in the coroutine issue list.)

According to 9.5.4 [dcl.fct.def.coroutine] paragraph 13,

When a coroutine is invoked, a copy is created for each coroutine parameter. Each such copy is an object with automatic storage duration that is direct-initialized from an lvalue referring to the corresponding parameter if the parameter is an lvalue reference, and from an xvalue referring to it otherwise.

This means that parameters to a coroutine that are const-qualified will be copy-constructed rather than move-constructed. For example, changing the signature of a coroutine from task<void> f(std::string) to task<void>(const std::string) can introduce an extra string copy and potential heap allocation that may not be obvious to the author.

It also means that it is not possible to write a coroutine with a const-qualified move-only parameter type like const std::unique_ptr<T>.

The original parameter to the function is generally not observable to the coroutine body, so there seems to be little benefit to preserving the constness of the original parameter when copying the parameter into the coroutine frame.

Suggested resolution:

When a coroutine is invoked, a copy is created for each coroutine parameter. Each such copy is an object or reference with automatic storage duration that and has the same type as the corresponding parameter. Each copy is direct-initialized (9.4 [dcl.init]) from an lvalue referring to the corresponding parameter if the parameter is an lvalue reference, and from an xvalue referring to it otherwise. If the type of the copy is an rvalue reference type, then for the purpose of this initialization the value category of the corresponding parameter is an rvalue. A reference to use of a parameter in the function-body function-body of the coroutine and in the call to the coroutine promise constructor is replaced by a reference to its copy. The initialization and destruction of each parameter copy...



2430. Completeness of return and parameter types of member functions

Section: 11.4  [class.mem]     Status: C++20     Submitter: Krystian Stasiowski     Date: 2019-08-23

[Adopted as a DR at the November, 2019 meeting.]

According to 11.4 [class.mem] paragraph 7,

A class is considered a completely-defined object type (6.8 [basic.types]) (or complete type) at the closing } of the class-specifier. The class is regarded as complete within its complete-class contexts; otherwise it is regarded as incomplete within its own class member-specification.

The complete-class contexts (paragraph 6) include the body of a member function but not its return and parameter types. Thus it appears that an example like the following is ill-formed:

  struct S {
    S f(S s) { return s; }
  };

because of 9.5.1 [dcl.fct.def.general] paragraph 2

The type of a parameter or the return type for a function definition shall not be an incomplete or abstract (possibly cv-qualified) class type in the context of the function definition unless the function is deleted (9.5.3 [dcl.fct.def.delete]).

The words “in the context of the function definition” were added by the resolution of issue 1824 to address this problem, but “context” is most naturally read as referring to the lexical context where the definition appears rather than within its body.

Proposed resolution (October, 2019):

Change 9.5.1 [dcl.fct.def.general] paragraph 2 as follows:

...The type of a parameter or the return type for a function definition shall not be an incomplete or abstract (possibly cv-qualified) class type in the context of the function definition a (possibly cv-qualified) class type that is incomplete or abstract within the function body unless the function is deleted (9.5.3 [dcl.fct.def.delete]).



1621. Member initializers in anonymous unions

Section: 11.9.3  [class.base.init]     Status: C++20     Submitter: Daveed Vandevoorde     Date: 2013-02-12

[Adopted as a DR at the November, 2019 meeting.]

The effect of a non-static data member initializer in an anonymous union is not clearly described in the current wording. Consider the following example:

  struct A {
    struct B {
      union {
        int x = 37;
      };
      union {
        int y = x + 47;  // Well-formed?
      };
    } a;
  };

Does an anonymous union have a constructor that applies a non-static data member initializer? Or is the initialization performed by the constructor of the class in which the anonymous union appears? In particular, is the reference to x in the initializer for y well-formed or not? If the initialization of y is performed by B's constructor, there is no problem because B::x is a member of the object being initialized. If an anonymous union has its own constructor, B::x is just a member of the containing class and is a reference to a non-static data member without an object, which is ill-formed. Implementations currently appear to take the latter interpretation and report an error for that initializer.

As a further example, consider:

  union {       // #1
    union {     // #2
      union {   // #3
        int y = 32;
      };
    };
  } a { } ;

One interpretation might be that union #3 has a non-trivial default constructor because of the initializer of y, which would give union #2 a deleted default constructor, which would make the example ill-formed.

As yet another example, consider:

  union {
    union {
      int x;
    };
    union {
      int y = 3;
    };
    union {
      int z;
    };
  } a { };

Assuming the current proposed resolution of issue 1502, what is the correct interpretation of this code? Is it well-formed, and if so, what initialization is performed?

Finally, consider

  struct S {
    union { int x = 1; };
    union { int y = 2; };
  } s{};

Does this violate the prohibition of aggregates containing member initializers in 9.4.2 [dcl.init.aggr] paragraph 1?

See also issues 1460, 1562, 1587, and 1623.

Proposed resolution (October, 2019):

  1. Change 11.4.3 [class.mfct.non.static] paragraph 1 as follows:

  2. ...A non-static member function may also be called directly using the function call syntax (7.6.1.3 [expr.call], 12.2.2.2 [over.match.call]) from within the body of a member function of its class or of a class derived from its class, or a member thereof, as described below. .
  3. Change 11.4.3 [class.mfct.non.static] paragraph 3 as follows:

  4. When an id-expression (7.5.4 [expr.prim.id]) that is not part of a class member access syntax (7.6.1.5 [expr.ref]) and not used to form a pointer to member (7.6.2.2 [expr.unary.op]) is used in a member of class X in a context where this can be used (7.5.2 [expr.prim.this]), if name lookup (6.5 [basic.lookup]) resolves the name in the id-expression to a non-static non-type member of some class C, and if either the id-expression is potentially evaluated or C is X or a base class of X, the id-expression is transformed into a class member access expression (7.6.1.5 [expr.ref]) using (*this) (_N4868_.11.4.3.2 [class.this]) as the postfix-expression to the left of the . operator. [Note: If C is not X or a base class of X, the class member access expression is ill-formed. —end note] Similarly during name lookup, when an unqualified-id (7.5.4.2 [expr.prim.id.unqual]) used in the definition of a member function for class X resolves to a static member, an enumerator or a nested type of class X or of a base class of X, the unqualified-id is transformed into a qualified-id (7.5.4.3 [expr.prim.id.qual]) in which the nested-name-specifier names the class of the member function. These transformations do This transformation does not apply in the template definition context (13.8.3.2 [temp.dep.type]). [Example:...
  5. Delete 11.4.9 [class.static] paragraph 3:

  6. If an unqualified-id (7.5.4.2 [expr.prim.id.unqual]) is used in the definition of a static member following the member's declarator-id, and name lookup (6.5.3 [basic.lookup.unqual]) finds that the unqualified-id refers to a static member, enumerator, or nested type of the member's class (or of a base class of the member's class), the unqualified-id is transformed into a qualified-id expression in which the nested-name-specifier names the class scope from which the member is referenced. [Note: See 7.5.4 [expr.prim.id] for restrictions on the use of non-static data members and non-static member functions. —end note]
  7. Change 11.5.2 [class.union.anon] paragraph 1 as follows:

  8. A union of the form

    is called an anonymous union; it defines an unnamed type and an unnamed object of that type called an anonymous union object. Each member-declaration in the member-specification of an anonymous union shall either define a non-static data member or be a static_assert-declaration. [Note: Nested types, anonymous unions, and functions cannot shall not be declared within an anonymous union. end note] The names of the members...




2414. Unclear results if both member and friend operator<=> are declared

Section: 11.10.1  [class.compare.default]     Status: C++20     Submitter: Daveed Vandevoorde     Date: 2019-05-20

[Resolved by paper P2002R1, adopted at the February, 2020 meeting.]

According to 11.10.1 [class.compare.default] paragraph 2,

If the class definition does not explicitly declare an == operator function, but declares a defaulted three-way comparison operator function, an == operator function is declared implicitly with the same access as the three-way comparison operator function. The implicitly-declared == operator for a class X is an inline member and is defined as defaulted in the definition of X. If the three-way comparison operator function is declared as a non-static const member, the implicitly-declared == operator function is a member of the form

  bool X::operator==(const X&) const;

Otherwise, the implicitly-declared == operator function is of the form

  friend bool operator==(const X&, const X&);

Paragraph 1 of the section does not preclude declaring both a member and a friend operator<=>, and it is not clear how the operator== should be declared in that case.




2432. Return types for defaulted <=>

Section: 11.10.3  [class.spaceship]     Status: C++20     Submitter: Richard Smith     Date: 2019-08-14

[Adopted as a DR at the November, 2019 meeting.]

It is unclear what the constraints are on the type R. We define the "synthesized three-way comparison for comparison category type R", but it's defined in such a way that it works for an arbitrary type R, and the uses of it do not impose a constraint that R is a comparison category type. Should it be permissible to default an operator<=> with some other return type, so long as the construction described in 11.10.3 [class.spaceship] works (specifically, so long as all subobjects have operator<=>s that can be converted to the specified return type)?

Proposed resolution (September, 2019)

Change 11.10.3 [class.spaceship] paragraphs 1-3, changing the running text of paragraph 2 to into a bulleted list, as follows:

The synthesized three-way comparison for comparison category of type R (17.11.2 [cmp.categories]) of glvalues a and b...

Let R be the declared return type of a defaulted three-way comparison operator function. Given an expanded list of subobjects for an object x of type C, let Ri be the type of the expression xi <=> xi is denoted by Ri. If , or void if overload resolution as applied to xi <=> xi that expression does not find a usable function, then Ri is void.

...until the first index i where the synthesized three-way comparison for comparison category of type R between xi and yi yields...




2437. Conversion of std::strong_ordering in a defaulted operator<=>

Section: 11.10.3  [class.spaceship]     Status: C++20     Submitter: Richard Smith     Date: 2019-08-14

[Adopted as a DR at the November, 2019 meeting.]

According to 11.10.3 [class.spaceship] paragraph 3,

The return value V of type R of the defaulted three-way comparison operator function with parameters x and y of the same type is determined by comparing corresponding elements xi and yi in the expanded lists of subobjects for x and y (in increasing index order) until the first index i where the synthesized three-way comparison for comparison category type R between xi and yi yields a result value vi where vi != 0, contextually converted to bool, yields true; V is vi converted to R. If no such index exists, V is std::strong_ordering::equal converted to R.

This is meaningless, however, because the kind of conversion is not specified. According to bullet 1.1,

so consistency would suggest that static_cast<R>(std::strong_ordering::equal) is probably the right answer.

Proposed resolution (October, 2019):

Change 11.10.3 [class.spaceship] paragraph 3 as follows:

...If no such index exists, V is static_cast<R>(std::strong_ordering::equal) converted to R.



2442. Incorrect requirement for default arguments

Section: 12.2.3  [over.match.viable]     Status: C++20     Submitter: S. B. Tam     Date: 2019-09-24

[Adopted as a DR at the November, 2019 meeting.]

(From editorial issue 3235.)

According to 12.2.3 [over.match.viable] bullet 2.3 says,

First, to be a viable function, a candidate function shall have enough parameters to agree in number with the arguments in the list.

However, this is incorrect; 9.3.4.7 [dcl.fct.default] paragraph 4 permits parameter packs to follow parameters with default arguments.

Proposed resolution (October, 2019):

Change 12.2.3 [over.match.viable] bullet 2.3 as follows:




2343. void* non-type template parameters

Section: 13.2  [temp.param]     Status: C++20     Submitter: Daveed Vandevoorde     Date: 2017-04-17

[Adopted at the November, 2019 meeting as part of paper P1907R1.]

According to 13.2 [temp.param] bullet 4.2, non-type template parameters of pointer type must be either

This excludes void*, which is an object pointer but not a pointer to object. However, most or all current implementations accept void* as a non-type template parameter.

Notes from the April, 2018 teleconference:

Not all implementations accept a void* template parameter, so this should not be a DR if it is eventually adopted. Furthermore, there is some implementation divergence over the kinds of template arguments that can be passed to a void* template parameter. CWG felt that EWG should weigh in on the desirability and content of this change.

Additional note, February, 2021:

Although this issue was ultimately resolved for C++20 by the adoption of paper P1907R1, it was previously addressed in a different manner by paper P0732R2, adopted at the June, 2018 meeting.




2411. Comparison of pointers to members in template non-type arguments

Section: 13.6  [temp.type]     Status: C++20     Submitter: Jens Maurer     Date: 2017-11-11

[Adopted at the November, 2019 meeting as part of paper P1907R1.]

The status quo appears to be that pointers-to-members as members of a non-type template parameter of class type are permitted, and are compared with == (which is unsound), whereas pointers-to-members as non-type template parameter are permitted with different semantics (equality only if they denote the same member).




2422. Incorrect grammar for deduction-guide

Section: 13.7.2.3  [temp.deduct.guide]     Status: C++20     Submitter: Barry Revzin     Date: 2019-07-28

[Adopted as a DR at the November, 2019 meeting.]

According to 13.7.2.3 [temp.deduct.guide] paragraph 1, the syntax of a deduction-guide is:

Instead of explicit, this production should use the explicit-specifier nonterminal. (The wording of 12.2.2.9 [over.match.class.deduct] has references to the explicit-specifier of a deduction guide, for example.)

Proposed resolution (September, 2019):

Change the grammar in 13.7.2.3 [temp.deduct.guide] paragraph 1 as follows:




2445. Partial ordering with rewritten candidates

Section: 13.7.7.3  [temp.func.order]     Status: C++20     Submitter: Hubert Tong     Date: 2020-01-29

[Accepted at the February, 2020 (Prague) meeting.]

The tiebreaker based on partial ordering of function templates should presumably operate upon rewritten candidates based on their parameter lists for the purpose of overload resolution (12.2.2 [over.match.funcs]) without substitution of template arguments instead of the function parameter lists of the templates themselves.

It is observed, however, that neither GCC nor Clang performs partial ordering in the manner described above. In the following case, considering the templates with the reordering should yield 1a as being more specialized than 2; however, the wording is not especially clear about this and both GCC and Clang seems to fall past the partial ordering tiebreaker to pick 2 for the case as-is. If 1b is introduced, then it is more specialized than 2 in either ordering, and it is chosen by both GCC and Clang.

  template <typename> constexpr bool F = false;
  template <typename T> struct A { };

  template <typename T, typename U>
  // bool operator==(A<T>, A<U *>);       // 1b
  bool operator==(T, A<U *>);             // 1a

  template <typename T, typename U>
  bool operator!=(A<T>, U) {              // 2
   static_assert(F<T>, "Isn't this less specialized?");
   return false;
  }

  bool f(A<int> ax, A<int *> ay) { return ay != ax; }

Proposed resolution (February, 2020):

Change 13.7.7.3 [temp.func.order] paragraph 3 as follows:

To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs (13.7.4 [temp.variadic]) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template. [Note: The type replacing the placeholder in the type of the value synthesized for a non-type template parameter is also a unique synthesized type. —end note] If only one of the Each function templates template M that is a member function, and that function is a non-static member of some class A, M is considered to have a new first parameter of type X(M), described below, inserted in its function parameter list. Given cv as the If exactly one of the function templates was considered by overload resolution via a rewritten candidate (12.2.2.3 [over.match.oper]) with a reversed order of parameters, then the order of the function parameters in its transformed template is reversed. For a function template M with cv-qualifiers of M (if any), the new parameter cv that is a member of a class A:

[Note: This allows...




2446. Questionable type-dependency of concept-ids

Section: 13.8.3.3  [temp.dep.expr]     Status: C++20     Submitter: Hubert Tong     Date: 2019-02-03

[Accepted at the February, 2020 (Prague) meeting.]

The rules for type-dependency do not appear to take the bool type associated with concept-ids into account. For example:

  template <typename T> concept C = true;
  template <typename T> struct A;
  template <> struct A<bool> { using type = bool; };

  template <typename T>
  void f(A<decltype(C<T>)>::type); // error: needs typename to avoid vexing parse

Proposed resolution (February, 2020):

  1. Change 13.8.3.3 [temp.dep.expr] paragraph 3 as follows:

  2. An id-expression is type-dependent if it is not a concept-id and it contains

  3. Change 13.8.3.4 [temp.dep.constexpr] paragraph 3 as follows:

  4. An id-expression is value-dependent if:




2416. Explicit specializations vs constexpr and consteval

Section: 13.9.4  [temp.expl.spec]     Status: C++20     Submitter: Mike Miller     Date: 2019-06-07

[Accepted as a DR at the November, 2019 meeting.]

According to 13.9.4 [temp.expl.spec] paragraph 13,

An explicit specialization of a function or variable template is inline only if it is declared with the inline specifier or defined as deleted, and independently of whether its function or variable template is inline.

The current wording does not specify the status of explicit specializations of constexpr and consteval function templates and members of class templates. Should a similar rule apply in those cases?

Proposed resolution (November, 2019):

Change 13.9.4 [temp.expl.spec] paragraph 14 as follows:

An explicit specialization of a function or variable template is inline only if it is declared with the inline specifier or defined as deleted, and independently of whether its function or variable template is inline. The inline, constexpr, and consteval properties of an explicit specialization of a function or variable template are determined by the explicit specialization and are independent of those properties of the template. [Example:...



2426. Reference to destructor that cannot be invoked

Section: 14.3  [except.ctor]     Status: C++20     Submitter: Mike Miller     Date: 2019-08-13

[Adopted as a DR at the November, 2019 meeting.]

Consider the following example:

   template<typename T> struct A {
   private:
     ~A() {}
   };
   A<char> g();
   A<char> f() { return g(); }

According to 14.3 [except.ctor] paragraph 2,

If an exception is thrown during the destruction of temporaries or local variables for a return statement (8.7.4 [stmt.return]), the destructor for the returned object (if any) is also invoked.

In f() there is no possibility of an exception occuring during the processing of the return statement, so there appears to be no reason for a reference to the private destructor of A<char>. Current implementations, however, issue an access error for this example. Is wording needed to make that destructor potentially invoked in such cases?

Proposed resolution (September, 2019):

  1. Change 8.7.4 [stmt.return] paragraph 2 as follows:

  2. ...[Note: A return statement can involve an invocation of a constructor to perform a copy or move of the operand if it is not a prvalue or if its type differs from the return type of the function. A copy operation associated with a return statement may be elided or converted to a move operation if an automatic storage duration variable is returned (11.9.6 [class.copy.elision]). —end note] [Example:

      std::pair<std::string,int> f(const char* p, int x) {
        return {p,x};
      }
    
    end example] The destructor for the returned object is potentially invoked (11.4.7 [class.dtor], 14.3 [except.ctor]). [Example:
      class A {
        ~A() {}
      };
      A f() { return A(); }   // error: destructor of A is private (even though it is never invoked)
    
    end example] Flowing off the end...
  3. Change 11.4.7 [class.dtor] paragraph 14 as follows:

  4. A destructor can also be invoked explicitly. A destructor is potentially invoked if it is invoked or as specified in 7.6.2.8 [expr.new], 8.7.4 [stmt.return], 9.4.2 [dcl.init.aggr], 11.9.3 [class.base.init], and 14.2 [except.throw]. A program is ill-formed if a destructor that is potentially invoked is deleted or not accessible from the context of the invocation.





Issues with "C++23" Status


2520. Template signature and default template arguments

Section: 3.50  [defns.signature.templ]     Status: C++23     Submitter: John Spicer     Date: 2022-01-25

[Accepted as a DR at the February, 2023 meeting.]

According to 3.50 [defns.signature.templ], the signature of a function template includes:

name, parameter-type-list, enclosing namespace, return type, template-head, and trailing requires-clause (if any)

The mention of the template-head is a change from previous versions of the Standard, which referred to the “template parameter list”, in order to include the requires-clause in the signature. However, template-head is a syntactic nonterminal and thus includes everything in that production, including default template arguments. It seems undesirable to make the default arguments part of the template signature.

CWG 2022-11-11

CWG agrees with the suggested direction.

Proposed resolution (approved by CWG 2023-02-09):

  1. Change in 3.49 [defns.signature.friend] as follows:

    signature
    <function template>
    name, parameter-type-list, enclosing namespace, return type, signature of the template-head, and trailing requires-clause (if any)
  2. Change in 3.50 [defns.signature.templ] as follows:

    signature
    <friend function template with constraint involving enclosing template parameters>
    name, parameter-type-list, return type, enclosing class, signature of the template-head, and trailing requires-clause (if any)
  3. Change in 3.53 [defns.signature.member] as follows:

    signature
    <class member function template>
    name, parameter-type-list, class of which the function is a member, cv-qualifiers (if any), ref-qualifier (if any), return type (if any), signature of the template-head, and trailing requires-clause (if any)
  4. Add a new section in Clause 3 [intro.defs] as follows:

    signature
    <template-head>
    template parameter list, excluding template parameter names and default arguments, and requires-clause (if any)



2518. Conformance requirements and #error/#warning

Section: 4.1.1  [intro.compliance.general]     Status: C++23     Submitter: CWG     Date: 2022-01-13     Liaison: (EWG)

[Accepted as a DR at the February, 2023 meeting.]

According to 4.1.1 [intro.compliance.general] bullet 2.2,

a conforming implementation shall issue at least one diagnostic message

for an ill-formed program that is not “no diagnostic required.” The relationship between this diagnostic message and the ones required by the #error and #warning preprocessor directives is not clear. For example, is an implementation required to emit multiple diagnostics if multiple #error directives are encountered, even though conformance requires only one? Could an implementation count the diagnostic emitted by #warning as satisfying the requirement for an ill-formed program?

See also issue 745.

Suggested resolution [SUPERSEDED]:

Add a new paragraph after 4.1.1 [intro.compliance.general] paragraph 2 as follows:

Although this document states only requirements on C++ implementations, those requirements are often easier to understand if they are phrased as requirements on programs, parts of programs, or execution of programs. Such requirements have the following meaning:

[Note: ... — end note]

Furthermore, a conforming implementation

For classes and class templates, ...

Notes from the November, 2022 meeting

EWG review solicited via cplusplus/papers#1366.

EWG 2023-02-06

EWG agreed with the suggested resolution, but resolved to amend it to treat static_assert similarly.

Proposed resolution (February, 2023) [SUPERSEDED]:

  1. Change and add a new paragraph after 4.1.1 [intro.compliance.general] paragraph 2 as follows:

    Although this document states only requirements on C++ implementations, those requirements are often easier to understand if they are phrased as requirements on programs, parts of programs, or execution of programs. Such requirements have the following meaning:
    • If a program contains no violations of the rules in Clause Clause 5 [lex] through Clause Clause 33 [thread] and Annex D, a conforming implementation shall, within its resource limits as described in Annex B, accept and correctly execute [ Footnote: ... ] that program.
    • If a program contains a violation of a rule for which no diagnostic is required, this document places no requirement on implementations with respect to that program.
    • Otherwise, if If a program contains a violation of any diagnosable rule or an occurrence of a construct described in this document as “conditionally-supported” when the implementation does not support that construct, a conforming implementation shall issue at least one diagnostic message.
    • If a program contains a violation of a rule for which no diagnostic is required, this document places no requirement on implementations with respect to that program. [Note: During template argument deduction and substitution, certain constructs that in other contexts require a diagnostic are treated differently; see [temp.deduct] — end note]
    • Furthermore, a conforming implementation

      • shall not accept a preprocessing translation unit containing a #error preprocessing directive (15.8 [cpp.error]),
      • shall issue at least one diagnostic message for each #warning or #error preprocessing directive not following a #error preprocessing directive in a preprocessing translation unit, and
      • shall not accept a translation unit with a failed static_assert-declaration (9.1 [dcl.pre]) outside an uninstantiated template.

    For classes and class templates, ...

  2. Change in 5.1 [lex.separate] paragraph 1 as follows:

    The text of the program is kept in units called source files in this document. A source file together with all the headers (16.4.2.3 [headers]) and source files included (15.3 [cpp.include]) via the preprocessing directive #include, less any source lines skipped by any of the conditional inclusion (15.2 [cpp.cond]) preprocessing directives, is called a preprocessing translation unit.
  3. Change in 5.2 [lex.phases] paragraph 7 as follows:

    ... The resulting tokens constitute a translation unit and are syntactically and semantically analyzed and translated as a translation unit.
  4. Change in 9.1 [dcl.pre] paragraph 10 as follows:

    In a static_assert-declaration, the constant-expression is contextually converted to bool and the converted expression shall be a constant expression (7.7 [expr.const]). If the value of the expression when so converted is true, the declaration has no effect. Otherwise, the static_assert-declaration has failed, the program is ill-formed, and the resulting diagnostic message (4.1 [intro.compliance]) should include the text of the string-literal, if one is supplied.

CWG 2023-02-10

CWG decided to fold P2593R1 (static_assert(false)) into the proposed resolution.

Proposed resolution (approved by CWG 2023-02-10):

  1. Change and add a new paragraph after 4.1.1 [intro.compliance.general] paragraph 2 as follows:

    Although this document states only requirements on C++ implementations, those requirements are often easier to understand if they are phrased as requirements on programs, parts of programs, or execution of programs. Such requirements have the following meaning:
    • If a program contains no violations of the rules in Clause Clause 5 [lex] through Clause Clause 33 [thread] and Annex D, a conforming implementation shall, within its resource limits as described in Annex B, accept and correctly execute [ Footnote: ... ] that program.
    • If a program contains a violation of a rule for which no diagnostic is required, this document places no requirement on implementations with respect to that program.
    • Otherwise, if If a program contains a violation of any diagnosable rule or an occurrence of a construct described in this document as “conditionally-supported” when the implementation does not support that construct, a conforming implementation shall issue at least one diagnostic message.
    • If a program contains a violation of a rule for which no diagnostic is required, this document places no requirement on implementations with respect to that program. [Note: During template argument deduction and substitution, certain constructs that in other contexts require a diagnostic are treated differently; see [temp.deduct] — end note]
    • Furthermore, a conforming implementation

      • shall not accept a preprocessing translation unit containing a #error preprocessing directive (15.8 [cpp.error]),
      • shall issue at least one diagnostic message for each #warning or #error preprocessing directive not following a #error preprocessing directive in a preprocessing translation unit, and
      • shall not accept a translation unit with a static_assert-declaration that fails (9.1 [dcl.pre]).

    For classes and class templates, ...

  2. Change in 5.1 [lex.separate] paragraph 1 as follows:

    The text of the program is kept in units called source files in this document. A source file together with all the headers (16.4.2.3 [headers]) and source files included (15.3 [cpp.include]) via the preprocessing directive #include, less any source lines skipped by any of the conditional inclusion (15.2 [cpp.cond]) preprocessing directives, is called a preprocessing translation unit.
  3. Change in 5.2 [lex.phases] paragraph 7 as follows:

    ... The resulting tokens constitute a translation unit and are syntactically and semantically analyzed and translated as a translation unit.
  4. Change in 9.1 [dcl.pre] paragraph 10 as follows:

    In a static_assert-declaration, the constant-expression is contextually converted to bool and the converted expression shall be a constant expression (7.7 [expr.const]). If the value of the expression when so converted is true or the expression is evaluated in the context of a template definition, the declaration has no effect. Otherwise, the static_assert-declaration fails, the program is ill-formed, and the resulting diagnostic message (4.1 [intro.compliance]) should include the text of the string-literal, if one is supplied. [ Example:
      static_assert(sizeof(int) == sizeof(void*), "wrong pointer size");
      static_assert(sizeof(int[2]));   // OK, narrowing allowed
    
    template <class T> void f(T t) { if constexpr (sizeof(T) == sizeof(int)) { use(t); } else { static_assert(false, "must be int-sized"); } } void g(char c) { f(0); // OK f(c); // error: must be int-sized }
    -- end example ]
  5. Change in 13.8 [temp.res] paragraph 6 as follows:

    ... The program is ill-formed, no diagnostic required, if:
    • no valid specialization, ignoring static_assert-declarations that fail, can be generated for a template or a substatement of a constexpr if statement (8.5.2 [stmt.if]) within a template and the template is not instantiated, or
    • ...



2639. new-lines after phase 1

Section: 5.2  [lex.phases]     Status: C++23     Submitter: US     Date: 2022-11-03

P2720R0 comment US 3-030

[Accepted at the November, 2022 meeting.]

Translation phases 2 and 3 assume that lines are terminated by "new-line characters". However, the current specification of phase 1 does not guarantee that to be true. In particular, for a UTF-8 file the verbatim sequence of source file characters forms the input for phase 2, even on systems where the line terminator is a carriage return. The non-UTF-8 specification is also defective in that it speaks of "introducing" new-line characters, even for encodings like Latin-1 where new-lines might already be present and no "introduction" is needed or appropriate.

Proposed resolution [SUPERSEDED]:

Change in 5.2 [lex.phases] paragraph 1.1 as follows:

... If an input file is determined to be a UTF-8 file, then it shall be a well-formed UTF-8 code unit sequence and it is decoded to produce a sequence of UCS scalar values that constitutes the sequence of elements of the translation character set, representing each line-termination character or character sequence as a new-line character.

For any other kind of input file supported by the implementation, characters are mapped, in an implementation-defined manner, to a sequence of translation character set elements (5.3 [lex.charset]) (introducing new-line characters for representing end-of-line indicators as new-line characters).

Proposed resolution (approved by CWG 2022-11-08):

Change in 5.2 [lex.phases] paragraph 1.1 as follows:

... If an input file is determined to be a UTF-8 file, then it shall be a well-formed UTF-8 code unit sequence and it is decoded to produce a sequence of UCS scalar values that constitutes the sequence of elements of the translation character set. In the resulting sequence, each pair of characters in the input sequence consisting of U+000D CARRIAGE RETURN followed by U+000A LINE FEED, as well as each U+000D CARRIAGE RETURN not immediately followed by a U+000A LINE FEED, is replaced by a single new-line character.

For any other kind of input file supported by the implementation, characters are mapped, in an implementation-defined manner, to a sequence of translation character set elements (5.3 [lex.charset]) (introducing new-line characters for , representing end-of-line indicators as new-line characters ).




2640. Allow more characters in an n-char sequence

Section: 5.3  [lex.charset]     Status: C++23     Submitter: US     Date: 2022-11-03

P2720R0 comment US 1-028

[Accepted at the November, 2022 meeting.]

The n-char grammar term is defined to match only the Latin uppercase, Latin digit, hyphen and space characters. This results in \N{ABC} matching named-universal-character while \N{abc} does not. This leads to programs like the following being unexpectedly well-formed because the \N{abc} sequence is lexed as the preprocessing token sequence , N, {, abc, }. The expansion of macro a then leads to the token sequence being passed as an argument to macro z where it is discarded.

  #define z(x) 0
  #define a z(
  int x = a\N{abc});

Changes to make the above program ill-formed would provide two benefits:

Proposed resolution (approved by CWG 2022-11-07):

Change the grammar in 5.3 [lex.charset] paragraph 3 as follows:

n-char:
     A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
     0 1 2 3 4 5 6 7 8 9
     U+002d hyphen-minus
     U+0020 space
     any member of the translation character set except the U+007D RIGHT CURLY BRACKET or new-line character



2641. Redundant specification of value category of literals

Section: 5.13  [lex.literal]     Status: C++23     Submitter: Andrey Erokhin     Date: 2022-11-07

[Accepted as a DR at the November, 2022 meeting.]

Subclause 7.5.1 [expr.prim.literal] paragraph 1 specifies:

... A string-literal is an lvalue designating a corresponding string literal object (5.13.5 [lex.string]), a user-defined-literal has the same value category as the corresponding operator call expression described in 5.13.9 [lex.ext], and any other literal is a prvalue.

Yet, there is redundant specification in 5.13.2 [lex.icon] paragraph 3:

The type of an integer-literal is the first type in the list in Table 9 corresponding to its optional integer-suffix in which its value can be represented. An integer-literal is a prvalue.

And in 5.13.7 [lex.bool] paragraph 1:

The Boolean literals are the keywords false and true. Such literals are prvalues and have type bool.

And in 5.13.8 [lex.nullptr] paragraph 1:

The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t.

Proposed resolution (approved by CWG 2022-11-10):

  1. Change in 5.13.1 [lex.literal.kinds] paragraph 1 as follows:

    There are several kinds of literals. [ Footnote: ... ]
    literal:
        integer-literal
        character-literal
        floating-point-literal
        string-literal
        boolean-literal
        pointer-literal
        user-defined-literal
    
    [ Note: When appearing as an expression, a literal has a type and a value category (7.5.1 [expr.prim.literal]). -- end note ]
  2. Change in 5.13.2 [lex.icon] paragraph 3 as follows:

    The type of an integer-literal is the first type in the list in Table 9 corresponding to its optional integer-suffix in which its value can be represented. An integer-literal is a prvalue.
  3. Change in 5.13.7 [lex.bool] paragraph 1 as follows:

    The Boolean literals are the keywords false and true. Such literals are prvalues and have type bool.
  4. Change in 5.13.8 [lex.nullptr] paragraph 1 as follows:

    The pointer literal is the keyword nullptr. It is a prvalue of has type std::nullptr_t.



2691. hexadecimal-escape-sequence is too greedy

Section: 5.13.3  [lex.ccon]     Status: C++23     Submitter: Fraser Gordon     Date: 2023-01-26

[Accepted at the February, 2023 meeting.]

The lexer grammar production hexadecimal-escape-sequence matches text of the form \x{20}ab due to its recursive definition.

Proposed resolution (approved by CWG 2023-01-27):

Change in 5.13.3 [lex.ccon] as follows:

hexadecimal-escape-sequence :
     \x hexadecimal-digit simple-hexadecimal-digit-sequence
     hexadecimal-escape-sequence hexadecimal-digit
     \x{ simple-hexadecimal-digit-sequence }



2242. ODR violation with constant initialization possibly omitted

Section: 6.3  [basic.def.odr]     Status: C++23     Submitter: Hubert Tong     Date: 2016-03-05

P2720R0 comment US 5-033

[Accepted as a DR at the November, 2022 meeting.]

Consider the following example:

  // tu1.cpp
  extern const int a = 1;
  inline auto f() {
    static const int b = a;
    struct A { auto operator()() { return &b; } } a;
    return a;
  }

  // tu2.cpp
  extern const int a;
  inline auto f() {
    static const int b = a;
    struct A { auto operator()() { return &b; } } a;
    return a;
  }
  int main() {
    return *decltype(f())()();
  }

Here, b may or may not have constant initialization. This example should be an ODR violation.

(Split off from issue 2123.)

Proposed resolution (approved by CWG 2022-11-11):

Insert after 6.3 [basic.def.odr] bullet 14.7 as follows:




2530. Multiple definitions of enumerators

Section: 6.3  [basic.def.odr]     Status: C++23     Submitter: Naiver Miigon     Date: 2022-02-10

[Accepted as a DR at the February, 2023 meeting.]

Issue 2494 specified a list of definable items and required that no translation unit contain more than one definition of any of those items. However, the list omits enumeration constants, implicitly allowing an example like:

  enum E { e, e };

According to 6.1 [basic.pre] paragraph 3, an enumerator is an entity. According to 6.2 [basic.def] paragraph 2,

Each entity declared by a declaration is also defined by that declaration unless: ...

and enumerators are not on the list of excluded cases, so an enumerator-definition is a definition. Furthermore, 6.6 [basic.link] paragraph 8 says,

Two declarations of entities declare the same entity if, considering declarations of unnamed types to introduce their names for linkage purposes, if any (9.2.4 [dcl.typedef], 9.7.1 [dcl.enum]), they correspond (6.4.1 [basic.scope.scope]), have the same target scope that is not a function or template parameter scope, and either

In the example above, both enumerators thus define the same entity, so the one-definition rule is responsible for excluding the duplicate definitions but does not do so.

Suggested resolution [SUPERSEDED]:

Change 6.3 [basic.def.odr] paragraph 1 as follows:
Each of the following is termed a definable item:

Proposed resolution (approved by CWG 2022-12-02):

Change in 9.7.1 [dcl.enum] paragraph 2 as follows:

... The identifiers in an enumerator-list are declared as constants, and can appear wherever constants are required. The same identifier shall not appear as the name of multiple enumerators in an enumerator-list. An enumerator-definition with = gives the associated enumerator the value indicated by the constant-expression. If the first enumerator has no initializer, the value of the corresponding constant is zero. An enumerator-definition without an initializer gives the enumerator the value obtained by increasing the value of the previous enumerator by one.



2678. std::source_location::current is unimplementable

Section: 6.3  [basic.def.odr]     Status: C++23     Submitter: Richard Smith     Date: 2023-01-07     Liaison: (EWG)

[Accepted as a DR at the February, 2023 meeting.]

Consider:

  #include <source_location>

  inline char *f() {
    static char array[std::source_location::current().line()];
    return array;
  }

The sequence of tokens comprising the definition of f can appear in multiple translation units, on different lines. The one-definition rule is not violated. Thus, there is a single function f in the program with a unique static local object array, but that object would have a different type in each translation unit. It is unclear how to implement this, absent the conservative approach of always returning a value-initialized object from std::source_location::current, which would defeat its purpose.

Possible approaches to resolve this issue might include:

2023-01-08

Forwarded to LWG / LEWG via cplusplus/papers#1416, by decision of the CWG chair.

EWG 2023-02-07

EWG approves of the approach for CWG2678 of changing the ODR to make use of source_location in a way that causes an inline function/function template/etc to 'be different' be an ODR violation.

Proposed resolution (approved by CWG 2023-02-08):

Add a new bullet after 6.3 [basic.def.odr] bullet 14.8 as follows:




2516. Locus of enum-specifier or opaque-enum-declaration

Section: 6.4.2  [basic.scope.pdecl]     Status: C++23     Submitter: Jiang An     Date: 2021-10-03

[Accepted as a DR at the February, 2023 meeting.]

According to 6.4.2 [basic.scope.pdecl] paragraph 3,

The locus of an enum-specifier or opaque-enum-declaration is immediately after the identifier (if any) in it (9.7.1 [dcl.enum]).

Equivalent wording has been present for a very long time; see, for instance, issue 1482. However, most or all implementations reject the example from that issue:

   template<typename T> struct S { typedef char I; };
   enum E: S<E>::I { e };   // Implementations say E is undeclared in S<E>

In addition to recognizing current implementation practice, it would be practically useful if the locus were specified instead as after the enum-head or complete opaque-enum-declaration, as it would allow use of SFINAE in std::is_scoped_enum to distinguish between scoped and unscoped enumerations rather than requiring special compiler support.

CWG 2022-11-11

Move the locus to immediately after the enum-head.

Proposed resolution (approved by CWG 2023-02-06):

The locus of an enum-specifier or opaque-enum-declaration is immediately after the identifier (if any) in it its enum-head; the locus of an opaque-enum-declaration is immediately after it (9.7.1 [dcl.enum]).



2642. Inconsistent use of T and C

Section: 6.5.2  [class.member.lookup]     Status: C++23     Submitter: US     Date: 2022-11-03

[Accepted as a DR at the February, 2023 meeting.]

P2720R0 comment US 7-035

T and C are used inconsistently throughout these paragraphs.

Proposed resolution [SUPERSEDED]:

  1. Change in 6.5.2 [class.member.lookup] paragraph 1 as follows:

    A search in a scope X for a name N from a program point P is a single search in X for N from P unless X is the scope of a class or class template T C, in which case the following steps define the result of the search.
  2. Change in 6.5.2 [class.member.lookup] paragraph 4 as follows:

    [Note 2: If T C is incomplete, only base classes whose base-specifier appears before P are considered. If T C is an instantiated class, its base classes are not dependent. —end note]
  3. Change in 6.5.2 [class.member.lookup] paragraph 6 as follows:

    The result of the search is the declaration set of S(N, T C). If it is an invalid set, the program is ill-formed. If it differs from the result of a search in T C for N in a complete-class context (11.4 [class.mem]) of T C, the program is ill-formed, no diagnostic required.
  4. Change in 6.5.2 [class.member.lookup] paragraph 7 as follows:

    If N is a non-dependent conversion-function-id, conversion function templates that are members of T C are considered. For each such template F, the lookup set S(t, T C) is constructed, considering a function template declaration to have the name t only if it corresponds to a declaration of F (6.4.1 [basic.scope.scope]).
  5. Change in 6.5.2 [class.member.lookup] paragraph 8 as follows:

    [Note 4: A static member, a nested type or an enumerator defined in a base class T B can unambiguously be found even if an object has more than one base class subobject of type T B. Two base class subobjects share the non-static member subobjects of their common virtual base classes. —end note]

CWG 2022-12-02

The resolution proposed above is incorrect: T is the parameter for the overall search and C is the parameter for the S(N,C) construction. Highlight that fact in paragraph 2.

Proposed resolution (approved by CWG 2023-01-06):

  1. Change in 6.5.2 [class.member.lookup] paragraph 1 as follows:

    A search in a scope X for a name N M from a program point P is a single search in X for N M from P unless X is the scope of a class or class template T, in which case the following steps define the result of the search. [Note 1: The result differs only if N M is a conversion-function-id or if the single search would find nothing. —end note]
  2. Change in 6.5.2 [class.member.lookup] paragraph 2 as follows:

    The lookup set for a name N in a class or class template C, called S(N, C), consists of two component sets: the declaration set, a set of members named N ; and the subobject set, a set of subobjects where declarations of these members were found (possibly via using-declarations). In the declaration set, type declarations (including injected-class-names) are replaced by the types they designate. S(N, C) is calculated as follows:
  3. Change in 6.5.2 [class.member.lookup] paragraph 4 as follows:

    ... [Note 2: If T C is incomplete, only base classes whose base-specifier appears before P are considered. If T C is an instantiated class, its base classes are not dependent. —end note]
  4. Change in 6.5.2 [class.member.lookup] paragraph 6 as follows:

    The result of the search is the declaration set of S(NM, T). If it is an invalid set, the program is ill-formed. If it differs from the result of a search in T for N M in a complete-class context (11.4 [class.mem]) of T , the program is ill-formed, no diagnostic required.
  5. Change in 6.5.2 [class.member.lookup] paragraph 7 as follows:

    If N M is a non-dependent conversion-function-id, conversion function templates that are members of T are considered. For each such template F, the lookup set S(t, T) is constructed, considering a function template declaration to have the name t only if it corresponds to a declaration of F (6.4.1 [basic.scope.scope]). The members of the declaration set of each such lookup set, which shall not be an invalid set, are included in the result.



2489. Storage provided by array of char

Section: 6.7.2  [intro.object]     Status: C++23     Submitter: Jiang An     Date: 2021-04-15

[Accepted as a DR at the February, 2023 meeting.]

According to 6.7.2 [intro.object] paragraph 3,

If a complete object is created (7.6.2.8 [expr.new]) in storage associated with another object e of type “array of N unsigned char” or of type “array of N std::byte” (17.2.1 [cstddef.syn]), that array provides storage for the created object if...

However, note 4 in paragraph 13 indicates that a char array can also provide storage:

An operation that begins the lifetime of an array of char, unsigned char, or std::byte implicitly creates objects within the region of storage occupied by the array.

[Note 4: The array object provides storage for these objects. —end note]

The normative text and the note should be reconciled.

Proposed resolution (approved by CWG 2023-02-09):

Change in 6.7.2 [intro.object] paragraph 13 as follows:

An operation that begins the lifetime of an array of char, unsigned char, or std::byte implicitly creates objects within the region of storage occupied by the array.



2625. Deletion of pointer to out-of-lifetime object

Section: 6.7.3  [basic.life]     Status: C++23     Submitter: Blacktea Hamburger     Date: 2022-08-27

[Accepted as a DR at the November, 2022 meeting.]

Consider:

struct S {};

int main() {
  S* p = new S;
  p->~S();
  delete p;
}

This code appears to be allowed per 6.7.3 [basic.life] bullet 6.1:

The program has undefined behavior if:

However, this calls the (trivial) destructor on *p twice. Invoking a non-static member function of an out-of-lifetime object is generally undefined behavior per 6.7.3 [basic.life] bullet 6.2 and 6.7.3 [basic.life] bullet 7.2. The rules ought to be consistent.

Proposed resolution (approved by CWG 2022-10-07):

Change in 6.7.3 [basic.life] bullet 6.1 as follows:

The program has undefined behavior if:



900. Lifetime of temporaries in range-based for

Section: 6.7.7  [class.temporary]     Status: C++23     Submitter: Thomas J. Gritzan     Date: 2009-05-12

[Accepted at the November, 2022 meeting as part of paper P2718R0.]

Temporaries created in the expression of the range-based for statement are not given special treatment, so they only persist to the end of the expression. This can lead to undefined behavior as __range and the iterators are used in the expansion of the statement. Such temporaries should have their lifetimes extended until the end of the statement.

Rationale (October, 2009):

In the expansion, expression is used to initialize a reference. If expression is a temporary, its lifetime is thus extended to that of the reference, which is the entire for statement.

Additional notes, February, 2017:

Posting from Daniel Frey to the std-discussion group:

Some people have tried

  namespace detail {
    template< class C > struct reverse_range {
      explicit reverse_range (C& _c) : c(_c) {}
      auto begin () { using std::rbegin; return rbegin(c); }
      auto end () { using std::rend; return rend(c); }
     private:
      C& c;
    };
  }


  template< class C > auto reverse (C& c) {
    return detail::reverse_range<C>{c};
  }

In an attempt to allow:

  // some function
  std::vector<int> foo();

  // correct usage
  auto v = foo();
  for( auto i : reverse(v) ) { std::cout << i << std::endl; }

  // problematic usage
  for( auto i : reverse(foo()) ) { std::cout << i << std::endl; }

The problem is that the temporary returned by foo() is destructed before the loop starts executing. [This issue] was supposed to be about that, but considers only the top-level temporary.

It might be reasonable to make the range-based for treat the for-range-initializer like a function argument: all temporaries of the expression should be destructed after the execution of the loop. This also removes the only place where binding a reference to a temporary extends its lifetime implicitly, unseen by the user.

Notes from the February, 2017 meeting:

CWG was inclined to accept the suggested change but felt that EWG involvement was necessary prior to such a decision.




2598. Unions should not require a non-static data member of literal type

Section: 6.8.1  [basic.types.general]     Status: C++23     Submitter: Richard Smith     Date: 2022-06-18

[Accepted as a DR at the November, 2022 meeting.]

According to 6.8.1 [basic.types.general] paragraph 10, a type is a literal type only if it satisfies the following:

A type is a literal type if it is: [Note 4: A literal type is one for which it might be possible to create an object within a constant expression. ... —end note]

However, the normative rule disagrees with the note. Consider:

  struct A { A(); };
  union U {
    A a;
    constexpr U() {}
    constexpr ~U() {}
  };

It is certainly possible to create an object of type U in a constant expression, even though U is not a literal type.

In the suggested resolution, the aggregate type rule is intended to capture the fact that it is not possible for aggregate initialization of a non-empty union to leave no active member (and similarly for each anonymous union member in a non-union union-like class).

Suggested resolution [SUPERSEDED]:

Change in 6.8.1 [basic.types.general] paragraph 10 as follows:

A type is a literal type if it is:

Proposed resolution (CWG telecon 2022-08-12) [SUPERSEDED]:

Change in 6.8.1 [basic.types.general] paragraph 10 as follows:

A type is a literal type if it is:

Proposed resolution (approved by CWG 2022-11-09):

Change 6.8.1 [basic.types.general] paragraph 10 as follows:

A type is a literal type if it is:




2643. Completing a pointer to array of unknown bound

Section: 6.8.1  [basic.types.general]     Status: C++23     Submitter: US     Date: 2022-11-03

P2720R0 comment US 10-039

[Accepted as a DR at the November, 2022 meeting.]

Subclause 6.8.1 [basic.types.general] paragraph 6 specifies:

... The type of a pointer to array of unknown bound, or of a type defined by a typedef declaration to be an array of unknown bound, cannot be completed.

This is misleading; such a type is already complete.

Proposed resolution:

Change in 6.8.1 [basic.types.general] paragraph 6 as follows:

... [ Note: The type of a pointer or reference to array of unknown bound permanently points to or refers to an incomplete type. An array of unknown bound , or of a type defined named by a typedef declaration to be an array of unknown bound, permanently refers to an incomplete type. In either case, the array type cannot be completed. -- end note ]



2475. Object declarations of type cv void

Section: 6.8.2  [basic.fundamental]     Status: C++23     Submitter: Krystian Stasiowski     Date: 2020-04-22     Liaison: WG14

[Accepted as a DR at the February, 2023 meeting.]

(From editorial issue 3953.)

Although an object cannot be defined with a type of cv void, there is nothing preventing a non-defining declaration of an object with that type. Should it be disallowed?

Notes from the December, 2020 teleconference:

Such declarations are permitted in C, so this question was referred to the C liaison for investigation.

CWG 2022-11-11

CWG resolved to making such declarations ill-formed.

Proposed resolution (approved by CWG 2022-12-02; amended 2023-02-06):

Change in 9.1 [dcl.pre] paragraph 7 as follows:

If the decl-specifier-seq contains the typedef specifier, the declaration is called a typedef declaration and each declarator-id is declared to be a typedef-name, synonymous with its associated type (9.2.4 [dcl.typedef]). [ Note 4: Such a declarator-id is an identifier (11.4.8.3 [class.conv.fct]). —end note] If the decl-specifier-seq contains no typedef specifier, Otherwise, if the type associated with a declarator-id is a function type (9.3.4.6 [dcl.fct]), the declaration is called a function declaration if the type associated with a declarator-id is a function type (9.3.4.6 [dcl.fct]) and . Otherwise, if the type associated with a declarator-id is an object or reference type, the declaration is an object declaration otherwise. Otherwise, the program is ill-formed.

[ Example:

int f(), x;            // OK, function declaration for f and object declaration for x
extern void g(),       // OK, function declaration for g
  y;                   // error: void is not an object type

-- end example ]




2528. Three-way comparison and the usual arithmetic conversions

Section: 7.4  [expr.arith.conv]     Status: C++23     Submitter: Cameron DaCamara     Date: 2022-01-26

[Accepted as a DR at the February, 2023 meeting.]

Consider an example like:

  void f(unsigned char i, unsigned ui) {
    i <=> ui;
  }

According to 7.6.8 [expr.spaceship] paragraph 4, the usual arithmetic conversions are applied to the operands. According to 7.4 [expr.arith.conv] bullet 1.5, the integral promotions are performed on both operands, resulting in i being converted from unsigned char to int. The operands are then of types int and unsigned int, so bullet 1.5.5 applies, further converting i to type unsigned int.

Unfortunately, that latter conversion, from int to unsigned int, is a narrowing conversion, which runs afoul of 7.6.8 [expr.spaceship] bullet 4.1, which prohibits narrowing conversions other than integral to floating in three-way comparisons.

Suggested resolution [SUPERSEDED]:

Change 7.4 [expr.arith.conv] bullet 1.5 as follows:

Otherwise, the integral promotions (7.3.7 [conv.prom]) shall be performed on both operands each operand shall be converted to a common type C. The integral promotion rules (7.3.7 [conv.prom] shall be used to determine a type T1 and type T2 for each operand.50 Then the following rules shall be applied to the promoted operands determine C:

Proposed resolution (approved by CWG 2023-02-09):

Change in 7.4 [expr.arith.conv] bullet 1.3 as follows, adding sub-bullets:

Otherwise, the integral promotions (7.3.7 [conv.prom]) are performed on both operands each operand is converted to a common type C. The integral promotion rules (7.3.7 [conv.prom]) are used to determine a type T1 and type T2 for each operand. [ Footnote: ... ] Then the following rules are applied to the promoted operands determine C:



2644. Incorrect comment in example

Section: 7.5.5.3  [expr.prim.lambda.capture]     Status: C++23     Submitter: US     Date: 2022-11-03

P2720R0 comment US 13-042

[Accepted as a DR at the November, 2022 meeting.]

The comment in the example in 7.5.5.3 [expr.prim.lambda.capture] paragraph 6 refers to "local variable", but should refer to init-capture instead.

Possible resolution:

Change in 7.5.5.3 [expr.prim.lambda.capture] paragraph 6 as follows:

  auto z = [a = 42](int a) { return 1; };   // error: parameter and conceptual local variable have the same name



2517. Useless restriction on use of parameter in constraint-expression

Section: 7.5.7.5  [expr.prim.req.nested]     Status: C++23     Submitter: Richard Smith     Date: 2019-06-10

[Accepted as a DR at the February, 2023 meeting.]

According to 7.5.7.5 [expr.prim.req.nested] paragraph 2,

A local parameter shall only appear as an unevaluated operand (7.2.3 [expr.context]) within the constraint-expression. [Example 2:

  template<typename T> concept C = requires (T a) {
    requires sizeof(a) == 4; // OK
    requires a == 0; // error: evaluation of a constraint variable
  };

end example]

However, a can't be used in a constant expression in any event, so the restriction is meaningless, except for ruling out an expression like true ? true : a, but there seems no reason to have a special rule for such a case.

Proposed resolution (approved by CWG 2023-01-06):

Remove 7.5.7.5 [expr.prim.req.nested] paragraph 2, including its example:

A local parameter shall only appear as an unevaluated operand (7.2.3 [expr.context]) within the constraint-expression. [Example 2:

  template<typename T> concept C = requires (T a) {
    requires sizeof(a) == 4; // OK
    requires a == 0; // error: evaluation of a constraint variable
  };

Additional notes (February, 2023)

After adopting paper P2280, it is no longer accurate that any use of requirement parameters makes an expression non-constant. However, the resolution as adopted makes the treatment of examples like the following uniform in requirements and other constant expression contexts:

  template<typename ArrayType> concept LargeArray =
    requires (ArrayType my_array) { requires my_array.size() > 5; }



2599. What does initializing a parameter include?

Section: 7.6.1.3  [expr.call]     Status: C++23     Submitter: Davis Herring     Date: 2022-06-18

[Accepted as a DR at the November, 2022 meeting.]

Subclause 7.6.1.3 [expr.call] paragraph 8 specifies:

The postfix-expression is sequenced before each expression in the expression-list and any default argument. The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter. [Note 8: All side effects of argument evaluations are sequenced before the function is entered (see 6.9.1 [intro.execution]). —end note]

Consider:

  f(std::unique_ptr<int>(new int),std::unique_ptr<int>(new int));

It is not clear from the phrasing whether the evaluation of each new int is part of the "initialization of [its] parameter" or whether only the initialization of f's parameters from the completed std::unique_ptr<int> objects is included. The note does not help, since it can be read as distinguishing argument evaluations from initialization.

Suggested resolution [SUPERSEDED]:

Insert before 9.4.1 [dcl.init.general] paragraph 18 as follows:

An initializer-clause followed by an ellipsis is a pack expansion (13.7.4 [temp.variadic]).

Initialization includes the evaluation of all subexpressions of each initializer-clause of the initializer (possibly nested within braced-init-lists).

If the initializer is a parenthesized expression-list, the expressions are evaluated in the order specified for function calls (7.6.1.3 [expr.call]).

Proposed resolution (approved by CWG 2022-08-26):

Insert before 9.4.1 [dcl.init.general] paragraph 18 as follows:

An initializer-clause followed by an ellipsis is a pack expansion (13.7.4 [temp.variadic]).

Initialization includes the evaluation of all subexpressions of each initializer-clause of the initializer (possibly nested within braced-init-lists) and the creation of any temporary objects for function arguments or return values (6.7.7 [class.temporary]).

If the initializer is a parenthesized expression-list, the expressions are evaluated in the order specified for function calls (7.6.1.3 [expr.call]).




2645. Unused term "default argument promotions"

Section: 7.6.1.3  [expr.call]     Status: C++23     Submitter: US     Date: 2022-11-03

P2720R0 comment US 15-044

[Accepted as a DR at the November, 2022 meeting.]

The phrase "default argument promotions" is apparently unused.

Possible resolution [SUPERSEDED]:

  1. Change in 7.6.1.3 [expr.call] paragraph 12 as follows:

    ...If the argument has integral or enumeration type that is subject to the integral promotions (7.3.7 [conv.prom]), or a floating-point type that is subject to the floating-point promotion (7.3.8 [conv.fpprom]), the value of the argument is converted to the promoted type before the call. These promotions are referred to as the default argument promotions.
  2. Change in 17.13.2 [cstdarg.syn] as follows:

    See also: ISO C 7.16.1.1 7.16.1

CWG 2022-11-10

The phrase is useful and needed to override C's deviating definition, as applicable to va_arg.

Proposed resolution (approved by CWG and LWG 2022-11-11):

Change in 17.13.2 [cstdarg.syn] as follows:

The contents of the header <cstdarg> are the same as the C standard library header <stdarg.h>, with the following changes: In lieu of the default argument promotions specified in ISO C 6.5.2.2, the definition in 7.6.1.3 [expr.call] applies. The restrictions that ISO C places on the second parameter to the va_start macro in header <stdarg.h> are different in this document. ...



2614. Unspecified results for class member access

Section: 7.6.1.5  [expr.ref]     Status: C++23     Submitter: Andrey Erokhin     Date: 2021-10-27

[Accepted as a DR at the November, 2022 meeting.]

Subclause 7.6.1.5 [expr.ref] paragraph 6 specifies:

If E2 is declared to have type “reference to T”, then E1.E2 is an lvalue; the type of E1.E2 is T. Otherwise, ...

This does not specifiy which object or functiom the resulting lvalue designates. A similar problem exists with member enumerators:

If E2 is a member enumerator and the type of E2 is T, the expression E1.E2 is a prvalue. The type of E1.E2 is T.

Proposed resolution (approved by CWG 2022-09-23):

  1. Split and change in 7.6.1.5 [expr.ref] paragraph 6 as follows:

    If E2 is declared to have type “reference to T”, then E1.E2 is an lvalue; the of type of E1.E2 is T. If E2 is a static data member, E1.E2 designates the object or function to which the reference is bound, otherwise E1.E2 designates the object or function to which the corresponding reference member of E1 is bound.

    Otherwise, ...

  2. Change in 7.6.1.5 [expr.ref] bullet 6.5 as follows:

    If E2 is a member enumerator and the type of E2 is T, the expression E1.E2 is a prvalue. The of type of E1.E2 is T whose value is the value of the enumerator.



2626. Rephrase ones' complement using base-2 representation

Section: 7.6.2.2  [expr.unary.op]     Status: C++23     Submitter: Jim X     Date: 2022-09-10

[Accepted as a DR at the November, 2022 meeting.]

Subclause 7.6.2.2 [expr.unary.op] paragraph 10 specifies:

The operand of ~ shall have integral or unscoped enumeration type; the result is the ones' complement of its operand. Integral promotions are performed. The type of the result is the type of the promoted operand. There is an ambiguity in the grammar...

This should be phrased in terms of the base-2 representation similar to bitwise-AND, instead of alluding to some bit representation by using the term "ones' complement".

Proposed resolution (approved by CWG 2022-10-07):

Subclause 7.6.2.2 [expr.unary.op] paragraph 10 specifies:

The operand of ~ shall have integral or unscoped enumeration type; the result is the ones' complement of its operand. Integral promotions are performed. The type of the result is the type of the promoted operand. Given the coefficients xi of the base-2 representation (6.8.2 [basic.fundamental]) of the promoted operand x, the coefficient ri of the base-2 representation of the result r is 1 if xi is 0, and 0 otherwise. There is an ambiguity in the grammar...



2624. Array delete expression with no array cookie

Section: 7.6.2.9  [expr.delete]     Status: C++23     Submitter: Blacktea Hamburger     Date: 2022-08-22

[Accepted as a DR at the November, 2022 meeting.]

Consider:

char *p = static_cast<char*>(operator new[](2));
p = new (p) char[2];  // #1
delete[] p;           // #2

Subclause 7.6.2.8 [expr.new] paragraph 16 specifies:

... When a new-expression calls an allocation function and that allocation has not been extended, the new-expression passes the amount of space requested to the allocation function as the first argument of type std::size_t. That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array and the allocation function is not a non-allocating form (17.6.3.4 [new.delete.placement]). ...

Subclause 7.6.2.9 [expr.delete] paragraph 2 specifies:

... In an array delete expression, the value of the operand of delete may be a null pointer value or a pointer value that resulted from a previous array new-expression. [ Footnote: ... ] If not, the behavior is undefined.

The non-allocating form of the new-expression at #1 is constrained not to place an array cookie at the start of the array. Yet, the array delete appears to be expected to divine that fact.

Proposed resolution (approved by CWG 2022-10-07):

Change in 7.6.2.9 [expr.delete] paragraph 2 as follows:

... In an array delete expression, the value of the operand of delete may be a null pointer value or a pointer value that resulted from a previous array new-expression whose allocation function was not a non-allocating form (17.6.3.4 [new.delete.placement]). [ Footnote: ... ] If not, the behavior is undefined.



2526. Relational comparison of void* pointers

Section: 7.6.9  [expr.rel]     Status: C++23     Submitter: Paul Keir     Date: 2020-06-15

[Accepted as a DR at the February, 2023 meeting.]

Prior to the adoption of paper N3624 (resolving issue 1512), comparison of void* pointers was explicitly unspecified. The current wording of 7.6.9 [expr.rel], however, describes only comparison of “pointers to objects” (paragraphs 4 and 5), but a pointer to void is not a pointer to an object, only an object pointer type (as opposed to a function pointer type). Formally, that means that comparing void* pointers is undefined behavior, which seems undesirable.

As a related note, there is implementation divergence over whether relational comparisons of void* pointers are accepted in constant expressions (when the void* values are converted from pointers that would otherwise be comparable in constant expressions).

CWG 2022-11-11

Paper N3624 erroneously removed support for void* and function pointer relational comparisons. That ought to be restored.

Proposed resolution (approved by CWG 2023-02-09):

Change in 7.6.9 [expr.rel] paragraph 5 as follows:

... Otherwise, the result of each of the operators is unspecified. [ Note: A relational operator applied to unequal function pointers or to unequal pointers to void yields an unspecified result. -- end note ]



2654. Un-deprecation of compound volatile assignments

Section: 7.6.19  [expr.ass]     Status: C++23     Submitter: US     Date: 2022-11-03     Liaison: EWG

P2720R0 comment US 16-045

[Accepted as a DR at the November, 2022 meeting.]

Many functions of volatile were deprecated in C++20. In C++23, volatile compound operations were de-deprecated by P2327. The rationale for this de-deprecation is lacking.

Deprecation is not removal. P2327 in C++23 is stopping the change that we began with C++20, mere moments ago if counting by adoption time. It primarily argued that it's an inconvenient change, and that some of the audience would just not cooperate with WG21's indicated direction, so WG21 should compromise the technical consistency of the Standard so they could continue to not cooperate.

The paper did not bring new information on the technical merits of the case, and net the result of applying it was a technically dissonant Standard specification --- a strictly worse specification than C++20. Bitwise compound operations are not special for any technical reason, they are only special because making them special shields some from deprecation diagnostics.

EWG 2022-11-07

Contrary to the direction desired in the NB comment, EWG resolved to un-deprecate all volatile compound assignments.

Proposed resolution (approved by CWG 2022-11-08):

Change in 7.6.19 [expr.ass] paragraph 6 as follows:

The behavior of an expression of the form E1 op= E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once. [ Note: The object designated by E1 is accessed twice. -- end note ] Such expressions are deprecated if E1 has volatile-qualified type and op is not one of the bitwise operators |, &, ^; see D.5.

Change in D.4 [depr.volatile.type] paragraph 2 as follows:

  brachiosaur += neck;                // deprecatedOK
  brachiosaur = brachiosaur + neck;   // OK
  brachiosaur |= neck;                // OK, bitwise compound expression



2392. new-expression size check and constant evaluation

Section: 7.7  [expr.const]     Status: C++23     Submitter: Tam S. B     Date: 2018-12-05

[Accepted as a DR at the November, 2022 meeting.]

According to 7.6.2.8 [expr.new] paragraph 8, if the expression in a noptr-new-declarator is a core constant expression, the program is ill-formed if the expression is erroneous, e.g., negative. However, consider the following example:

  template<class T = void> constexpr int f() { T t; return 1; }
  using _ = decltype(new int[f()]);

f() is a core constant expression, so it must be evaluated to determine its value. However, because the expression appears in an unevaluated operand, it is not “potentially constant evaluated” and thus f is not “needed for constant evaluation”, so the template is not instantiated (13.9.2 [temp.inst] paragraph 7). There is implementation divergence on the handling of this example.

CWG telecon 2022-09-09:

The example should be well-formed, because f is not instantiated.

A similar situation arises for narrowing conversions, except that in the latter case, determining the value at compile-time empowers to allow additional cases, whereas the new-expression case uses a compile-time value to prohibit additional cases.

Proposed resolution (approved by CWG 2022-09-23):

Change in 7.6.2.8 [expr.new] paragraph 8 as follows:

If the expression is erroneous after converting to std::size_t:



2440. Allocation in core constant expressions

Section: 7.7  [expr.const]     Status: C++23     Submitter: Davis Herring     Date: 2019-08-28

[Accepted as a DR at the November, 2022 meeting.]

7.7 [expr.const] paragraph 5 attempts to describe allowable allocation/deallocation calls in terms of what could be called “core constant subexpressions,” but the actual definition of a core constant expression in paragraph 4 is in terms of evaluation.

Suggested resolution [SUPERSEDED]:

Replace the entirety of 7.7 [expr.const] paragraph 6 with the following:

For the purposes of determining whether an expression E is a core constant expression, the evaluation of a call to the body of a member function of std::allocator<T> as defined in 20.2.10.2 [allocator.members], where T is a literal type, does not disqualify E from being a core constant expression, even if the actual evaluation of such a call would otherwise fail the requirements for a core constant expression is ignored. Similarly, the evaluation of a call to the body of std::destroy_at, std::ranges::destroy_at, std::construct_at, or std::ranges::construct_at (27.11.8 [specialized.construct]) does not disqualify E from being a core constant expression unless the first argument, of type T*, does not point to storage allocated with std::allocator<T> or to an object whose lifetime began within the evaluation of E, or the evaluation of is considered to include only the underlying constructor call disqualifies E from being a core constant expression (for the functions construct_at) or destructor (for the functions destroy_at) call if the first argument (of type T*) points to storage allocated with std::allocator<T>.

CWG telecon 2022-10-21:

The references to destroy_at were removed in an unrelated update to the Working Draft. Also, restore the reference to local objects whose lifetime began within E.

Proposed resolution (approved by CWG 2022-11-10):

Change in 7.7 [expr.const] paragraph 6 as follows:

For the purposes of determining whether an expression E is a core constant expression, the evaluation of a call to the body of a member function of std::allocator<T> as defined in 20.2.10.2 [allocator.members], where T is a literal type, does not disqualify E from being a core constant expression, even if the actual evaluation of such a call would otherwise fail the requirements for a core constant expression is ignored. Similarly, the evaluation of a call to the body of std::construct_at or std::ranges::construct_at (27.11.8 [specialized.construct]) does not disqualify E from being a core constant expression unless the first argument, of type T*, does not point to storage allocated with std::allocator<T> or to an object whose lifetime began within the evaluation of E, or the evaluation of is considered to include only the underlying constructor call disqualifies E from being a core constant expression call if the first argument (of type T*) points to storage allocated with std::allocator<T> or to an object whose lifetime began within the evaluation of E.



2523. Undefined behavior via omitted destructor call in constant expressions

Section: 7.7  [expr.const]     Status: C++23     Submitter: Jiang An     Date: 2021-09-06

[Accepted as a DR at the February, 2023 meeting.]

According to 7.7 [expr.const] bullet 5.8, one criterion that disqualifies an expression from being a core constant expression is:

an operation that would have undefined behavior as specified in Clause 4 [intro] through Clause 15 [cpp]

One potential source of undefined behavior is the omission of a call to a destructor for a constructed object, as described in 6.7.3 [basic.life] paragraph 5:

A program may end the lifetime of an object of class type without invoking the destructor, by reusing or releasing the storage as described above. [Note 3: A delete-expression (7.6.2.9 [expr.delete]) invokes the destructor prior to releasing the storage. —end note] In this case, the destructor is not implicitly invoked and any program that depends on the side effects produced by the destructor has undefined behavior.

For example:

  #include <memory>

  constexpr int test_basic_life_p5() {
    class guard_t {
      int &ref_;
    public:
      explicit constexpr guard_t(int &i) : ref_{i} {}
      constexpr ~guard_t() { ref_ = 42; }
    };

    int result = 0;

    auto alloc = std::allocator<guard_t>{};
    auto pguard = alloc.allocate(1);
    std::construct_at(pguard, result);
    // std::destroy_at(pguard);
    alloc.deallocate(pguard, 1);

    return result;  // value depends on destructor execution
  }

  int main() {
    constexpr auto v = test_basic_life_p5();
    return v;
  }

It is not clear that it is reasonable to require implementations to diagnose this form of undefined behavior in constant expressions.

A somewhat related question is raised by the restrictions on the use of longjmp in 17.13.3 [csetjmp.syn] paragraph 2:

A setjmp/longjmp call pair has undefined behavior if replacing the setjmp and longjmp by catch and throw would invoke any non-trivial destructors for any objects with automatic storage duration.

Here the undefined behavior occurs for any non-trivial destructor that is skipped, not just one for which the program depends on its side effects, as in 6.7.3 [basic.life] paragraph 5. Perhaps these two specifications should be harmonized.

Additional notes (April, 2022):

The phrase "[a] program that depends on the side effects" may have these meanings:

The second option would need a fork in the evaluation of constant expressions to determine whether undefined behavior occurs.

Proposed resolution (approved by CWG 2022-11-11):

Change in 6.7.3 [basic.life] paragraph 5 as follows:

A program may end the lifetime of an object of class type without invoking the destructor, by reusing or releasing the storage as described above. [Note 3: A delete-expression (7.6.2.9 [expr.delete]) invokes the destructor prior to releasing the storage. —end note] In this case, the destructor is not implicitly invoked and any program that depends on the side effects produced by the destructor has undefined behavior. [Note: The correct behavior of a program often depends on the destructor being invoked for each object of class type. -- end note]



2529. Constant destruction of constexpr references

Section: 7.7  [expr.const]     Status: C++23     Submitter: Jiang An     Date: 2022-02-08

[Accepted as a DR at the February, 2023 meeting.]

According to 9.2.6 [dcl.constexpr] paragraph 10, a constexpr variable must have constant destruction. However, 7.7 [expr.const] paragraph 7 only defines constant destruction for objects, not for references. Presumably constexpr references should also be able to have constant destruction, and any temporary object to which such a reference is bound should also be required to have constant destruction.

Proposed resolution (approved by CWG 2022-12-02):

Change in 9.2.6 [dcl.constexpr] paragraph 7 as follows:

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression (7.7 [expr.const]). A constexpr variable that is an object, as well as any temporary to which a constexpr reference is bound, shall have constant destruction. [ Example: ... ]



2558. Uninitialized subobjects as a result of an immediate invocation

Section: 7.7  [expr.const]     Status: C++23     Submitter: Aaron Ballman     Date: 2022-03-29     Liaison: (EWG)

[Accepted as a DR at the February, 2023 meeting.]

Consider:

  struct A {
    int n;
    consteval A() {}
  };
  constexpr A a; // implementations reject

Paper P1331R2 (Permitting trivial default initialization in constexpr contexts) dropped the restriction that immediate invocations cannot yield results with some subobjects left uninitialized. It is unclear whether that change was intentional or accidental.

Furthermore, indeterminate values of pointer type are currently not permitted as the result of a constant expression per 7.7 [expr.const] bullet 12.2; indeterminate values of scalar types are permitted only due to the absence of a restriction.

This issue is closely related to issue 2536.

2022-12-03

Forwarded to EWG with cplusplus/papers#1380.

EWG 2023-01-19

Uninitialized non-variant direct subobjects should not be allowed to appear in the result of a constant expression. There was no consensus in support of the statements "Union types shall be initialized such that they have an active member in the result of a constant expression" and "EWG confirms that padding bits and data belonging to non-active variant members are permitted to have indeterminate values in the static initialization of objects".

Proposed resolution (approved by CWG 2023-01-27):

Change in 7.7 [expr.const] paragraph 12 as follows:

A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:



2631. Immediate function evaluations in default arguments

Section: 7.7  [expr.const]     Status: C++23     Submitter: Aaron Ballman     Date: 2022-09-16     Liaison: EWG

P2720R0 comment FR 019-005

[Accepted as a DR at the November, 2022 meeting.]

Consider:

consteval int const_div(int a, int b) { return a / b; }
int func(int x = const_div(10, 0));

According to 7.7 [expr.const] paragraph 14:

An expression or conversion is an immediate invocation if it is a potentially-evaluated explicit or implicit invocation of an immediate function and is not in an immediate function context. An immediate invocation shall be a constant expression.

Subclause 9.3.4.7 [dcl.fct.default] paragraph 5 specifies:

The default argument has the same semantic constraints as the initializer in a declaration of a variable of the parameter type, using the copy-initialization semantics (9.4 [dcl.init]). The names in the default argument are looked up, and the semantic constraints are checked, at the point where the default argument appears. Name lookup and checking of semantic constraints for default arguments of templated functions are performed as described in 13.9.2 [temp.inst].

Checking the semantic constraints of the default argument appears to include a check whether the immediate invocation of const_div is actually a constant expression, even though the default argument's value might never actually be used for any function call in the program.

However, instantiation of a consteval function template to be able to perform the constant evaluation is not permitted per 13.9.2 [temp.inst] paragraph 11:

... The use of a template specialization in a default argument shall not cause the template to be implicitly instantiated except that a class template may be instantiated where its complete type is needed to determine the correctness of the default argument. The use of a default argument in a function call causes specializations in the default argument to be implicitly instantiated.

Example 2:

constexpr int g();
consteval int f() {
  return g();
}

int k(int x = f()) {  // error: constexpr evaluation of undefined function g
  return x;
}

constexpr int g() {
  return 42;
}

int main() {
  return k();
}

Example 3:

#include <source_location>
#include <iostream>

consteval int const_div(int a, int b) {
  return a / b;
}

#line 5
void foo(int x = const_div(1000, std::source_location::current().line() - 15)) {
  std::cout << x << "\n";
}

// Should the definition of `bar` produce errors? (division by zero during constant
// evaluation for constraint checking)
#line 10
void bar(int x = const_div(1000, std::source_location::current().line() - 10)) {
  std::cout << x << "\n";
}

int main() {
  // Should this call produce errors? (division by zero during constant
  // evaluation of the default argument)
  #line 15
  foo();
  #line 20
  bar();
}

Note that source_location::current() is specified to take its value from the location where it is evaluated, if it appears in a default argument (17.8.2.2 [support.srcloc.cons] paragraph 2):

Remarks: Any call to current that appears as a default member initializer (11.4 [class.mem]), or as a subexpression thereof, should correspond to the location of the constructor definition or aggregate initialization that uses the default member initializer. Any call to current that appears as a default argument (9.3.4.7 [dcl.fct.default]), or as a subexpression thereof, should correspond to the location of the invocation of the function that uses the default argument (7.6.1.3 [expr.call]).

Proposed resolution (September, 2022) [SUPERSEDED]:

  1. Split 9.3.4.7 [dcl.fct.default] paragraph 5 and change as follows:

    The default argument has the same semantic constraints as the initializer in a declaration of a variable of the parameter type, using the copy-initialization semantics (9.4 [dcl.init]).

    A default argument context is

    • the initializer-clause in a parameter-declaration, including any conversions to the parameter type,
    • a subexpression of one of the above that is not a subexpression of a nested unevaluated operand.
    The names in the default argument are looked up, and the semantic constraints are checked, at the point where the default argument appears, except that an immediate invocation (7.7 [expr.const]) in a default argument context is neither evaluated nor checked for whether it is a constant expression at that point. Name lookup and checking of semantic constraints for default arguments of templated functions are performed as described in 13.9.2 [temp.inst].

  2. Change in 13.9.2 [temp.inst] paragraph 11 as follows:

    ... The use of a template specialization in a default argument shall not cause the template to be implicitly instantiated except that a class template or a function template with a deduced return type may be instantiated where its complete type is needed to determine the correctness of the default argument. The use of a default argument in a function call causes specializations in the default argument to be implicitly instantiated.

Approved by EWG telecon 2022-09-29.

Amendment to also cover default member initializers (October, 2022) [SUPERSEDED]:

  1. Split 9.3.4.7 [dcl.fct.default] paragraph 5 and change as follows:

    The default argument has the same semantic constraints as the initializer in a declaration of a variable of the parameter type, using the copy-initialization semantics (9.4 [dcl.init]).

    A default argument context of an initializer is

    • the initializer, including any conversions to the target type, or
    • a subexpression thereof that is not a subexpression of a nested unevaluated operand.
    The names in the default argument are looked up, and the semantic constraints are checked, at the point where the default argument appears, except that an immediate invocation (7.7 [expr.const]) in a default argument context of the initializer-clause in a parameter-declaration is neither evaluated nor checked for whether it is a constant expression at that point. Name lookup and checking of semantic constraints for default arguments of templated functions are performed as described in 13.9.2 [temp.inst].

  2. Change in 11.4.1 [class.mem.general] paragraph 11 as follows:

    A brace-or-equal-initializer for a non-static data member specifies a default member initializer for the member, and shall not directly or indirectly cause the implicit definition of a defaulted default constructor for the enclosing class or the exception specification of that constructor. An immediate invocation (7.7 [expr.const]) in a default argument context (9.3.4.7 [dcl.fct.default]) of a default member initializer is neither evaluated nor checked for whether it is a constant expression at the point where it appears.
  3. Change in 13.9.2 [temp.inst] paragraph 11 as follows:

    ... The use of a template specialization in a default argument shall not cause the template to be implicitly instantiated except that a class template or a function template with a deduced return type may be instantiated where its complete type is needed to determine the correctness of the default argument. The use of a default argument in a function call causes specializations in the default argument to be implicitly instantiated.

CWG telecon 2022-10-07:

The new term should be defined in 6.9.1 [intro.execution] without reference to immediacy.

Possible resolution [SUPERSEDED]:

  1. Change in 6.9.1 [intro.execution] paragraph 4 as follows:

    A subexpression of an expression E is an immediate subexpression of E or a subexpression of an immediate subexpression of E. [ Note: ... -- end note ]
    The potentially-evaluated subexpressions of an expression, conversion, or initializer E are
    • the constituent expressions of E and
    • the subexpressions thereof that are not subexpressions of a nested unevaluated operand (7.2.3 [expr.context]).
  2. Change in 7.7 [expr.const] paragraph 16 as follows:

    An expression or conversion is potentially constant evaluated if it is:
    • ...
    • a potentially-evaluated subexpression (6.9.1 [intro.execution]) of one of the above that is not a subexpression of a nested unevaluated operand (7.2.3 [expr.context]).
  3. Change in 9.3.4.7 [dcl.fct.default] paragraph 5 as follows:

    The default argument has the same semantic constraints as the initializer in a declaration of a variable of the parameter type, using the copy-initialization semantics (9.4 [dcl.init]). The names in the default argument are looked up, and the semantic constraints are checked, at the point where the default argument appears, except that an immediate invocation (7.7 [expr.const]) that is a potentially-evaluated subexpression (6.9.1 [intro.execution]) of the initializer-clause in a parameter-declaration is neither evaluated nor checked for whether it is a constant expression at that point. Name lookup and checking of semantic constraints for default arguments of templated functions are performed as described in 13.9.2 [temp.inst].

  4. Add a paragraph before 9.4.1 [dcl.init.general] paragraph 17 as follows:

    An immediate invocation (7.7 [expr.const]) that is not evaluated where it appears (9.3.4.7 [dcl.fct.default], 11.4.1 [class.mem.general]) is evaluated and checked for whether it is a constant expression at the point where the enclosing initializer is used in a function call, a constructor definition, or an aggregate initialization.
    An initializer-clause followed by an ellipsis is a pack expansion (13.7.4 [temp.variadic]).
  5. Change in 11.4.1 [class.mem.general] paragraph 11 as follows:

    A brace-or-equal-initializer for a non-static data member specifies a default member initializer for the member, and shall not directly or indirectly cause the implicit definition of a defaulted default constructor for the enclosing class or the exception specification of that constructor. An immediate invocation (7.7 [expr.const]) that is a potentially-evaluated subexpression (6.9.1 [intro.execution]) of a default member initializer is neither evaluated nor checked for whether it is a constant expression at the point where the subexpression appears.
  6. Change in 13.9.2 [temp.inst] paragraph 11 as follows:

    ... The use of a template specialization in a default argument or default member initializer shall not cause the template to be implicitly instantiated except that a templated class template , a templated function with a deduced return type, or a variable template with a deduced type may be instantiated where its complete type is needed to determine the correctness of the default argument or default member initializer. The use of a default argument in a function call causes specializations in the default argument to be implicitly instantiated. Similarly, the use of a default member initializer in a constructor definition or an aggregate initialization causes specializations in the default member initializer to be instantiated.

CWG telecon 2022-10-21:

In the above wording, "constituent expression of a conversion" is not a defined term.

Possible resolution [SUPERSEDED]:

  1. Change in 6.9.1 [intro.execution] paragraph 2 as follows:

    A constituent expression is defined as follows:
    • The constituent expression of an expression is that expression.
    • The constituent expression of a conversion is the corresponding implicit function call, if any, or the converted expression otherwise.
    • The constituent expressions of a braced-init-list or of a (possibly parenthesized) expression-list are the constituent expressions of the elements of the respective list.
    • The constituent expressions of a brace-or-equal-initializer of the form = initializer-clause are the constituent expressions of the initializer-clause.
  2. Change in 6.9.1 [intro.execution] paragraph 4 as follows:

    A subexpression of an expression E is an immediate subexpression of E or a subexpression of an immediate subexpression of E. [ Note: ... -- end note ]
    The potentially-evaluated subexpressions of an expression, conversion, or initializer E are
    • the constituent expressions of E and
    • the subexpressions thereof that are not subexpressions of a nested unevaluated operand (7.2.3 [expr.context]).
  3. Change in 7.7 [expr.const] paragraph 16 as follows:

    An expression or conversion is potentially constant evaluated if it is:
    • ...
    • a potentially-evaluated subexpression (6.9.1 [intro.execution]) of one of the above that is not a subexpression of a nested unevaluated operand (7.2.3 [expr.context]).
  4. Change in 9.3.4.7 [dcl.fct.default] paragraph 5 as follows:

    The default argument has the same semantic constraints as the initializer in a declaration of a variable of the parameter type, using the copy-initialization semantics (9.4 [dcl.init]). The names in the default argument are looked up, and the semantic constraints are checked, at the point where the default argument appears, except that an immediate invocation (7.7 [expr.const]) that is a potentially-evaluated subexpression (6.9.1 [intro.execution]) of the initializer-clause in a parameter-declaration is neither evaluated nor checked for whether it is a constant expression at that point. Name lookup and checking of semantic constraints for default arguments of templated functions are performed as described in 13.9.2 [temp.inst].

  5. Add a paragraph before 9.4.1 [dcl.init.general] paragraph 17 as follows:

    An immediate invocation (7.7 [expr.const]) that is not evaluated where it appears (9.3.4.7 [dcl.fct.default], 11.4.1 [class.mem.general]) is evaluated and checked for whether it is a constant expression at the point where the enclosing initializer is used in a function call, a constructor definition, or an aggregate initialization.
    An initializer-clause followed by an ellipsis is a pack expansion (13.7.4 [temp.variadic]).
  6. Change in 11.4.1 [class.mem.general] paragraph 11 as follows:

    A brace-or-equal-initializer for a non-static data member specifies a default member initializer for the member, and shall not directly or indirectly cause the implicit definition of a defaulted default constructor for the enclosing class or the exception specification of that constructor. An immediate invocation (7.7 [expr.const]) that is a potentially-evaluated subexpression (6.9.1 [intro.execution]) of a default member initializer is neither evaluated nor checked for whether it is a constant expression at the point where the subexpression appears.
  7. Change in 13.9.2 [temp.inst] paragraph 11 as follows:

    ... The use of a template specialization in a default argument or default member initializer shall not cause the template to be implicitly instantiated except that a templated class template , a templated function with a deduced return type, or a variable template with a deduced type may be instantiated where its complete type is needed to determine the correctness of the default argument or default member initializer. The use of a default argument in a function call causes specializations in the default argument to be implicitly instantiated. Similarly, the use of a default member initializer in a constructor definition or an aggregate initialization causes specializations in the default member initializer to be instantiated.

CWG 2022-11-07

Expand requirement to avoid template instantiations in default arguments by not giving a specific, closed list.

Proposed resolution (approved by CWG 2022-11-08):

  1. Change in 6.9.1 [intro.execution] paragraph 2 as follows:

    A constituent expression is defined as follows:
    • The constituent expression of an expression is that expression.
    • The constituent expression of a conversion is the corresponding implicit function call, if any, or the converted expression otherwise.
    • The constituent expressions of a braced-init-list or of a (possibly parenthesized) expression-list are the constituent expressions of the elements of the respective list.
    • The constituent expressions of a brace-or-equal-initializer of the form = initializer-clause are the constituent expressions of the initializer-clause.
  2. Change in 6.9.1 [intro.execution] paragraph 4 as follows:

    A subexpression of an expression E is an immediate subexpression of E or a subexpression of an immediate subexpression of E. [ Note: ... -- end note ]
    The potentially-evaluated subexpressions of an expression, conversion, or initializer E are
    • the constituent expressions of E and
    • the subexpressions thereof that are not subexpressions of a nested unevaluated operand (7.2.3 [expr.context]).
  3. Change in 7.7 [expr.const] paragraph 16 as follows:

    An expression or conversion is potentially constant evaluated if it is:
    • ...
    • a potentially-evaluated subexpression (6.9.1 [intro.execution]) of one of the above that is not a subexpression of a nested unevaluated operand (7.2.3 [expr.context]).
  4. Change in 9.3.4.7 [dcl.fct.default] paragraph 5 as follows:

    The default argument has the same semantic constraints as the initializer in a declaration of a variable of the parameter type, using the copy-initialization semantics (9.4 [dcl.init]). The names in the default argument are looked up, and the semantic constraints are checked, at the point where the default argument appears, except that an immediate invocation (7.7 [expr.const]) that is a potentially-evaluated subexpression (6.9.1 [intro.execution]) of the initializer-clause in a parameter-declaration is neither evaluated nor checked for whether it is a constant expression at that point. Name lookup and checking of semantic constraints for default arguments of templated functions are performed as described in 13.9.2 [temp.inst].

  5. Add a paragraph before 9.4.1 [dcl.init.general] paragraph 17 as follows:

    An immediate invocation (7.7 [expr.const]) that is not evaluated where it appears (9.3.4.7 [dcl.fct.default], 11.4.1 [class.mem.general]) is evaluated and checked for whether it is a constant expression at the point where the enclosing initializer is used in a function call, a constructor definition, or an aggregate initialization.
    An initializer-clause followed by an ellipsis is a pack expansion (13.7.4 [temp.variadic]).
  6. Change in 11.4.1 [class.mem.general] paragraph 11 as follows:

    A brace-or-equal-initializer for a non-static data member specifies a default member initializer for the member, and shall not directly or indirectly cause the implicit definition of a defaulted default constructor for the enclosing class or the exception specification of that constructor. An immediate invocation (7.7 [expr.const]) that is a potentially-evaluated subexpression (6.9.1 [intro.execution]) of a default member initializer is neither evaluated nor checked for whether it is a constant expression at the point where the subexpression appears.
  7. Change in 13.9.2 [temp.inst] paragraph 11 as follows:

    ... The use of a template specialization in a default argument or default member initializer shall not cause the template to be implicitly instantiated except that a class template may be instantiated where its complete type is needed to determine the correctness of the default argument or default member initializer. The use of a default argument in a function call causes specializations in the default argument to be implicitly instantiated. Similarly, the use of a default member initializer in a constructor definition or an aggregate initialization causes specializations in the default member initializer to be instantiated.



2647. Fix for "needed for constant evaluation"

Section: 7.7  [expr.const]     Status: C++23     Submitter: US     Date: 2022-11-03

P2720R0 comment US 17-047

[Accepted as a DR at the November, 2022 meeting.]

The criteria for a variable to be needed for constant evaluation are inconsistent with those for it to be usable in constant expressions (/4).

Proposed resolution (approved by CWG 2022-11-08):

  1. Change in 7.7 [expr.const] paragraph 3 as follows:

    A variable is potentially-constant if it is constexpr or it has reference or non-volatile const-qualified integral or enumeration type.
  2. Change in 7.7 [expr.const] paragraph 16.7 as follows:

    A function or variable is needed for constant evaluation if it is:
    • a constexpr function that is named by an expression (6.3) that is potentially constant evaluated, or
    • a potentially-constant variable named by a potentially constant evaluated expression that is either a constexpr variable or is of non-volatile const-qualified integral type or of reference type.



2658. Trivial copying of unions in core constant expressions

Section: 7.7  [expr.const]     Status: C++23     Submitter: Hubert Tong     Date: 2022-12-04

[Accepted as a DR at the February, 2023 meeting.]

Consider:

  struct A { char c; int x; };
  union U { A a; };
  constexpr int f() {
    U u;
    u.a.c = 1;
    u.a.x = 2;
    U v = u; // indeterminate padding bytes read!
    return u.a.x;
  }
  extern constexpr int x = f();

Subclause 7.7 [expr.const] bullet 5.11 added by P1331R2 prohibits lvalue-to-rvalue conversion of objects having indeterminate value during evaluation of a core constant expression. Trivial copy constructors of unions copy the object representation (not just the active member). The new prohibition causes cases where bytes not involved in the value presentation of the active member and having indeterminate values would prevent a union from being copied by a trivial copy constructor (for example, the padding bytes in the above case).

Note: The source of a union copy is never a prvalue within the evaluation of a trivial copy constructor because the reference parameter is bound to a glvalue.

Proposed resolution (January, 2023):

Add a new paragraph after 7.7 [expr.const] paragraph 6 as follows:

... to an object whose lifetime began within the evaluation of E.

For the purposes of determining whether E is a core constant expression, lvalue-to-rvalue conversion of an object of indeterminate value during the evaluation of a call to a trivial copy/move constructor or copy/move assignment operator of a union does not disqualify E from being a core constant expression unless the active member of the source union object contains a subobject of indeterminate value.

Proposed resolution (approved by CWG 2023-02-07):

Add a new paragraph after 7.7 [expr.const] paragraph 6 as follows:

... to an object whose lifetime began within the evaluation of E.

For the purposes of determining whether E is a core constant expression, the evaluation of a call to a trivial copy/move constructor or copy/move assignment operator of a union is considered to copy/move the active member of the union, if any. [ Note: The copy/move of the active member is trivial. -- end note ]




2616. Imprecise restrictions on break and continue

Section: Clause 8  [stmt.stmt]     Status: C++23     Submitter: Jim X     Date: 2022-08-24

[Accepted as a DR at the November, 2022 meeting.]

Consider:

for (int i = 0; i< 10; ++i){
  auto f = [](){
    break; // #1
  };
}

Subclause 8.7.2 [stmt.break] paragraph 1 specifies:

The break statement shall occur only in an iteration-statement or a switch statement and causes termination of the smallest enclosing iteration-statement or switch statement; control passes to the statement following the terminated statement, if any.

Does the break at #1 "occur" in the for loop?

Proposed resolution (approved by CWG 2022-08-26):

  1. Append to 8.1 [stmt.pre] paragraph 3 as follows:

    ... A statement S1 is enclosed by a statement S2 if S2 encloses S1.

    The rules for conditions apply both...

  2. Change in 8.2 [stmt.label] paragraph 2 as follows:

    Case labels and default labels shall occur only in switch statements A labeled-statement whose label is a case or default label shall be enclosed by (8.1 [stmt.pre]) a switch statement (8.5.3 [stmt.switch])..
  3. Change in 8.7.2 [stmt.break] paragraph 1 as follows:

    The A break statement shall occur only in be enclosed by (8.1 [stmt.pre]) an iteration-statement (8.6 [stmt.iter]) or a switch statement and (8.5.3 [stmt.switch]). The break statement causes termination of the smallest such enclosing iteration-statement or switch statement; control passes to the statement following the terminated statement, if any.
  4. Change in 8.7.3 [stmt.cont] paragraph 1 as follows:

    The A continue statement shall occur only in be enclosed by (8.1 [stmt.pre]) an iteration-statement (8.6 [stmt.iter]) and. The continue statement causes control to pass to the loop-continuation portion of the smallest such enclosing iteration-statement statement, that is, to the end of the loop. ...



2629. Variables of floating-point type as switch conditions

Section: 8.5.3  [stmt.switch]     Status: C++23     Submitter: Jim X     Date: 2022-09-07

[Accepted as a DR at the November, 2022 meeting.]

Consider:

switch(float v = 0) {
  case 0: ;
}

Subclause 8.1 [stmt.pre] paragraph 5 specifies:

... The value of a condition that is an initialized declaration in a switch statement is the value of the declared variable if it has integral or enumeration type, or of that variable implicitly converted to integral or enumeration type otherwise. ...

That appears to permit variables of floating-point type, whose value can be converted to integral type. In contrast, expressions of floating-point type are prohibited by 8.5.3 [stmt.switch] paragraph 2:

The condition shall be of integral type, enumeration type, or class type. ...

Possible resolution [SUPERSEDED]:

Change in 8.1 [stmt.pre] paragraph 5 as follows:

... The value of a condition that is an initialized declaration in a switch statement is the value of the declared variable if it has integral or enumeration type, or of that variable implicitly converted to integral or enumeration type if it has class type; otherwise the program is ill-formed. ...

CWG telecon 2022-10-21:

Rewording is needed.

Proposed resolution (approved by CWG 2022-11-09):

  1. Change in 8.1 [stmt.pre] paragraph 5 as follows:

    ... The value of a condition that is an initialized declaration in a switch statement is the value of the declared variable if it has integral or enumeration type, or of that variable implicitly converted to integral or enumeration type otherwise. ...
  2. Change in 8.5.3 [stmt.switch] paragraph 2 as follows:

    The value of a condition that is an initialized declaration is the value of the declared variable, or the value of the expression otherwise. The value of the condition shall be of integral type, enumeration type, or class type. If of class type, the condition is contextually implicitly converted (7.3 [conv]) to an integral or enumeration type. If the (possibly converted) type is subject to integral promotions (7.3.7 [conv.prom]), the condition is converted to the promoted type. ...



2635. Constrained structured bindings

Section: 9.1  [dcl.pre]     Status: C++23     Submitter: Corentin Jabot     Date: 2022-10-20

[Accepted as a DR at the November, 2022 meeting.]

Consider:

template<class T> concept C = true;
C auto [x, y] = std::pair{1, 2}; // ok?

Subclause 9.1 [dcl.pre] paragraph 6 specifies:

A simple-declaration with an identifier-list is called a structured binding declaration (9.6 [dcl.struct.bind]). If the decl-specifier-seq contains any decl-specifier other than static, thread_local, auto (9.2.9.7 [dcl.spec.auto]), or cv-qualifier s, the program is ill-formed.

Use of the word "contains" leads to an interpretation that any placeholder-type-specifier (9.2.9.7.1 [dcl.spec.auto.general]), possibly including a type-constraint, is valid here, since a placeholder-type-specifier is a decl-specifier and "contains" auto.

However, paper P1141R2 (Yet another approach for constrained declarations), applied in November 2018, expressly excludes structured bindings from constrained auto:

Structured bindings do deduce auto in some cases; however, the auto is deduced from the whole (and not from the individual components). It is somewhat doubtful that applying the constraint to the whole, as opposed to (for example) applying separately to each component, is the correct semantic. Therefore, we propose to defer enabling the application of constraints to structured bindings to separate papers.

Notwithstanding, clang, gcc, and MSVC accept the example.

Proposed resolution (approved by CWG 2022-10-21):

Change in 9.1 [dcl.pre] paragraph 6 specifies:

A simple-declaration with an identifier-list is called a structured binding declaration (9.6 [dcl.struct.bind]). If the decl-specifier-seq contains any decl-specifier other than static, thread_local, auto (9.2.9.7 [dcl.spec.auto]), or cv-qualifiers, the program is ill-formed. Each decl-specifier in the decl-specifier-seq shall be static, thread_local, auto (9.2.9.7 [dcl.spec.auto]), or a cv-qualifier. [ Example:
template<class T> concept C = true;
C auto [x, y] = std::pair{1, 2}; // error: constrained placeholder-type-specifier not permitted for structured bindings
-- end example ]



2410. Implicit calls of immediate functions

Section: 9.2.6  [dcl.constexpr]     Status: C++23     Submitter: John Spicer     Date: 2019-03-27

[Accepted as a DR at the November, 2022 meeting.]

The intent for immediate functions is that they can only be called at compile time. That rule is enforced by the wording of 7.5.4 [expr.prim.id] paragraph 3:

An id-expression that denotes an immediate function (9.2.6 [dcl.constexpr]) shall appear as a subexpression of an immediate invocation or in an immediate function context (7.7 [expr.const]).

However, this restriction does not apply to implicit function calls such as constructor and operator invocations. Presumably some additional wording is needed for such cases.

Additional note, July, 2019:

This issue would appear to be NAD because of the following wording from 7.7 [expr.const] paragraph 10:

An expression or conversion is an immediate invocation if it is an explicit or implicit invocation of an immediate function and is not in an immediate function context. An immediate invocation shall be a constant expression.

Proposed resolution (approved by CWG 2022-10-21):

Change in 7.7 [expr.const] paragraph 10 as follows:

An expression or conversion invocation is an immediate invocation if it is an explicit or implicit invocation of an immediate function and is not in an immediate function context. An immediate invocation shall be a constant expression.



2602. consteval defaulted functions

Section: 9.2.6  [dcl.constexpr]     Status: C++23     Submitter: Aaron Ballman     Date: 2022-06-16

[Accepted as a DR at the February, 2023 meeting.]

It is not clear whether a defaulted consteval function is still an immediate function even if it is not a valid constexpr function. For example:

  template <typename Ty>
  struct A {
    Ty n;
    consteval A() {}
  };

  template <typename Ty>
  struct B {
    Ty n;
    consteval B() = default;
  };

  A<int> a;
  B<int> b;

The declarations of a and b should both fail due to an uninitialized member n in each of A and B. The = default; should not make a difference. However, there is implementation divergence. We should be able to lean on 7.7 [expr.const] bullet 5.5 to handle this when the immediate invocation is required.

Possible resolution:

Change in 9.2.6 [dcl.constexpr] paragraph 7 as follows:

If the instantiated template specialization of a constexpr templated function template or member function of a class template would fail to satisfy the requirements for a constexpr function, that specialization is still a constexpr function, even though a call to such a function cannot appear in a constant expression. Similarly, if the instantiated template specialization of a consteval templated function would fail to satisfy the requirements for a consteval function, that specialization is still an immediate function, even though an immediate invocation would be ill-formed. If no specialization of the template would satisfy the requirements for a constexpr or consteval function when considered as a non-template function, the template is ill-formed, no diagnostic required.

Proposed resolution (August, 2022) [SUPERSEDED]:

Change in 9.2.6 [dcl.constexpr] paragraph 4 as follows:

If the instantiated template specialization of a constexpr templated function template or member function of a class template would fail to satisfy the requirements for a constexpr function, that specialization is still a constexpr function, even though a call to such a function cannot appear in a constant expression. Similarly, if the instantiated template specialization of a consteval templated function would fail to satisfy the requirements for a consteval function, that specialization is still an immediate function, even though an immediate invocation would be ill-formed.

Additional notes (November, 2022)

The proposed wording is possibly not addressing the point of the issue; the issue has been retracted from the WG21 plenary straw polls for further consideration in CWG.

Proposed resolution (approved by CWG 2023-01-27):

  1. Change in 9.2.6 [dcl.constexpr] paragraph 3 as follows:

    The definition of a constexpr function shall satisfy the following requirements: A function is constexpr-suitable if:
    • it shall is not be a coroutine (9.5.4 [dcl.fct.def.coroutine]); , and
    • if the function is a constructor or destructor, its class shall does not have any virtual base classes.
    Except for instantiated constexpr functions, non-templated constexpr functions shall be constexpr-suitable.
  2. Delete 9.2.6 [dcl.constexpr] paragraph 4:

    If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function, that specialization is still a constexpr function, even though a call to such a function cannot appear in a constant expression.
  3. Change in 7.5.5.2 [expr.prim.lambda.closure] paragraph 5 as follows:

    ... The function call operator or any given operator template specialization is a constexpr function if either the corresponding lambda-expression's parameter-declaration-clause is followed by constexpr or consteval, or it satisfies the requirements for a constexpr function is constexpr-suitable (9.2.6 [dcl.constexpr]). ...
  4. Change in 7.7 [expr.const] bullet 5.5 as follows:

    • an invocation of an instantiated constexpr function that fails to satisfy the requirements for a constexpr function is not constexpr-suitable;
  5. Change in 9.5.2 [dcl.fct.def.default] paragraph 3 as follows:

    A function explicitly defaulted on its first declaration is implicitly inline (9.2.8 [dcl.inline]), and is implicitly constexpr (9.2.6 [dcl.constexpr]) if it satisfies the requirements for a constexpr function is constexpr-suitable.
  6. Change in 11.4.7 [class.dtor] paragraph 9 as follows:

    A defaulted destructor is a constexpr destructor if it satisfies the requirements for a constexpr function is constexpr-suitable (9.2.6 [dcl.constexpr]).



2543. constinit and optimized dynamic initialization

Section: 9.2.7  [dcl.constinit]     Status: C++23     Submitter: Zhihao Yuan     Date: 2022-03-01     Liaison: (EWG)

[Accepted as a DR at the February, 2023 meeting.]

Subclause 9.2.7 [dcl.constinit] paragraph 2 states:

If a variable declared with the constinit specifier has dynamic initialization (6.9.3.3 [basic.start.dynamic]), the program is ill-formed. [ Note: The constinit specifier ensures that the variable is initialized during static initialization (6.9.3.2 [basic.start.static]). —end note]

Subclause 6.9.3.2 [basic.start.static] paragraph 3 gives permission for an implementation to perform static initialization in lieu of dynamic initialization:

An implementation is permitted to perform the initialization of a variable with static or thread storage duration as a static initialization even if such initialization is not required to be done statically, provided that ...

constinit will assuredly not give a diagnostic for variables that are constant initialized (7.7 [expr.const] paragraph 2), because those are required to be statically initialized and thus will never be dynamically initialized. Conversely, constinit is guaranteed to give a diagnostic for variables that cannot be statically initialized, for example those with an initializer whose value depends on runtime conditions.

Between those boundaries, it is unclear whether constinit ought to give a diagnostic for variables whose initializer does not satisfy the constraints of constant-initialized, yet the implementation takes advantage of the permission to turn dynamic initialization into static initialization. For example,

  float f;
  constinit int * pi = (int*) &f;    // reinterpret_cast, not constant-initialized

The current wording seems to imply that constinit accurately reflects whether dynamic initialization was turned into static initialization by the implementation. However, that is impossible to implement, because such decisions are often made by the optimizer, which runs later than the compiler front-end interpreting the program text containing constinit.

There is value in permitting constinit not to diagnose some of the dynamic initializations that are turned into static initializations.

There is also value in having portable semantics of constinit.

See also issue 2536.

Notes from the November, 2022 meeting

CWG seeks the advice of EWG how to proceed with this issue, via cplusplus/papers#1379.

EWG 2023-01-19

EWG resolved to desire portable, guaranteed semantics for constinit. That means, constinit should produce a diagnostic if de-jure dynamic initialization is turned into de-facto static initialization as permitted by 6.9.3.2 [basic.start.static] paragraph 3.

Proposed resolution (approved by CWG 2023-02-06):

Change in 9.2.7 [dcl.constinit] paragraph 2 as follows:

If a variable declared with the constinit specifier has dynamic initialization (6.9.3.3 [basic.start.dynamic]), the program is ill-formed, even if the implementation would perform that initialization as a static initialization (6.9.3.2 [basic.start.static]). [Note 1: The constinit specifier ensures that the variable is initialized during static initialization (6.9.3.2 [basic.start.static]). —end note]



2620. Nonsensical disambiguation rule

Section: 9.3.3  [dcl.ambig.res]     Status: C++23     Submitter: Krystian Stasiowski     Date: 2019-04-18

[Accepted as a DR at the November, 2022 meeting.]

Subclause 9.3.3 [dcl.ambig.res] paragraph 1 specifies:

The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 8.9 [stmt.ambig] can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 8.9 [stmt.ambig], the resolution is to consider any construct that could possibly be a declaration a declaration.

The specification correctly describes an ambiguity between a function declaration and an object declaration, but resolves the ambiguity to a "declaration", which does not offer any insight.

Proposed resolution (2022-09-09) [SUPERSEDED]:

Change in 9.3.3 [dcl.ambig.res] paragraph 1 as follows:

... Just as for the ambiguities mentioned in 8.9 [stmt.ambig], the The resolution is to consider any construct that could possibly be a function declaration a function declaration.

Proposed resolution (approved by CWG 2022-09-23):

Change in 9.3.3 [dcl.ambig.res] paragraph 1 as follows:

The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 8.9 [stmt.ambig] can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer and a declaration involving a function declarator with a redundant set of parentheses around a parameter name. Just as for the ambiguities mentioned in 8.9 [stmt.ambig], the resolution is to consider any construct, such as the potential parameter declaration, that could possibly be a declaration to be a declaration.



2653. Can an explicit object parameter have a default argument?

Section: 9.3.4.6  [dcl.fct]     Status: C++23     Submitter: GB     Date: 2022-11-03     Liaison: EWG

P2720R0 comment GB 051

[Accepted at the November, 2022 meeting.]

The syntax and semantics appear to allow:

  struct S { void f(this const S& = S{}); };
This is probably no more than an oddity, but perhaps it should be prevented.

Proposed resolution (approved by CWG 2022-11-08):

Change in 9.3.4.6 [dcl.fct] paragraph 3 as follows:

parameter-declaration:
    attribute-specifier-seqopt thisopt decl-specifier-seq declarator
    attribute-specifier-seqopt thisopt decl-specifier-seq declarator = initializer-clause
    attribute-specifier-seqopt thisopt decl-specifier-seq abstract-declaratoropt
    attribute-specifier-seqopt thisopt decl-specifier-seq abstract-declaratoropt = initializer-clause



2612. Incorrect comment in example

Section: 9.4.1  [dcl.init.general]     Status: C++23     Submitter: Jiang An     Date: 2022-05-24

[Accepted as a DR at the November, 2022 meeting.]

Subclause 9.4.1 [dcl.init.general] bullet 16.6.1 says:

[Example 2: T x = T(T(T())); calls the T default constructor to initialize x. —end example]

This is incorrect; in some situations, the default constructor is not invoked (see 9.4.1 [dcl.init.general] paragraph 9).

Proposed resolution (approved by CWG 2022-08-26):

Change in 9.4.1 [dcl.init.general] bullet 16.6.1 as follows:

[Example 2: T x = T(T(T())); calls the T default constructor to initialize value-initializes x. —end example]



2610. Indirect private base classes in aggregates

Section: 9.4.2  [dcl.init.aggr]     Status: C++23     Submitter: Chris Bowler     Date: 2022-07-21

[Accepted as a DR at the November, 2022 meeting.]

The wording appears to prohibit aggregates from having indirect private base classes. That does not match existing practice and is an unnecessary restriction. For example:

#include <type_traits>

struct B1 {};
struct B2 : private B1 {};
struct S : public B2 {};

void f() {
  static_assert(std::is_aggregate<S>::value);
}

Proposed resolution (approved by CWG 2022-08-26):

Change in 9.4.2 [dcl.init.aggr] paragraph 1 as follows:

An aggregate is an array or a class (Clause 11 [class]) with



2619. Kind of initialization for a designated-initializer-list

Section: 9.4.2  [dcl.init.aggr]     Status: C++23     Submitter: Jim X     Date: 2022-07-13

[Accepted as a DR at the November, 2022 meeting.]

Consider:

struct S {
  explicit S(int){}
};
struct A {
  S s;
};
struct B {
  union {
    S s;
  };
};
int main() {
  A a1 = {.s{0}};  // #1
  A a2{.s{0}};     // #2
  B b1 = {.s{0}};  // #3
  B b2{.s{0}};     // #4
}

Subclause 9.4.2 [dcl.init.aggr] bullet 4.2 specifies:

Otherwise, the element is copy-initialized from the corresponding initializer-clause or is initialized with the brace-or-equal-initializer of the corresponding designated-initializer-clause.

It is unclear what kind of initialization is performed for "is initialized". For example, one could imagine that the top-level kind of initialization is inherited. On the other hand, 9.4.1 [dcl.init.general] paragraph 14 specifies:

The initialization that occurs in the = form of a brace-or-equal-initializer or condition (8.5 [stmt.select]), as well as in argument passing, function return, throwing an exception (14.2 [except.throw]), handling an exception (14.4 [except.handle]), and aggregate member initialization (9.4.2 [dcl.init.aggr]), is called copy-initialization.

There is implementation divergence: gcc and icc reject the example; clang and MSVC accept.

Suggested resolution [SUPERSEDED]:

  1. Change in 9.4.2 [dcl.init.aggr] bullet 4.1 as follows:

    If the element is an anonymous union member and the initializer list is a brace-enclosed designated-initializer-list, the element is initialized by the designated-initializer-list braced-init-list { D }, where D is the designated-initializer-clause naming a member of the anonymous union member.
  2. Change in 9.4.2 [dcl.init.aggr] bullet 4.2 as follows:

    Otherwise, the element is copy-initialized from the corresponding initializer-clause or is initialized copy-initialized or direct-initialized with the brace-or-equal-initializer of the corresponding designated-initializer-clause, according to the form of the brace-or-equal-initializer (9.4.1 [dcl.init.general]). ...

CWG telecon 2022-09-09:

The examples #1 to #4 should all be valid, direct-initializing the s member.

Proposed resolution (approved by CWG 2022-09-23):

  1. Change in 9.4.1 [dcl.init.general] paragraph 14 as follows:

    The initialization that occurs in the = form of a brace-or-equal-initializer or condition (8.5 [stmt.select]), as well as in argument passing, function return, throwing an exception (14.2 [except.throw]), handling an exception (14.4 [except.handle]), and aggregate member initialization other than by a designated-initializer-clause (9.4.2 [dcl.init.aggr]), is called copy-initialization.
  2. Change in 9.4.2 [dcl.init.aggr] bullet 4.1 as follows:

    If the element is an anonymous union member and the initializer list is a brace-enclosed designated-initializer-list, the element is initialized by the designated-initializer-list braced-init-list { D }, where D is the designated-initializer-clause naming a member of the anonymous union member.
  3. Change in 9.4.2 [dcl.init.aggr] bullet 4.2 as follows:

    Otherwise, the element is copy-initialized from the corresponding initializer-clause or is initialized with the brace-or-equal-initializer of the corresponding designated-initializer-clause. If that initializer is of the form assignment-expression or = assignment-expression and a narrowing conversion (9.4.5 [dcl.init.list]) is required to convert the expression, the program is ill-formed. [ Note: If the initialization is by designated-initializer-clause, its form determines whether copy-initialization or direct-initialization is performed.-- end note]



2627. Bit-fields and narrowing conversions

Section: 9.4.5  [dcl.init.list]     Status: C++23     Submitter: Tim Song     Date: 2021-08-13

[Accepted as a DR at the November, 2022 meeting.]

Consider:

struct C {
  long long i : 8;
};

void f() {
  C x{1}, y{2};
  x.i <=> y.i; // error: narrowing conversion required (7.6.8 [expr.spaceship] bullet 4.1)
}

The rules for narrowing conversions in 9.4.5 [dcl.init.list] paragraph 7 consider only the source type, even though integral promotions can change the type of a bit-field to a smaller integer type without loss of value range according to 7.3.7 [conv.prom] paragraph 5:

A prvalue for an integral bit-field (11.4.10 [class.bit]) can be converted to a prvalue of type int if int can represent all the values of the bit-field; otherwise, it can be converted to unsigned int if unsigned int can represent all the values of the bit-field. If the bit-field is larger yet, no integral promotion applies to it. If the bit-field has enumeration type, it is treated as any other value of that type for promotion purposes.

There is implementation divergence in the handling of this example.

Proposed resolution (approved by CWG 2022-10-07):

Change in 9.4.5 [dcl.init.list] bullet 7.4 as follows:

A narrowing conversion is an implicit conversion




2646. Defaulted special member functions

Section: 9.5.2  [dcl.fct.def.default]     Status: C++23     Submitter: US     Date: 2022-11-03

P2720R0 comment US 20-052

[Accepted as a DR at the November, 2022 meeting.]

The exposition in terms of F1 and F2 as well as T1 and T2 is confusing.

Possible resolution:

Change in 9.5.2 [dcl.fct.def.default] paragraph 2 as follows:

An explicitly defaulted special member function F1 with type T1 is allowed to differ from the corresponding special member function F2 with type T2 that would have been implicitly declared, as follows: If T1 the type of F1 differs from T2 the type of F2 in a way other than as allowed by the preceding rules, then:



2451. promise.unhandled_exception() and final suspend point

Section: 9.5.4  [dcl.fct.def.coroutine]     Status: C++23     Submitter: Lewis Baker     Date: 2020-02-14

[Accepted as a DR at the November, 2022 meeting.]

According to 9.5.4 [dcl.fct.def.coroutine] paragraph 14,

If the evaluation of the expression promise.unhandled_exception() exits via an exception, the coroutine is considered suspended at the final suspend point.

However, the “final suspend point” is defined as being “the await-expression containing the call to final_suspend” (bullet 5.2), and it is not desired to evaluate the final_suspend expression in this case.

Suggested resolution [SUPERSEDED]:

  1. Change 9.5.4 [dcl.fct.def.coroutine] paragraph 5 as follows:

  2. ...where

  3. Change bullet 3.2 of 7.6.2.4 [expr.await] as follows:

  4. Evaluation of an await-expression involves the following auxiliary types, expressions, and objects:

  5. If needed, change 9.5.4 [dcl.fct.def.coroutine] paragraph 14 as follows:

  6. If the evaluation of the expression promise.unhandled_exception() exits via an exception, the coroutine is considered suspended at the final suspend point and the exception propagates to the caller or resumer.

Notes from the August, 2020 teleconference [SUPERSEDED]:

CWG expressed some concern about the lack of a precise definition of “suspend point”. Gor Nishanov suggests the following change, in 7.6.2.4 [expr.await] bullet 5.1:

Proposed resolution (approved by CWG 2022-11-10):

  1. Change in 7.6.2.4 [expr.await] bullet 3.2 as follows:

    Evaluation of an await-expression involves the following auxiliary types, expressions, and objects:

    • ...

    • a is the cast-expression if the await-expression was implicitly produced by a yield-expression (7.6.17 [expr.yield]), an initial suspend point await expression, or a final suspend point await expression (9.5.4 [dcl.fct.def.coroutine]). Otherwise

    • ...

  2. Change in 7.6.2.4 [expr.await] bullet 5.1 as follows:

    • If the evaluation of await-suspend exits via an exception, the exception is caught, the coroutine is resumed, and the exception is immediately re-thrown (14.2 [except.throw]). Otherwise, control flow returns to the current coroutine caller or resumer (9.5.4 [dcl.fct.def.coroutine]) without exiting any scopes (8.7 [stmt.jump]). The point in the coroutine immediately prior to control returning to its caller or resumer is a coroutine suspend point.

  3. Change in 9.5.4 [dcl.fct.def.coroutine] paragraph 5 as follows:

    ...where

    • the await-expression containing the call to initial_suspend is the initial suspend point await expression, and

    • the await-expression containing the call to final_suspend is the final suspend point await expression, and

    • initial-await-resume-called is initially false and is set to true immediately before the evaluation of the await-resume expression (7.6.2.4 [expr.await]) of the initial suspend point await expression, and

    • ...

    • promise-constructor-arguments is determined as follows: overload resolution is performed on a promise constructor call created by assembling an argument list with lvalues p1 ... pn. If a viable constructor is found (12.2.3 [over.match.viable]), then promise-constructor-arguments is (p1, ..., pn), otherwise promise-constructor-arguments is empty. , and

    • a coroutine is suspended at the initial suspend point if it is suspended at the initial await expression, and
    • a coroutine is suspended at a final suspend point if it is suspended

      • at a final await expression or

      • due to an exception exiting from unhandled_exception()

  4. Change 9.5.4 [dcl.fct.def.coroutine] paragraph 14 as follows:

    If the evaluation of the expression promise.unhandled_exception() exits via an exception, the coroutine is considered suspended at the final suspend point and the exception propagates to the caller or resumer.



2613. Incomplete definition of resumer

Section: 9.5.4  [dcl.fct.def.coroutine]     Status: C++23     Submitter: Jim X     Date: 2022-02-15

[Accepted as a DR at the November, 2022 meeting.]

Subclause 9.5.4 [dcl.fct.def.coroutine] paragraph 8 specifies:

A suspended coroutine can be resumed to continue execution by invoking a resumption member function (17.12.4.6 [coroutine.handle.resumption]) of a coroutine handle (17.12.4 [coroutine.handle]) that refers to the coroutine. The function that invoked a resumption member function is called the resumer.

However, non-functions can also resume a coroutine, for example:

Task task() {
  std::cout << "in task\n";
  int r = co_await Line();
  std::cout << "resumed\n";
  co_return r;
}
auto r = task();
auto c = (r.coro_.resume(), 0); // #1

Proposed resolution (approved by CWG 2022-08-26):

Change in 9.5.4 [dcl.fct.def.coroutine] paragraph 8 as follows:

A suspended coroutine can be resumed to continue execution by invoking a resumption member function (17.12.4.6 [coroutine.handle.resumption]) of a coroutine handle (17.12.4 [coroutine.handle]) that refers to the coroutine. The function evaluation that invoked a resumption member function is called the resumer.



2590. Underlying type should determine size and alignment requirements of an enum

Section: 9.7.1  [dcl.enum]     Status: C++23     Submitter: Brian Bi     Date: 2022-05-15

[Accepted as a DR at the November, 2022 meeting.]

Subclause 9.7.1 [dcl.enum] specifies how the underlying type of an enumeration is determined, and, for enumerations whose underlying type is fixed, specifies that the enumeration has the same set of values as the underlying type. However, the specification does not relate the size and alignment requirements of the enumeration to those of the underlying type. Those ought to be the same.

Suggested resolution [SUPERSEDED]:

Add a new paragraph after 9.7.1 [dcl.enum] paragraph 8:

For an enumeration whose underlying type is fixed, ...

An enumeration has the same size, value representation, and alignment requirements (6.7.6 [basic.align]) as its underlying type. Furthermore, each value of an enumeration has the same representation as the same value of the underlying type.

Two enumeration types are layout-compatible enumerations if ...

Proposed resolution (approved by CWG 2022-08-26):

Add a new paragraph after 9.7.1 [dcl.enum] paragraph 8:

For an enumeration whose underlying type is fixed, ...

An enumeration has the same size, value representation, and alignment requirements (6.7.6 [basic.align]) as its underlying type. Furthermore, each value of an enumeration has the same representation as the corresponding value of the underlying type.

Two enumeration types are layout-compatible enumerations if ...




2621. Kind of lookup for using enum declarations

Section: 9.7.2  [enum.udecl]     Status: C++23     Submitter: Sean Baxter     Date: 2022-08-17

P2720R0 comment CA 054

[Accepted as a DR at the November, 2022 meeting.]

Consider:

enum class E {
  a, b, c
};

using MyE = E;

int main() {
  using enum MyE;   // #1
}

Does the lookup for the elaborated-enum-specifier at #1 use type-only lookup per 6.5.6 [basic.lookup.elab]? There is implementation divergence; EDG, gcc, and MSVC accept, clang rejects.

Suggested resolution [SUPERSEDED]:

Change in 9.7.2 [enum.udecl] paragraph 1 as follows:

Lookup for the elaborated-enum-specifier is as specified in 6.5.6 [basic.lookup.elab]. The elaborated-enum-specifier shall not name a dependent type and the type shall have a reachable enum-specifier.

CWG telecon 2022-09-09:

The example at #1 is intended to be valid; even though the grammar suggests that an elaborated-type-specifier is used here, an ordinary (not a type-only) lookup is performed.

Proposed resolution (approved by CWG 2022-09-23) [SUPERSEDED]:

Change in 9.7.2 [enum.udecl] paragraph 1 as follows:

The terminal name of the elaborated-enum-specifier undergoes ordinary lookup (6.5.3 [basic.lookup.unqual], 6.5.5 [basic.lookup.qual]). The elaborated-enum-specifier shall not name a dependent type and the type shall have a reachable enum-specifier.

CWG 2022-11-07:

Use the wording approach presented in CA-054, with necessary adjunct fixes.

Proposed resolution (approved by CWG 2022-11-10):

Change the grammar before 9.2.9.5 [dcl.type.elab] paragraph 1 as follows, merging the grammar productions:

elaborated-type-specifier :
    class-key attribute-specifier-seqopt nested-name-specifieropt identifier
    class-key simple-template-id
    class-key nested-name-specifier templateopt simple-template-id
    elaborated-enum-specifier
    
elaborated-enum-specifier :
    enum nested-name-specifieropt identifier

Change the grammar before 9.7.2 [enum.udecl] paragraph 1 as follows:

using-enum-declaration:
    using elaborated-enum-specifier enum using-enum-declarator ;

using-enum-declarator:
    nested-name-specifieropt identifier
    nested-name-specifieropt simple-template-id

Change in 9.7.2 [enum.udecl] paragraph 1 as follows:

A using-enum-declarator names the set of declarations found by lookup (6.5.3 [basic.lookup.unqual], 6.5.5 [basic.lookup.qual]) for the using-enum-declarator. The elaborated-enum-specifier using-enum-declarator shall not name a dependent designate a non-dependent type and the type shall have with a reachable enum-specifier.



2483. Language linkage of static member functions

Section: 9.11  [dcl.link]     Status: C++23     Submitter: Davis Herring     Date: 2021-03-11

[Accepted as a DR at the February, 2023 meeting.]

According to 9.11 [dcl.link] paragraph 5,

A C language linkage is ignored in determining the language linkage of class members, friend functions with a trailing requires-clause, and the function type of class member functions.

It doesn't make sense that static member functions should behave like non-static member functions in this regard:

   extern "C" {
     struct A {
       static void f();
       constexpr static void (*p)()=f; // error: must point to a function whose type has C language linkage
     };
   }

Suggested resolution:

Change 9.11 [dcl.link] paragraph 5 as follows:

A C language linkage is ignored in determining the language linkage of class members, friend functions with a trailing requires-clause, and the function type of non-static class member functions.

Notes from the August, 2021 teleconference:

There was some question as to whether a linkage specification should affect the language linkage of any function declarators within class scope. The question was also raised as to whether some non-typedef syntax should be available for affecting language linkage, which would be a question for EWG.

Proposed resolution (approved by CWG 2022-11-10):

Change 9.11 [dcl.link] paragraph 5 as follows:

A C language linkage is ignored in determining the language linkage of class members, friend functions with a trailing requires-clause, and the function type of non-static class member functions.



2538. Can standard attributes be syntactically ignored?

Section: 9.12.1  [dcl.attr.grammar]     Status: C++23     Submitter: Jens Maurer     Date: 2021-12-02     Liaison: EWG

P2720R0 comment GB 055

[Accepted as a DR at the November, 2022 meeting.]

Subclause 9.12.1 [dcl.attr.grammar] paragraph 6 specifies that an unrecognized attribute-token is ignored:

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.

The intent is that only non-standard unrecognized attribute-tokens can be ignored; in particular, an implementation is required to syntax-check all standard attributes, even if the implementation then chooses not to effect any semantics for that attribute.

The paper introducing attributes was N2761; the phrasing in question was introduced by P0283R2 attempting to implement the design change presented in P0283R1.

See also paper P2552 (On the ignorability of standard attributes).

Suggested resolution:

Change in 9.12.1 [dcl.attr.grammar] paragraph 6 as follows:

For an attribute-token (including an attribute-scoped-token) not specified in this document, the behavior is implementation-defined. Any ; any such attribute-token that is not recognized by the implementation is ignored.

EWG telecon 2022-05-26

See paper issue 1252.

There was consensus for the statement "It is EWG's intent that [dcl.attr]/6 ONLY permits an implementation to ignore a standard attribute's effect, but not appertainment and argument parsing." To be confirmed by electronic polling.

Proposed resolution (approved by CWG 2022-07-01) [SUPERSEDED]:

Change in 9.12.1 [dcl.attr.grammar] paragraph 6 as follows:

For an attribute-token (including an attribute-scoped-token) not specified in this document, the behavior is implementation-defined. Any ; any such attribute-token that is not recognized by the implementation is ignored. [ Note: A program is ill-formed if it contains an attribute specified in 9.12 [dcl.attr] that violates the rules to which entity or statement the attribute may apply or the syntax rules for the attribute's attribute-argument-clause, if any. -- end note ]

EWG 2022-06 electronic polling

No consensus. See vote.

EWG 2022-11-08

Approved the direction of the 2022-07-01 proposed resolution.

Proposed resolution (approved by CWG 2022-11-08):

Change in 9.12.1 [dcl.attr.grammar] paragraph 6 as follows:

For an attribute-token (including an attribute-scoped-token) not specified in this document, the behavior is implementation-defined. Any ; any such attribute-token that is not recognized by the implementation is ignored. [ Note: A program is ill-formed if it contains an attribute specified in 9.12 [dcl.attr] that violates the rules specifying to which entity or statement the attribute may apply or the syntax rules for the attribute's attribute-argument-clause, if any. -- end note ]



2695. Semantic ignorability of attributes

Section: 9.12.1  [dcl.attr.grammar]     Status: C++23     Submitter: Timur Doumler     Date: 2023-02-09

[Accepted as a DR at the February, 2023 meeting.]

EWG resolved to reflect the understanding of semantic ignorability of attributes in a note.

Proposed resolution (approved by CWG 2023-02-09):

Add to 9.12.1 [dcl.attr.grammar] paragraph 6 as follows:

[Note 4: A program is ill-formed if it contains an attribute specified in 9.12 [dcl.attr] that violates the rules specifying to which entity or statement the attribute can apply or the syntax rules for the attribute's attribute-argument-clause, if any. —end note] [Note: The attributes specified in 9.12 [dcl.attr] have optional semantics: given a well-formed program, removing all instances of any one of those attributes results in a program whose set of possible executions (4.1.2 [intro.abstract]) for a given input is a subset of those of the original program for the same input, absent implementation-defined guarantees with respect to that attribute. -- end note ]



2443. Meaningless template exports

Section: 10.2  [module.interface]     Status: C++23     Submitter: Davis Herring     Date: 2019-11-09     Liaison: EWG

[Accepted as a DR at the November, 2022 meeting, as part of paper P2615R1 (Meaningful exports).]

According to 10.2 [module.interface] paragraph 1, export does not interfere with other definitions; paragraph 3 merely requires that it appear in a declaration that declares at least one name. 13.1 [temp.pre] paragraph 4 prevents using an export-declaration as the declaration of a template-declaration.

With some interpretation, these rules appear to allow various useless constructs like:

   template export void f();
   export template void f();
   export template<> void g(int);
   template<> export void g(int);
   export template<class T> struct trait<T*>;

Simply forbidding them in 10.2 [module.interface] paragraph 3 would also prohibit their appearance in export blocks:

   export {
     template<class> struct A;
     template<class T> struct A<T*>;
   }

It is already the case that the closely-related example

   export {
     template<class T> struct A {A(non_deducible<T>);};
     template<class U> A(U) -> A<find_param<U>>;
   }

is disallowed, although a fix is pending in EWG.

Suggested resolution: Forbid the direct use of the export keyword in these contexts but continue to allow them (and perhaps more) in export { }.

Notes from the February, 2021 teleconference:

CWG agreed with the suggested direction.

Notes from the 2022-05-20 CWG telecon:

CWG agreed with the wording suggested by Herring; forwarding to EWG for approval.




2605. Implicit-lifetime aggregates

Section: 11.2  [class.prop]     Status: C++23     Submitter: Davis Herring     Date: 2022-06-27

[Accepted as a DR at the November, 2022 meeting.]

Subclause 11.2 [class.prop] paragraph 9 specifies:

A class S is an implicit-lifetime class if

However, an aggregate may have a non-deleted non-trivial destructor:

  struct X {
    Y i;
    ~X();
  };

This class is an aggregate, but destroying X itself (ignoring the subobjects) does not satisfy "destroying an instance of the type runs no code"; see P0593R6 "Implicit creation of objects for low-level object manipulation" section 3.1.

Additional notes (September, 2022):

From a thread starting here: What if X had a deleted destructor (either explicitly or implicitly)?

CWG 2022-11-09:

A deleted destructor does not prevent an aggregate from being an implicit-lifetime class.

Proposed resolution (approved by CWG 2022-11-09):

Change in 11.2 [class.prop] paragraph 9 as follows:

A class S is an implicit-lifetime class if



2583. Common initial sequence should consider over-alignment

Section: 11.4.1  [class.mem.general]     Status: C++23     Submitter: Brian Bi     Date: 2022-05-03

[Accepted as a DR at the November, 2022 meeting.]

Consider:

  struct A {
    int i;
    char c;
  };

  struct B {
    int i;
    alignas(8) char c;
  };

  union U { A a; B b; };

On a lot of platforms, A and B do not have the same layout, yet 11.4.1 [class.mem.general] paragraph 23 does not consider differences in alignment in the rules for "common initial sequence":

The common initial sequence of two standard-layout struct (11.2 [class.prop]) types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that corresponding entities have layout-compatible types (6.8 [basic.types]), either both entities are declared with the no_unique_address attribute (9.12.11 [dcl.attr.nouniqueaddr]) or neither is, and either both entities are bit-fields with the same width or neither is a bit-field.

In the following example,

  struct S0 {
    alignas(16) char x[128];
    int i;
  };
  struct alignas(16) S1 {
    char x[128];
    int i;
  };

S0 and S1 have the same alignment, yet per the suggested rules below, they will not be layout-compatible.

Suggested resolution [SUPERSEDED]:

Change in 11.4.1 [class.mem.general] paragraphs 23-25 as follows (also add bullets):

The common initial sequence of two standard-layout struct (11.2 [class.prop]) types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that

[...]

Two standard-layout struct (11.2 [class.prop]) types are layout-compatible classes if their common initial sequence comprises all members and bit-fields of both classes (6.8 [basic.types]) and either both types are declared with alignment-specifiers that specify equivalent alignment or neither type has an alignment-specifier.

Two standard-layout unions are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in any order)

Proposed resolution (approved by CWG telecon 2022-08-26):

Change in 11.4.1 [class.mem.general] paragraphs 23-25 as follows (also add bullets):

The common initial sequence of two standard-layout struct (11.2 [class.prop]) types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that

[...]




2630. Syntactic specification of class completeness

Section: 11.4.1  [class.mem.general]     Status: C++23     Submitter: Jim X     Date: 2022-07-04

[Accepted as a DR at the November, 2022 meeting.]

Consider:

// translation unit 1
export module A;
export class X {};

// translation unit 2
import A;
X x; // is X complete at this point?

Subclause 11.4.1 [class.mem.general] paragraph 8 specifies:

A class is considered a completely-defined object type (6.8.1 [basic.types.general]) (or complete type) at the closing } of the class-specifier. ...

The syntactic (even lexical) reference to the closing } does not address the question when a different translation unit regards a class as complete. However, it seems this provision is entirely redundant given 6.3 [basic.def.odr] paragraph 13:

A definition of a class shall be reachable in every context in which the class is used in a way that requires the class type to be complete.

The standard never asks the question: "Is class X complete?"; it always specifies "X shall be complete" (otherwise the program is ill-formed).

Possible resolution [SUPERSEDED]:

Change in 11.4.1 [class.mem.general] paragraph 8 as follows:

A class is considered a completely-defined object type (6.8.1 [basic.types.general]) (or complete type) at the closing } of the class-specifier. The A class is regarded as complete within its complete-class contexts; otherwise it is regarded as incomplete within its own class member-specification.

CWG telecon 2022-10-21:

Explicitly refer to reachable definitions.

Proposed resolution (approved by CWG 2022-11-09):

Change in 11.4.1 [class.mem.general] paragraph 8 as follows:

A class is considered a completely-defined object type (6.8.1 [basic.types.general]) (or complete type) at the closing } of the class-specifier. The A class is regarded as complete where its definition is reachable and within its complete-class contexts; otherwise it is regarded as incomplete within its own class member-specification.



2674. Prohibit explicit object parameters for constructors

Section: 11.4.5.1  [class.ctor.general]     Status: C++23     Submitter: Erich Keane     Date: 2022-01-11

[Accepted at the February, 2023 meeting.]

Constructors are not intended to have explicit object parameters, but the standard is missing a corresponding prohibition.

Proposed resolution (approved by CWG 2023-01-06):

Add a new paragraph after 11.4.5.1 [class.ctor.general] paragraph 7 as follows:

A constructor shall not be a coroutine.

A constructor shall not have an explicit object parameter (9.3.4.6 [dcl.fct]).




2690. Semantics of defaulted move assignment operator for unions

Section: 11.4.6  [class.copy.assign]     Status: C++23     Submitter: Cassio Neri     Date: 2023-01-20

[Accepted as a DR at the February, 2023 meeting.]

Subclause 11.4.6 [class.copy.assign] paragraph 13 does not specify the semantics of a defaulted move assignment operator:

The implicitly-defined copy assignment operator for a union X copies the object representation (6.8.1 [basic.types.general]) of X. ...

In contrast, the corresponding rule for move constructors is present in 11.4.5.3 [class.copy.ctor] paragraph 15:

The implicitly-defined copy/move constructor for a union X copies the object representation (6.8.1 [basic.types.general]) of X. ...

Proposed resolution (approved by CWG 2023-01-27):

Change in 11.4.6 [class.copy.assign] paragraph 13 as follows:

The implicitly-defined copy/move assignment operator for a union X copies the object representation (6.8.1 [basic.types.general]) of X. ...



2662. Example for member access control vs. overload resolution

Section: 11.8.1  [class.access.general]     Status: C++23     Submitter: Shafik Yaghmour     Date: 2022-12-02

[Accepted as a DR at the February, 2023 meeting.]

Issue 600 was resolved by P1787R6, but no example was added.

Proposed resolution (approved by CWG 2023-01-06):

Change in 11.8.1 [class.access.general] paragraph 4 as follows:

... When a using-declarator is named, access control is applied to it, not to the declarations that replace it. For an overload set, access control is applied only to the function selected by overload resolution.

[ Example:

  struct S {
    void f(int);
  private:
    void f(double);
  };

  void g(S* sp) {
    sp->f(2);    // OK, access control applied after overload resolution
  }

-- end example ]




2539. Three-way comparison requiring strong ordering for floating-point types

Section: 11.10.3  [class.spaceship]     Status: C++23     Submitter: Richard Smith     Date: 2022-02-24

[Accepted as a DR at the February, 2023 meeting.]

Consider:

  struct MyType {
    int i;
    double d;
    std::strong_ordering operator<=> (const MyType& c) const = default;
  };

The defaulted three-way comparison operator is defined only if it is used, per 11.10.1 [class.compare.default] paragraph 1:

A comparison operator function for class C that is defaulted on its first declaration and is not defined as deleted is implicitly defined when it is odr-used or needed for constant evaluation.

The current rules make an odr-use of the three-way comparison operator ill-formed, but it would be preferable if it were deleted instead. In particular, 11.10.3 [class.spaceship] bullet 2.2 specifies

If the synthesized three-way comparison of type R between any objects xi and xi is not defined, the operator function is defined as deleted.

This refers to bullets 1.2 and 1.3 of 11.10.3 [class.spaceship] paragraph 1:

The synthesized three-way comparison of type R (17.11.2 [cmp.categories]) of glvalues a and b of the same type is defined as follows:

However, a <=> b is actually usable, because 11.10.1 [class.compare.default] paragraph 3 defines:

A binary operator expression a @ b is usable if either
MyType().d <=> MyType().d is a valid expression.

Proposed resolution (approved by CWG 2022-11-11) [SUPERSEDED]:

The synthesized three-way comparison of type R (17.11.2 [cmp.categories]) of glvalues a and b of the same type is defined as follows:

CWG 2023-02-06

A simplification of the wording is sought.

Proposed resolution (approved by CWG 2023-02-07):

The synthesized three-way comparison of type R (17.11.2 [cmp.categories]) of glvalues a and b of the same type is defined as follows:



2687. Calling an explicit object member function via an address-of-overload-set

Section: 12.2.2.2.1  [over.match.call.general]     Status: C++23     Submitter: Matthew House     Date: 2023-01-16

[ Resolved by paper P2797R0, adopted in February 2023. ]

Subclause 12.2.2.2.1 [over.match.call.general] paragraph 2 specifies:

If the postfix-expression is the address of an overload set, overload resolution is applied using that set as described above. If the function selected by overload resolution is a non-static member function, the program is ill-formed.

However, 7.6.2.2 [expr.unary.op] paragraph 3 states that the address of an explicit object member function is a plain pointer to function, not a pointer to member. The former can be invoked using the regular function call syntax (7.6.1.3 [expr.call]) without the need for a pointer-to-member expression (7.6.4 [expr.mptr.oper]). For example, absent any overloading and given some function f, the expression (&A::f)(A()) could be valid if f is a static member function or an explicit object member function. However, that expression cannot possibly be valid if f is an implicit object member function.

Proposed resolution (approved by CWG 2023-01-27):

Change in 12.2.2.2.1 [over.match.call.general] paragraph 2 as follows:

If the postfix-expression is the address of an overload set, overload resolution is applied using that set as described above. If the function selected by overload resolution is a non-static an implicit object member function, the program is ill-formed.

EWG 2023-02-06

Subclause 12.3 [over.over] paragraph 4 says that explicit-object member functions match both kinds of pointer type, and 11.4.3 [class.mfct.non.static] paragraph 2 seems to try to transform (&A::f)(A()) into (&(*this).A::f)(A()) if f is an explicit-object member function.

Editor's observation: 12.3 [over.over] paragraph 4 was a missed edit applying P0847R7; it was rectified with commit 0c9dd96bb on 2023-01-17.

Additional notes (March, 2023)

The concern about incorrectly applying the this transformation was addressed by P2797R0 (Proposed resolution for CWG2692 Static and explicit object member functions with the same parameter-type-lists).




2692. Static and explicit object member functions with the same parameter-type-lists

Section: 12.2.2.2.1  [over.match.call.general]     Status: C++23     Submitter: Matthew House     Date: 2023-01-16     Liaison: EWG

[Accepted at the February, 2023 meeting as part of paper P2797R0.]

(Split off from issue 2687.)

Consider:

  struct A {
    static void f(A);
    void f(this A);

    void g();
  };

  void A::g() {
    (&A::f)(A()); // #1
    (&A::f)();    // #2
  }

It is obvious that #2 is ill-formed, but what about #1? One possible answer is to make such declarations conflict.

Suggested resolution:

  1. Change in 6.4.1 [basic.scope.scope] paragraph 3, adding bullets, a follows:

    Two function templates have corresponding signatures if
    • their template-parameter-lists have the same length,
    • their corresponding template-parameters are equivalent,
    • they have equivalent
      • parameter-type-lists or non-object-parameter-type-lists and
      • return types (if any), and,
    • if both are non-static members, they have corresponding object parameters.
  2. Change in 6.4.1 [basic.scope.scope] bullet 4.3.1 as follows:

    • both declare functions with the same parameter-type-list or non-object-parameter-type-list [Footnote: ...], equivalent (13.7.7.2 [temp.over.link]) trailing requires-clauses (if any, except as specified in 13.7.5 [temp.friend]), and, if both are non-static members, they have corresponding object parameters, or

CWG 2023-01-27

Forward to EWG to determine whether such member declarations are considered sufficiently confusing to outweigh concerns of language orthogonality; see plusplus/papers#1455.




2649. Incorrect note about implicit conversion sequence

Section: 12.2.2.2.3  [over.call.object]     Status: C++23     Submitter: CA     Date: 2022-11-03

P2720R0 comment CA 064

[Accepted as a DR at the November, 2022 meeting.]

Contrary to the note in the subject paragraph, overload resolution in selecting a surrogate call function can prefer a different conversion operator for the implicit conversion sequence because the conversion function from which the surrogate call function was derived is not the best viable function.

For example, noting that surrogate call functions are not generated from conversion function templates, the single surrogate call function derived from the (non-template) conversion function below is the sole candidate for the call; however, the specialization of the conversion function template is a better candidate f or the implicit conversion sequence during overload resolution:

  using ff = int (*)(int);
  constexpr int ffimpl0(int x) {
   return x;
  }
  constexpr int ffimpl1(int x) {
   return x + 1;
  }
  struct A {
   template <typename T> constexpr operator T() const { return ffimpl0; }
   constexpr operator ff() const volatile { return ffimpl1; }
  };
  char x[A()(42.f)];
  extern char x[43];

Proposed resolution (approved CWG 2022-11-08):

Change in 12.2.2.2.3 [over.call.object] paragraph 3 as follows:

[Note 1: When comparing the call against the function call operators, the implied object argument is compared against the object parameter of the function call operator. When comparing the call against a surrogate call function, the implied object argument is compared against the first parameter of the surrogate call function. The conversion function from which the surrogate call function was derived will be used in the conversion sequence for that parameter since it converts the implied object argument to the appropriate function pointer or reference required by that first parameter.end note]



2673. User-declared spaceship vs. built-in operators

Section: 12.2.2.3  [over.match.oper]     Status: C++23     Submitter: Barry Revzin     Date: 2022-12-30

[Accepted as a DR at the February, 2023 meeting.]

Consider:

  #include <compare>

  enum class E : int {
    Lo = 0,
    Hi = 1
  };

  constexpr auto operator<=>(E lhs, E rhs) -> std::strong_ordering {
    return (int)rhs <=> (int)lhs;
  }

  // everybody agrees this is true
  static_assert((E::Lo <=> E::Hi) == std::strong_ordering::greater);

  // gcc rejects this, msvc and clang accept
  static_assert(E::Lo > E::Hi);  // #1

The intent here is for the user-provided operator<=> to suppress the built-in operator<=> for E. And gcc, clang, and msvc all agree that this does happen when the comparison expression explicitly uses a <=> b.

But when the comparison expression is a @ b for one of the relational operators, gcc disagrees, conforming to 12.2.2.3 [over.match.oper] bullet 3.3:

For all other operators, the built-in candidates include all of the candidate operator functions defined in 12.5 [over.built] that, compared to the given operator, ... do not have the same parameter-type-list as any non-member candidate that is not a function template specialization.

The issue is that, for #1, the user-provided operator<=> is not a non-member candidate, but a rewritten candidate. A similar situation arises for a user-declared operator==, which will be called for e1 == e2, but not for e1 != e2. Again, clang and MSVC disagree.

Proposed resolution (January, 2023) [SUPERSEDED]:

Change 12.2.2.3 [over.match.oper] bullet 3.3.4 as follows:

Proposed resolution (approved by CWG 2023-02-10):

Change 12.2.2.3 [over.match.oper] bullet 3.3.4 as follows:




2664. Deduction failure in CTAD for alias templates

Section: 12.2.2.9  [over.match.class.deduct]     Status: C++23     Submitter: Christof Meerwald     Date: 2022-12-05

[Accepted as a DR at the February, 2023 meeting.]

Subclause 12.2.2.9 [over.match.class.deduct] paragraph 3 has an exception only for deduction failure for non-deduced contexts when deducing the return type from the defining-type-id, but not for other cases where deduction fails according to 13.10.3.6 [temp.deduct.type] paragraph 2. For example,

  template <class S1, class S2> struct C {
    C(...);
  };

  template<class T1> C(T1) -> C<T1, T1>;
  template<class T1, class T2> C(T1, T2) -> C<T1 *, T2>;

  template<class V1, class V2> using A = C<V1, V2>;

  C c1{""};
  A a1{""};

  C c2{"", 1};
  A a2{"", 1};

resulting in A having neither of these deduction guides. There is implementation divergence in the handling of this example.

Suggested resolution:

We could say that cases where P involves a template parameter and A is not of the same form (under 13.10.3.6 [temp.deduct.type] paragraph 8) are non-deduced contexts for the purpose of these deductions. That should be enough to make it clear what happens for a2, where we'd deduce T2 = V2, and not deduce anything for T1, but wouldn't fix a1 due to the inconsistent deductions for T1; maybe this is what MSVC is doing. We could further fix a1 by allowing inconsistent deductions and treating them as if no value was deduced. Another option might be to do independent deductions for each template argument of the simple-template-id, and then try to merge the results for template arguments where deduction was successful; that'd be clearer that deduction can't fail, but would deduce less.

CWG 2023-02-08

In the example, A is the most trivial alias template imaginable; having this cause issues depending on the details of C is concerning.

Proposed resolution (approved by CWG 2023-02-09):

Change in 12.2.2.9 [over.match.class.deduct] paragraph 3 as follows:

... For each function or function template f in the guides of the template named by the simple-template-id of the defining-type-id, the template arguments of the return type of f are deduced from the defining-type-id of A according to the process in 13.10.3.6 [temp.deduct.type] with the exception that deduction does not fail if not all template arguments are deduced. If deduction fails for another reason, proceed with an empty set of deduced template arguments Let g denote the result of substituting these deductions into f. ...



2681. Deducing member array type from string literal

Section: 12.2.2.9  [over.match.class.deduct]     Status: C++23     Submitter: Jonathan Caves     Date: 2020-03-17

[Accepted as a DR at the February, 2023 meeting.]

Consider:

  template<typename T, std::size_t N>
  struct A {
    T array[N];
  };

  A a = { "meow" };

The current wording says in 12.2.2.9 [over.match.class.deduct] bullet 1.8:

This will fail overload resolution, because a string literal (which is an lvalue) does not match a parameter of type T (&&)[N].

Proposed resolution (approved by CWG 2023-02-07):

  1. Change in 12.2.2.9 [over.match.class.deduct] paragraph 1.8 as follows:

    • if ei is of array type and xi is a braced-init-list or string-literal, Ti is an rvalue reference to the declared type of ei, and
    • if ei is of array type and xi is a string-literal, Ti is an lvalue reference to the const-qualified declared type of ei, and
    • ...
  2. Append to the example in 12.2.2.9 [over.match.class.deduct] paragraph 2 as follows:

      G g(true, 'a', 1); // OK, deduces G<char, bool>
    
      template<class T, std::size_t N>
      struct H {
        T array[N];
      };
      template<class T, std::size_t N>
      struct I {
        volatile T array[N];
      };
      template<std::size_t N>
      struct J {
        unsigned char array[N];
      };
    
      H h = { "abc" };  // OK, deduces H<char, 4> (not T = const char)
      I i = { "def" };  // OK, deduces I<char, 4>
      J j = { "ghi" };  // error: cannot bind reference to array of unsigned char to array of char in deduction
    



2685. Aggregate CTAD, string, and brace elision

Section: 12.2.2.9  [over.match.class.deduct]     Status: C++23     Submitter: Jason Merrill     Date: 2020-07-14

[Accepted as a DR at the February, 2023 meeting.]

Consider:

  template <class T>
  struct A {
    T ar[4];
  };
  A a = { "foo" }; 

Subclause 12.2.2.9 [over.match.class.deduct] bullet 1.5 specifies:

For each xi, let ei be the corresponding aggregate element of C or of one of its (possibly recursive) subaggregates that would be initialized by xi (9.4.2 [dcl.init.aggr]) if

The normative rule does not properly consider arrays with dependent element type, initialized by a string literal. MSVC accepts, gcc and clang reject the example.

Suggested resolution [SUPERSEDED]:

Change in 12.2.2.9 [over.match.class.deduct] bullet 1.5 as follows:

For each xi, let ei be the corresponding aggregate element of C or of one of its (possibly recursive) subaggregates that would be initialized by xi (9.4.2 [dcl.init.aggr]) if

Proposed resolution (approved by CWG 2023-02-09):

Change in 12.2.2.9 [over.match.class.deduct] bullet 1.5 as follows:

For each xi, let ei be the corresponding aggregate element of C or of one of its (possibly recursive) subaggregates that would be initialized by xi (9.4.2 [dcl.init.aggr]) if



2648. Correspondence of surrogate call function and conversion function

Section: 12.4.4  [over.call]     Status: C++23     Submitter: CA     Date: 2022-11-03

P2720R0 comment CA 063

[Accepted as a DR at the November, 2022 meeting.]

Post-Prague "editorial" change cplusplus/draft#3625 removed the normative text supporting the interpretation that surrogate call functions, when chosen, call the functions they were formed from.

Proposed resolution (approved by CWG 2022-11-08):

Change in 12.4.4 [over.call] paragraph 1 as follows:

If a surrogate call function for a conversion function named operator conversion-type-id is selected, let e be the result of invoking the corresponding conversion operator function on the postfix-expression; is invoked the expression is interpreted as
postfix-expression . operator conversion-type-id () e ( expression-listopt )
Otherwise, ...



2521. User-defined literals and reserved identifiers

Section: 12.6  [over.literal]     Status: C++23     Submitter: Jim X     Date: 2022-01-07     Liaison: (EWG)

[Accepted as a DR at the February, 2023 meeting.]

The example in 12.6 [over.literal] paragraph 8 has the following lines:

  double operator""_Bq(long double);  // OK: does not use the reserved identifier _Bq (5.10)
  double operator"" _Bq(long double); // ill-formed, no diagnostic required:
                                      // uses the reserved identifier _Bq (5.10)

The referenced rule in 5.10 [lex.name] is in bullet 3.1:

Each identifier that contains a double underscore __ or begins with an underscore followed by an uppercase letter is reserved to the implementation for any use.

The distinction being drawn in the user-defined literal example apparently relies on the grammar for literal-operator-id at the beginning of 12.6 [over.literal]:

The second production does not mention the syntactic non-terminal identifier, so the literal-operator-id operator""_Bq presumably does not run afoul of the restriction in 5.10 [lex.name]. However, the grammar for user-defined-string-literal in 5.13.9 [lex.ext] is:

There doesn't seem to be a rule that exempts the identifier that is the ud-suffix of a user-defined-string-literal from the restriction in 5.10 [lex.name]. Either the example is incorrect or there needs to be a refinement of the rule in 5.10 [lex.name].

CWG 2022-11-11

CWG feels that the ostensible significance of whitespace in this context is unfortunate. In addition, since the normative rule is not consistent with the example, CWG solicits EWG input on the handling of this issue via cplusplus/papers#1367.

EWG 2023-02-06

EWG had consensus on "The form of User Defined Literals that permits a space between the quotes and the name of the literal should be deprecated, and eventually removed. Additionally, the UDL name should be excluded from the restriction in 5.10 [lex.name] in the non-deprecated form (sans space)."

Proposed resolution (February, 2023) [SUPERSEDED]:

  1. Change in 5.10 [lex.name] paragraph 3 as follows:

    In addition, some identifiers appearing as a token or pp-token are reserved for use by C++ implementations and shall not be used otherwise; no diagnostic is required.
  2. Change in 12.6 [over.literal] paragraph 1 as follows:

    The string-literal or user-defined-string-literal in a literal-operator-id shall have no encoding-prefix and shall contain no characters other than the implicit terminating '\0'. The ud-suffix of the user-defined-string-literal or the identifier in a literal-operator-id is called a literal suffix identifier. The first form of literal-operator-id is deprecated. Some literal suffix identifiers are reserved for future standardization; see 16.4.5.3.6 [usrlit.suffix]. A declaration whose literal-operator-id uses such a literal suffix identifier is ill-formed, no diagnostic required.
  3. Change in 12.6 [over.literal] paragraph 8 as follows:

      void operator "" _km(long double);         // OK
      string operator "" _i18n(const char*, std::size_t); // OK, deprecated
      template <char...> double operator "" _\u03C0();  // OK, UCN for lowercase pi
      float operator ""_e(const char*);          // OK
      float operator ""E(const char*);          // ill-formed, no diagnostic required:
    			    // reserved literal suffix ([usrlit.suffix], [lex.ext])
      double operator""_Bq(long double);         // OK, does not use the reserved identifier _Bq ([lex.name])
      double operator"" _Bq(long double);         // ill-formed, no diagnostic required:
    			    // uses the reserved identifier _Bq ([lex.name])
      float operator " " B(const char*);         // error: non-empty string-literal
      string operator "" 5X(const char*, std::size_t);  // error: invalid literal suffix identifier
      double operator "" _miles(double);         // error: invalid parameter-declaration-clause
      template <char...> int operator "" _j(const char*); // error: invalid parameter-declaration-clause
      extern "C" void operator "" _m(long double);    // error: C language linkage
    
  4. Add a new subclause after D.7 [depr.impldec]:

    D.9 Literal operator function declarations using an identifier [depr.lit]

    A literal-operator-id (12.6 [over.literal]) of the form

        operator string-literal identifier
    
    is deprecated.

CWG 2023-02-07

Some implementers are concerned about the lack of space for implementation extensions. The suggestion is to reserve literal suffix identifiers starting with two underscores for the implementation in 16.4.5.3.6 [usrlit.suffix]. EWG is invited to comment on that direction.

EWG / LEWG 2023-02-07

EWG and LEWG resolved to amend the proposed resolution for CWG2521 to reserve literal suffix identifiers with double underscores anywhere for implementation use.

Proposed resolution (approved by CWG 2023-02-09):

  1. Change in 5.10 [lex.name] paragraph 3 as follows:

    In addition, some identifiers appearing as a token or pp-token are reserved for use by C++ implementations and shall not be used otherwise; no diagnostic is required.
  2. In 5.13.9 [lex.ext], modify all occurrences as follows:

    operator "" X
    
  3. Change in 5.13.9 [lex.ext] paragraph 7 as follows:

    long double operator "" _w(long double);
    std::string operator "" _w(const char16_t*, std::size_t);
    unsigned operator "" _w(const char*);
    int main() {
      1.2_w;      // calls operator "" _w(1.2L)
      u"one"_w;   // calls operator "" _w(u"one", 3)
      12_w;       // calls operator "" _w("12")
      "two"_w;    // error: no applicable literal operator
    }
    
  4. Change in 12.6 [over.literal] paragraph 1 as follows:

    The string-literal or user-defined-string-literal in a literal-operator-id shall have no encoding-prefix and shall contain no characters other than the implicit terminating '\0'. The ud-suffix of the user-defined-string-literal or the identifier in a literal-operator-id is called a literal suffix identifier. The first form of literal-operator-id is deprecated. Some literal suffix identifiers are reserved for future standardization; see 16.4.5.3.6 [usrlit.suffix]. A declaration whose literal-operator-id uses such a literal suffix identifier is ill-formed, no diagnostic required.
  5. Change in 12.6 [over.literal] paragraph 8 as follows:

      void operator "" _km(long double);         // OK
      string operator "" _i18n(const char*, std::size_t); // OK, deprecated
      template <char...> double operator "" _\u03C0();  // OK, UCN for lowercase pi
      float operator ""_e(const char*);          // OK
      float operator ""E(const char*);          // ill-formed, no diagnostic required:
    			    // reserved literal suffix ([usrlit.suffix], [lex.ext])
      double operator""_Bq(long double);         // OK, does not use the reserved identifier _Bq ([lex.name])
      double operator"" _Bq(long double);         // ill-formed, no diagnostic required:
    			    // uses the reserved identifier _Bq ([lex.name])
      float operator " " B(const char*);         // error: non-empty string-literal
      string operator "" 5X(const char*, std::size_t);  // error: invalid literal suffix identifier
      double operator "" _miles(double);         // error: invalid parameter-declaration-clause
      template <char...> int operator "" _j(const char*); // error: invalid parameter-declaration-clause
      extern "C" void operator "" _m(long double);    // error: C language linkage
    
  6. Change in 16.4.5.3.6 [usrlit.suffix] as follows:

    Literal suffix identifiers (12.6 [over.literal]) that do not start with an underscore are reserved for future standardization. Literal suffix identifiers that contain a double underscore __ are reserved for use by C++ implementations.
  7. Add a new subclause after D.7 [depr.impldec]:

    D.9 Literal operator function declarations using an identifier [depr.lit]

    A literal-operator-id (12.6 [over.literal]) of the form

        operator string-literal identifier
    
    is deprecated.



2682. Templated function vs. function template

Section: 13.1  [temp.pre]     Status: C++23     Submitter: Matthew House     Date: 2023-01-11

[Accepted as a DR at the February, 2023 meeting.]

In 13.1 [temp.pre] paragraph 8, the phrase "templated entity" is defined. The derived term "templated function" is never actually defined, but is intended to apply to function templates as well as non-template members of class templates. Similarly, the phrases "templated variable" and "templated class" should be properly defined.

Proposed resolution (approved by CWG 2023-01-27):

  1. Change in 9.2.9.7.1 [dcl.spec.auto.general] paragraph 12 as follows:

    Return type deduction for a templated entity that is a function or function template with a placeholder in its declared type occurs when the definition is instantiated even if the function body contains a return statement with a non-type-dependent operand.
  2. Change in 13.1 [temp.pre] paragraph 8 as follows:

    [Note 6: A local class, a local or block variable, or a friend function defined in a templated entity is a templated entity. —end note]
    A templated function is a function template or a function that is templated. A templated class is a class template or a class that is templated. A templated variable is a variable template or a variable that is templated.



2611. Missing parentheses in expansion of fold-expression could cause syntactic reinterpretation

Section: 13.7.4  [temp.variadic]     Status: C++23     Submitter: Richard Smith     Date: 2022-08-05

[Accepted as a DR at the November, 2022 meeting.]

13.7.4 [temp.variadic] paragraph 10 expands a fold-expression (including its enclosing parentheses) to an unparenthesized expression. If interpreted literally, this could result in reassociation and misinterpretation of the expression. For example, given:

template<int ...N> int k = 2 * (... + N);

... k<1, 2, 3> is specified as expanding to int k<1, 2, 3> = 2 * 1 + (2 + 3); resulting in a value of 7 rather than the intended value of 12.

Further, there is implementation divergence for the following example:

#include <type_traits>
template<class ...TT>
void f(TT ...tt) {
  static_assert(std::is_same_v<decltype((tt, ...)), int&>);
}
template void f(int /*,int*/);

gcc and MSVC apply the general expression interpretation of decltype, whereas clang and icc apply the identifier special case.

Proposed resolution (approved by CWG 2022-08-26):

Change in 13.7.4 [temp.variadic] paragraph 10 as follows:
The instantiation of a fold-expression (7.5.6 [expr.prim.fold]) produces: ...



2603. Holistic functional equivalence for function templates

Section: 13.7.7.2  [temp.over.link]     Status: C++23     Submitter: Davis Herring     Date: 2022-06-20

[Accepted as a DR at the November, 2022 meeting.]

In C++20, 13.7.7.2 [temp.over.link] paragraph 7 defined equivalence for function templates in terms of equivalence of several of its components; functional equivalence for them was similar in that it was defined recursively for their "return types and parameter lists", but differed with regard to constraints in that it required that they "accept and are satisfied by the same set of template argument lists". P1787R6 simplified the treatment by relying entirely on the "depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent" rule to make the correspondence check between the function templates ill-formed, no diagnostic required.

This created a situation where moving a constraint between a template-head and a requires-clause makes a function template truly different (because there is no reasonable way to read 6.4.1 [basic.scope.scope] bullet 4.3.2's "equivalent [...], template-heads, and trailing requires-clauses (if any)" as requiring a joint check for functional equivalence), even if overload resolution would never be able to distinguish them.

Suggested resolution [SUPERSEDED]:

Change in 13.7.7.2 [temp.over.link] paragraph 7 as follows:

If the validity or meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent, the program is ill-formed, no diagnostic required. Furthermore, if two function templates do not correspond, but accept and are satisfied by the same set of template argument lists, the program is ill-formed, no diagnostic required.

Suggested resolution (August, 2022) [SUPERSEDED]:

  1. Append to 6.4.1 [basic.scope.scope] paragraph 3 as follows:

    Two function templates have corresponding signatures if their template-parameter-lists have the same length, corresponding template-parameters are equivalent, they have equivalent non-object-parameter-type-lists and return types (if any), and, if both are non-static members, they have corresponding object parameters.

  2. Change in 6.4.1 [basic.scope.scope] paragraph 4 as follows:

  3. Change in 13.7.7.2 [temp.over.link] paragraph 7 as follows:

    If the validity or meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent, the program is ill-formed, no diagnostic required. Furthermore, if two function templates with corresponding signatures do not correspond, but accept and are satisfied by the same set of template argument lists, the program is ill-formed, no diagnostic required.

Proposed resolution (approved by CWG 2022-09-09):

  1. Append to 6.4.1 [basic.scope.scope] paragraph 3 as follows:

    Two function templates have corresponding signatures if their template-parameter-lists have the same length, corresponding template-parameters are equivalent, they have equivalent non-object-parameter-type-lists and return types (if any), and, if both are non-static members, they have corresponding object parameters.

  2. Change in 6.4.1 [basic.scope.scope] paragraph 4 as follows:

  3. Change in 13.7.7.2 [temp.over.link] paragraph 7 as follows:

    If the validity or meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent, the program is ill-formed, no diagnostic required. Furthermore, if two function templates that do not correspond
    • have the same name,
    • have corresponding signatures (6.4.1 [basic.scope.scope]),
    • would declare the same entity (6.6 [basic.link]) considering them to correspond, and
    • accept and are satisfied by the same set of template argument lists,
    the program is ill-formed, no diagnostic required.



2428. Deprecating a concept

Section: 13.7.9  [temp.concept]     Status: C++23     Submitter: Eric Niebler     Date: 2018-12-10

[Accepted as a DR at the November, 2022 meeting.]

The grammar for a concept-definition does not include an attribute-specifier-seqopt, making it impossible to deprecate an attribute. This seems like an oversight.

CWG telecon 2022-10-07:

Agreed.

Proposed resolution (approved by CWG 2022-10-21):

  1. Change in 9.12.5 [dcl.attr.deprecated] paragraph 2 as follows:

    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, a concept, or a template specialization.
  2. Change in 13.7.9 [temp.concept] paragraph 1 as follows:

    A concept is a template that defines constraints on its template arguments.
    concept-definition:
        concept concept-name attribute-specifier-seqopt = constraint-expression ;
    
    concept-name:
        identifier
    
    A concept-definition declares a concept. Its identifier becomes a concept-name referring to that concept within its scope. The optional attribute-specifier-seq appertains to the concept.



2508. Restrictions on uses of template parameter names

Section: 13.8.2  [temp.local]     Status: C++23     Submitter: Daveed Vandevoorde     Date: 2021-11-01

[Accepted as a DR at the November, 2022 meeting.]

The status of an example like the following is unclear:

  template<typename T> T T(T) {}

According to 13.8.2 [temp.local] paragraph 6,

The name of a template-parameter shall not be bound to any following declaration contained by the scope to which the template-parameter belongs. [Example 5:

  ...
  template<class X> class X; // error: hidden by template-parameter

end example]

The intent would appear to be that the function template could not have the same name as the template parameter. However, according to 6.4.9 [basic.scope.temp] paragraph 2,

Each template-declaration D introduces a template parameter scope that extends from the beginning of its template-parameter-list to the end of the template-declaration. Any declaration outside the template-parameter-list that would inhabit that scope instead inhabits the same scope as D.

This would indicate that the function template inhabits the namespace scope, not the template parameter scope, so the prohibition against use of the template parameter name would not apply.

To reject both the function and class template examples, 13.8.2 [temp.local] paragraph 6 could be changed to read:

The name of a template-parameter shall not be bound to any following declaration whose locus is contained by the scope to which the template-parameter belongs.

To accept both examples, the change could be:

The name of a template-parameter shall not be bound to any following declaration that inhabits a scope contained by the scope to which the template-parameter belongs.

Notes from the December, 2021 teleconference:

The consensus of CWG was to reject both examples, i.e., the first option.

Additional note (December, 2021):

It was observed that this issue is, strictly speaking, not a defect: the word “contains” is used in 6.4.1 [basic.scope.scope] paragraph 1 in its usual English sense to refer to the lexical nesting of scopes, so the template parameter scope of T “contains” the declaration of the function T. However, the use of the term “locus” would make the intent clearer.

Proposed resolution (December, 2021):

Change 13.8.2 [temp.local] paragraph 6 as follows:

The name of a template-parameter shall not be bound to any following declaration whose locus is contained by the scope to which the template-parameter belongs.



1396. Deferred instantiation and checking of non-static data member initializers

Section: 13.9.2  [temp.inst]     Status: C++23     Submitter: Jason Merrill     Date: 2011-09-22

[Resolved by issue 2631, which was accepted as a DR at the November, 2022 meeting.]

Non-static data member initializers get the same late parsing as member functions and default arguments, but are they also instantiated as needed like them? And when is their validity checked?

Notes from the October, 2012 meeting:

CWG agreed that non-static data member initializers should be handled like default arguments.

Additional note (March, 2013):

Determining whether a defaulted constructor is constexpr or not requires parsing the class's non-static data member initializers; see also issue 1360.

CWG 2022-11-11

Resolved by issue 2631.




2072. Default argument instantiation for member functions of templates

Section: 13.9.2  [temp.inst]     Status: C++23     Submitter: Maxim Kartashev     Date: 2015-01-19

[Accepted as a DR at the February, 2023 meeting.]

Default argument instantiation is described in 13.9.2 [temp.inst] paragraph 12, and although instantiation of default arguments for member functions of class templates is mentioned elsewhere a number of times, this paragraph only describes default argument instantiation for function templates.

Proposed resolution (approved by CWG 2023-02-06):

Change in 13.9.2 [temp.inst] paragraph 12 as follows:

If a templated function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point, except that the scope in which a closure type is declared (7.5.5.2 [expr.prim.lambda.closure]) --- and therefore its associated namespaces --- remain as determined from the context of the definition for the default argument. This analysis is called default argument instantiation. The instantiated default argument is then used as the argument of f.



2478. Properties of explicit specializations of implicitly-instantiated class templates

Section: 13.9.4  [temp.expl.spec]     Status: C++23     Submitter: Mark Hall     Date: 2021-02-02

[Accepted as a DR at the February, 2023 meeting.]

According to 13.9.4 [temp.expl.spec] paragraph 16,

A member or a member template of a class template may be explicitly specialized for a given implicit instantiation of the class template, even if the member or member template is defined in the class template definition. An explicit specialization of a member or member template is specified using the syntax for explicit specialization.

The relationship between this construct and paragraph 14 is not clear:

Whether an explicit specialization of a function or variable template is inline, constexpr, or an immediate function is determined by the explicit specialization and is independent of those properties of the template.

(See also 9.2.6 [dcl.constexpr] paragraph 1, note 1.) Is this intended to apply to explicit specializations of members of implicitly-instantiated class templates? For example:

  template<typename T> struct S {
    int f();
    constexpr int g();
  };
  template<> constexpr int S<int>::f() {  // OK, constexpr?
    return 0;
  }
  template<> int S<int>::g() {            // OK, not constexpr?
    return 0;
  }

There is implementation divergence on the treatment of this example. This divergence may relate to interpretation of the requirement in 9.2.6 [dcl.constexpr] paragraph 1,

If any declaration of a function or function template has a constexpr or consteval specifier, then all its declarations shall contain the same specifier.

Is an explicit specialization of a member of an implicitly-instantiated class template a declaration of that member? A similar question also applies to the constinit specifier as specified in 9.2.7 [dcl.constinit] paragraph 1:

If the specifier is applied to any declaration of a variable, it shall be applied to the initializing declaration.

(Note that constinit is not mentioned in 13.9.4 [temp.expl.spec] paragraph 14.) For example:

  template<typename T> struct S {
    static constinit T x;
  };
  template<> int S<int>::x = 10;    // constinit required?
  extern char c;
  template<> short S<char>::x = c;  // error, c not constant?

(Possibly relevant is the fact that default arguments are prohibited in explicit specializations of member functions of implicitly-instantiated class templates, per 13.9.4 [temp.expl.spec] bullet 21.3.)

CWG 2022-11-10

A specialization of a member of a class template redeclares the member of the primary template and thus the redeclaration rules apply. In passing, it was noticed that the rule governing explicit specializations in general omitted treatment of constinit, which was considered an oversight.

Proposed resolution (approved 2023-02-09):

Change in 13.9.4 [temp.expl.spec] paragraph 13 as follows:

Whether an explicit specialization of a function or variable template is inline, constexpr, constinit, or consteval an immediate function is determined by the explicit specialization and is independent of those properties of the template.



2604. Attributes for an explicit specialization

Section: 13.9.4  [temp.expl.spec]     Status: C++23     Submitter: Aaron Ballman     Date: 2022-06-23

[Accepted as a DR at the November, 2022 meeting.]

It is unclear whether an explicit template specialization "inherits" the attributes written on the primary template, or whether the specialization has to repeat the attributes. For example:

  template <typename Ty>
  [[noreturn]] void func(Ty);

  template <>
  void func<int>(int) {
    // Warning about returning from a noreturn function or not?
  }

A similar question arises for attributes written on the parameters of the primary function template. For example:

  template <typename Ty>
  void func([[maybe_unused]] int i);

  template <>
  void func<int>(int i) {
   // i is not used, should it be warned on or not?
  }

There is implementation divergence for the example.

Suggested resolution [SUPERSEDED]:

Change in 13.9.4 [temp.expl.spec] paragraph 13 as follows:

Any attributes applying to any part of the declaration of an explicit specialization of a function or variable template, as well as Whether whether such an explicit specialization of a function or variable template is inline, constexpr, or an immediate function, is determined by the explicit specialization and is independent of those properties of the template. [ Note: Attributes that would affect the association of the declaration of an explicit specialization with the declaration of the primary template need to match. -- end note ]

Proposed resolution (approved by CWG 2022-11-09):

Change 13.9.4 [temp.expl.spec] paragraph 13 as follows:

Whether an explicit specialization of a function or variable template is inline, constexpr, or an immediate function is determined by the explicit specialization and is independent of those properties of the template. Similarly, attributes appearing in the declaration of a template have no effect on an explicit specialization of that template. [Example 7:
  template<class T> void f(T) { /* ... */ }
  template<class T> inline T g(T) { /* ... */ }

  template<> inline void f<>(int) { /* ... */ } // OK, inline
  template<> int g<>(int) { /* ... */ }         // OK, not inline

  template<typename> [[noreturn]] void h([[maybe_unused]] int i);
  template<> void h<int>(int i) {
    // Implementations are expected not to warn that the function returns but can
    // warn about the unused parameter.
  }

end example]




2618. Substitution during deduction should exclude exception specifications

Section: 13.10.3.1  [temp.deduct.general]     Status: C++23     Submitter: Christof Meerwald     Date: 2021-11-27

[Accepted as a DR at the November, 2022 meeting.]

Subclause 13.10.3.1 [temp.deduct.general] paragraph 7 specifies:

The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside sizeof, decltype, and other contexts that allow non-constant expressions. The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered. If substitution into different declarations of the same function template would cause template instantiations to occur in a different order or not at all, the program is ill-formed; no diagnostic required.

[Note 4: The equivalent substitution in exception specifications is done only when the noexcept-specifier is instantiated, at which point a program is ill-formed if the substitution results in an invalid type or expression. —end note]

The note says that substitution into the noexcept-specifier occurs late, but the normative text does not support that, because the exception specification is part of the function type.

Subclause 13.10.3.1 [temp.deduct.general] paragraph 8 specifies:

Only invalid types and expressions in the immediate context of the function type, its template parameter types, and its explicit-specifier can result in a deduction failure.

However, paragraph 7 does not mention the explicit-specifier when describing the substitution.

Proposed resolution (approved by CWG 2022-09-09):

Change in 13.10.3.1 [temp.deduct.general] paragraph 7 as follows:

The deduction substitution loci are

The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations deduction substitution loci. ...

Change in 13.10.3.1 [temp.deduct.general] paragraph 8 as follows:

... Only invalid Invalid types and expressions can result in a deduction failure only in the immediate context of the function type, its template parameter types, and its explicit-specifier deduction substitution loci can result in a deduction failure.



2650. Incorrect example for ill-formed non-type template arguments

Section: 13.10.3.1  [temp.deduct.general]     Status: C++23     Submitter: US     Date: 2022-11-03

P2720R0 comment US 28-068

[Accepted as a DR at the November, 2022 meeting.]

The example intends to illustrate that a class type cannot be the type of a non-type template parameter (although the example is still ill-formed because "T()" is interpreted as a function type).

Proposed resolution (approved by CWG 2022-11-08):

Change in 13.10.3.1 [temp.deduct.general] bullet 11.8 as follows:




2651. Conversion function templates and "noexcept"

Section: 13.10.3.4  [temp.deduct.conv]     Status: C++23     Submitter: US     Date: 2022-11-03

P2720R0 comment US 29-069

[Accepted as a DR at the November, 2022 meeting.]

The rule in 13.10.3.4 [temp.deduct.conv] bullet 5.2 seems to allow

template<class T,bool B>
using get=T(*)() noexcept(B);

struct A {
 template<class T>
 operator get<T,false>() const;
};

auto *p=A().operator get<int,true>();

Proposed resolution (approved by CWG 2022-11-09):

Change in 13.10.3.4 [temp.deduct.conv] paragraph 1 as follows:

... If the conversion-function-id is constructed during overload resolution ([over.match.funcs]), the following transformations rules in the remainder of this subclause apply.

Change in 13.10.3.4 [temp.deduct.conv] bullet 5.2 as follows:

However, certain attributes of A may be ignored:



2601. Tracking of created and destroyed subobjects

Section: 14.3  [except.ctor]     Status: C++23     Submitter: Richard Smith     Date: 2022-06-16

[Accepted as a DR at the November, 2022 meeting.]

Subclause 14.3 [except.ctor] paragraph 3 specifies:

If the initialization or destruction of an object other than by delegating constructor is terminated by an exception, the destructor is invoked for each of the object's direct subobjects and, for a complete object, virtual base class subobjects, whose initialization has completed (9.4 [dcl.init]) and whose destructor has not yet begun execution, except that in the case of destruction, the variant members of a union-like class are not destroyed.

A traditional implementation has no way of knowing which subobjects are in the state that their initialization has completed but their destructor has not yet begun execution. For example, the program might call the destructor explicitly, and the implementation does not track whether that has happened. The intent here is that it only matters whether the implied destructor call generated implicitly as part of the class object's destructor has begun yet, but it does not say that, and the reference to variant members reinforces the interpretation that the set of subobjects that are destroyed is determined dynamically based on which objects are within their lifetimes. Also, combining construction and destruction rules here confuses the matter further -- in "whose initialization has completed and whose destructor has not yet begun" we care exclusively about the first part in constructors and exclusively about the second part in destructors.

The set of things that we actually want to destroy here is the things that were initialized by the initialization (constructor or aggregate initializer) itself, not the things that have been constructed and not destroyed by evaluations that the initialization happens to perform. For example:

  struct A {
    union { T x; U y; };
    A() { throw "does not destroy x"; }
    A(int) : x() { throw "does destroy x"; }
    A(float) : x() { x.~T(); throw "still destroys x, oops"; }
    A(double) : x() {
      x.~T();
      new(&y) U();
      throw "destroys x, does not destroy y";
    }
  };

and similarly for aggregate initialization:

  struct B {
    union { T x; U y; };
    int a;
  };
  B b = { .x = {}, .a = (b.x.~T(), new (&b.y) U(), throw "destroys x not y")};

Destruction is completely different: we just want to destroy all the things that the destructor was going to destroy anyway and hasn't already started destroying.

Suggested resolution [SUPERSEDED]:

Change in 14.3 [except.ctor] paragraph 3 as follows:

If the initialization or destruction of an object other than by delegating constructor is terminated by an exception, the destructor is invoked for each of the object's direct subobjects and, for a complete object, virtual base class subobjects that were directly initialized by the object's initialization and , whose initialization has completed (9.4 [dcl.init]) and whose destructor has not yet begun execution, except that in the case of destruction, the variant members of a union-like class are not destroyed. A subobject is directly initialized if its initialization is specified in 11.9.3 [class.base.init] for initialization by constructor, in 11.9.4 [class.inhctor.init] for initialization by inherited constructor, in 9.4.2 [dcl.init.aggr] for aggregate initialization, or in 9.4.1 [dcl.init.general] for default-initialization, value-initialization, or direct-initialization of an array. [Note: This includes virtual base class subobjects if the initialization is for a complete object, and can include variant members that were nominated explicitly by a mem-initializer or designated-initializer-clause or that have a default member initializer. -- end note]

If the destructor of an object is terminated by an exception, each destructor invocation that would be performed after executing the body of the destructor (11.4.7 [class.dtor]) and that has not yet begun execution is performed. [Note: This includes virtual base class subobjects if the destructor was invoked for a complete object. -- end note ]

Proposed resolution (CWG telecon 2022-08-12) [SUPERSEDED]:

Change in 14.3 [except.ctor] paragraph 3 as follows:

If the initialization or destruction of an object other than by delegating constructor is terminated by an exception, the destructor is invoked for each of the object's direct subobjects and, for a complete object, virtual base class subobjects that were known to be initialized by the object's initialization and , whose initialization has completed (9.4 [dcl.init]) and whose destructor has not yet begun execution, except that in the case of destruction, the variant members of a union-like class are not destroyed. A subobject is known to be initialized if its initialization is specified in 11.9.3 [class.base.init] for initialization by constructor, in 11.9.4 [class.inhctor.init] for initialization by inherited constructor, in 9.4.2 [dcl.init.aggr] for aggregate initialization, or in 9.4.1 [dcl.init.general] for default-initialization, value-initialization, or direct-initialization of an array. [Note: This includes virtual base class subobjects if the initialization is for a complete object, and can include variant members that were nominated explicitly by a mem-initializer or designated-initializer-clause or that have a default member initializer. -- end note]

If the destructor of an object is terminated by an exception, each destructor invocation that would be performed after executing the body of the destructor (11.4.7 [class.dtor]) and that has not yet begun execution is performed. [Note: This includes virtual base class subobjects if the destructor was invoked for a complete object. -- end note ]

Additional notes (August, 2022):

The proposed resolution above does not handle the situation where the initialization of a closure object is terminated by an exception during the evaluation of a lambda expression. It also does not handle 11.4.5.3 [class.copy.ctor] bullet 14.1 (array copies in defaulted constructors).

Proposed resolution (approved by CWG telecon 2022-09-09):

Change in 14.3 [except.ctor] paragraph 3 as follows:

If the initialization or destruction of an object other than by delegating constructor is terminated by an exception, the destructor is invoked for each of the object's direct subobjects and, for a complete object, virtual base class subobjects that were known to be initialized by the object's initialization and , whose initialization has completed (9.4 [dcl.init]) and whose destructor has not yet begun execution, except that in the case of destruction, the variant members of a union-like class are not destroyed. [Note: If such an object has a reference member that extends the lifetime of a temporary object, this ends the lifetime of the reference member, so the lifetime of the temporary object is effectively not extended. —end note] A subobject is known to be initialized if its initialization is specified

[Note: This includes virtual base class subobjects if the initialization is for a complete object, and can include variant members that were nominated explicitly by a mem-initializer or designated-initializer-clause or that have a default member initializer. —end note]

If the destructor of an object is terminated by an exception, each destructor invocation that would be performed after executing the body of the destructor (11.4.7 [class.dtor]) and that has not yet begun execution is performed. [Note: This includes virtual base class subobjects if the destructor was invoked for a complete object. —end note]

The subobjects are destroyed in the reverse order of the completion of their construction. Such destruction is sequenced before entering a handler of the function-try-block of the constructor or destructor, if any.




2615. Missing __has_cpp_attribute(assume)

Section: 15.2  [cpp.cond]     Status: C++23     Submitter: S. B. Tam     Date: 2022-08-17

P2720R0 comment GB 070

[Accepted at the November, 2022 meeting.]

Paper P1774R8 (accepted in July, 2022) adds a new attribute assume, but neglects to update table 22 in 15.2 [cpp.cond].

Proposed resolution (accepted by CWG 2022-08-26):

In 15.2 [cpp.cond], add a row to table tab:cpp.cond.ha as follows:

Attribute Value
assume 202207L



2667. Named module imports do not import macros

Section: 15.5  [cpp.import]     Status: C++23     Submitter: Richard Smith     Date: 2022-12-16

[Accepted as a DR at the February, 2023 meeting.]

It should be clarified via an example or a note that named module imports do not make macros available.

Proposed resolution (approved by CWG 2023-01-06):

  1. Change in 10.3 [module.import] paragraph 7 as follows:

    ... These rules can in turn lead to the importation of yet more translation units. [ Note: Such indirect importation does not make macros available, because a translation unit is a sequence of tokens in translation phase 7 (5.2 [lex.phases]). Macros can be made available by directly importing header units as described in 15.5 [cpp.import]. -- end note ]
  2. Add to the example in 15.5 [cpp.import] paragraph 8 as follows:

      import "a.h";  // point of definition of #1, #2, and #3, point of undefinition of #1 in "e.h"
      import "d.h";  // point of definition of #4 and #5 in "e.h"
      int a = Y;     // OK, active macro definitions #2 and #4 are valid redefinitions
      int c = Z;     // error: active macro definitions #3 and #5 are not valid redefinitions of Z
    
    Module unit f:
    export module f;
    export import "a.h";
    
    int a = Y;    // OK
    
    Translation unit #1:
    import f;
    int x = Y;   // error: Y is neither a defined macro nor a declared name
    
    -- end example ]



745. Effect of ill-formedness resulting from #error

Section: 15.8  [cpp.error]     Status: C++23     Submitter: Clark Nelson     Date: 13 November, 2008

[ Resolved by issue 2518, adopted in February, 2023. ]

C99 is very clear that a #error directive causes a translation to fail: Clause 4 paragraph 4 says,

The implementation shall not successfully translate a preprocessing translation unit containing a #error preprocessing directive unless it is part of a group skipped by conditional inclusion.

C++, on the other hand, simply says that a #error directive “renders the program ill-formed” (15.8 [cpp.error]), and the only requirement for an ill-formed program is that a diagnostic be issued; the translation may continue and succeed. (Noted in passing: if this difference between C99 and C++ is addressed, it would be helpful for synchronization purposes in other contexts as well to introduce the term “preprocessing translation unit.”)

See also issue 2518.




2652. Overbroad definition of __STDCPP_BFLOAT16_T__

Section: 15.11  [cpp.predefined]     Status: C++23     Submitter: US     Date: 2022-11-03

P2720R0 comment US 31-071

[Accepted at the November, 2022 meeting.]

The wording for the predefined macro __STDCPP_BFLOAT16_T__ added by P1467 can be interpreted more broadly than was intended.

Proposed resolution (approved by CWG 2022-11-08):

Change in 15.11 [cpp.predefined] paragraph 1 as follows:

__STDCPP_BFLOAT16_T__

Defined as the integer literal 1 if and only if the implementation supports an extended floating-point type with the properties of the typedef-name std::bfloat16_t as described in 6.8.3 [basic.extended.fp].



2659. Missing feature-test macro for lifetime extension in range-for loop

Section: 15.11  [cpp.predefined]     Status: C++23     Submitter: CWG     Date: 2022-12-02

[Accepted at the February, 2023 meeting.]

P2720R0 comment DE 038

Paper P2718R0 (Wording for P2644R1 Fix for Range-based for Loop), adopted at the November, 2022 meeting, omitted a consideration of a feature-test macro, although one is desirable.

Proposed resolution (approved by CWG 2023-01-06):

Change in 15.11 [cpp.predefined] table 23 as follows:

  __cpp_range_based_for 201603L 202211L



2622. Compounding types from function and pointer-to-member types

Section: Clause Annex B  [implimits]     Status: C++23     Submitter: Jim X     Date: 2022-09-02

[Accepted as a DR at the November, 2022 meeting.]

Clause Annex B [implimits] bullet 2.3 specifies:

This omits function types as the to-be-modified type, and ignores pointer-to-member declarators.

Proposed resolution (approved by CWG 2022-09-23):

Change in Clause Annex B [implimits] bullet 2.3 as follows:




2407. Missing entry in Annex C for defaulted comparison operators

Section: Clause Annex C  [diff]     Status: C++23     Submitter: Tomasz Kaminski     Date: 2019-02-26

[Accepted as a DR at the November, 2022 meeting.]

The changes from P1185R2 need an entry in Annex C, because they affect the interpretation of existing well-formed code. For example, given:

  struct A {
    operator int() const { return 10; }
  };

  bool operator==(A, int); // #1
  //built-in: bool operator==(int, int); // #2

  A a, b;

The expression 10 == a resolves to #2 in C++17 but now to #1. In addition, a == b is now ambiguous, because #1 has a user-defined conversion on the second argument, while the reversed order has it on the first argument. Similarly for operator!=.

Notes from the March, 2019 teleconference:

The ambiguity in 10 == a arises from the consideration of the reverse ordering of the operands.

CWG found this breakage surprising and asked for EWG's opinion before updating Annex C.

Proposed resolution (April, 2019) [SUPERSEDED]

Add the following as a new subclause in C.3 [diff.cpp17]:

C.5.6 Clause 12: Overloading

Affected subclause: 12.2.2.3 [over.match.oper]
Change: Overload resolution may change for equality operators 7.6.10 [expr.eq].
Rationale: Support calling operator== with reversed order of arguments.
Effect on original feature: Valid C++ 2017 code that uses equality operators with conversion functions may be ill-formed or have different semantics in this International Standard.

  struct A {
    operator int() const { return 10; }
  };

  bool operator==(A, int);               // #1
  // built-in: bool operator==(int, int);  // #2
  bool b = 10 == A();                   // uses #1 with reversed order of arguments; previously used #2

Proposed resolution:

Add the following as a new subclause in C.3 [diff.cpp17]:

C.5.6 Clause 12: Overloading

Affected subclause: 12.2.2.3 [over.match.oper]
Change: Overload resolution may change for equality operators 7.6.10 [expr.eq].
Rationale: Support calling operator== with reversed order of arguments.
Effect on original feature: Valid C++ 2017 code that uses equality operators with conversion functions may be ill-formed or have different semantics in this International Standard.

  struct A {
    operator int() const { return 10; }
  };

  bool operator==(A, int);               // #1
  // built-in: bool operator==(int, int);   // #2
  bool b = 10 == A();                   // uses #1 with reversed order of arguments; previously used #2

  struct B {
    bool operator==(const B&);          // member function with no cv-qualifier
  };
  B b1;
  bool eq = (b1 == b1);                   // ambiguous; previously well-formed



2636. Update Annex E based on Unicode 15.0 UAX #31

Section: Clause Annex E  [uaxid]     Status: C++23     Submitter: Steve Downey     Date: 2022-10-20

P2720R0 comment US 64-132

[Accepted as a DR at the November, 2022 meeting.]

Unicode 15.0 UAX #31 clarified that rule R3 was, in fact, intended to apply to programming languages. WG21's prior understanding was that programming languages are not in scope of that rule. The proposed resolution updates E.4 [uaxid.pattern] to the revised understanding. See paper P2653R1 (Update Annex E based on Unicode 15.0 UAX 31) for more details.

Proposed resolution (approved by CWG 2022-10-21):

Change in E.4 [uaxid.pattern] as follows:

UAX #31 describes how formal languages that use or interpret patterns of characters, such as regular expressions or number formats, may describe that syntax with Unicode properties such as computer languages should describe and implement their use of whitespace and syntactically significant characters during the processes of lexing and parsing.

C++ does not do this as part of the language, deferring to library components for such usage of patterns. This requirement does not apply to C++ claim conformance with this requirement.