Document number:  WG21 N3675
Date:  2013-05-03
Project:  Programming Language C++
Reference:  ISO/IEC IS 14882:2003
Reply to:  William M. Miller
 Edison Design Group, Inc.
 wmm@edg.com


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


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," "TC1," and "FDIS," 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 N3485.


Issues with "DR" Status


616. Definition of “indeterminate value”

Section: 1.3  [intro.defs]     Status: DR     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, 4.1 [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 4.1 [conv.lval] paragraphs 1 and 2 as follows (including changing the running text of paragraph 2 into bullets):

  2. A glvalue (3.10 [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 5 [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 5.2.5 [expr.ref] paragraph 4 second bullet as follows:

  4. Change 5.5 [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 (3.10 [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, and 1013.

(See also issue 1213.)

Additional note (August, 2012):

It was observed that the phrase in the fourth bullet of the change to 4.1 [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: 1.3  [intro.defs]     Status: DR     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, 17.6.4.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?”

7.1.6.2 [dcl.type.simple] paragraph 2 says,

The auto specifier is a placeholder for a type to be deduced (7.1.6.4 [dcl.spec.auto]). The other simple-type-specifiers specify either a previously-declared user-defined type or one of the fundamental types (3.9.1 [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 7.1.6.2 [dcl.type.simple] paragraph 2 as follows:

  2. The auto specifier is a placeholder for a type to be deduced (7.1.6.4 [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 (3.9.1 [basic.fundamental]). Table 10 summarizes the valid combinations of simple-type-specifiers and the types they specify.
  3. Change 4 [conv] paragraph 4 as follows:

  4. [Note: For user-defined class types, user-defined conversions are considered as well; see 12.3 [class.conv]. In general, an implicit conversion sequence (13.3.3.1 [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 13.3.1.2 [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.
      }
    



129. Stability of uninitialized auto variables

Section: 1.9  [intro.execution]     Status: DR     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
    }
3.9.1 [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: 1.9 [intro.execution] paragraph 10 says,

An instance of each object with automatic storage duration (3.7.3 [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: 7.1.6.1 [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 1.9 [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.




1472. odr-use of reference variables

Section: 3.2  [basic.def.odr]     Status: DR     Submitter: Richard Smith     Date: 2012-03-01

[Moved to DR at the April, 2013 meeting.]

We have a special case in 3.2 [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 3.2 [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 (5.19 [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 (4.1 [conv.lval]) is applied to e, or e is a discarded-value expression (Clause 5 [expr]). this is odr-used...



1511. const volatile variables and the one-definition rule

Section: 3.2  [basic.def.odr]     Status: DR     Submitter: Richard Smith     Date: 2012-06-18

[Moved to DR at the April, 2013 meeting.]

One of the criteria in 3.2 [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 3.4 [basic.lookup], shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (13.3 [over.match]) and after matching of partial template specialization (14.8.3 [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 (5.19 [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 7.1.6.1 [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 3.2 [basic.def.odr] paragraph 6 as follows:

  2. There can be more than one definition...

  3. Change 3.5 [basic.link] paragraph 3 as follows:

  4. A name having namespace scope (3.3.6 [basic.scope.namespace]) has internal linkage if it is the name of




1482. Point of declaration of enumeration

Section: 3.3.2  [basic.scope.pdecl]     Status: DR     Submitter: Daveed Vandevoorde     Date: 2012-03-20

[Moved to DR at the April, 2013 meeting.]

According to 3.3.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 (7.2 [dcl.enum]) or its first opaque-enum-declaration (7.2 [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 3.9 [basic.types] paragraph 5 as follows:

  2. A class that has been declared but not defined, an enumeration type in certain contexts (7.2 [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 7.2 [dcl.enum] paragraph 6:

  4. An enumeration whose underlying type is fixed is an incomplete type from its point of declaration (3.3.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.




1310. What is an “acceptable lookup result?”

Section: 3.4.3.1  [class.qual]     Status: DR     Submitter: Jason Merrill     Date: 2011-05-06

[Moved to DR at the April, 2013 meeting.]

In 3.4.3.1 [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 3.4.3.1 [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...




1489. Is value-initialization of an array constant initialization?

Section: 3.6.2  [basic.start.init]     Status: DR     Submitter: Steve Adamczyk     Date: 2012-03-29

[Moved to DR at the April, 2013 meeting.]

According to 3.6.2 [basic.start.init] 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 3.6.2 [basic.start.init] paragraph 2 as follows:

Constant initialization is performed:




312. “use” of invalid pointer value not defined

Section: 3.7.4.2  [basic.stc.dynamic.deallocation]     Status: DR     Submitter: Martin von Loewis     Date: 20 Sep 2001

[Moved to DR at the April, 2013 meeting.]

3.7.4.2 [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 5.2.4 [expr.pseudo], the variable x is 'evaluated' as part of evaluating the pseudo destructor call. This, in turn, would mean that all containers (23 [containers]) of pointers show undefined behaviour, e.g. 23.3.5.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.




496. Is a volatile-qualified type really a POD?

Section: 3.9  [basic.types]     Status: DR     Submitter: John Maddock     Date: 30 Dec 2004

[Moved to DR at the April, 2013 meeting.]

In 3.9 [basic.types] paragraph 10, the standard makes it quite clear that volatile qualified types are PODs:

Arithmetic types (3.9.1 [basic.fundamental]), enumeration types, pointer types, and pointer to member types (3.9.2 [basic.compound]), and cv-qualified versions of these types (3.9.3 [basic.type.qualifier]) are collectively called scalar types. Scalar types, POD-struct types, POD-union types (clause 9 [class]), arrays of such types and cv-qualified versions of these types (3.9.3 [basic.type.qualifier]) are collectively called POD types.

However in 3.9 [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.

Proposed resolution, October, 2012:

  1. Change 3.9 [basic.types] paragraph 9 as follows:

  2. ...Scalar types, trivially copyable class types (Clause 9 [class]), arrays of such types, and cv-qualified non-volatile const-qualified versions of these types (3.9.3 [basic.type.qualifier]) are collectively called trivially copyable types. Scalar types, trivial class types...
  3. Change 7.1.6.1 [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 1.9 [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 12.8 [class.copy] 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 12.8 [class.copy] 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




1361. Requirement on brace-or-equal-initializers of literal types

Section: 3.9  [basic.types]     Status: DR     Submitter: Richard Smith     Date: 2011-08-16

The requirement in 3.8 [basic.life] paragraph 10 that

is mostly redundant with the constexpr constructor requirements in 7.1.5 [dcl.constexpr] paragraph 4 (although 12.6.2 [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: 3.9  [basic.types]     Status: DR     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 5.19 [expr.const] paragraph 2 as follows:




1515. Modulo 2n arithmetic for implicitly-unsigned types

Section: 3.9.1  [basic.fundamental]     Status: DR     Submitter: Sean Hunt     Date: 2012-07-03

[Moved to DR at the April, 2013 meeting.]

According to 3.9.1 [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 3.9.1 [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: 3.9.1  [basic.fundamental]     Status: DR     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 3.7.4.3 [basic.stc.dynamic.safety] paragraph 1 as follows:

  2. A traceable pointer object is

  3. Change 3.9.1 [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 (3.11 [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 3.11 [basic.align] paragraph 6 as follows:

  6. The alignment requirement of a complete type can be queried using an alignof expression (5.3.6 [expr.alignof]). Furthermore, the types char, signed char, and unsigned char narrow character types (3.9.1 [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 (7.6.2 [dcl.align]). —end note]
  7. Change 8.5.2 [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 (3.9.1 [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 (2.14.5 [lex.string]). Successive characters of the value of the string literal initialize the elements of the array. [Example:



240. Uninitialized values and undefined behavior

Section: 4.1  [conv.lval]     Status: DR     Submitter: Mike Miller     Date: 8 Aug 2000

[Moved to DR at the April, 2013 meeting.]

4.1 [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 3.9.1 [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 4.1 [conv.lval] paragraph 1. Instead, something like the following ought to be added to 3.9 [basic.types] paragraph 4 (which is where the concept of "value" is introduced):

Any use of an indeterminate value (5.3.4 [expr.new], 8.5 [dcl.init], 12.6.2 [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 3.9 [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 (5.3.4 [expr.new], 8.5 [dcl.init], 12.6.2 [class.base.init]) of a type other than unsigned char results in undefined behavior.
  2. Change 4.1 [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 DR336. 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: 4.1  [conv.lval]     Status: DR     Submitter: Miller     Date: 2009-12-10

[Moved to DR at the April, 2013 meeting.]

According to 4.1 [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.




974. Default arguments for lambdas

Section: 5.1.2  [expr.prim.lambda]     Status: DR     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 5.1.2 [expr.prim.lambda] paragraph 5 against default arguments in lambda-expressions.

Proposed resolution (August, 2011):

Delete the following sentence from 5.1.2 [expr.prim.lambda] paragraph 5:

Default arguments (8.3.6 [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: 5.1.2  [expr.prim.lambda]     Status: DR     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 5.1.2 [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.




1557. Language linkage of converted lambda function pointer

Section: 5.1.2  [expr.prim.lambda]     Status: DR     Submitter: Scott Meyers     Date: 2012-09-19

[Moved to DR at the April, 2013 meeting.]

5.1.2 [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 5.1.2 [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 (7.5 [dcl.link]). having the same parameter and return types as the closure type's function call operator. The value returned...



1213. Array subscripting and xvalues

Section: 5.2.1  [expr.sub]     Status: DR     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 5.2.1 [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 5.3 [expr.unary] and 5.7 [expr.add] for details of * and + and 8.3.4 [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: 5.2.2  [expr.call]     Status: DR     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 5.2.2 [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 (10.3 [class.virtual]) in the dynamic type of the object expression is called; such a call is referred to as a virtual function call.



1320. Converting scoped enumerations to bool

Section: 5.2.9  [expr.static.cast]     Status: DR     Submitter: Jonathan Wakely     Date: 2011-05-18

[Moved to DR at the April, 2013 meeting.]

The specification of static_cast (5.2.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 5.2.9 [expr.static.cast] paragraph 9 as follows:

A value of a scoped enumeration type (7.2 [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: 5.2.9  [expr.static.cast]     Status: DR     Submitter: Nikolay Ivchenkov     Date: 2011-11-01

[Moved to DR at the April, 2013 meeting.]

The definedness of various pointer conversions (see 5.2.10 [expr.reinterpret.cast] paragraph 7, 9.2 [class.mem] paragraph 20, 5.2.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 4.10 [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 (1.8 [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 5.2.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 5.2.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 (3.9 [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.



1553. sizeof and xvalue bit-fields

Section: 5.3.3  [expr.sizeof]     Status: DR     Submitter: Richard Smith     Date: 2012-09-13

[Moved to DR at the April, 2013 meeting.]

According to 5.3.3 [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 5.3.3 [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 5 [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)...



1559. String too long in initializer list of new-expression

Section: 5.3.4  [expr.new]     Status: DR     Submitter: John Spicer     Date: 2012-09-21

[Moved to DR at the April, 2013 meeting.]

According to 5.3.4 [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 (15.3 [except.handle]) of type std::bad_array_new_length (18.6.2.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.




1504. Pointer arithmetic after derived-base conversion

Section: 5.7  [expr.add]     Status: DR     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 5.7 [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...




1512. Pointer comparison vs qualification conversions

Section: 5.9  [expr.rel]     Status: DR     Submitter: Steve Clamage     Date: 2012-06-22

[Moved to DR status at the April, 2013 meeting as paper N3624.]

According to 5.9 [expr.rel] paragraph 2, describing pointer comparisons,

Pointer conversions (4.10 [conv.ptr]) and qualification conversions (4.4 [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 4.4 [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.




1550. Parenthesized throw-expression operand of conditional-expression

Section: 5.16  [expr.cond]     Status: DR     Submitter: Nikolay Ivchenkov     Date: 2012-09-04

[Moved to DR at the April, 2013 meeting.]

The current wording of 5.16 [expr.cond] paragraph 2 says,

If either the second or the third operand has type void, then the lvalue-to-rvalue (4.1 [conv.lval]), array-to-pointer (4.2 [conv.array]), and function-to-pointer (4.3 [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 5.16 [expr.cond] paragraph 2 as follows:

If either the second or the third operand has type void, then the lvalue-to-rvalue (4.1 [conv.lval]), array-to-pointer (4.2 [conv.array]), and function-to-pointer (4.3 [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: 5.16  [expr.cond]     Status: DR     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 (4.1 [conv.lval]), array-to-pointer (4.2 [conv.array]), and function-to-pointer (4.3 [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: 5.17  [expr.ass]     Status: DR     Submitter: Mike Miller     Date: 2012-07-23

[Moved to DR at the April, 2013 meeting.]

According to 5.17 [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 5.17 [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: 5.17  [expr.ass]     Status: DR     Submitter: Daniel Krügler     Date: 2012-08-14

[Moved to DR at the April, 2013 meeting.]

According to 5.17 [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 (8.5.4 [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 5.17 [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 (8.5.4 [dcl.init.list]) is allowed x=T{v}. The meaning of x={} is x=T() x=T{}.



1456. Address constant expression designating the one-past-the-end address

Section: 5.19  [expr.const]     Status: DR     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 5.19 [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...



1535. typeid in core constant expressions

Section: 5.19  [expr.const]     Status: DR     Submitter: Richard Smith     Date: 2012-08-10

[Moved to DR at the April, 2013 meeting.]

One of the criteria in 5.19 [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 5.19 [expr.const] paragraph 2 as follows:




1537. Optional compile-time evaluation of constant expressions

Section: 5.19  [expr.const]     Status: DR     Submitter: John Spicer     Date: 2012-08-14

[Moved to DR at the April, 2013 meeting.]

According to the note in 5.19 [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 5.19 [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:...



1442. Argument-dependent lookup in the range-based for

Section: 6.5.4  [stmt.ranged]     Status: DR     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 6.5.4 [stmt.ranged] paragraph 1 bullet 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 6.5.4 [stmt.ranged] paragraph 1 bullet 3 as follows:




1541. cv void return types

Section: 6.6.3  [stmt.return]     Status: DR     Submitter: Sean Hunt     Date: 2012-08-21

[Moved to DR at the April, 2013 meeting.]

According to 6.6.3 [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 (12.1 [class.ctor]), or a destructor (12.4 [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 6.6.3 [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 (12.1 [class.ctor]), or a destructor (12.4 [class.dtor]). A return statement with an expression of non-void type...



1544. Linkage of member of unnamed namespace

Section: 7.1.1  [dcl.stc]     Status: DR     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 3.5 [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, 7.1.1 [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 7.1.1 [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: 7.1.3  [dcl.typedef]     Status: DR     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;

7.6.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 7.6.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 (15.3 [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 (7.1.6.3 [dcl.type.elab]) or class-head (Clause 9 [class]), respectively) and to the declaration or definition of an enumeration (in an opaque-enum-declaration or enum-head, respectively (7.2 [dcl.enum])). An alignment-specifier with an ellipsis is a pack expansion (14.5.3 [temp.variadic]).



1358. Unintentionally ill-formed constexpr function template instances

Section: 7.1.5  [dcl.constexpr]     Status: DR     Submitter: Richard Smith     Date: 2011-08-16

[Moved to DR status at the April, 2013 meeting.]

The permission granted implementations in 7.1.5 [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 7.1.5 [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 (5.19 [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 7.1.5 [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 7.1.5 [dcl.constexpr]. It is being retained in "drafting" status pending additional work.

Proposed resolution (October, 2012):

Change 7.1.5 [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 (5.19 [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.




1588. Deducing cv-qualified auto

Section: 7.1.6.4  [dcl.spec.auto]     Status: DR     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 7.1.6.4 [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 (14.8.2.1 [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.




977. When is an enumeration type complete?

Section: 7.2  [dcl.enum]     Status: DR     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.




1477. Definition of a friend outside its namespace

Section: 7.3.1.2  [namespace.memdef]     Status: DR     Submitter: John Spicer     Date: 2012-03-09

[Moved to DR at the April, 2013 meeting.]

According to 7.3.1.2 [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 (3.4.1 [basic.lookup.unqual]) or by qualified lookup (3.4.3 [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 7.3.1.2 [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 (3.4.1 [basic.lookup.unqual]) or by qualified lookup (3.4.3 [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...



1475. Errors in [[carries_dependency]] example

Section: 7.6.4  [dcl.attr.depend]     Status: DR     Submitter: Stephan Lavavej     Date: 2012-03-06

[Moved to DR at the April, 2013 meeting.]

The example in 7.6.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 7.6.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 7.6.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.



1528. Repeated cv-qualifiers in declarators

Section: 8  [dcl.decl]     Status: DR     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 7.1.6 [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 7.1.6.1 [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:...



1435. template-id as the declarator for a class template constructor

Section: 8.3  [dcl.meaning]     Status: DR     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>();
  };

8.3 [dcl.meaning] paragraph 1 appears to say that it is not allowed, but it is not clear.

Proposed resolution (October, 2012):

  1. Change 8.3 [dcl.meaning] paragraph 1 as follows:

  2. A list of declarators appears after an optional (Clause 7 [dcl.dcl]) decl-specifier-seq (7.1 [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 (12.1 [class.ctor], 12.3 [class.conv], 12.4 [class.dtor], 13.5 [over.oper]) and for the declaration of template specializations or partial specializations (14.7 [temp.spec]). A declarator-id shall not...
  3. Change 12.1 [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 (8.3.5 [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 12.1 [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 12.1 [class.ctor] paragraph 4 as follows:

  8. A constructor shall not be virtual (10.3 [class.virtual]) or static (9.4 [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 (9.3.2 [class.this]). const and volatile semantics (7.1.6.1 [dcl.type.cv]) are not applied on an object under construction. They come into effect when the constructor for the most derived object (1.8 [intro.object]) ends. A constructor shall not be declared with a ref-qualifier.
  9. Change 12.1 [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 12.4 [class.dtor] paragraphs 1-2 as follows:

  12. A special declarator syntax using an optional function-specifier (7.1.2 [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 (8.3.5 [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 (8.3.5 [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 (9.3.2 [class.this]). const and volatile semantics (7.1.6.1 [dcl.type.cv]) are not applied on an object under destruction. They stop being in effect when the destructor for the most derived object (1.8 [intro.object]) starts. A destructor shall not be declared with a ref-qualifier.




1510. cv-qualified references via decltype

Section: 8.3.2  [dcl.ref]     Status: DR     Submitter: Richard Smith     Date: 2012-06-14

[Moved to DR at the April, 2013 meeting.]

According to 8.3.2 [dcl.ref] paragraph 1,

Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef (7.1.3 [dcl.typedef]) or of a template type argument (14.3 [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 8.3.2 [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 (7.1.3 [dcl.typedef], 14.1 [temp.param]) or of a template type argument (14.3 [temp.arg]) decltype-specifier (7.1.6.2 [dcl.type.simple]), in which case the cv-qualifiers are ignored. [Example:...



1502. Value initialization of unions with member initializers

Section: 8.5  [dcl.init]     Status: DR     Submitter: Richard Smith     Date: 2012-05-06

[Moved to DR at the April, 2013 meeting.]

According to 8.5 [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 8.5 [dcl.init] paragraph 7 as follows:

To value-initialize an object of type T means:




1507. Value initialization with trivial inaccessible default constructor

Section: 8.5  [dcl.init]     Status: DR     Submitter: Johannes Schaub     Date: 2012-06-01

[Moved to DR at the April, 2013 meeting.]

According to 8.5 [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 8.5 [dcl.init] paragraph 7 as follows:

  2. To default-initialize an object of type T means:

  3. Change 8.5 [dcl.init] paragraph 8 as follows:

  4. To value-initialize an object of type T means:




1328. Conflict in reference binding vs overload resolution

Section: 8.5.3  [dcl.init.ref]     Status: DR     Submitter: Johannes Schaub     Date: 2011-05-31

[Moved to DR at the April, 2013 meeting.]

According to 13.3.1.6 [over.match.ref] paragraph 1, the determination of the candidate functions is based on whether 8.5.3 [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 13.3.1.6 [over.match.ref] paragraph 1 as follows:

  2. Change 13.3.3 [over.match.best] paragraph 1 as follows:




1494. Temporary initialization for reference binding in list-initialization

Section: 8.5.4  [dcl.init.list]     Status: DR     Submitter: Steve Adamczyk     Date: 2012-04-12

[Moved to DR at the April, 2013 meeting.]

One of the bullets in 8.5.4 [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 8.5.4 [dcl.init.list] paragraph 3 as follows:




1506. Value category of initializer_list object

Section: 8.5.4  [dcl.init.list]     Status: DR     Submitter: Steve Adamczyk     Date: 2012-05-29

[Moved to DR at the April, 2013 meeting.]

One of the bullets in 8.5.4 [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 (8.5 [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 8.5 [dcl.init] initialization.

Proposed resolution (October, 2012):

Change 8.5.4 [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: 8.5.4  [dcl.init.list]     Status: DR     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, 8.5.4 [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 8.5.4 [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 (Clause 11 [class.access]) in the context of the initializer list. —end note] If a narrowing conversion is required...



1318. Syntactic ambiguities with final

Section: 9  [class]     Status: DR     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 2.11 [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 2.11 [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 9 [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 (Clause 10 [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]




1411. More on global scope :: in nested-name-specifier

Section: 9  [class]     Status: DR     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 9 [class] paragraph 1 and removed the optional :: from class-or-decltype in 10 [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 5.1.1 [expr.prim.general] paragraph 8 as follows:




1425. Base-class subobjects of standard-layout structs

Section: 9.2  [class.mem]     Status: DR     Submitter: Ville Voutilainen     Date: 2011-12-07

[Moved to DR at the April, 2013 meeting.]

According to 9.2 [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 9.2 [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:...



1605. Misleading parenthetical comment for explicit destructor call

Section: 12.4  [class.dtor]     Status: DR     Submitter: Mike Miller     Date: 2013-01-13

[Moved to DR at the April, 2013 meeting.]

According to 12.4 [class.dtor] paragraph 13,

The invocation of a destructor is subject to the usual rules for member functions (9.3 [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 12.4 [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 (9.3 [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 5.3.5 [expr.delete]. —end note] [Example:...



1402. Move functions too often deleted

Section: 12.8  [class.copy]     Status: DR     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 12.8 [class.copy] 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 (12.8 [class.copy] paragraph 12: W::W(W&&) is not trivial) and our exception-specification rules (15.4 [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 12.8 [class.copy] paragraph 9 and paragraph 20.

Proposed resolution (February, 2012):

  1. Change 12.8 [class.copy] 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 12.8 [class.copy] 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 (8.4.3 [dcl.fct.def.delete]) if X has:

  5. Change 12.8 [class.copy] 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 12.8 [class.copy] 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 12.8 [class.copy] 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 12.8 [class.copy] paragraph 11 as follows:

  4. ...A defaulted copy/move constructor for a class X is defined as deleted (8.4.3 [dcl.fct.def.delete]) if X has:

    A defaulted move constructor that is defined as deleted is ignored by overload resolution (13.3 [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 12.8 [class.copy] 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 12.8 [class.copy] 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 (13.3 [over.match], 13.4 [over.over]).




1487. When are inheriting constructors declared?

Section: 12.9  [class.inhctor]     Status: DR     Submitter: Richard Smith     Date: 2012-03-27

[Moved to DR at the April, 2013 meeting.]

According to 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 9.2 [class.mem] paragraph 2 as follows:

  2. A class is considered a completely-defined object type (3.9 [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 (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 12 [special] paragraph 1 as follows:

  4. ...See 12.1 [class.ctor], 12.4 [class.dtor] and 12.8 [class.copy]. —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 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 (14.5.6.1 [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 15.4 [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.




1556. Constructors and explicit conversion functions in direct initialization

Section: 13.3.1.4  [over.match.copy]     Status: DR     Submitter: Edward Catmur     Date: 2012-09-18

[Moved to DR at the April, 2013 meeting.]

Issue 899 added wording to the second bullet of 13.3.1.4 [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 13.3.1.4 [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:




1543. Implicit conversion sequence for empty initializer list

Section: 13.3.3.1.5  [over.ics.list]     Status: DR     Submitter: Steve Adamczyk     Date: 2012-08-21

[Moved to DR at the April, 2013 meeting.]

According to 13.3.3.1.5 [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 13.3.3.2 [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 13.3.3.1.5 [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
  ...



1374. Qualification conversion vs difference in reference binding

Section: 13.3.3.2  [over.ics.rank]     Status: DR     Submitter: Michael Wong     Date: 2011-08-15

[Moved to DR at the April, 2013 meeting.]

The rule in 13.3.3.2 [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 13.3.3.1.4 [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, 13.3.3.2 [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 13.3.3.2 [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:




1563. List-initialization and overloaded function disambiguation

Section: 13.4  [over.over]     Status: DR     Submitter: Daniel Krügler     Date: 2012-10-08

[Moved to DR at the April, 2013 meeting.]

Presumably, 13.4 [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 8.5.4 [dcl.init.list] might be read as indicating that list-initialization is not included.

Proposed resolution (February, 2013):

Change 13.4 [over.over] paragraph 1 as follows:

...The target can be




1481. Increment/decrement operators with reference parameters

Section: 13.5.7  [over.inc]     Status: DR     Submitter: Ryou Ezoe     Date: 2012-03-20

[Moved to DR at the April, 2013 meeting.]

The phrase in 13.5.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 13.5 [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 13.5.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: 13.5.8  [over.literal]     Status: DR     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 13.5.8 [over.literal] as follows:

  2. Change 13.5.8 [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 17.6.4.3.5 [usrlit.suffix]. —end note]
  4. Change the example in 13.5.8 [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: 13.5.8  [over.literal]     Status: DR     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 13.5.8 [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 (8.3.6 [dcl.fct.default]), the program is ill-formed.




1533. Function pack expansion for member initialization

Section: 14.5.3  [temp.variadic]     Status: DR     Submitter: Jonathan Caves     Date: 2012-08-06

[Moved to DR at the April, 2013 meeting.]

The list of pack expansion contexts in 14.5.3 [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 12.6.2 [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 (14.5.3 [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 14.5.3 [temp.variadic] paragraph 4 as follows:

...Pack expansions can occur in the following contexts:




1495. Partial specialization of variadic class template

Section: 14.5.5  [temp.class.spec]     Status: DR     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 14.5.5 [temp.class.spec], but it is not more specialized than the primary template. However, 14.5.5.1 [temp.class.spec.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 14.5.5 [temp.class.spec] paragraph 8:




1471. Nested type of non-dependent base

Section: 14.6.2.1  [temp.dep.type]     Status: DR     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 14.6.2.1 [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 14.6.2.1 [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 14.6.2.1 [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 14.6.2.1 [temp.dep.type] paragraph 5 as follows:

  4. A name is a member of an unknown specialization if it is

  5. Change 14.6.2.1 [temp.dep.type] paragraph 8 as follows:

  6. A type is dependent if it is

  7. Change 14.6.2.2 [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 (14.5.1.3 [temp.static]). Expressions of the following forms...

  9. Change 14.6.2.3 [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 14.6.2.3 [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: 14.6.2.3  [temp.dep.constexpr]     Status: DR     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 4.10 [conv.ptr] paragraph 1 as follows:

  2. A null pointer constant is an integral constant expression (5.19 [expr.const]) prvalue of integer type that evaluates to integer literal (2.14.2 [lex.icon]) with value zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted...
  3. Change 5.19 [expr.const] paragraph 3 as follows:

  4. ...[Note: Such expressions may be used as array bounds (8.3.4 [dcl.array], 5.3.4 [expr.new]), as bit-field lengths (9.6 [class.bit]), as enumerator initializers if the underlying type is not fixed (7.2 [dcl.enum]), as null pointer constants (4.10 [conv.ptr]), and as alignments (7.6.2 [dcl.align]). —end note]...
  5. Change 8.5 [dcl.init] paragraph 5 as follows:

  6. To zero-initialize an object or reference of type T means:

  7. Change 14.3.2 [temp.arg.nontype] paragraph 5 as follows:

  8. Change 15.3 [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.2 [diff.cpp03] as follows:

  11. C.2.x Clause 4: standard conversions [diff.cpp03.conv]

    4.10 [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: 14.6.2.3  [temp.dep.constexpr]     Status: DR     Submitter: Richard Smith     Date: 2011-11-09

[Moved to DR at the April, 2013 meeting.]

The list of cases in 14.6.2.3 [temp.dep.constexpr] paragraph 2 in which an identifier is value-dependent should also include:

Proposed resolution (October, 2012):

  1. Change 14.6.2.3 [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 14.6.2.3 [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: 14.7.2  [temp.explicit]     Status: DR     Submitter: Johannes Schaub     Date: 2012-08-04

[Moved to DR at the April, 2013 meeting.]

According to 14.7.2 [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 14.6 [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 14.7.2 [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 14.7.2 [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]



1330. Delayed instantiation of noexcept specifiers

Section: 14.8.2  [temp.deduct]     Status: DR     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 14.5 [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 14.6 [temp.res] paragraph 11 as follows:

  4. [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 (14.5). —end note]
  5. Add a new paragraph following 14.6.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 14.7.1 [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 14.7.1 [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 (5.3.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 14.8.2 [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 15.4 [except.spec] paragraph 15:

  14. A deallocation function (3.7.4.2 [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 (14.7.1 [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 3.3.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 3.4.1 [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 3.4.1 [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 (9.2 [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 9.2 [class.mem] paragraph 2 as follows:

  8. A class is considered a completely-defined object type (3.9 [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 14.5 [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 14.6 [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 (14.5 [temp.decls]). —end note]
  13. Add the following as a new paragraph after 14.6.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 14.7.1 [temp.inst] paragraph 1 as follows:

  16. Unless a class template specialization has been explicitly instantiated (14.7.2 [temp.explicit]) or explicitly specialized (14.7.3 [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 14.7.1 [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 (15.4 [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: 14.6.4.1 [temp.point] defines the point of instantiation of a template specialization. —end note]

  19. Change 14.8.2 [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 15.4 [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 15.4 [except.spec] paragraph 15:

  24. A deallocation function (3.7.4.2 [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: 14.8.2  [temp.deduct]     Status: DR     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 14.8.2 [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...



1503. Exceptions during copy to exception object

Section: 15.1  [except.throw]     Status: DR     Submitter: Daniel Krügler     Date: 2012-05-11

[Moved to DR at the April, 2013 meeting.]

According to 15.1 [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 (15.5.1 [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 15.1 [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 (15.5.1 [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 (15.3 [except.handle])
      }
    }
  };

  int main() {
    try {
      throw C();   // calls std::terminate() if construction of the handler's
                   // exception-declaration object is not elided (12.8 [class.copy])
    } catch(C) { }
  }

end example]




223. The meaning of deprecation

Section: D  [depr]     Status: DR     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 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 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 "accepted" Status


1464. Negative array bound in a new-expression

Section: 5.3.4  [expr.new]     Status: accepted     Submitter: Mike Miller     Date: 2012-02-12

[Accepted at the April, 2013 meeting.]

Currently, 5.3.4 [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 (15.3 [except.handle]) of type std::bad_array_new_length (18.6.2.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 5.3.4 [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 3.9.2 [basic.compound] paragraph 2 as follows:

  2. These methods of constructing types can be applied recursively; restrictions are mentioned in 8.3.1 [dcl.ptr], 8.3.4 [dcl.array], 8.3.5 [dcl.fct], and 8.3.2 [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 (18.2 [support.types]) is ill-formed.
  3. Change 5.3.4 [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 (15.3 [except.handle]) of type std::bad_array_new_length (18.6.2.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 (15.3 [except.handle]) of type std::bad_array_new_length (18.6.2.2 [new.badlength]).

(This resolution also resolves issue 1559.)






Issues with "DRWP" Status


912. Character literals and universal-character-names

Section: 2.14.3  [lex.ccon]     Status: DRWP     Submitter: Alisdair Meredith     Date: 7 June, 2009

[Moved to DR at the October, 2012 meeting.]

According to 2.14.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 2.14.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: 2.14.3  [lex.ccon]     Status: DRWP     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 2.14.3 [lex.ccon] paragraphs 1-2 or in 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: 3.2  [basic.def.odr]     Status: DRWP     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, 9.4.2 [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 3.2 [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 (5.19 [expr.const]) and the lvalue-to-rvalue conversion (4.1 [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 5.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 3.2 [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 (5.19 [expr.const]) and ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1 [conv.lval]) is immediately applied to e, or e is a discarded-value expression (Clause 5 [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: 3.2  [basic.def.odr]     Status: DRWP     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 3.2 [basic.def.odr] paragraph 2 uses the term “overloaded” differently from its definition in 13 [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 3.2 [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 (3.4 [basic.lookup], 13.3 [over.match], 13.4 [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: 3.2  [basic.def.odr]     Status: DRWP     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 3.2 [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 3.2 [basic.def.odr] paragraph 4 as follows:




1352. Inconsistent class scope and completeness rules

Section: 3.3.7  [basic.scope.class]     Status: DRWP     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 3.3.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 3.4.1 [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 3.4.1 [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 (9.2 [class.mem]), or in the definition of a class member function (9.3 [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 (9.2 [class.mem]) of class X shall be declared in one of the following ways:...
[Drafting note: 9.2 [class.mem] paragraph 2 requires no changes. 3.3.7 [basic.scope.class] paragraph 1 bullet 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.]


1415. Missing prohibition of block-scope definition of extern object

Section: 3.5  [basic.link]     Status: DRWP     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 8.5 [dcl.init] paragraph 4:

[Note: Default arguments are more restricted; see 8.3.6 [dcl.fct.default].

The order of initialization of variables with static storage duration is described in 3.6 [basic.start] and 6.7 [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...




1003. Acceptable definitions of main

Section: 3.6.1  [basic.start.main]     Status: DRWP     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 3.6.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 (8.3.5 [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...




1438. Non-dereference use of invalid pointers

Section: 3.7.4.3  [basic.stc.dynamic.safety]     Status: DRWP     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 (3.7.4.2 [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 3.7.4.2 [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 (4.10 [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]



597. Conversions applied to out-of-lifetime non-POD lvalues

Section: 3.8  [basic.life]     Status: DRWP     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 3.8 [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 8.5.3 [dcl.init.ref], not to 4.10 [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 3.8 [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 3.8 [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.]




1453. Volatile members in literal classes?

Section: 3.9  [basic.types]     Status: DRWP     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 3.9 [basic.types] paragraph 10 as follows:

A type is a literal type if it is:




483. Normative requirements on integral ranges

Section: 3.9.1  [basic.fundamental]     Status: DRWP     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 3.9.1 [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 3.9.1 [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: 3.9.1  [basic.fundamental]     Status: DRWP     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 3.9.1 [basic.fundamental] paragraph 9 does not, but should, include an operand of the noexcept operator.

Proposed resolution (August, 2011):

Change 3.9.1 [basic.fundamental] paragraph 9 as follows:

An expression of type void shall be used only as an expression statement (6.2 [stmt.expr]), as an operand of a comma expression (5.18 [expr.comma]), as a second or third operand of ?: (5.16 [expr.cond]), as the operand of typeid, noexcept, or decltype, as the expression in a return statement (6.6.3 [stmt.return]) for a function with the return type void, or as the operand of an explicit conversion to type cv void.



1059. Cv-qualified array types (with rvalues)

Section: 3.9.3  [basic.type.qualifier]     Status: DRWP     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 3.9.3 [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 3.10 [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 3.9.3 [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 3.10 [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: 3.9.3  [basic.type.qualifier]     Status: DRWP     Submitter: Daniel Krügler     Date: 2011-12-08

[Moved to DR at the October, 2012 meeting.]

The definition of “const object” in 3.9.3 [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 3.9.3 [basic.type.qualifier] paragraph 1 as follows:

  2. The term object type (1.8 [intro.object]) includes the cv-qualifiers specified in the decl-specifier-seq (7.1 [dcl.spec]), declarator (Clause 8 [dcl.decl]), type-id (8.1 [dcl.name]), or new-type-id (5.3.4 [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 (3.9 [basic.types]).51

  3. Change 3.9.3 [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 8.3.5 [dcl.fct] and 9.3.2 [class.this] regarding function types...



1423. Convertibility of nullptr to bool

Section: 4.12  [conv.bool]     Status: DRWP     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 4.12 [conv.bool] paragraph 1 as follows:

...any other value is converted to true. A For direct-initialization (8.5 [dcl.init]), a prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.



1261. Explicit handling of cv-qualification with non-class prvalues

Section: 5  [expr]     Status: DRWP     Submitter: Nikolay Ivchenkov     Date: 2011-03-12

[Moved to DR at the October, 2012 meeting.]

Proposed resolution (December, 2011):

  1. Change 3.10 [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 (5.2.2 [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 5 [expr]. —end note]
  3. Add a new paragraph following 5 [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 5.2.2 [expr.call] paragraph 3 as follows:

  6. If the postfix-expression designates a destructor (12.4 [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 5.2.3 [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 (3.10 [basic.lval] Clause 5 [expr]). —end note]
  9. Change 5.4 [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 3.10 [basic.lval] Clause 5 [expr]. —end note]



1383. Clarifying discarded-value expressions

Section: 5  [expr]     Status: DRWP     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 (4.2 [conv.array]) and function-to-pointer (4.3 [conv.func]) standard conversions are not applied. The lvalue-to-rvalue conversion (4.1 [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 5 [expr] paragraph 10 as follows:

...The lvalue-to-rvalue conversion (4.1 [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.




1440. Acceptable decltype-specifiers used as nested-name-specifiers

Section: 5.1.1  [expr.prim.general]     Status: DRWP     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 5.1.1 [expr.prim.general]. (The corresponding prohibition for named types is handled as part of lookup in 3.4.3 [basic.lookup.qual] paragraph 1.)

Proposed resolution (February, 2012):

Add the following immediately after the grammar in 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...




1269. dynamic_cast of an xvalue operand

Section: 5.2.7  [expr.dynamic.cast]     Status: DRWP     Submitter: Michael Wong     Date: 2011-03-21

[Moved to DR at the October, 2012 meeting.]

5.2.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 5.2.7 [expr.dynamic.cast] paragraph 6 as follows:

Otherwise, v shall be a pointer to or an lvalue a glvalue of a polymorphic type (10.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: 5.2.8  [expr.typeid]     Status: DRWP     Submitter: Daniel Krügler     Date: 2011-11-17

[Moved to DR at the October, 2012 meeting.]

The requirement in 5.2.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 5.2.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:...



1447. static_cast of bit-field lvalue to rvalue reference

Section: 5.2.9  [expr.static.cast]     Status: DRWP     Submitter: Jason Merrill     Date: 2012-01-16

[Moved to DR at the October, 2012 meeting.]

According to 5.2.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” (8.5.3 [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 5.2.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” (8.5.3 [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 (4.1 [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 (Clause 11 [class.access]) or ambiguous (10.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: 5.2.10  [expr.reinterpret.cast]     Status: DRWP     Submitter: Michael Wong     Date: 2011-03-21

[Moved to DR at the October, 2012 meeting.]

5.2.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 5.2.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: 5.3  [expr.unary]     Status: DRWP     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 5.3.1 [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 8.3.4 [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 8.3.5 [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: 26.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 3.7.4.1 [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 3.7.4.3 [basic.stc.dynamic.safety] paragraph 2 as follows:

  4. Change 3.8 [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 4.11 [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 5.2.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 5.5 [expr.mptr.oper]. —end note]
  10. Change 5.3.1 [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 5.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 (1.8 [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 7.5 [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 8.3.2 [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 17.6.3.5 [allocator.requirements] table 27:

  19. Variable Definition
    ...
    ca dereferenceable pointer of type C* through which indirection is valid
    ...
  20. Change 20.6.3.2 [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 20.6.4 [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 20.6.12 [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 22.4.5.1.2 [locale.time.get.virtuals] paragraph 11 as follows:

  27. Requires: t shall be dereferenceable point to an object.
  28. Change 23.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 23.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 23.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 23.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 24.5.3 [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 28.12.1.3 [re.regiter.deref] as follows:

  39. regex_iterator dereference indirection
  40. Change the title of 28.12.2.3 [re.tokiter.deref] as follows:

  41. regex_token_iterator dereference indirection



1458. Address of incomplete type vs operator&()

Section: 5.3.1  [expr.unary.op]     Status: DRWP     Submitter: Richard Smith     Date: 2012-02-07

[Moved to DR at the October, 2012 meeting.]

According to 5.3.1 [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 5.3.1 [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.



292. Deallocation on exception in new before arguments evaluated

Section: 5.3.4  [expr.new]     Status: DRWP     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 5.3.4 [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, 5.3.4 [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 5.3.4 [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 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 4.

Suggested resolution:

Change the first sentence of 5.3.4 [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 5.3.4 [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...



1305. alignof applied to array of unknown size

Section: 5.3.6  [expr.alignof]     Status: DRWP     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 5.3.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 5.3.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: 5.3.7  [expr.unary.noexcept]     Status: DRWP     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 1.9 [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 5 [expr]). —end note] If a language construct...
  3. Change 5 [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 5.3.7 [expr.unary.noexcept] paragraph 3 should be hyphenated.]




1340. Complete type in member pointer expressions

Section: 5.5  [expr.mptr.oper]     Status: DRWP     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 (5.5 [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 5.5 [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 5.5 [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: 5.6  [expr.mul]     Status: DRWP     Submitter: Richard Smith     Date: 2012-01-31

[Moved to DR at the October, 2012 meeting.]

Issue 614 adopted the corresponding C99 wording for 5.6 [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 5.6 [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.



1457. Undefined behavior in left-shift

Section: 5.8  [expr.shift]     Status: DRWP     Submitter: Howard Hinnant     Date: 2012-02-04

[Moved to DR at the October, 2012 meeting.]

The current wording of 5.8 [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 5.8 [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.



1264. Use of this in constexpr constructor

Section: 5.19  [expr.const]     Status: DRWP     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: 5.19  [expr.const]     Status: DRWP     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 5.19 [expr.const] paragraph 2 as follows:




1311. Volatile lvalues in constant expressions

Section: 5.19  [expr.const]     Status: DRWP     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 5.19 [expr.const] paragraph 2 does not, but should, prohibit use of volatile glvalues in constant expressions.

Proposed resolution (August, 2011):

Change 5.19 [expr.const] paragraph 2 as follows:




1312. Simulated reinterpret_cast in constant expressions

Section: 5.19  [expr.const]     Status: DRWP     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 5.19 [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 5.19 [expr.const] paragraph 2 as follows:




1313. Undefined pointer arithmetic in constant expressions

Section: 5.19  [expr.const]     Status: DRWP     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 5.19 [expr.const] paragraph 2 as follows:




1364. constexpr function parameters

Section: 5.19  [expr.const]     Status: DRWP     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 5.19 [expr.const] paragraph 2 as follows:

  2. Delete the final bullet of 7.1.5 [dcl.constexpr] paragraph 3 and move the deleted "." to the preceding sub-bullet:

  3. Delete the final bullet of 7.1.5 [dcl.constexpr] paragraph 4 and change the preceding bullet as follows:




1365. Calling undefined constexpr functions

Section: 5.19  [expr.const]     Status: DRWP     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 5.19 [expr.const] paragraph 2 as follows:




1367. Use of this in a constant expression

Section: 5.19  [expr.const]     Status: DRWP     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: 5.19  [expr.const]     Status: DRWP     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 5.19 [expr.const] paragraph 2 as follows:

  2. A conditional-expression is a core constant expression unless it involves one of the following...

  3. Change 5.19 [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 (8.3.4 [dcl.array], 5.3.4 [expr.new]), as bit-field lengths (9.6 [class.bit]), as enumerator initializers if the underlying type is not fixed (7.2 [dcl.enum]), as null pointer constants (4.10 [conv.ptr]), and as alignments (7.6.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 (4.1 [conv.lval]), integral promotions (4.5 [conv.prom]), and integral conversions (4.7 [conv.integral]) other than narrowing conversions (8.5.4 [dcl.init.list]). [Note: such expressions may be used as case expressions (6.4.2 [stmt.switch]), as enumerator initializers if the underlying type is fixed (7.2 [dcl.enum]), and as integral or enumeration non-type template arguments (14.3 [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 7.1.5 [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: 5.19  [expr.const]     Status: DRWP     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.




631. Jumping into a “then” clause

Section: 6.4.1  [stmt.if]     Status: DRWP     Submitter: James Kanze     Date: 24 April 2007

[Moved to DR at the October, 2012 meeting.]

6.4.1 [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 6.4.1 [stmt.if] paragraph 1 as follows:

If the condition (6.4 [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...



1359. constexpr union constructors

Section: 7.1.5  [dcl.constexpr]     Status: DRWP     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 (7.1.5 [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 (12.6.2 [class.base.init] paragraph 8).

Proposed resolution (February, 2012):

Change 7.1.5 [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: 7.1.5  [dcl.constexpr]     Status: DRWP     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 7.1.5 [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: 7.1.5  [dcl.constexpr]     Status: DRWP     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 (7.1.5 [dcl.constexpr] paragraph 5) seems underspecified with respect to this.

Proposed resolution (August, 2011):

  1. Change the indicated bullet of 5.19 [expr.const] paragraph 2 as follows:

  2. Change 7.1.5 [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.




539. Constraints on type-specifier-seq

Section: 7.1.6  [dcl.type]     Status: DRWP     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 7.1.6 [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 5.3.4 [expr.new], 6.6 [stmt.jump], 8.1 [dcl.name], and 12.3.2 [class.conv.fct].

Proposed resolution (August, 2011):

Change 7.1.6 [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: 7.1.6.4  [dcl.spec.auto]     Status: DRWP     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 7.1.6.4 [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 7.1.6.4 [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: 7.1.6.4  [dcl.spec.auto]     Status: DRWP     Submitter: Richard Smith     Date: 2011-08-16

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

7.1.6.4 [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 7.1.6.4 [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: 7.1.6.4  [dcl.spec.auto]     Status: DRWP     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 7.1.6.4 [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 7.1.6.4 [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]




1439. Lookup and friend template declarations

Section: 7.3.1.2  [namespace.memdef]     Status: DRWP     Submitter: Richard Smith     Date: 2012-01-04

[Moved to DR at the October, 2012 meeting.]

According to 7.3.1.2 [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 (3.4.1 [basic.lookup.unqual]) or by qualified lookup (3.4.3 [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 3.4.2 [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 7.3.1.2 [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 (3.4.1 [basic.lookup.unqual]) or by qualified lookup (3.4.3 [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 (3.4.2 [basic.lookup.argdep]). If the name in a friend declaration...



565. Conflict rules for using-declarations naming function templates

Section: 7.3.3  [namespace.udecl]     Status: DRWP     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);

7.3.3 [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 7.3.3 [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 (8.3.5 [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:...



1297. Misplaced function attribute-specifier

Section: 8  [dcl.decl]     Status: DRWP     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 8 [dcl.decl] paragraph 4 and that of 8.3.5 [dcl.fct] paragraphs 1 and 2 and 8.4.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 8 [dcl.decl] paragraph 4 as follows:

  2. Change the grammar snippet in 13.3.1.1.2 [over.call.object] paragraph 2 as follows:




1382. Dead code for constructor names

Section: 8  [dcl.decl]     Status: DRWP     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 8 [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, 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 (12.1 [class.ctor]).

Proposed resolution (February, 2012):

  1. Change 8 [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 :: (, 12.1 [class.ctor], 12.4 [class.dtor]).

  3. Change 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 (3.3.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 (12.1 [class.ctor]). Where class-name ::~ class-name is used...



482. Qualified declarators in redeclarations

Section: 8.3  [dcl.meaning]     Status: DRWP     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 8.3 [dcl.meaning] paragraph 1,

A declarator-id shall not be qualified except for the definition of a member function (9.3 [class.mfct]) or static data member (9.4 [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.3 [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 8.3 [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 (12.3 [class.conv], 12.4 [class.dtor], 13.5 [over.oper]) and for the declaration of template specializations or partial specializations (). A declarator-id shall not be qualified except for the definition of a member function (9.3 [class.mfct]) or static data member (9.4 [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.3 [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 9.2 [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.]




332. cv-qualified void parameter types

Section: 8.3.5  [dcl.fct]     Status: DRWP     Submitter: Michiel Salters     Date: 9 Jan 2002

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

8.3.5 [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, 8.3.5 [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: 8.3.5  [dcl.fct]     Status: DRWP     Submitter: Ben Hutchings     Date: 22 April 2006

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

8.3.5 [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 8.3.5 [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: 8.3.5  [dcl.fct]     Status: DRWP     Submitter: Daniel Krügler     Date: 2011-08-22

[Moved to DR at the October, 2012 meeting.]

Although 8.3.5 [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 8.3.5 [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 14.1 [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: 8.3.5  [dcl.fct]     Status: DRWP     Submitter: Johannes Schaub     Date: 2011-09-11

[Moved to DR at the October, 2012 meeting.]

Currently, 8.3.5 [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 8.3.5 [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 (8.4.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: 8.3.6  [dcl.fct.default]     Status: DRWP     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 8.3.6 [dcl.fct.default] paragraph 5,

A default argument is implicitly converted (Clause 4 [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 8.3.6 [dcl.fct.default] paragraph 5:

A default argument is implicitly converted (Clause 4 [conv]) to the parameter type.



1327. virt-specifier in a defaulted definition

Section: 8.4.2  [dcl.fct.def.default]     Status: DRWP     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 8.4.2 [dcl.fct.def.default] and 8.4.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 8.4.2 [dcl.fct.def.default] paragraph 1 as follows:

  2. A function definition of the form:

    is called an explicitly-defaulted definition...

  3. Change 8.4.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: 8.4.2  [dcl.fct.def.default]     Status: DRWP     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 8.4.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 8.4.2 [dcl.fct.def.default] paragraph 2 as follows:

  2. ...If a function is explicitly defaulted on its first declaration,

  3. Change 12.8 [class.copy] 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 12.8 [class.copy] 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: 8.4.2  [dcl.fct.def.default]     Status: DRWP     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 8.4.2 [dcl.fct.def.default] paragraph 4 applies only to special member functions, while the definition of an aggregate in 8.5.1 [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 8.4.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.]




1093. Value-initializing non-objects

Section: 8.5  [dcl.init]     Status: DRWP     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.]

8.5 [dcl.init] paragraph 7 only describes how to initialize objects:

To value-initialize an object of type T means:

However, 5.2.3 [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 (8.5 [dcl.init]; no initialization is done for the void() case).

Proposed resolution (August, 2011):

Change 5.2.3 [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 (8.5 [dcl.init] type, whose value is that produced by value-initializing (8.5 [dcl.init]) an object of type T; no initialization is done for the void() case). [Note:...



1301. Value initialization of union

Section: 8.5  [dcl.init]     Status: DRWP     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 8.5 [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 8.5 [dcl.init] paragraph 7 as follows:

  2. To value-initialize an object of type T means:

  3. Change 8.5.4 [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: 8.5  [dcl.init]     Status: DRWP     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 8.5.4 [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: 8.5  [dcl.init]     Status: DRWP     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 8.5 [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.




1295. Binding a reference to an rvalue bit-field

Section: 8.5.3  [dcl.init.ref]     Status: DRWP     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 8.5.3 [dcl.init.ref] paragraph 5, ref is bound to the bit-field xvalue.

Proposed resolution (August, 2011):

Change the indicated sub-bullet of 8.5.3 [dcl.init.ref] paragraph 5 as follows:




1401. Similar types and reference compatibility

Section: 8.5.3  [dcl.init.ref]     Status: DRWP     Submitter: Daniel Krügler     Date: 2011-10-03

[Moved to DR at the October, 2012 meeting.]

The definition of reference-compatible types in 8.5.3 [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” (4.4 [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 8.5.3 [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 13.3.3.2 [over.ics.rank]). In all cases...
  3. Delete 13.3.3.1.4 [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 13.3.3.2 [over.ics.rank] and 8.5.3 [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: 8.5.4  [dcl.init.list]     Status: DRWP     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 8.5.1 [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 8.5.1 [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: 8.5.4  [dcl.init.list]     Status: DRWP     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 8.5.4 [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 8.5.4 [dcl.init.list] paragraph 3 as follows:




1290. Lifetime of the underlying array of an initializer_list member

Section: 8.5.4  [dcl.init.list]     Status: DRWP     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 (12.6.2 [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 8.5.4 [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 (12.2 [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 12.2 [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 (8.5.4 [dcl.init.list]) with its underlying temporary array. —end footnote] The temporary to which...



1418. Type of initializer_list backing array

Section: 8.5.4  [dcl.init.list]     Status: DRWP     Submitter: Johannes Schaub     Date: 2011-11-19

[Moved to DR at the October, 2012 meeting.]

According to 8.5.4 [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 18.9 [support.initlist] paragraph 2.

Proposed resolution (February, 2012):

Change 8.5.4 [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: 8.5.4  [dcl.init.list]     Status: DRWP     Submitter: Richard Smith     Date: 2012-01-28

[Moved to DR at the October, 2012 meeting.]

According to 8.5.4 [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 8.5.4 [dcl.init.list] paragraph 7 as follows:

A narrowing conversion is an implicit conversion

[Note:...




1363. Triviality vs multiple default constructors

Section: 9  [class]     Status: DRWP     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” (9 [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 9 [class] paragraph 6 as follows:

...A trivial class is a class that has a trivial default constructor (12.1 [class.ctor]), has no non-trivial default constructors, and is trivially copyable...



1308. Completeness of class type within an exception-specification

Section: 9.2  [class.mem]     Status: DRWP     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 9.2 [class.mem] paragraph 2,

A class is considered a completely-defined object type (3.9 [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 9.2 [class.mem] paragraph 2 as follows:

  2. A class is considered a completely-defined object type (3.9 [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 15.4 [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.



1357. brace-or-equal-initializers for function and typedef members

Section: 9.2  [class.mem]     Status: DRWP     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 9.2 [class.mem], delete paragraph 4 and change paragraph 5 as follows:

A member can be initialized using a constructor; see 12.1 [class.ctor]. [Note: See Clause 12 [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 9.4.2 [class.static.data]; for non-static data members, see 12.6.2 [class.base.init]).




1306. Modifying an object within a const member function

Section: 9.3.2  [class.this]     Status: DRWP     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 9.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 9.3.2 [class.this] paragraphs 1 and 2 and change the text as follows:

In the body of a non-static (9.3 [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:...



1375. Reference to anonymous union?

Section: 9.5  [class.union]     Status: DRWP     Submitter: Daveed Vandevoorde     Date: 2011-08-16

[Moved to DR at the October, 2012 meeting.]

According to 9.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 9.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
  }



675. Signedness of bit-field with typedef or template parameter type

Section: 9.6  [class.bit]     Status: DRWP     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 9.6 [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: 9.6  [class.bit]     Status: DRWP     Submitter: Mike Miller     Date: 3 November, 2008

[Moved to DR at the October, 2012 meeting.]

9.6 [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 5.16 [expr.cond] paragraph 4)?

Proposed resolution (August, 2011):

Change 9.6 [class.bit] paragraph 3 as follows:

A bit-field shall not be a static member. A bit-field shall have integral or enumeration type (3.9.1 [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 (14.6.2.1 [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 9.6 [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 (3.9.1 [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.1.8 [diff.class]:

  4. 9.6 [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.




1250. Cv-qualification of incomplete virtual function return types

Section: 10.3  [class.virtual]     Status: DRWP     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 10.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 10.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...



462. Lifetime of temporaries bound to comma expressions

Section: 12.2  [class.temporary]     Status: DRWP     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 12.2 [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 (5.18 [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 12.2 [class.temporary] unchanged and adding normative wording to 5.18 [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 5.18 [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 (12.2 [class.temporary]), the result is that temporary.



1336. Definition of “converting constructor”

Section: 12.3.1  [class.conv.ctor]     Status: DRWP     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 12.3.1 [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 12.3.1 [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 12.3.1 [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
    



1345. Initialization of anonymous union class members

Section: 12.6.2  [class.base.init]     Status: DRWP     Submitter: Sean Hunt     Date: 2011-08-16

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

12.6.2 [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 12.6.2 [class.base.init] paragraph 8 as follows:




535. Copy construction without a copy constructor

Section: 12.8  [class.copy]     Status: DRWP     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 (12.8 [class.copy] 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, 12.8 [class.copy] 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 (clause 11 [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 3.2 [basic.def.odr] paragraph 2 as follows:

  2. ...[Note: This covers calls to named functions (5.2.2 [expr.call]), operator overloading (Clause 13 [over]), user-defined conversions (12.3.2 [class.conv.fct]), allocation function for placement new (5.3.4 [expr.new]), as well as non-default initialization (8.5 [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 (12.8 [class.copy]). —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 12.8 [class.copy]. A move-assignment function for a class is odr-used by an implicitly-defined move-assignment function for another class as specified in 12.8 [class.copy]. A default constructor...
  3. Delete 12.1 [class.ctor] paragraph 9:

  4. A copy constructor (12.8 [class.copy]) is used to copy objects of class type. A move constructor (12.8 [class.copy]) is used to move the contents of objects of class type.

  5. Change 12.2 [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 (Clause 11 [class.access]) and whether the function is deleted (8.4.3 [dcl.fct.def.delete]), shall be satisfied. this includes accessibility (11 [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 12.8 [class.copy] 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 (3.2 [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 8.5 [dcl.init] for more details on direct and copy initialization. —end footnote] or when it is explicitly defaulted...

  9. Change 12.8 [class.copy] 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 13.3.3.1.2 [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 15.1 [except.throw] paragraph 3 as follows:

  14. A throw-expression copy-initializes (8.5 [dcl.init]) a temporary object, called the exception object...
  15. Change 15.1 [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 (12.8 [class.copy]).

[Drafting note: 5.17 [expr.ass] paragraph 4, 9 [class] paragraph 4, 9.5 [class.union] paragraph 1, 12.2 [class.temporary] paragraph 2, 12.8 [class.copy] paragraphs 1-2, and 15.4 [except.spec] paragraph 14 do not require any changes.]




1350. Incorrect exception specification for inherited constructors

Section: 12.9  [class.inhctor]     Status: DRWP     Submitter:     Date: 2011-08-16

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

According to 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 12.9 [class.inhctor] paragraph 2:

  2. Change 12.9 [class.inhctor] paragraph 3 as follows:

  3. ...[Note: Default arguments are not inherited. An exception-specification is implied as specified in 15.4 [except.spec].end note]
  4. Change 15.4 [except.spec] paragraph 14 as follows:

  5. An inheriting constructor (12.9 [class.inhctor]) and an implicitly declared special member function (Clause 12 [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:...



1385. Syntactic forms of conversion functions for surrogate call functions

Section: 13.3.1.2  [over.match.oper]     Status: DRWP     Submitter: Daniel Krügler     Date: 2011-08-31

[Moved to DR at the October, 2012 meeting.]

In 13.3.1.1.2 [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 13.3.1.1.2 [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...




1392. Explicit conversion functions for references and non-references

Section: 13.3.1.6  [over.match.ref]     Status: DRWP     Submitter: Jason Merrill     Date: 2011-09-08

[Moved to DR at the October, 2012 meeting.]

In 13.3.1.5 [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, 13.3.1.6 [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 13.3.1.6 [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: 13.3.3.1.5  [over.ics.list]     Status: DRWP     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 13.3.3.1.5 [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 13.3.3.2 [over.ics.rank] paragraph 3 bullet 2 unncessarily unclear.

Proposed resolution (February, 2012):

Change 13.3.3.1.5 [over.ics.list] paragraphs 3-4 as follows:

Otherwise, if the parameter is a non-aggregate class X and overload resolution per 13.3.1.7 [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 (8.5.1 [dcl.init.aggr]), the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion sequence an identity conversion. [Example:...




1298. Incorrect example in overload resolution

Section: 13.3.3.2  [over.ics.rank]     Status: DRWP     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 13.3.3.2 [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 14.8.2.1 [temp.deduct.call] paragraph 3 and the reference-collapsing rules of 8.3.2 [dcl.ref] paragraph 6, the parameter type for both will be void(&)().

Proposed resolution (August, 2011):

Change the example in 13.3.3.2 [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



1408. What is “the same aggregate initialization?”

Section: 13.3.3.2  [over.ics.rank]     Status: DRWP     Submitter: Sebastian Redl     Date: 2011-10-24

[Moved to DR at the October, 2012 meeting.]

Bullet 2 of 13.3.3.2 [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 13.3.3.2 [over.ics.rank] paragraph 3 bullet 2 as follows:




1410. Reference overload tiebreakers should apply to rvalue references

Section: 13.3.3.2  [over.ics.rank]     Status: DRWP     Submitter: Michael Wong     Date: 2011-10-26

[Moved to DR at the October, 2012 meeting.]

In bullet 3 of paragraph 4 of 13.3.3.2 [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 13.3.3.2 [over.ics.rank] paragraph 4 bullet 3 as follows:




1275. Incorrect comment in example of template parameter pack restriction

Section: 14.1  [temp.param]     Status: DRWP     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 14.1 [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 14.8.1 [temp.arg.explicit] paragraph 3:

A trailing template parameter pack (14.5.3 [temp.variadic]) not otherwise deduced will be deduced to an empty sequence of template arguments.

Proposed resolution (August, 2011):

Change 14.1 [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 (14.8.2 [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: 14.3.2  [temp.arg.nontype]     Status: DRWP     Submitter: Daniel Krügler     Date: 2011-09-27

[Moved to DR at the October, 2012 meeting.]

Although 14.1 [temp.param] paragraph 4 explicitly allows non-type template parameters of type std::nullptr_t, they cannot actually be used: 14.3.2 [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 14.3.2 [temp.arg.nontype] paragraph 1:




1321. Equivalency of dependent calls

Section: 14.5.6.1  [temp.over.link]     Status: DRWP     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 14.5.6.1 [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 (3.2 [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 3.2 [basic.def.odr] paragraph 5 says,

in each definition of D, corresponding names, looked up according to 3.4 [basic.lookup], shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (13.3 [over.match]) and after matching of partial template specialization (14.8.3 [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 (5.19 [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 14.5.6.1 [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 (3.2 [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 (14.6.2 [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 14.6.2 [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 14.6.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 (3.4.1 [basic.lookup.unqual], 3.4.2 [basic.lookup.argdep], 3.4.3 [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: 14.5.6.2  [temp.func.order]     Status: DRWP     Submitter: Richard Smith     Date: 2011-10-21

[Moved to DR at the October, 2012 meeting.]

In describing the partial ordering of function templates, 14.5.6.2 [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 14.5.6.2 [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 (14.5.3 [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: 14.6  [temp.res]     Status: DRWP     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 14.6 [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 14.6 [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...



1227. Mixing immediate and non-immediate contexts in deduction failure

Section: 14.8.2  [temp.deduct]     Status: DRWP     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 14.8.2 [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: 14.8.2  [temp.deduct]     Status: DRWP     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 14.8.2 [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:...



1388. Missing non-deduced context following a function parameter pack

Section: 14.8.2.1  [temp.deduct.call]     Status: DRWP     Submitter: James Widman     Date: 2011-09-02

[Moved to DR at the October, 2012 meeting.]

Presumably 14.8.2.5 [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 14.8.2.1 [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 (14.8.2.5 [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: 14.8.2.1  [temp.deduct.call]     Status: DRWP     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: 14.8.2.3  [temp.deduct.conv]     Status: DRWP     Submitter: Michael Wong     Date: 2011-08-15

[Moved to DR at the October, 2012 meeting.]

According to 14.8.2.3 [temp.deduct.conv] paragraph 1,

Template argument deduction is done by comparing the return type of the conversion function template (call it P; see 8.5 [dcl.init], 13.3.1.5 [over.match.conv], and 13.3.1.6 [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 14.8.2.5 [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 14.8.2.3 [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 8.5 [dcl.init], 13.3.1.5 [over.match.conv], and 13.3.1.6 [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 8.5 [dcl.init], 13.3.1.5 [over.match.conv], and 13.3.1.6 [over.match.ref] for the determination of that type) as described in 14.8.2.5 [temp.deduct.type].



1387. Missing non-deduced context for decltype

Section: 14.8.2.5  [temp.deduct.type]     Status: DRWP     Submitter: James Widman     Date: 2011-09-02

[Moved to DR at the October, 2012 meeting.]

Presumably 14.8.2.5 [temp.deduct.type] paragraph 5 should include a bullet for a decltype-specifier whose expression references a template parameter.

Proposed resolution (February, 2012):

Change 14.8.2.5 [temp.deduct.type] paragraph 5 as follows:

The non-deduced contexts are:




1431. Exceptions from other than throw-expressions

Section: 15  [except]     Status: DRWP     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 3.7.4.1 [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 (15.1 [except.throw]) of a type that would match a handler (15.3 [except.handle]) of type std::bad_alloc (18.6.2.1 [bad.alloc]).
  3. Change 3.7.4.1 [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 (3.7.1 [basic.stc.static]), for objects or references with thread storage duration (3.7.2 [basic.stc.thread]), for objects of type std::type_info (5.2.8 [expr.typeid]), or for the copy of an object thrown by a throw expression an exception object (15.1 [except.throw]). —end note]
  5. Change 5.2.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 (15.1 [except.throw]) of a type that would match a handler (15.3 [except.handle]) of type std::bad_cast (18.7.2 [bad.cast]).
  7. Change 5.2.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 (4.10 [conv.ptr]), the typeid expression throws the an exception (15.1 [except.throw]) of a type that would match a handler of type std::bad_typeid exception (18.7.3 [bad.typeid]).
  9. Change 5.3.4 [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 (15.1 [except.throw]) of a type that would match a handler (15.3 [except.handle]) of type std::bad_array_new_length (18.6.2.2 [new.badlength]).
  11. Change 15 [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 15 [except] paragraph 2 as follows:

  14. A try-block is a statement (Clause 6 [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 15.1 [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 (3.7.4.1 [basic.stc.dynamic.allocation]), dynamic_cast (5.2.7 [expr.dynamic.cast]), typeid (5.2.8 [expr.typeid]), new-expression (5.3.4 [expr.new]), and standard library functions (17.5.1.4 [structure.specifications]). —end note] An object is passed and the type of that object determines which handlers can catch it. [Example:...
  17. Change 15.1 [except.throw] paragraph 3 as follows:

  18. A throw-expression Throwing an exception copy-initializes (8.5 [dcl.init], 12.8 [class.copy]) 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 (15.3 [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 15.3 [except.handle], the operand of throw is treated exactly as a function argument in a call (5.2.2 [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 15.1 [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 18.8.5 [propagation] and 30.6 [futures]. —end note]
  21. Change 15.1 [except.throw] paragraph 8 as follows:

  22. A throw-expression with no operand rethrows the currently handled exception (15.3 [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 15.2 [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 15.2 [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 15.3 [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 18.8.3.4 [terminate] paragraph 1 as follows:

  30. Remarks: Called by the implementation when exception handling must be abandoned for any of several reasons (15.5.1 [except.terminate]), in effect immediately after evaluating the throw-expression (18.8.3.1 [terminate.handler]) throwing the exception. May also be called directly by the program.



388. Catching base*& from a throw of derived*

Section: 15.3  [except.handle]     Status: DRWP     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 15.3 [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 15.3 [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: 15.3  [except.handle]     Status: DRWP     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 15.3 [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: 15.4  [except.spec]     Status: DRWP     Submitter: Michael Wong     Date: 2011-03-21

[Moved to DR at the October, 2012 meeting.]

The types that may appear in an exception-specification (15.4 [except.spec] paragraph 2) include rvalue reference types, although they are excluded as handler types (15.3 [except.handle] paragraph 1). This appears to have been an oversight.

Proposed resolution (February, 2012):

Change 15.4 [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: 15.4  [except.spec]     Status: DRWP     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 15.4 [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 15.4 [except.spec] paragraph 14 as follows:

An implicitly declared special member function (Clause 12 [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: 15.4  [except.spec]     Status: DRWP     Submitter: David Svoboda     Date: 2011-08-26

[Moved to DR at the October, 2012 meeting.]

According to 15.4 [except.spec] paragraph 14,

An implicitly declared special member function (Clause 12 [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 15.4 [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: 16.3  [cpp.replace]     Status: DRWP     Submitter: Nikolay Ivchenkov     Date: 2011-08-14

[Moved to DR at the October, 2012 meeting.]

16.3 [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 16 [cpp] paragraph 1.

Proposed resolution (February, 2012):

Change 16.3 [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: B  [implimits]     Status: DRWP     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 B [implimits] as follows:




1251. C compatibility: casting to unqualified void*

Section: C.1.3  [diff.conv]     Status: DRWP     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 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*





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).




357. Definition of signature should include name

Section: 1.3  [intro.defs]     Status: CD1     Submitter: Steve Clamage     Date: 26 May 2002

[Voted into WP at April, 2007 meeting.]

Section 1.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, 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 13 [over]. It is used in linkage and declaration matching (e.g., 14.5.6.1 [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 1.3 [intro.defs] “signature” with the following:

  2. the name and the parameter-type-list (8.3.5 [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 14.5.6.1 [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 1.3 [intro.defs]. The names of the template parameters are significant...

(See also issue 537.)




537. Definition of “signature”

Section: 1.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: 1.3 [intro.defs] and 14.5.6.1 [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 1.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 1.3 [intro.defs] the definition states that “Function signatures do not include return type, because that does not participate in overload resolution,” while 14.5.6.1 [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 13.3 [over.match]. 14.5.6.1 [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.




513. Non-class “most-derived” objects

Section: 1.8  [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, 1.3 [intro.defs] “dynamic type,” 5.3.5 [expr.delete]) to refer to objects of both class and non-class type. However, 1.8 [intro.object] only formally defines it for objects of class type.

Possible fix: Change the wording in 1.8 [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 1.8 [intro.object] paragraph 4:

If a complete object, a data member (9.2 [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.



637. Sequencing rules and example disagree

Section: 1.9  [intro.execution]     Status: CD1     Submitter: Ofer Porat     Date: 2 June 2007

[Voted into the WP at the September, 2008 meeting.]

In 1.9 [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 (5.17 [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 1.9 [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



639. What makes side effects “different” from one another?

Section: 1.9  [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;
    }

1.9 [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 1.9 [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...




362. Order of initialization in instantiation units

Section: 2.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 2.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 3.6.2 [basic.start.init] 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: 2.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 2.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: 2.14.3  [lex.ccon]     Status: CD1     Submitter: Mike Miller     Date: 14 Apr 2005

[Voted into WP at the October, 2006 meeting.]

The current wording of 2.14.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 2.14.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: 3  [basic]     Status: CD1     Submitter: Mike Miller     Date: 17 Sep 2001

[Voted into the WP at the June, 2008 meeting.]

3 [basic] paragraph 8, 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 (3.5 [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: 3  [basic]     Status: CD1     Submitter: Gabriel Dos Reis     Date: 9 Nov 2004

[Voted into the WP at the June, 2008 meeting.]

Clause 3 [basic] paragraph 4 says:

A name is a use of an identifier (2.11 [lex.name]) that denotes an entity or label (6.6.4 [stmt.goto], 6.1 [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 3.4.2 [basic.lookup.argdep] paragraph 1,

When an unqualified name is used as the postfix-expression in a function call (5.2.2 [expr.call]), other namespaces not considered during the usual unqualified lookup (3.4.1 [basic.lookup.unqual]) may be searched, and in those namespaces, namespace-scope friend function declarations (11.3 [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 clause 3 [basic] 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 (2.11 [lex.name]), operator-function-id (13.5 [over.oper]), conversion-function-id (12.3.2 [class.conv.fct]), or template-id (14.2 [temp.names]) that denotes an entity or label (6.6.4 [stmt.goto], 6.1 [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 (6.6.4 [stmt.goto]) or a labeled-statement (6.1 [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 (3.4 [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 (14.4 [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 (3.5 [basic.link]) of the identifier name specified in each translation unit.

  2. Change 3.3.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 (8.3.6 [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: 3.2  [basic.def.odr]     Status: CD1     Submitter: Mike Miller     Date: 7 Nov 2000

[Moved to DR at October 2002 meeting.]

3.2 [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. 3.2 [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 (12.4 [class.dtor])" to the specification in 3.2 [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 3.2 [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 5.3.4 [expr.new] and 12.5 [class.free]. A deallocation function for a class is used by a delete expression appearing in a potentially-evaluated expression as specified in 5.3.5 [expr.delete] and 12.5 [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 (12.4 [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: 3.2  [basic.def.odr]     Status: CD1     Submitter: Mike Miller     Date: 25 May 2001

[Moved to DR at October 2002 meeting.]

3.2 [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 3.2 [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: 3.3.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 3.3.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: 3.3.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.

9 [class]/2 says that the injected class name is "inserted into the scope of the class."

3.3.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 3.3.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 3.3.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 9 [class]) is immediately following the opening brace of the class definition.

(Note that this change overlaps a change in issue 417.)

Also change 3.3.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 9 [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).




139. Error in friend lookup example

Section: 3.4.1  [basic.lookup.unqual]     Status: CD1     Submitter: Mike Miller     Date: 14 Jul 1999

[Moved to DR at 10/01 meeting.]

The example in 3.4.1 [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 3.4.1 [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: 3.4.1  [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 3.4.1 [basic.lookup.unqual] paragraph 12 and 8.5 [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 3.4.1 [basic.lookup.unqual] may be instructive:

A name used in the definition of a static data member of class X (9.4.2 [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 (3.4.1 [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 3.4.1 [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: 3.4.2  [basic.lookup.argdep]     Status: CD1     Submitter: Mike Miller     Date: 21 Jul 1999

[Moved to DR at 4/02 meeting.]

Paragraphs 1 and 2 of 3.4.2 [basic.lookup.argdep] say, in part,

When an unqualified name is used as the postfix-expression in a function call (5.2.2 [expr.call] )... namespace-scope friend function declarations (11.3 [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 3.4.2 [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 3.4.2 [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 3.4.2 [basic.lookup.argdep], change

    When an unqualified name is used as the postfix-expression in a function call (5.2.2 [expr.call]), other namespaces not considered during the usual unqualified lookup (3.4.1 [basic.lookup.unqual]) may be searched, and namespace-scope friend function declarations (11.3 [class.friend]) not otherwise visible may be found.

    to

    When an unqualified name is used as the postfix-expression in a function call (5.2.2 [expr.call]), other namespaces not considered during the usual unqualified lookup (3.4.1 [basic.lookup.unqual]) may be searched, and in those namespaces, namespace-scope friend function declarations (11.3 [class.friend]) not otherwise visible may be found.
  2. In 3.4.2 [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: 3.4.2  [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 (3.4.1 [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 (3.4.2 [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 3.4.2 [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 (7.3.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 3.4.2 [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. 3.4 [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 5.2.2 [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 3.4 [basic.lookup]). I suspect that a clarification of 5.2.2 [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 3.4.2 [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 (3.4.1 [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 3.4.1 [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 (3.4.3.2 [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.3 [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: 3.4.2  [basic.lookup.argdep]     Status: CD1     Submitter: John Spicer     Date: 18 Sep 2003

[Voted into WP at March 2004 meeting.]

Spun off from issue 384.

3.4.2 [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 3.4.2 [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: 3.4.2  [basic.lookup.argdep]     Status: CD1     Submitter: Mike Miller     Date: 8 February 2006

[Voted into WP at the October, 2006 meeting.]

One might assume from 14.7.1 [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 (14.7.2 [temp.explicit]) or explicitly specialized (14.7.3 [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 3.4.2 [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 3.4.2 [basic.lookup.argdep] paragraph 2 as indicated:

  2. Delete bullet 8 of 3.4.2 [basic.lookup.argdep] paragraph 2:




298. T::x when T is cv-qualified

Section: 3.4.3.1  [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 3.4.3.1 [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 (10.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 (clause 10 [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 7.1.3 [dcl.typedef] paragraph 4:

Argument and text removed from proposed resolution (October 2002):

7.1.3 [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 (3.5 [basic.link]). [Example: ...

Proposed resolution (October 2002):

3.4.4 [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.

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 (9.1 [class.name]). Except as the identifier in the declarator for a constructor or destructor definition outside of a class member-specification (12.1 [class.ctor], 12.4 [class.dtor]), a typedef-name that names a class may be used in a qualified-id to refer to a constructor or destructor. ]

7.1.3 [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 9.1 [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 (9.1 [class.name]). If a typedef-name is used following the class-key in an elaborated-type-specifier (7.1.6.3 [dcl.type.elab]), or in the class-head of a class declaration (9 [class]), or is used as the identifier in the declarator for a constructor or destructor declaration (12.1 [class.ctor], 12.4 [class.dtor]), to identify the subject of an elaborated-type-specifier (7.1.6.3 [dcl.type.elab]), class declaration (clause 9 [class]), constructor declaration (12.1 [class.ctor]), or destructor declaration (12.4 [class.dtor]), the program is ill-formed. ] [Example: ...

7.1.6.3 [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: ...

8 [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.

9.1 [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 7.1.3 [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 9.1 [class.name].")

A typedef-name (7.1.3 [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 7.1.3 [dcl.typedef]. as the identifier in a class-head.

12.1 [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 (7.1.3 [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.

12.4 [class.dtor] paragraph 1:

The same comments apply here as to 12.1 [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: 3.4.3.1  [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 3.4.3.1 [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 9 [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 9 [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: 3.4.3.2  [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. 7.3.3 [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.

3.4.3.2 [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. 7.3.3 [namespace.udecl] paragraph 10 has an example for namespaces.

Proposed resolution (October 2003):

Add a bullet to the end of 3.4.3.1 [class.qual] paragraph 1:

Change the beginning of 7.3.3 [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 (10.2 [class.member.lookup], 3.4.3.1 [class.qual]).



245. Name lookup in elaborated-type-specifiers

Section: 3.4.4  [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 3.4.4 [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 3.3.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 3.4.4 [basic.lookup.elab]. Note that the section does not make finding a type template formal ill-formed, as is done in 7.1.6.3 [dcl.type.elab]. I don't see anything that makes a type template formal name a class-name. So the example in 7.1.6.3 [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: 3.4.4  [basic.lookup.elab]     Status: CD1     Submitter: Clark Nelson     Date: 26 Oct 2000

[Voted into WP at April 2003 meeting.]

  1. The text in 3.4.4 [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 3.4.4 [basic.lookup.elab] and 7.1.6.3 [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.




141. Non-member function templates in member access expressions

Section: 3.4.5  [basic.lookup.classref]     Status: CD1     Submitter: fvali     Date: 31 July 1999

[Voted into the WP at the June, 2008 meeting.]

3.4.5 [basic.lookup.classref] paragraph 1 says,

In a class member access expression (5.2.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 (14.2 [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 3.4.5 [basic.lookup.classref] paragraph 1 as follows:

In a class member access expression (5.2.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 (14.2 [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: 3.4.5  [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 3.4.5 [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 3.4.5 [basic.lookup.classref] paragraph 2:

    If the id-expression in a class member access (5.2.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 3.4.5 [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: 3.4.5  [basic.lookup.classref]     Status: CD1     Submitter: Steve Adamczyk     Date: 8 Nov 2002

[Voted into WP at October 2004 meeting.]

The example in 3.4.5 [basic.lookup.classref] paragraph 4 is wrong (see 11.2 [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 3.4.5 [basic.lookup.classref] paragraph 4, including the entire example.




414. Multiple types found on destructor lookup

Section: 3.4.5  [basic.lookup.classref]     Status: CD1     Submitter: John Spicer     Date: 1 May 2003

[Voted into WP at the October, 2006 meeting.]

By 3.4.5 [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.




216. Linkage of nameless class-scope enumeration types

Section: 3.5  [basic.link]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 13 Mar 2000

[Moved to DR at 10/01 meeting.]

3.5 [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, 3.5 [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 3.5 [basic.link] paragraph 5, but I would like confirmation on that.

Proposed resolution:

Change text in 3.5 [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 (7.1.3 [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: 3.5  [basic.link]     Status: CD1     Submitter: Clark Nelson     Date: 29 Oct 2001

[Voted into WP at October 2004 meeting.]

According to 3.5 [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 3.5 [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 14.6.2.1 [temp.dep.type].

Add the following text at the end of Paragraph 8 of Section 3.5 [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 (5.19 [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 14.3.1 [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 14.3.1 [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 14.3.1 [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 (3.9 [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: 3.5  [basic.link]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 31 Oct 2002

[Voted into WP at October 2004 meeting.]

3.5 [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 (3.3.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 3.5 [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 3.5 [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 3.5 [basic.link] paragraph 8, change

A name with no linkage (notably, the name of a class or enumeration declared in a local scope (3.3.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 (7.5 [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 14.3.1 [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 (3.5 [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: 3.5  [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 3.5 [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 7.3.1 [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 7.3.1.2 [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]




270. Order of initialization of static data members of class templates

Section: 3.6.2  [basic.start.init]     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 3.6.2 [basic.start.init] or 9.4.2 [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 3.6.2 [basic.start.init] 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 3.6.2 [basic.start.init] 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: 3.6.2  [basic.start.init]     Status: CD1     Submitter: Mike Miller     Date: 1 Dec 2003

[Voted into WP at April 2005 meeting.]

I have a couple of questions about 3.6.2 [basic.start.init], "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 5.19 [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 3.6.2 [basic.start.init] 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 3.6.2 [basic.start.init] 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 3.6.2 [basic.start.init] 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 3.6.2 [basic.start.init] 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: 3.6.2  [basic.start.init]     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” (3.6.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 (5.19 [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 3.6.2 [basic.start.init] 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 (5.19 [expr.const]); this If a reference with static storage duration is initialized with a constant expression (5.19 [expr.const]) or if the initialization of an object with static storage duration satisfies the requirements for the object being declared with constexpr (7.1.5 [dcl.constexpr]), that initialization is called constant initialization...
  3. Change 6.7 [stmt.dcl] paragraph 4 as follows:

  4. ...A local object of trivial or literal type (3.9 [basic.types]) with static storage duration initialized with constant-expressions is initialized Constant initialization (3.6.2 [basic.start.init]) of a local entity with static storage duration is performed before its block is first entered...
  5. Change 7.1.5 [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 (8.5 [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 (5.19 [expr.const])...
  7. Replace 8.5.1 [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 (3.6.2 [basic.start.init]); 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 3.6.2 [basic.start.init] and 6.7 [stmt.dcl]. —end note]

(Note: the change to 3.6.2 [basic.start.init] paragraph 1 needs to be reconciled with the conflicting change in issue 684.)




28. 'exit', 'signal' and static object destruction

Section: 3.6.3  [basic.start.term]     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” (18.10 [support.runtime] paragraph 6).

Proposed Resolution (November, 2006):

Change the footnote in 18.10 [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 1.9 [intro.execution]).



521. Requirements for exceptions thrown by allocation functions

Section: 3.7.4.1  [basic.stc.dynamic.allocation]     Status: CD1     Submitter: Alisdair Meredith     Date: 22 May 2005

[Voted into WP at the October, 2006 meeting.]

According to 3.7.4.1 [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 (18.6.2.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 3.7.4.1 [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 (18.6.2.1 [bad.alloc]) or a class derived from std::bad_alloc a type that would match a handler (15.3 [except.handle]) of type std::bad_alloc (18.6.2.1 [bad.alloc]).



220. All deallocation functions should be required not to throw

Section: 3.7.4.2  [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 17.6.4.8 [res.on.functions] paragraph 2, as applied to 18.6.1.1 [new.delete.single] paragraph 12 and 18.6.1.2 [new.delete.array] paragraph 11).
  2. Section 17.6.4.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 3.7.4.2 [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: 3.7.4.2  [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 (5.3.5 [expr.delete]). If it is, standard does not specify what user-supplied deallocation function shall do with the null pointer operand (18.6.1 [new.delete]). Instead, Standard uses the term "has no effect", which meaning is too vague in context given (5.3.5 [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. 5.3.5 [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 5.3.5 [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 (3.7.4.2 [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 3.7.4.2 [basic.stc.dynamic.deallocation], or in 18.6.1.1 [new.delete.single] and 18.6.1.2 [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 (18.6.1.1 [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 5.3.5 [expr.delete]).
  2. Specify what user deallocation function shall do when null is given (either in 3.7.4.2 [basic.stc.dynamic.deallocation], or in 18.6.1.1 [new.delete.single], and 18.6.1.2 [new.delete.array]).

Notes from October 2002 meeting:

We believe that study of 18.6.1.1 [new.delete.single] paragraphs 12 and 13, 18.6.1.2 [new.delete.array] paragraphs 11 and 12, and 3.7.4.2 [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.

5.3.5 [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 3.7.4.2 [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 5.3.5 [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 (1.8 [intro.object]) representing a base class of such an object (clause 10 [class.derived])...
  2. Change 5.3.5 [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 (3.7.4.2 [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 5.3.5 [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 12.6.2 [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 (3.7.4.2 [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 3.7.4.2 [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.]




119. Object lifetime and aggregate initialization

Section: 3.8  [basic.life]     Status: CD1     Submitter: Jack Rouse     Date: 20 May 1999

[Moved to DR at 4/02 meeting.]

Jack Rouse: 3.8 [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 (8.5.1 [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 3.8 [basic.life] paragraph 1, change

If T is a class type with a non-trivial constructor (12.1 [class.ctor]), the constructor call has completed.

to

If T is a class type with a non-trivial constructor (12.1 [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 (8.5.1 [dcl.init.aggr]).]



274. Cv-qualification and char-alias access to out-of-lifetime objects

Section: 3.8  [basic.life]     Status: CD1     Submitter: Mike Miller     Date: 14 Mar 2001

[Voted into WP at April 2003 meeting.]

The wording in 3.8 [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 3.8 [basic.life] paragraph 6 change the third bullet:

to read:




404. Unclear reference to construction with non-trivial constructor

Section: 3.8  [basic.life]     Status: CD1     Submitter: Mike Miller     Date: 8 Apr 2003

[Voted into WP at March 2004 meeting.]

3.8 [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: 3.8  [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 3.8 [basic.life] paragraph 1 reads,

if T is a class type with a non-trivial constructor (12.1 [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 (12.1 [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 (12.1 [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 (12.1 [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 (12.1 [class.ctor]), the principal constructor call 12.6.2 [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” (15.2 [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 3.8 [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...




644. Should a trivial class type be a literal type?

Section: 3.9  [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 3.9 [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 3.9 [basic.types] paragraph 10 as follows:

A type is a literal type if it is:



158. Aliasing and qualification conversions

Section: 3.10  [basic.lval]     Status: CD1     Submitter: Mike Stump     Date: 20 Aug 1999

[Moved to DR at 4/02 meeting.]

3.10 [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, 4.4 [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 3.10 [basic.lval] to include all possible conversions of the type via 4.4 [conv.qual].

Proposed resolution (04/01):

Add a new bullet to 3.10 [basic.lval] paragraph 15, following "a cv-qualified version of the dynamic type of the object:"




649. Optionally ill-formed extended alignment requests

Section: 3.11  [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: 3.11 [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, 7.6.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 3.11 [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]



519. Null pointer preservation in void* conversions

Section: 4.10  [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++ (4.10 [conv.ptr] paragraph 2) says nothing about the value of v.

    void *v = 0;
    A *b = (A*)v; // aka static_cast<A*>(v)

C++ (5.2.9 [expr.static.cast] paragraph 10) says nothing about the value of b.

Suggested changes:

  1. Add the following sentence to 4.10 [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 5.2.9 [expr.static.cast] paragraph 10:

  4. The null pointer value (4.10 [conv.ptr]) is converted to the null pointer value of the destination type.

Proposed resolution (October, 2005):

  1. Add the indicated words to 4.10 [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 (1.8 [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 5.2.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: 4.10  [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: 4.11  [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 4.11 [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 4.11 [conv.mem] paragraph 2 as indicated:

  2. ...If B is an inaccessible (clause 11 [class.access]), ambiguous (10.2 [class.member.lookup]) or virtual (10.1 [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 5.2.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 5.2.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...



222. Sequence points and lvalue-returning operators

Section: 5  [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?

5.17 [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 5.17 [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 5.3.2 [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):

See issue 637.




351. Sequence point error: unspecified or undefined?

Section: 5  [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 5 [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 5 [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: 5  [expr]     Status: CD1     Submitter: Gennaro Prota     Date: 19 Jan 2004

[Voted into WP at October 2005 meeting.]

Clause 5 [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 5 [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 (5.19 [expr.const]), in which case the program is ill-formed.



122. template-ids as unqualified-ids

Section: 5.1.1  [expr.prim.general]     Status: CD1     Submitter: Mike Miller     Date: 3 June 1999

[Moved to DR at 10/01 meeting.]

5.1.1 [expr.prim.general] paragraph 11 reads,

A template-id shall be used as an unqualified-id only as specified in 14.7.2 [temp.explicit] , 14.7 [temp.spec] , and 14.5.5 [temp.class.spec] .

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 14.8.1 [temp.arg.explicit], "Explicit template argument specification?" Does its absence from the list in 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: 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 (7.1.6.2 [dcl.type.simple] paragra 1) and an init-declararator (8 [dcl.decl] paragraph 1) contain an optional :: and an optional nested-name-specifier (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 7.1 [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 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 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.




113. Visibility of called function

Section: 5.2.2  [expr.call]     Status: CD1     Submitter: Christophe de Dinechin     Date: 5 May 1999

[Moved to DR at 10/01 meeting.]

Christophe de Dinechin: In 5.2.2 [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 (3.4 [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 5.2.2 [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 (3.4 [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: 5.2.2  [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 5.2.2 [expr.call] paragraph 1:

The function called in a member function call is normally selected according to the static type of the object expression (clause 10 [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 (10.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 5.5 [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, 5.5 [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 5.2.2 [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 5.2.2 [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 (9.3.1 [class.mfct.non-static] , 9.4 [class.static] ) or explicit class member access (5.2.5 [expr.ref] ) whose id-expression is a function member name, or a pointer-to-member expression (5.5 [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 5.2.2 [expr.call] paragraph 1 as follows:

    ... For a member function call, the postfix expression shall be an implicit (9.3.1 [class.mfct.non-static], 9.4 [class.static]) or explicit class member access (5.2.5 [expr.ref]) whose id-expression is a function member name, or a pointer-to-member expression (5.5 [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 (5.2.5 [expr.ref], 5.5 [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 9.3.1 [class.mfct.non-static]). —end note] If a function or member function name is used, the name can be overloaded (clause 13 [over]), in which case the appropriate function shall be selected according to the rules in 13.3 [over.match]. The function called in a member function call is normally selected according to the static type of the object expression (clause 10 [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 (10.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 (10.3 [class.virtual]) in the dynamic type of the object expression is called. ...
  2. Change 5.5 [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: 5.2.2  [expr.call]     Status: CD1     Submitter: Mike Miller     Date: 14 Apr 2005

[Voted into WP at the October, 2006 meeting.]

The current wording of 5.2.2 [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 (18.10 [support.runtime]). The lvalue-to-rvalue (4.1 [conv.lval]), array-to-pointer (4.2 [conv.array]), and function-to-pointer (4.3 [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 9 [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 5.2.2 [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: 5.2.2  [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 5.2.2 [expr.call] paragraph 7 as follows:

...Passing an a potentially-evaluated argument of non-trivial class type (clause 9 [class]) with no corresponding parameter is conditionally-supported, with implementation-defined semantics...



466. cv-qualifiers on pseudo-destructor type

Section: 5.2.4  [expr.pseudo]     Status: CD1     Submitter: Mark Mitchell     Date: 18 Mar 2004

[Voted into WP at April, 2006 meeting.]

5.2.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 5.2.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.



421. Is rvalue.field an rvalue?

Section: 5.2.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 5.2.5 [expr.ref] paragraph 4 bullet 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 5.2.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: 5.2.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 5.2.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 5.2.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: 5.2.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 4.10 [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 5.2.9 [expr.static.cast]/6:

The inverse of any standard conversion sequence (clause 4 [conv]), other than the lvalue-to-rvalue (4.1 [conv.lval]), array-to-pointer (4.2 [conv.array]), function-to-pointer (4.3 [conv.func]), and boolean (4.12 [conv.bool]) conversions, can be performed explicitly using static_cast. The lvalue-to-rvalue (4.1 [conv.lval]), array-to-pointer (4.2 [conv.array]), and function-to-pointer (4.3 [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 (5.2.11 [expr.const.cast]), and the following additional rules for specific cases:

with two paragraphs:

The inverse of any standard conversion sequence (clause 4 [conv]), other than the lvalue-to-rvalue (4.1 [conv.lval]), array-to-pointer (4.2 [conv.array]), function-to-pointer (4.3 [conv.func]), and boolean (4.12 [conv.bool]) 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 (4.1 [conv.lval]), array-to-pointer (4.2 [conv.array]), and function-to-pointer (4.3 [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 (5.2.11 [expr.const.cast]), and the following additional rules for specific cases:

In addition, modify the second sentence of 5.4 [expr.cast]/5. The first two sentences of 5.4 [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 5.4 [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: 5.2.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 5.2.9 [expr.static.cast]/5; the second is 5.2.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 5.2.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 (clause 10 [class.derived]) from B, if a valid standard conversion from ``pointer to D'' to ``pointer to B'' exists (4.10 [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 5.2.9 [expr.static.cast]/2.

Insert Otherwise, before the text of paragraph 5.2.9 [expr.static.cast]/2 (which will become 5.2.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 (8.5 [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 (8.3.2 [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: 5.2.9  [expr.static.cast]     Status: CD1     Submitter: Mark Mitchell     Date: 30 Oct 2003

[Voted into WP at April 2005 meeting.]

Paragraph 5.2.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 5.2.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 5.2.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: 5.2.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 (4.5 [conv.prom] and 4.7 [conv.integral]) and 5.2.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 5.2.9 [expr.static.cast] paragraph 8:

A value of a scoped enumeration type (7.2 [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: 5.2.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 5.2.9 [expr.static.cast] and 5.2.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 5.2.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 5.2.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 (8.3.5 [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 4.10 [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 (12.1 [class.ctor]) or conversion functions (12.3 [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 1.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 (1.3 [intro.defs]).

  2. Add the indicated words to 1.4 [intro.compliance] paragraph 2, bullet 2:

  3. Insert the following as a new paragraph following 5.2.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 7.4 [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 7.5 [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 14 [temp] paragraph 4 as indicated:

    A template, a template explicit specialization (14.7.3 [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: 5.2.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: 5.2.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 5.2.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 5.2.10 [expr.reinterpret.cast] paragraph 5 reading,

    Converting an integral constant expression (5.19 [expr.const]) with value zero always yields a null pointer (4.10 [conv.ptr]), but converting other expressions that happen to have value zero need not yield a null pointer.
  2. Add the indicated note to 5.2.10 [expr.reinterpret.cast] paragraph 8:

    The null pointer value (4.10 [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: 5.3.1  [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 5.3.2 [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 5.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 5.17 [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 (5.17 [expr.ass] paragraph 1).

In 5.18 [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):

5.3.1 [expr.unary.op] paragraph 4:

The operand of & shall not be a bit-field.

8.5.3 [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) ...




256. Overflow in size calculations

Section: 5.3.4  [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 B [implimits], and, according to 1.4 [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 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: 5.3.4  [expr.new]     Status: CD1     Submitter: Mark Mitchell     Date: 19 Jul 2001

[Voted into WP at October 2005 meeting.]

In 5.3.4 [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 6.4.2 [stmt.switch] paragraph 2.

Suggested resolution:

In 5.3.4 [expr.new] paragraph 6, replace "integral or enumeration type (3.9.1 [basic.fundamental])" with "integral or enumeration type (3.9.1 [basic.fundamental]), or a class type for which a single conversion function to integral or enumeration type exists".

Proposed resolution (October, 2004):

Change 5.3.4 [expr.new] paragraph 6 as follows:

Every constant-expression in a direct-new-declarator shall be an integral constant expression (5.19 [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 (12.3 [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 5.3.4 [expr.new] paragraph 6 as follows:

Every constant-expression in a direct-new-declarator shall be an integral constant expression (5.19 [expr.const]) and evaluate to a strictly positive value. The expression in a direct-new-declarator shall have integral or enumeration type (3.9.1 [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 (12.3 [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: 5.3.4  [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 15.2 [except.ctor] paragraph 2 that implied a syntax-based interpretation, leaving wording in 5.3.4 [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: 3.7.4.2 [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*)." 3.7.4.1 [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 5.3.4 [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.")

5.3.4 [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 5.3.4 [expr.new] paragraph 19:

If the lookup finds the two-parameter form of a usual deallocation function (3.7.4.2 [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: 5.3.4  [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 5.3.4 [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 5.3.4 [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 (15.3 [except.handle]) of type std::length_error (19.2.4 [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: 5.3.5  [expr.delete]     Status: CD1     Submitter: James Kuyper     Date: 19 May 2001

[Voted into the WP at the June, 2008 meeting.]

For delete expressions, 5.3.5 [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 5.10 [expr.eq] paragraph 2, and 8.1 [dcl.name] paragraph 1.

10.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).

12.5 [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

5.3.5 [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 5.3.5 [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 12.5 [class.free] paragraph 4 as follows:

  4. A similar provision is not needed for the array version of operator delete because 5.3.5 [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 12.5 [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 5.3.5 [expr.delete].

[Drafting notes: No change is required for 10.3 [class.virtual] paragraph 7 because “the type of the pointer” includes the pointed-to type. No change is required for 12.5 [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: 5.3.5  [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 5.3.5 [expr.delete] paragraph 7 the highlighted text:

The delete-expression will call a deallocation function (3.7.4.2 [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: 5.3.5  [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 5.3.5 [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 5.3.5 [expr.delete] paragraph 4 as follows:

If the delete-expression calls the implementation deallocation function (3.7.4.2 [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.




659. Alignment of function types

Section: 5.3.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 (5.3.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 5.3.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 5.3.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.



520. Old-style casts between incomplete class types

Section: 5.4  [expr.cast]     Status: CD1     Submitter: comp.std.c++     Date: 19 May 2005

[Voted into WP at April, 2007 meeting.]

5.4 [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 5.4 [expr.cast] paragraph 6 seems to allow. I believe the intent of 5.4 [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 (4.10 [conv.ptr] paragraph 3) nor static_cast (5.2.9 [expr.static.cast] paragraph 5) talk about incomplete class types. If the committee agrees with me, I would like to see 4.10 [conv.ptr] paragraph 3 and 5.2.9 [expr.static.cast] paragraph 5 explicitly disallow incomplete class types and the wording of 5.4 [expr.cast] paragraph 6 changed to not allow any other interpretation.

Proposed resolution (April, 2006):

Change 5.4 [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: 5.5  [expr.mptr.oper]     Status: CD1     Submitter: Giovanni Bajo     Date: 03 Jan 2005

[Voted into WP at October 2005 meeting.]

5.5 [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 8.5 [dcl.init] paragraph 9.

Proposed resolution (April, 2005):

Change the example in 5.5 [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: 5.6  [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 5 [expr] paragraph 5 causes undefined behavior; however, that is not the “result” of the % operation, so it's not clear. The wording of 5.6 [expr.mul] paragraph 4 appears to allow % to cause undefined behavior only when the second operand is 0.

Proposed resolution (August, 2008):

Change 5.6 [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: 5.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 5.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 5.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: 5.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. 12.2 [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 5.16 [expr.cond] paragraph 3 bullet 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 5.16 [expr.cond] paragraph 6 bullet 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 4.1 [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 (5.3.3 [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: 5.19  [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.

5.19 [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 14.8.2.4 [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: 5.19  [expr.const]     Status: CD1     Submitter: Martin v. Loewis     Date: 29 July 2002

[Voted into WP at October 2003 meeting.]

According to 16.1 [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:

5.19 [expr.const] paragraph 1 says that an integral constant expression may involve literals (2.14 [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, 16.1 [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 5.19 [expr.const] paragraph 1 which currently reads

An integral constant-expression can involve only literals (2.14 [lex.literal]), ...
to say
An integral constant-expression can involve only literals of arithmetic types (2.14 [lex.literal], 3.9.1 [basic.fundamental]), ...




367. throw operator allowed in constant expression?

Section: 5.19  [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 5.19 [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 (8.3.4 [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 5.19 [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: 5.19  [expr.const]     Status: CD1     Submitter: Mark Mitchell     Date: 03 Feb 2004

[Voted into WP at April 2005 meeting.]

I'm looking at 5.19 [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 7.1.6.1 [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 5.19 [expr.const] paragraph 1 as follows:

An integral constant-expression can involve only literals of arithmetic types (2.14 [lex.literal], 3.9.1 [basic.fundamental]), enumerators, non-volatile const variables or static data members of integral or enumeration types initialized with constant expressions (8.5 [dcl.init]), non-type template parameters of integral or enumeration types, and sizeof expressions.



530. Nontype template arguments in constant expressions

Section: 5.19  [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 5.19 [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 5.19 [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 5.19 [expr.const] that was done in conjunction with the constexpr proposal, paper N2235.




684. Constant expressions involving the address of an automatic variable

Section: 5.19  [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 5.19 [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 5.19 [expr.const] paragraph 2:

Proposed resolution (June, 2008):

  1. Change 3.6.2 [basic.start.init] paragraph 1 as follows:

  2. Objects with static storage duration (3.7.1 [basic.stc.static]) or thread storage duration (3.7.2) shall be zero-initialized (8.5 [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 (5.19 [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 (5.19 [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 5.19 [expr.const] paragraph 2:

(Note: the change to 3.6.2 [basic.start.init] paragraph 1 needs to be reconciled with the conflicting change in issue 688.)




276. Order of destruction of parameters and temporaries

Section: 6.6  [stmt.jump]     Status: CD1     Submitter: James Kanze     Date: 28 Mar 2001

[Voted into the WP at the June, 2008 meeting.]

According to 6.6 [stmt.jump] paragraph 2,

On exit from a scope (however accomplished), destructors (12.4 [class.dtor]) are called for all constructed objects with automatic storage duration (3.7.3 [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 5.2.2 [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 6.6 [stmt.jump] paragraph 2 as follows:

On exit from a scope (however accomplished), destructors (12.4 [class.dtor]) are called for all constructed objects with automatic storage duration (3.7.3 [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 (3.7.3 [basic.stc.auto]) that have been constructed in that scope are destroyed in the reverse order of their construction. [Note: For temporaries, see 12.2 [class.temporary]. —end note] Transfer out of a loop...



378. Wording that says temporaries are declared

Section: 6.6  [stmt.jump]     Status: CD1     Submitter: Gennaro Prota     Date: 07 September 2002

Paragraph 6.6 [stmt.jump] paragraph 2 of the standard says:

On exit from a scope (however accomplished), destructors (12.4 [class.dtor]) are called for all constructed objects with automatic storage duration (3.7.3 [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: 7.1.2  [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 7.1.2 [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: 7.1.2  [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 7.1.2 [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 (3.2 [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: 7.1.2  [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 7.1.1 [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 7.1.1 [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 (6.8 [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 (6.8 [stmt.ambig]), which may aid readers. --- end note]




397. Same address for string literals from default arguments in inline functions?

Section: 7.1.2  [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 (7.1.2 [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 7.1.2 [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 7.1.2 [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: 7.1.2  [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 7.1.2 [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 10.3 [class.virtual].

I don't think that excludes a friend declaration (which is a valid member-specification by 9.2 [class.mem]).

John Spicer: I agree that virtual should not be allowed on friend declarations. I think the wording in 7.1.2 [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 7.1.2 [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 10.3 [class.virtual].

The explicit specifier shall be used only in declarations the declaration of constructors a constructor within a its class definition; see 12.3.1 [class.conv.ctor].




424. Wording problem with issue 56 resolution on redeclaring typedefs in class scope

Section: 7.1.3  [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 7.1.3 [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 7.1.3 [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: 7.1.5  [dcl.constexpr]     Status: CD1     Submitter: Mike Miller     Date: 12 Aug 2007

[Voted into the WP at the September, 2008 meeting.]

According to 7.1.5 [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 7.1.5 [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 7.1.5 [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 (7.1.2 [dcl.fct.spec]). A constexpr function shall not be virtual (10.3).
  3. Change 7.1.5 [dcl.constexpr] paragraph 3 as follows:

  4. The definition of a constexpr function shall satisfy the following constraints:

    [Example:...

  5. Change 7.1.5 [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 7.1.5 [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 7.1.5 [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 (9.3.1 [class.mfct.non-static]). [Note: ...



648. Constant expressions in constexpr initializers

Section: 7.1.5  [dcl.constexpr]     Status: CD1     Submitter: Mike Miller     Date: 12 Aug 2007

[Voted into the WP at the September, 2008 meeting.]

The current wording of 7.1.5 [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 (8.5 [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 7.1.5 [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: 7.1.6.2  [dcl.type.simple]     Status: CD1     Submitter: Clark Nelson     Date: 01 May 2001

[Voted into WP at April 2003 meeting.]

Although 14.1 [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 7.1.6.2 [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 14.1 [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 7.1.6.3 [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: 7.1.6.2  [dcl.type.simple]     Status: CD1     Submitter: comp.std.c++     Date: 25 Apr 2005

[Voted into WP at the October, 2006 meeting.]

7.1.6.2 [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 (9.6 [class.bit]).

Proposed resolution (October, 2005):

Change 7.1.6.2 [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 (9.6 [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: 7.1.6.2  [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 7.1.6.2 [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 7.1.6.2 [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 5 [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: 7.1.6.4  [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? 7.1.6.4 [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.




172. Unsigned int as underlying type of enum

Section: 7.2  [dcl.enum]     Status: CD1     Submitter: Bjarne Stroustrup     Date: 26 Sep 1999

[Moved to DR at October 2002 meeting.]

According to 7.2 [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 4.5 [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 (4.5 [conv.prom] paragraph 3).

On a related note, 7.2 [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 7.2 [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 4.5 [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 4.5 [conv.prom] paragraph 2 from

An rvalue of type wchar_t (3.9.1 [basic.fundamental]) or an enumeration type (7.2 [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 (3.9.1 [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 (7.2 [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 7.2 [dcl.enum]): int, unsigned int, long, or unsigned long.




377. Enum whose enumerators will not fit in any integral type

Section: 7.2  [dcl.enum]     Status: CD1     Submitter: Mark Mitchell     Date: 30 August 2002

[Voted into WP at April 2003 meeting.]

7.2 [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 7.2 [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: 7.2  [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 7.2 [dcl.enum] paragraph 1.

Proposed resolution (October, 2005):

Change the grammar in 7.2 [dcl.enum] paragraph 1 as indicated:

enum-specifier:



660. Unnamed scoped enumerations

Section: 7.2  [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 7 [dcl.dcl] paragraph 3,

In a simple-declaration, the optional init-declarator-list can be omitted only when declaring a class (clause 9 [class]) or enumeration (7.2 [dcl.enum]), that is, when the decl-specifier-seq contains either a class-specifier, an elaborated-type-specifier with a class-key (9.1 [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 (9.6 [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 (3.3 [basic.scope]); the scope rules are summarized in 3.4 [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 7 [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 3.3.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 7 [dcl.dcl] paragraph 2 must be addressed. (A note in 7.2 [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 7.2 [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: 7.3.1  [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 8.3.2 [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 (8.3.5 [dcl.fct] paragraph 3) and in template argument deduction (14.8.2.1 [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: 7.3.3  [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 (7.3.3 [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 7.3.3 [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 7.3.3 [namespace.udecl]:

If a using-declaration uses the keyword typename and specifies a dependent name (14.6.2 [temp.dep]), the name introduced by the using-declaration is treated as a typedef-name (7.1.3 [dcl.typedef]).



258. using-declarations and cv-qualifiers

Section: 7.3.3  [namespace.udecl]     Status: CD1     Submitter: Liam Fitzpatrick     Date: 2 Nov 2000

[Voted into WP at April 2003 meeting.]

According to 7.3.3 [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 10.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 7.3.3 [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 (8.3.5 [dcl.fct]), and cv-qualification in a base class (rather than conflicting).

In 10.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 (8.3.5 [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: 7.3.3  [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 7.3.3 [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 7.3.3 [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: 7.5  [dcl.link]     Status: CD1     Submitter: Mike Anderson     Date: unknown

[Moved to DR at 4/01 meeting.]

7.5 [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 7.5 [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 7.5 [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 7.5 [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 7.5 [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 7.5 [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 7.5 [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 (7.1.1 [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 (3.1 [basic.def]); the form of linkage-specification directly containing a single declaration is treated as an extern specifier (7.1.1 [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 (7.1.1 [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: 7.5  [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 7 [dcl.dcl] paragraph 1,

and 8.4 [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.




686. Type declarations/definitions in type-specifier-seqs and type-ids

Section: 8.1  [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. 5.3.4 [expr.new], 6.4 [stmt.select], and 12.3.2 [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. 5.1.2 [expr.prim.lambda], 7 [dcl.dcl], and 8.3.5 [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 5.2.7 [expr.dynamic.cast] paragraph 1:

  2. ...Types shall not be defined in a dynamic_cast....
  3. Delete the indicated words from 5.2.8 [expr.typeid] paragraph 4:

  4. ...Types shall not be defined in the type-id....
  5. Delete the indicated words from 5.2.9 [expr.static.cast] paragraph 1:

  6. ...Types shall not be defined in a static_cast....
  7. Delete the indicated words from 5.2.10 [expr.reinterpret.cast] paragraph 1:

  8. ...Types shall not be defined in a reinterpret_cast....
  9. Delete the indicated words from 5.2.11 [expr.const.cast] paragraph 1:

  10. ...Types shall not be defined in a const_cast....
  11. Delete paragraph 5 of 5.3.3 [expr.sizeof]:

  12. Types shall not be defined in a sizeof expression.
  13. Delete paragraph 5 of 5.3.4 [expr.new]:

  14. The type-specifier-seq shall not contain class declarations, or enumeration declarations.
  15. Delete paragraph 4 of 5.3.6 [expr.alignof]:

  16. A type shall not be defined in an alignof expression.
  17. Delete paragraph 3 of 5.4 [expr.cast]:

  18. Types shall not be defined in casts.
  19. Delete the indicated words from 6.4 [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 7.1.6 [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 (7.1.3 [dcl.typedef]).
  23. Delete the indicated words from 12.3.2 [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 15.3 [except.handle] paragraph 1:

  26. ...Types shall not be defined in an exception-declaration.
  27. Delete paragraph 6 of 15.4 [except.spec]:

  28. Types shall not be defined in exception-specifications.

[Drafting note: no changes are required to 5.1.2 [expr.prim.lambda], 7.1.3 [dcl.typedef], 7.6.2 [dcl.align], 7.2 [dcl.enum], 8.3.5 [dcl.fct], 14.1 [temp.param], or 14.2 [temp.names].]




160. Missing std:: qualification

Section: 8.2  [dcl.ambig.res]     Status: CD1     Submitter: Al Stevens     Date: 23 Aug 1999

[Moved to DR at 10/01 meeting.]

8.2 [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 1.9 [intro.execution] paragraph 9, replace all two instances of "sig_atomic_t" by "std::sig_atomic_t".

In 3.1 [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 3.6.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 3.6.1 [basic.start.main] first sentence of paragraph 5, replace "exit" by "std::exit".

In 3.6.2 [basic.start.init] paragraph 4, replace "terminate" by "std::terminate".

In 3.6.3 [basic.start.term] paragraph 1, replace "exit" by "std::exit" (see also issue 28).

In 3.6.3 [basic.start.term] paragraph 3, replace all three instances of "atexit" by "std::atexit" and both instances of "exit" by "std::exit" (see also issue 28).

In 3.6.3 [basic.start.term] 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 3.7.4.1 [basic.stc.dynamic.allocation] paragraph 1 third sentence, replace "size_t" by "std::size_t".

In 3.7.4.1 [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 3.7.4.1 [basic.stc.dynamic.allocation] paragraph 4, replace "type_info" by "std::type_info" in the note.

In 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 3, replace all four instances of "size_t" by "std::size_t".

In 3.8 [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 3.9 [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 3.9 [basic.types] paragraph 3, replace "memcpy" by "std::memcpy", once in the normative text and once in the example (see also issue 43).

In 3.9.1 [basic.fundamental] paragraph 8 last sentence, replace "numeric_limits" by "std::numeric_limits".

In 5.2.7 [expr.dynamic.cast] paragraph 9 second sentence, replace "bad_cast" by "std::bad_cast".

In 5.2.8 [expr.typeid] paragraph 2, replace "type_info" by "std::type_info" and "bad_typeid" by "std::bad_typeid".

In 5.2.8 [expr.typeid] paragraph 3, replace "type_info" by "std::type_info".

In 5.2.8 [expr.typeid] paragraph 4, replace both instances of "type_info" by "std::type_info".

In 5.3.3 [expr.sizeof] paragraph 6, replace both instances of "size_t" by "std::size_t".

In 5.3.4 [expr.new] paragraph 11 last sentence, replace "size_t" by "std::size_t".

In 5.7 [expr.add] paragraph 6, replace both instances of "ptrdiff_t" by "std::ptrdiff_t".

In 5.7 [expr.add] paragraph 8, replace "ptrdiff_t" by "std::ptrdiff_t".

In 6.6 [stmt.jump] paragraph 2, replace "exit" by "std::exit" and "abort" by "std::abort" in the note.

In 8.2 [dcl.ambig.res] paragraph 3, replace "size_t" by "std::size_t" in the example.

In 8.4 [dcl.fct.def] paragraph 5, replace "printf" by "std::printf" in the note.

In 12.4 [class.dtor] paragraph 13, replace "size_t" by "std::size_t" in the example.

In 12.5 [class.free] paragraph 2, replace all four instances of "size_t" by "std::size_t" in the example.

In 12.5 [class.free] paragraph 6, replace both instances of "size_t" by "std::size_t" in the example.

In 12.5 [class.free] paragraph 7, replace all four instances of "size_t" by "std::size_t" in the two examples.

In 12.7 [class.cdtor] paragraph 4, replace "type_info" by "std::type_info".

In 13.6 [over.built] paragraph 13, replace all five instances of "ptrdiff_t" by "std::ptrdiff_t".

In 13.6 [over.built] paragraph 14, replace "ptrdiff_t" by "std::ptrdiff_t".

In 13.6 [over.built] paragraph 21, replace both instances of "ptrdiff_t" by "std::ptrdiff_t".

In 14.2 [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 14.3 [temp.arg] paragraph 1, replace "complex" by "std::complex", once in the example code and once in the comment.

In 14.7.3 [temp.expl.spec] paragraph 8, issue 24 has already corrected the example.

In 15.1 [except.throw] paragraph 6, replace "uncaught_exception" by "std::uncaught_exception".

In 15.1 [except.throw] paragraph 7, replace "terminate" by "std::terminate" and both instances of "unexpected" by "std::unexpected".

In 15.1 [except.throw] paragraph 8, replace "terminate" by "std::terminate".

In 15.2 [except.ctor] paragraph 3, replace "terminate" by "std::terminate".

In 15.3 [except.handle] paragraph 9, replace "terminate" by "std::terminate".

In 15.4 [except.spec] paragraph 8, replace "unexpected" by "std::unexpected".

In 15.4 [except.spec] paragraph 9, replace "unexpected" by "std::unexpected" and "terminate" by "std::terminate".

In 15.5 [except.special] paragraph 1, replace "terminate" by "std::terminate" and "unexpected" by "std::unexpected".

In the heading of 15.5.1 [except.terminate], replace "terminate" by "std::terminate".

In 15.5.1 [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 15.5.1 [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 15.5.2 [except.unexpected], replace "unexpected" by "std::unexpected".

In 15.5.2 [except.unexpected] paragraph 1, replace

...the function
void unexpected();
is called...

by

...the function std::unexpected() is called...
.

In 15.5.2 [except.unexpected] paragraph 2, replace "unexpected" by "std::unexpected" and "terminate" by "std::terminate".

In 15.5.2 [except.unexpected] paragraph 3, replace "unexpected" by "std::unexpected".

In the heading of 15.5.3 [except.uncaught], replace "uncaught_exception" by "std::uncaught_exception".

In 15.5.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: 8.3.4  [dcl.array]     Status: CD1     Submitter: Steve Clamage     Date: 4 May 1999

[Moved to DR at 10/01 meeting.]

Steve Clamage: Section 8.3.4 [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 (7.1.6.1 [dcl.type.cv] ) and must be initialized as specified in 8.5 [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 3.9.3 [basic.type.qualifier] paragraph 2 says:

A compound type (3.9.2 [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 (8.3.4 [dcl.array] )."
The Note appears to contradict that section as well.

Mike Miller: Yes, but consider the last two sentences of 3.9.3 [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 8.3.4 [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 8.3.4 [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 3.9.3 [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 8.3.4 [dcl.array] paragraph 1 reading

such an array has internal linkage unless explicitly declared extern (7.1.6.1 [dcl.type.cv]) and must be initialized as specified in 8.5 [dcl.init].

with

see 3.9.3 [basic.type.qualifier].



140. Agreement of parameter declarations

Section: 8.3.5  [dcl.fct]     Status: CD1     Submitter: Steve Clamage     Date: 15 Jul 1999

[Moved to DR at 10/01 meeting.]

8.3.5 [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 3.5 [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 7.5 [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 1.3 [intro.defs] “signature,” change "the types of its parameters" to "its parameter-type-list (8.3.5 [dcl.fct])".

  2. In the third bullet of 3.5 [basic.link] paragraph 9 change "the function types are identical for the purposes of overloading" to "the parameter-type-lists of the functions (8.3.5 [dcl.fct]) are identical."

  3. In the sub-bullets of the third bullet of 5.2.5 [expr.ref] paragraph 4, change all four occurrences of "function of (parameter type list)" to "function of parameter-type-list."

  4. In 8.3.5 [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 8.3.5 [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 8.3.5 [dcl.fct] paragraph 4, change "the parameter type list" to "the parameter-type-list."

  7. In the second bullet of 13.1 [over.load] paragraph 2, change all occurrences of "parameter types" to "parameter-type-list."

  8. In 13.3 [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 13.3.1.2 [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: 8.3.5  [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. 8.3.6 [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 8.3.5 [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 8.3.5 [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 8.3.6 [dcl.fct.default] is correct but could be clearer.

In 8.3.6 [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: 8.3.5  [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

8.3.2 [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. 8.3.5 [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 8.3.5 [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 8.3.5 [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 14.8.2 [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: 8.3.5  [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 8.3.5 [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 8 [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 8.3.5 [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 8.3.5 [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 8.3.5 [dcl.fct] paragraph 2.

  2. Add a new paragraph following 8 [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 (8.3.5 [dcl.fct]).

Proposed resolution (June, 2008):

  1. Change the grammar in 8 [dcl.decl] paragraph 4 as follows:

  2. Change the grammar in 8.1 [dcl.name] paragraph 1 as follows:

  3. Change 8.3.5 [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 5.3.4 [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 (5.19 [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 (12.3 [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: 8.3.6  [dcl.fct.default]     Status: CD1     Submitter: Daveed Vandevoorde     Date: 9 July 1999

[Moved to DR at 10/01 meeting.]

8.3.6 [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: 8.3.6 [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 8.3.6 [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: 8.5  [dcl.init]     Status: CD1     Submitter: Josee Lajoie     Date: unknown

[Moved to DR at 4/01 meeting.]

The description of copy-initialization in 8.5 [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 8.5 [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 8.5 [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: 8.5  [dcl.init]     Status: CD1     Submitter: Judy Ward     Date: 15 Dec 1998

Paragraph 9 of 8.5 [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 8.5 [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: 8.5  [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 8.5 [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, 8.5.3 [dcl.init.ref] paragraph 5 requires that such a reference be bound only to lvalues.

It is not clear from 3.10 [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 8.5 [dcl.init] paragraph 14, insert the following after "the call initializes a temporary of the destination type":

    The temporary is an rvalue.
  2. In 15.1 [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: 8.5  [dcl.init]     Status: CD1     Submitter: Andrew Sawyer     Date: 5 Apr 2001

[Moved to DR at 10/01 meeting.]

The intent of 8.5 [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 4.10 [conv.ptr] paragraph 1:

A null pointer constant is an integral constant expression (5.19 [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 8.5 [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 4.10 [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: 8.5  [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 8.5 [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 3.2 [basic.def.odr] pararaph 2:

A default constructor for a class is used by default initialization or value initialization as specified in 8.5 [dcl.init].

Add a footnote to the indicated bullet in 8.5 [dcl.init] paragraph 5:

Add the indicated wording to the first sentence of 12.1 [class.ctor] paragraph 7:

An implicitly-declared default constructor for a class is implicitly defined when it is used (3.2 [basic.def.odr]) to create an object of its class type (1.8 [intro.object]).



509. Dead code in the specification of default initialization

Section: 8.5  [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 (8.5 [dcl.init] paragraph 5) is:

However, default initialization is invoked only for non-POD class types and arrays thereof (5.3.4 [expr.new] paragraph 15 for new-expressions, 8.5 [dcl.init] paragraph 10 for top-level objects, and 12.6.2 [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: 8.5  [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 3.2 [basic.def.odr] paragraph 2 is:

A default constructor for a class is used by default initialization or value initialization as specified in 8.5 [dcl.init].

This sounds as if 8.5 [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 (12.1 [class.ctor] paragraph 7), and the implicitly-defined constructor fails to initialize a const-qualified member (12.6.2 [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 3.2 [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 (8.5 [dcl.init]).

Notes from the April, 2006 meeting:

The approach favored by the CWG was to leave 3.2 [basic.def.odr] unchanged and to add normative wording to 8.5 [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 3.2 [basic.def.odr] to state flatly that the default constructor is used by value initialization (removing the implication that 8.5 [dcl.init] determines the conditions under which it is used).

  2. Change 8.5 [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 8.5 [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 8.5 [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: 8.5.1  [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 8.5.1 [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 8.5.1 [dcl.init.aggr]

Steve Adamczyk: I believe the standard is clear that each initializer expression in the above is a full-expression (1.9 [intro.execution]/12-13; see also issue 392) and therefore there is a sequence point after each expression (1.9 [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: 8.5.1  [dcl.init.aggr]     Status: CD1     Submitter: Nathan Sidwell     Date: 15 Dec 2004

[Voted into WP at April, 2007 meeting.]

The current wording of 8.5.1 [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 8.5.1 [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 12.6.1 [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: 8.5.1  [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++: 8.5.1 [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 8.5.1 [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 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: 8.5.3  [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) 8.5.3 [dcl.init.ref] paragraph 5 bullet 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 13.3.1.3 [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 13.3.1.3 [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 15.1 [except.throw].

David Abrahams (February 2004): It appears, looking at core 291, that there may be a need to tighten up 8.5.3 [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 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: 8.5.3  [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 8.5.3 [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 5.2.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).

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 8.5.3 [dcl.init.ref] paragraph 5 bullet 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 3.10 [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: 8.5.3  [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 8.5.3 [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 8.5.3 [dcl.init.ref] paragraph 5 bullet 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 3.10 [basic.lval]).
  2. Change 3.10 [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.



175. Class name injection and base name access

Section: 9  [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.1 [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: 9  [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 assignme