Document number:  PL22.16/09-0197 = WG21 N3007
Date:  2009-11-08
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, Revision 67


This document contains the C++ core language issues that have been categorized as Defect Reports by the Committee (J16 + WG21), that is, issues with status "DR," "WP," "CD1," and "TC1," along with their proposed resolutions. ONLY RESOLUTIONS FOR ISSUES WITH TC1 STATUS ARE PART OF THE INTERNATIONAL STANDARD FOR C++. The other issues 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 PL22.16/09-0150 = WG21 N2960.


Issues with "DR" Status


612. Requirements on a conforming implementation

Section: 1.9  [intro.execution]     Status: DR     Submitter: Clark Nelson     Date: 23 January 2007

[Voted into WP at October, 2009 meeting.]

The execution requirements on a conforming implementation are described twice in the Standard, once in 1.9 [intro.execution] paragraphs 5-6 and again in paragraph 11. These descriptions differ in at least a couple of important ways:

The most significant discrepancy has to do with the way output is described. In paragraph 11, the least requirements are described in terms of data written at program termination, clearly allowing arbitrary buffering, whereas in paragraph 6, the observable behavior is described in terms of calls to I/O functions. For example, there are compilers which transform a call to printf with a single argument into a call to fputs. That's valid under paragraph 11, but not under paragraph 6.

Also, in paragraph 6, volatile accesses and I/O operations are included in a single sequence, suggesting that they are equally constrained by sequencing requirements, whereas in paragraph 11, they are clearly not.

There are also editorial discrepancies that should be cleaned up.

Proposed resolution (September, 2009):

The resolution of issue 785 also resolves this issue.




785. “Execution sequence” is inappropriate phraseology

Section: 1.9  [intro.execution]     Status: DR     Submitter: US/UK     Date: 3 March, 2009

N2800 comment US 16
N2800 comment UK 8
N2800 comment UK 7

[Voted into WP at October, 2009 meeting.]

In the presence of threads, it is no longer appropriate to characterize the abstract machine as having an “execution sequence.”

Proposed resolution (September, 2009):

  1. Change 1.9 [intro.execution] paragraph 3 as follows:

  2. ...An instance of the abstract machine can thus have more than one possible execution sequence for a given program and a given input.
  3. Change 1.9 [intro.execution] paragraph 5 as follows:

  4. A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible execution sequences executions of the corresponding instance of the abstract machine with the same program and the same input. However, if any such execution sequence contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).
  5. Delete 1.9 [intro.execution] paragraph 6, including the footnote:

  6. The observable behavior of the abstract machine is its sequence of reads and writes to volatile data and calls to library I/O functions. [Footnote: An implementation can offer additional library I/O functions as an extension. Implementations that do so should treat calls to those functions as “observable behavior” as well. —end footnote]
  7. Change 1.9 [intro.execution] paragraph 9 as follows:

  8. The least requirements on a conforming implementation are:

    These collectively are referred to as the observable behavior of the program. [Note: more stringent correspondences between abstract and actual semantics may be defined by each implementation. —end note]

(Note; this resolution also resolves issue 612.)




726. Atomic and non-atomic objects in the memory model

Section: 1.10  [intro.multithread]     Status: DR     Submitter: Clark Nelson     Date: 30 September, 2008

[Voted into WP at October, 2009 meeting.]

In general, the description of the memory model is very careful to specify when the objects under discussion are atomic or non-atomic. However, there are a few cases where it could be clearer.

Proposed resolution (March, 2009):

  1. Modify 1.10 [intro.multithread] paragraph 5 as follows:

  2. All modifications to a particular atomic object M occur in some particular total order, called the modification order of M. If A and B are modifications of an atomic object M and A happens before (as defined below) B, then A shall precede B in the modification order of M, which is defined below. [Note: This states that the modification orders must respect happens before. —end note] [Note: There is a separate order for each scalar atomic object. There is no requirement that these can be combined into a single total order for all objects. In general this will be impossible since different threads may observe modifications to different variables in inconsistent orders. —end note]
  3. Modify 1.10 [intro.multithread] paragraph 7 as follows:

  4. Certain library calls synchronize with other library calls performed by another thread. In particular, an atomic operation A that performs a release operation on an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M and reads a value written by any side effect in the release sequence headed by A...
  5. Modify 1.10 [intro.multithread] paragraph 12 as follows:

  6. A visible side effect A on an a scalar object or bit-field M with respect to a value computation B of M satisfies the conditions:

    The value of a non-atomic scalar object or bit-field M, as determined by evaluation B, shall be the value stored by the visible side effect A. [Note: If there is ambiguity about which side effect to a non-atomic object or bit-field is visible, then there is a data race, and the behavior is undefined. —end note] ...




786. Definition of “thread”

Section: 1.10  [intro.multithread]     Status: DR     Submitter: US     Date: 3 March, 2009

N2800 comment US 17

[Voted into WP at October, 2009 meeting.]

The term “thread” is introduced but not defined in 1.10 [intro.multithread] paragraph 1. A definition is needed.

Proposed resolution (September, 2009):

Chamge 1.10 [intro.multithread] paragraph 1 as follows:

A thread of execution (a.k.a. thread) is a single flow of control within a program, including the initial invocation of a specific top-level function, and recursively including every function invocation subsequently executed by the thread. [Note: When one thread creates another, the initial call to the top-level function of the new thread is executed by the new thread, not by the creating thread. —end note] Every thread in a program can potentially access every object and function in the program. [Footnote: An object with automatic or thread storage duration (3.7 [basic.stc]) is associated with one specific thread, and can be accessed by a different thread only indirectly through a pointer or reference (3.9.2 [basic.compound]). —end footnote] Under a hosted implementation, a C++ program can have more than one thread of execution (a.k.a. thread) thread running concurrently...



630. Equality of narrow and wide character values in the basic character set

Section: 2.3  [lex.charset]     Status: DR     Submitter: Tom Plum     Date: 21 April 2007

[Voted into WP at October, 2009 meeting.]

WG14 accepted DR 279 regarding the rule known colloquially as the L'x'=='x' rule. This change was made to C99 in TC2. The Austin Group subsequently opened DR 321 against TC2, observing that the change made in TC2 would invalidate existing conforming C code that relied on the L'x'=='x' rule.

DR 321 is now closed and will be included in the TC3 to C99. This change defines a new standard macro, which WG14 drafted as follows:

__STDC_MB_MIGHT_NEQ_WC__: The integer constant 1, intended to indicate that there might be some character x in the basic character set, such that 'x' need not be equal to L'x'.

WG14 requests that WG21 adopt this revision and this macro in C++0x.

Proposed resolution (July, 2009):

Add the following to 16.8 [cpp.predefined] paragraph 2:

__STDC_MB_MIGHT_NEQ_WC__
The integer constant 1, intended to indicate that, in the encoding for wchar_t, a member of the basic character set need not have a code value equal to its value when used as the lone character in an ordinary character literal.



832. Value of preprocessing numbers

Section: 2.10  [lex.ppnumber]     Status: DR     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 13

[Voted into WP at October, 2009 meeting.]

2.10 [lex.ppnumber] paragraph 2 says,

A preprocessing number does not have a type or a value; it acquires both after a successful conversion (as part of translation phase 7, 2.2 [lex.phases]) to an integral literal token or a floating literal token.

However, preprocessing directives are executed in phase 4, and the evaluation of constant-expressions in #if directives requires that preprocessing numbers have values.

Proposed resolution (July, 2009):

Change 2.10 [lex.ppnumber] paragraph 2 as follows:

A preprocessing number does not have a type or a value; it acquires both after a successful conversion (as part of translation phase 7 (2.2 [lex.phases])) to an integral literal token or a floating literal token.



933. 32-bit UCNs with 16-bit wchar_t

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

[Voted into WP at October, 2009 meeting.]

According to 2.14.3 [lex.ccon] paragraph 2,

A character literal that begins with the letter L, such as L'x', is a wide-character literal. A wide-character literal has type wchar_t. The value of a wide-character literal containing a single c-char has value equal to the numerical value of the encoding of the c-char in the execution wide-character set.

A c-char that is a universal character name might, when translated to the execution character set, result in a multi-character sequence that is larger than can be represented in a wchar_t. There is wording that prevents this in char16_t literals, but not for wchar_t literals. This seems undesirable.

Proposed resolution (July, 2009):

  1. Change 2.14.3 [lex.ccon] paragraph 2 as follows:

  2. ...The value of a wide-character literal containing a single c-char has value equal to the numerical value of the encoding of the c-char in the execution wide-character set, unless the c-char has no representation in the execution wide-character set, in which case the value is implementation-defined. [Note: The type wchar_t is able to represent all members of the execution wide-character set, see 3.9.1 [basic.fundamental]. —end note]. The value of a wide-character literal containing multiple c-chars is implementation-defined.
  3. Change 2.14.3 [lex.ccon] paragraph 5 as follows:

  4. A universal-character-name is translated to the encoding, in the appropriate execution character set, of the character named...



790. Concatenation of raw and non-raw string literals

Section: 2.14.5  [lex.string]     Status: DR     Submitter: JP     Date: 3 March, 2009

N2800 comment JP 5

[Voted into WP at October, 2009 meeting.]

The description of concatenation of string literals in 2.14.5 [lex.string] paragraph 11 does not mention raw strings explicitly, so it is not clear whether, and if so, how, they combine with non-raw strings.

Notes from the March, 2009 meeting:

A raw string should be considered equivalent to the corresponding non-raw string in string literal concatenation.

Proposed resolution (September, 2009):

  1. In 2.14.5 [lex.string], replace the definition of string-literal with:


  2. Change 2.14.5 [lex.string] paragraph 5 as follows:

  3. A After translation phase 6, a string literal that does not begin with u8, u, U, or L an encoding-prefix is an ordinary string literal, and is initialized with the given characters.
  4. Change 2.14.5 [lex.string] paragraph 12 as follows:

  5. In translation phase 6 (2.2 [lex.phases]), adjacent string literals are concatenated. If both string literals have the same prefix encoding-prefix, the resulting concatenated string literal has that prefix encoding-prefix. If one string literal has no prefix encoding-prefix, it is treated as a string literal of the same prefix encoding-prefix as the other operand. If a UTF-8 string literal token is adjacent to a wide string literal token, the program is ill-formed. Any other concatenations are conditionally supported with implementation-defined behavior. [Note: This concatenation is an interpretation, not a conversion. Because the interpretation happens in translation phase 6 (after each character from each literal has been translated into a value from the appropriate character set), a string literal's initial rawness has no effect on the interpretation or well-formedness of the concatenation.end note] [Example:...

(Note: this resolution also resolves issue 834.)




834. What is an “ordinary string literal”?

Section: 2.14.5  [lex.string]     Status: DR     Submitter: Mike Miller     Date: 6 March, 2009

[Voted into WP at October, 2009 meeting.]

According to 2.14.5 [lex.string] paragraph 4,

A string literal that does not begin with u8, u, U, or L is an ordinary string literal, and is initialized with the given characters.

This is not as clear as it could be that a string like u8R"[xxx]" is not an ordinary string literal, because the string's prefix is not one of those listed (i.e., it's not obvious that possible substrings of the prefix are in view). This would be clearer if it simply said,

A string literal with no prefix or a prefix of R is an ordinary string literal.

Proposed resolution (September, 2009):

This issue is resolved by the resolution of issue 790.




719. Specifications for operator-function-id that should also apply to literal-operator-id

Section: 3  [basic]     Status: DR     Submitter: Daveed Vandevoorde     Date: 19 September, 2008

[Voted into WP at October, 2009 meeting.]

When user-defined literals were added, a new form of operator function was created. Presumably many of the existing specifications that deal with operator-function-ids (the definition of name, for instance, in paragraph 4 of 3 [basic]) should also apply to literal-operator-ids.

Proposed resolution (June, 2009):

  1. Change 3 [basic] paragraph 4 as follows:

  2. A name is a use of an identifier (2.11 [lex.name]), operator-function-id (13.5 [over.oper]), literal-operator-id (13.5.8 [over.literal]), conversion-function-id (12.3.2 [class.conv.fct]), or template-id (14.3 [temp.names]) that denotes an entity or label (6.6.4 [stmt.goto], 6.1 [stmt.label]).
  3. Change 5.1.1 [expr.prim.general] paragraph 3 as follows:

  4. The operator :: followed by an identifier, a qualified-id, or an operator-function-id, or a literal-operator-id is a primary-expression. Its type is specified by the declaration of the identifier, qualified-id, or operator-function-id, or literal-operator-id. The result is the entity denoted by the identifier, qualified-id, or operator-function-id, or literal-operator-id. The result is an lvalue if the entity is a function or variable. The identifier, qualified-id, or operator-function-id, or literal-operator-id shall have global namespace scope or be visible in global scope because of a using-directive (7.3.4 [namespace.udir])...
  5. Add the following production to the grammar for qualified-id in 5.1.1 [expr.prim.general] paragraph 7:

  6. Add the following production to the grammar for template-id in 14.3 [temp.names] paragraph 1:

  7. Change 14.3 [temp.names] paragraph 3 as follows:

  8. After name lookup (3.4 [basic.lookup]) finds that a name is a template-name, or that an operator-function-id or a literal-operator-id refers to a set of overloaded functions any member of which is a function template...
  9. Change 14.5 [temp.type] paragraph 1 bullet 1 as follows:




481. Scope of template parameters

Section: 3.3  [basic.scope]     Status: DR     Submitter: Gabriel Dos Reis     Date: 01 Nov 2004

N2800 comment FR 16

[Voted into WP at October, 2009 meeting.]

Sections 3.3.3 [basic.scope.local] to 3.3.7 [basic.scope.class] define and summarize different kinds of scopes in a C++ program. However it is missing a description for the scope of template parameters. I believe a section is needed there — even though some information may be found in clause 14.

Proposed resolution (September, 2009):

  1. Insert the following as a new paragraph following 3.3.2 [basic.scope.pdecl] paragraph 8:

  2. The point of declaration of a template parameter is immediately after its complete template-parameter. [Example:
      typedef unsigned char T;
      template<class T
           = T         // Lookup finds the typedef name of unsigned char.
      
           , T         //Lookup finds the template parameter.
               N = 0> struct A {};
    

    end example]

  3. Delete 14.2 [temp.param] paragraph 14:

  4. A template-parameter shall not be used in its own default argument.
    [Drafting note: This change conflicts with the resolution for issue 187 but is in accord with widespread implementation practice.]
  5. Insert the following as a new section following 3.3.10 [basic.scope.enum]:

  6. Template Parameter Scope [basic.scope.temp]

    The declarative region of the name of a template parameter of a template template-parameter is the smallest template-parameter-list in which the name was introduced.

    The declarative region of the name of a template parameter of a template is the smallest template-declaration in which the name was introduced. Only template parameter names belong to this declarative region; any other kind of name introduced by the declaration of a template-declaration is instead introduced into the same declarative region where it would be introduced as a result of a non-template declaration of the same name. [Example:

      namespace N {
        template<class T> struct A{};               // line 2
        template<class U> void f(U){}               // line 3
        struct B {
          template<class V>friend int g(struct C*); // line 5
        };
      }
    

    The declarative regions of T, U and V are the template-declarations on lines 2, 3 and 5, respectively. But the names A, f, g and C all belong to the same declarative region—namely, the namespace-body of N. (g is still considered to belong to this declarative region in spite of its being hidden during qualified and unqualified name lookup.) —end example]

    The potential scope of a template parameter name begins at its point of declaration (3.3.2 [basic.scope.pdecl]) and ends at the end of its declarative region. [Note: this implies that a template-parameter can be used in the declaration of subsequent template-parameters and their default arguments but cannot be used in preceding template-parameters or their default arguments. For example,

      template<class T, T* p, class U = T> class X { /* ... */ }; 
      template<class T> void f(T* p = new T); 
    

    This also implies that a template-parameter can be used in the specification of base classes. For example,

      template<class T> class X : public Array<T> { /* ... */ }; 
      template<class T> class Y : public T { /* ... */ }; 
    

    The use of a template parameter as a base class implies that a class used as a template argument must be defined and not just declared when the class template is instantiated. —end note]

    The declarative region of the name of a template parameter is nested within the immediately-enclosing declarative region. [Note: as a result, a template-parameter hides any entity with the same name in an enclosing scope (3.3.11 [basic.scope.hiding]). [Example:

      typedef int N;
      template<N X, typename N, template<N Y> class T>
        struct A;
    

    Here, X is a non-type template parameter of type int and Y is a non-type template parameter of the same type as the second template parameter of A. —end example] —end note]

    [Note: because the name of a template parameter cannot be redeclared within its potential scope (14.7.1 [temp.local]), a template parameter's scope is often its potential scope. However, it is still possible for a template parameter name to be hidden; see 14.7.1 [temp.local]. —end note]

  7. Delete 14.2 [temp.param] paragraph 13, including the example:

  8. The scope of a template-parameter extends...
  9. Delete 14.7.1 [temp.local] paragraph 6, including the note and example:

  10. The scope of a template-parameter extends...



705. Suppressing argument-dependent lookup via parentheses

Section: 3.4.2  [basic.lookup.argdep]     Status: DR     Submitter: Mike Miller     Date: 29 July, 2008

[Voted into WP at October, 2009 meeting.]

During the discussion of issue 704, some people expressed a desire to reconsider whether parentheses around the name of the function in a function call should suppress argument-dependent lookup, on the basis that this is overly subtle and not obvious. Others pointed out that this technique is used (both intentionally and inadvertently) in existing code and changing the behavior could cause problems.

It was also observed that the normative text that specifies this behavior is itself subtle, relying an a very precise interpretation of the preposition used in 3.4.2 [basic.lookup.argdep] paragraph 1:

When an unqualified name is used as the postfix-expression in a function call...

This is taken to mean that something like (f)(x) is not subject to argument-dependent lookup because the name f is used in but not as the postfix-expression. This could be confusing, especially in light of the use of the term postfix-expression to refer to the name inside the parentheses, not to the parenthesized expression, in 13.3.1.1 [over.match.call] paragraph 1. If the decision is to preserve this effect of a parenthesized name in a function call, the wording should probably be revised to specify it more explicitly.

Notes from the September, 2008 meeting:

The CWG agreed that the suppression of argument-dependent lookup by parentheses surrounding the postfix-expression is widely known and used in the C++ community and must be preserved. The wording should be changed to make this effect clearer.

Proposed resolution (September, 2008):

Change 3.4.2 [basic.lookup.argdep] paragraph 1 as follows:

When an unqualified name is used as the postfix-expression in a function call (5.2.2 [expr.call]) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1 [basic.lookup.unqual]) may be searched...

Proposed resolution (September, 2009):

Change 3.4.2 [basic.lookup.argdep] paragraph 1 as follows:

When an unqualified name is used as the postfix-expression in a function call (5.2.2 [expr.call]) is an unqualified-id, 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.4 [class.friend]) not otherwise visible may be found. These modifications to the search depend on the types of the arguments (and for template template arguments, the namespace of the template argument). [Example:

    namespace N {
      struct S { };
      void f(S);
    }

    void g() {
      N::S s;
      f(s);      // calls N::f
      (f)(s);    // error: N::f not considered; parentheses prevent argument-dependent lookup
    }

end example]




527. Problems with linkage of types

Section: 3.5  [basic.link]     Status: DR     Submitter: Daveed Vandevoorde     Date: 28 July 2005

[Voted into WP at October, 2009 meeting.]

The resolution of issue 389 makes code like

    static struct {
        int i;
        int j;
    } X;

ill-formed. This breaks a lot of code for no apparent reason, since the name X is not known outside the translation unit in which it appears; there is therefore no danger of collision and no need to mangle its name.

There has also been recent discussion on the email reflectors as to whether the restrictions preventing use of types without linkage as template arguments is needed or not, with the suggestion that a mechanism like that used to give members of the unnamed namespace unique names could be used for unnamed and local types. See also issue 488, which would become moot if types without linkage could be used as template parameters.

Notes from the October, 2005 meeting:

The Evolution Working Group is discussing changes that would address this issue. CWG will defer consideration until the outcome of the EWG discussions is clear.

Notes from the April, 2006 meeting:

The CWG agreed that the restriction in 3.5 [basic.link] paragraph 8 on use of a type without linkage should apply only to variables and functions with external linkage, not to variables and functions with internal linkage (i.e., the example should be accepted). This is a separate issue from the question before the EWG and should be resolved independently.

Additional note (April, 2006):

Even the restriction of the rule to functions and objects with external linkage may not be exactly what we want. Consider an example like:

    namespace {
        struct { int i; } s;
    }

The variable s has external linkage but can't be named outside its translation unit, so there's again no reason to prohibit use of a type without linkage in its declaration.

Notes from the June, 2008 meeting:

Paper N2657, adopted at the June, 2008 meeting, allows local and unnamed types to be used as template parameters. That resolution is narrowly focused, however, and does not address this issue.

Proposed resolution (June, 2009):

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

...A type without linkage shall not be used as the type of a variable or function with external linkage, unless

[Drafting note: the context shown for the preceding resolution assumes that the resolution for issue 757 has been applied.]




792. Effects of std::quick_exit

Section: 3.6.1  [basic.start.main]     Status: DR     Submitter: US     Date: 3 March, 2009

N2800 comment US 24

[Voted into WP at October, 2009 meeting.]

3.6.1 [basic.start.main] paragraph 4 discusses the effects of calling std::exit but says nothing about std::quick_exit.

Proposed resolution (July, 2009):

Change 3.6.1 [basic.start.main] paragraph 4 as follows:

Calling the function std::exit(int) declared in <cstdlib> (18.5 [support.start.term]) terminates Terminating the program without leaving the current block (e.g., by calling the function std::exit(int) (18.5 [support.start.term])) and hence without destroying does not destroy any objects with automatic storage duration (12.4 [class.dtor])...


882. Defining main as deleted

Section: 3.6.1  [basic.start.main]     Status: DR     Submitter: Steve Adamczyk     Date: 27 April, 2009

It should be stated in 3.6.1 [basic.start.main] that it a program that defines main as deleted is ill-formed.

Proposed resolution (July, 2009):

Change 3.6.1 [basic.start.main] paragraph 3 as follows:

...A program that declares main to be inline, static, or constexpr, or that defines main as deleted, is ill-formed...



776. Delegating constructors, destructors, and std::exit

Section: 3.6.3  [basic.start.term]     Status: DR     Submitter: Michael Wong     Date: 12 February, 2009

[Voted into WP at October, 2009 meeting.]

According to 3.6.3 [basic.start.term] paragraph 1,

Destructors (12.4 [class.dtor]) for initialized objects with static storage duration are called as a result of returning from main and as a result of calling std::exit (18.5 [support.start.term]).

It is unclear, in the presence of delegating constructors, exactly what an “initialized object” is. 3.8 [basic.life] paragraph 1 says that the lifetime of an object does not begin until it is completely initialized, i.e., when its principal constructor finishes execution. 15.2 [except.ctor] paragraph 2 says that an exception during the construction of class object only invokes destructors for fully-constructed base and member sub-objects (those for which the principal constructor has completed). On the other hand, the destructor for a complete class object is called if its non-delegating constructor has completed, even if the principal constructor has not yet finished. Which of these models is appropriate for the behavior of std::exit?

Notes from the March, 2009 meeting:

The CWG agreed that the destructor for a complete object should be called by std::exit if its non-delegating constructor has finished, just as for an exception.

Notes from the July, 2009 meeting:

The CWG decided that the direction adopted at the March, 2009 meeting was incorrect. Instead, the model should be the way completely-constructed base and member subobjects are handled: their destructors are called when an exception is thrown but not when std::exit is called.

Proposed resolution (July, 2009):

Change 3.6.3 [basic.start.term] paragraph 1 as follows:

Destructors (12.4 [class.dtor]) for initialized objects (that is, objects whose lifetime (3.8 [basic.life]) has begun) with static storage duration are called as a result of returning from main and as a result of calling std::exit (18.5 [support.start.term]). Destructors for initialized objects with thread storage duration...



735. Missing case in specification of safely-derived pointers

Section: 3.7.4.3  [basic.stc.dynamic.safety]     Status: DR     Submitter: Jens Maurer     Date: 14 October, 2008

N2800 comment DE 3

[Voted into WP at October, 2009 meeting.]

The bullets in 3.7.4.3 [basic.stc.dynamic.safety] paragraph 2 do not appear to cover the following example:

   int& i = *new int(5);
   // do something with i
   delete &i;

Should &i be a safely-derived pointer value?

Proposed resolution (September, 2009):

Change 3.7.4.3 [basic.stc.dynamic.safety] paragraph 2, bullet 2, as follows:




883. std::memcpy vs std::memmove

Section: 3.9  [basic.types]     Status: DR     Submitter: Lawrence Crowl     Date: 29 April, 2009

[Voted into WP at October, 2009 meeting.]

The std::memcpy library function is singled out for special treatment in 3.9 [basic.types] paragraph 3:

For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the value of obj1 is copied into obj2, using the std::memcpy library function, obj2 shall subsequently hold the same value as obj1.

This specification should not be restricted to std::memcpy but should apply to any bytewise copying, including std::memmove (as is done in the footnote in the preceding paragraph, for example).

Proposed resolution (July, 2009):

Change 3.9 [basic.types] paragraph 3 as follows:

For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the value of underlying bytes (1.7 [intro.memory]) making up obj1 is are copied into obj2, using the std::memcpy library function [Footnote: By using, for example, the library functions (17.6.1.2 [headers]) std::memcpy or std::memmove. —end footnote], obj2 shall subsequently hold the same value as obj1. [Example:...



693. New string types and deprecated conversion

Section: 4.2  [conv.array]     Status: DR     Submitter: Alisdair Meredith     Date: 21 April, 2008

N2800 comment DE 4

[Voted into WP at October, 2009 meeting.]

The deprecated conversion from string literal to pointer to (non-const) character in 4.2 [conv.array] paragraph 2 has been extended to apply to char16_t and char32_t types, but not to UTF8 and raw string literals. Is this disparity intentional? Should it be extended to all new string types, reverted to just the original character types, or revoked altogether?

Additional places in the Standard that may need to change include 15.1 [except.throw] paragraph 3 and 13.3.3.2 [over.ics.rank] paragraph 3.

Additional discussion (August, 2008):

The removal of this conversion for current string literals would affect overload resolution for existing programs. For example,

    struct S {
        S(const char*);
    };
    int f(char *);
    int f(X);
    int i = f("hello");

If the conversion were removed, the result would be a quiet change in behavior. Another alternative to consider would be a required diagnostic (without making the program ill-formed).

Notes from the September, 2008 meeting:

The CWG agreed that the deprecated conversion should continue to apply to the literals to which it applied in C++ 2003. Consensus was not reached regarding whether it should apply only to those literals or to all the new literals as well, although it was agreed that the current situation in which it applies to some, but not all, of the new literals is unacceptable.

Notes from the July, 2009 meeting:

The CWG reached consensus that the deprecated conversion should be removed altogether.

Proposed resolution (September, 2009):

  1. Remove 4.2 [conv.array] paragraph 2:

  2. A string literal (2.14.5 [lex.string]) with no prefix, with a u prefix, with a U prefix, or with an L prefix can be converted to an rvalue of type “pointer to char”, “pointer to char16_t”, “pointer to char32_t”, or “pointer to wchar_t”, respectively. In any case, the result is a pointer to the first element of the array. This conversion is considered only when there is an explicit appropriate pointer target type, and not when there is a general need to convert from an lvalue to an rvalue. [Note: this conversion is deprecated. See Annex D [depr]. —end note] For the purpose of ranking in overload resolution (13.3.3.1.1 [over.ics.scs]), this conversion is considered an array-to-pointer conversion followed by a qualification conversion (4.4 [conv.qual]). [Example: "abc" is converted to “pointer to const char” as an array-to-pointer conversion, and then to “pointer to char” as a qualification conversion. —end example]
  3. Delete the indicated text from the third sub-bullet of the first bullet of paragraph 3 of 13.3.3.2 [over.ics.rank]:

  4. Delete the note from 15.1 [except.throw] paragraph 3 as follows:

  5. A throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T”, respectively. [Note: the temporary object created for a throw-expression that is a string literal is never of type char*, char16_t*, char32_t*, or wchar_t*; that is, the special conversions for string literals from the types “array of const char”, “array of const char16_t”, “array of const char32_t”, and “array of const wchar_t” to the types “pointer to char”, “pointer to char16_t”, “pointer to char32_t”, and “pointer to wchar_t”, respectively (4.2 [conv.array]), are never applied to a throw-expression. —end note] The temporary is an lvalue...
  6. Change the discussion of 2.14.5 [lex.string] in C.1.1 [diff.lex] as follows:

  7. Change: String literals made const
    The type of a string literal is changed... “array of const wchar_t.”

        char* p = "abc";   // valid in C, invalid in C++
    

    ...

    Difficulty of converting: Simple syntactic transformation, because string literals can be converted to char*; (4.2 [conv.array]). The most common cases are handled by a new but deprecated standard conversion Syntactic transformation. The fix is to add a cast:

      char* p = "abc";                // valid in C, deprecated in C++
      char* q = expr ? "abc" : "de";  // valid in C, invalid in C++
      void f(char*) {
          char* p = (char*)"abc";  // cast added
          f(p);
          f((char*)"def");         // cast added
       }
    
  8. Delete D.4 [depr.string]:

  9. D.4 Implicit conversion from const strings [depr.string]

    The implicit conversion from const to non-const qualification for string literals (4.2 [conv.array]) is deprecated.




695. Compile-time calculation errors in constexpr functions

Section: 5  [expr]     Status: DR     Submitter: Mike Miller     Date: 9 June, 2008

[Voted into WP at October, 2009 meeting.]

Evaluating an expression like 1/0 is intended to produce undefined behavior during the execution of a program but be ill-formed at compile time. The wording for this is in 5 [expr] paragraph 4:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression appears where an integral constant expression is required (5.19 [expr.const]), in which case the program is ill-formed.

The formulation “appears where an integral constant expression is required” is intended as an acceptable Standardese circumlocution for “evaluated at compile time,” a concept that is not directly defined by the Standard. It is not clear that this formulation adequately covers constexpr functions.

Notes from the September, 2008 meeting:

The CWG felt that the concept of “compile-time evaluation” needs to be defined for use in discussing constexpr functions. There is a tension between wanting to diagnose errors at compile time versus not diagnosing errors that will not actually occur at runtime. In this context, a constexpr function might never be invoked, either in a constant expression context or at runtime, although the requirement that the expression in a constexpr function be a potential constant expression could be interpreted as triggering the provisions of 5 [expr] paragraph 4.

There are also contexts in which it is not known in advance whether an expression must be constant or not, notably in the initializer of a const integer variable, where the nature of the initializer determines whether the variable can be used in constant expressions or not. In such a case, it is not clear whether an erroneous expression should be considered ill-formed or simply non-constant (and thus subject to runtime undefined behavior, if it is ever evaluated). The consensus of the CWG was that an expression like 1/0 should simply be considered non-constant; any diagnostic would result from the use of the expression in a context requiring a constant expression.

Proposed resolution (July, 2009):

This issue is resolved by the resolution of issue 699.




835. Scoped enumerations and the “usual arithmetic conversions”

Section: 5  [expr]     Status: DR     Submitter: Beman Dawes     Date: 5 March, 2009

[Voted into WP at October, 2009 meeting.]

A number of the operators described in clause 5 [expr] take operands of enumeration type, relying on the “usual arithmetic conversions” (5 [expr] paragraph 10) to convert them to an appropriate integral type. The assumption behind this pattern is invalid when one or more of the operands has a scoped enumeration type.

Each operator that accepts operands of enumeration type should be evaluated as to whether the operation makes sense for scoped enumerations (for example, it is probably a good idea to allow comparison of operands having the same scoped enumeration type and conditional expressions in which the second and third operands have the same scoped enumeration type) and, if so, create a special case. The usual arithmetic conversions should not be invoked for scoped enumeration types.

(See also issue 880.)

Proposed resolution (July, 2009):

  1. Change 5 [expr] paragraph 10 as follows:

  2. ...This pattern is called the usual arithmetic conversions, which are defined as follows:

  3. Change 5.2.1 [expr.sub] paragraph 1 as follows:

  4. ...One of the expressions shall have the type “pointer to T” and the other shall have unscoped enumeration or integral type...
  5. Change 5.3 [expr.unary] paragraphs 7-8 and 10 as follows:

  6. The operand of the unary + operator shall have arithmetic, unscoped enumeration, or pointer type...

    The operand of the unary - operator shall have arithmetic or unscoped enumeration type...

    The operand of ~ shall have integral or unscoped enumeration type...

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

  8. ...The expression in a noptr-new-declarator shall be of integral type, unscoped enumeration type, or a class type for which a single non-explicit conversion function to integral or unscoped enumeration type exists (12.3 [class.conv]). If the expression...
  9. Change 5.6 [expr.mul] paragraph 2 as follows:

  10. The operands of * and / shall have arithmetic or unscoped enumeration type; the operands of % shall have integral or unscoped enumeration type....
  11. Change 5.7 [expr.add] paragraph 1-2 as follows:

  12. ...For addition, either both operands shall have arithmetic or unscoped enumeration type, or one operand shall be a pointer to a completely-defined effective object type and the other shall have integral or unscoped enumeration type.

    For subtraction, one of the following shall hold:

  13. Change 5.8 [expr.shift] paragraph 1 as follows:

  14. ...The operands shall be of integral or unscoped enumeration type...
  15. Change 5.9 [expr.rel] paragraph 4 as follows:

  16. If both operands (after conversions) are of arithmetic or enumeration type, each of the operators shall yield true if the specified relationship is true and false if it is false.
  17. Change 5.11 [expr.bit.and] paragraph 1 as follows:

  18. ...The operator applies only to integral or unscoped enumeration operands.
  19. Change 5.12 [expr.xor] paragraph 1 as follows:

  20. ...The operator applies only to integral or unscoped enumeration operands.
  21. Change 5.13 [expr.or] paragraph 1 as follows:

  22. ...The operator applies only to integral or unscoped enumeration operands.



850. Restrictions on use of non-static data members

Section: 5.1.1  [expr.prim.general]     Status: DR     Submitter: Jason Merrill     Date: 1 April, 2009

[Voted into WP at October, 2009 meeting.]

The resolution of issue 613, as reflected in the sixth bullet of 5.1.1 [expr.prim.general] paragraph 10, allows an id-expression designating a non-static data member to be used

The requirement that the id-expression be the “sole constituent” of the unevaluated operand seems unnecessarily strict, forbidding such plausible use cases as

    struct S {
        int ar[42];
    };
    int i = sizeof(S::ar[0]);

or the use of the member as a function argument in template metaprogramming. The more general version of the restriction seems not to be very difficult to implement and may actually represent a simplification in some implementations.

Proposed resolution (July, 2009):

Change 5.1.1 [expr.prim.general] paragraph 10 as follows:




731. Omitted reference qualification of member function type

Section: 5.2.5  [expr.ref]     Status: DR     Submitter: Daniel Krügler     Date: 6 October, 2008

N2800 comment DE 5
N2800 comment DE 10
N2800 comment DE 12

[Voted into WP at October, 2009 meeting.]

There are several places in the Standard that were overlooked when reference qualification of member functions was added. For example, 5.2.5 [expr.ref] paragraph 4, bullet 3, sub-bullet 2 says,

...if E1.E2 refers to a non-static member function, and the type of E2 is “function of parameter-type-list cv returning T”, then...

This wording incorrectly excludes member functions declared with a ref-qualifier.

Another place that should consider reference qualification is 5.5 [expr.mptr.oper]; it should not be possible to invoke an &-qualified member function with an rvalue object expression.

A third place is 7.3.3 [namespace.udecl] paragraph 15, which does not mention reference qualification in connection with the hiding/overriding of member functions brought in from a base class via a using-declaration.

Proposed resolution (September, 2009):

  1. Change 5.2.5 [expr.ref] paragraph 4, bullet 3, sub-bullet 2 as follows:

  2. Change 5.5 [expr.mptr.oper] paragraph 6 as follows:

  3. ...—end example] In a .* expression whose object expression is an rvalue, if the second operand is a pointer to member function with ref-qualifier &, the program is ill-formed. In a ->* expression, or in a .* expression whose object expression is an lvalue, if the second operand is a pointer to member function with ref-qualifier &&, the program is ill-formed. The result of a .* expression is an lvalue only if its first operand is an lvalue and...
  4. Change 7.3.3 [namespace.udecl] paragraph 15 as follows:

  5. When a using-declaration brings names from a base class into a derived class scope, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list (8.3.5 [dcl.fct]), and cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting). [Note:...



833. Explicit conversion of a scoped enumeration value to a floating type

Section: 5.2.9  [expr.static.cast]     Status: DR     Submitter: John Spicer     Date: 6 March, 2009

[Voted into WP at October, 2009 meeting.]

The current wording of 5.2.9 [expr.static.cast] paragraph 9 does not permit conversion of a value of a scoped enumeration type to a floating point type. This was presumably an oversight during the specification of scoped enumerations and should be rectified.

Proposed resolution (July, 2009):

Change 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 value is unchanged if the original value can be represented by the specified type. Otherwise, the resulting value is unspecified. A value of a scoped enumeration type can also be explicitly converted to a floating point type; the result is the same as that of converting from the original value to the floating point type.



842. Casting to rvalue reference type

Section: 5.2.10  [expr.reinterpret.cast]     Status: DR     Submitter: Steve Adamczyk     Date: 20 March, 2009

[Voted into WP at October, 2009 meeting.]

Both const_cast (5.2.11 [expr.const.cast] paragraph 1) and reinterpret_cast (5.2.10 [expr.reinterpret.cast] paragraph 1) say,

If T is an lvalue reference type, the result is an lvalue; otherwise, the result is an rvalue and the lvalue-to-rvalue (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 expression v.

This introduces a contradiction in the text. According to 5.2.11 [expr.const.cast] paragraph 4,

The result of a reference const_cast refers to the original object.

However, the lvalue-to-rvalue conversion applied to the operand when the target is an rvalue reference type creates a temporary if the operand has class type (4.1 [conv.lval] paragraph 2), meaning that the result will not refer to the original object but to the temporary.

A similar problem exists for reinterpret_cast: according to 5.2.10 [expr.reinterpret.cast] paragraph 11,

a reference cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with the built-in & and * operators (and similarly for reinterpret_cast<T&&>(x)). The result refers to the same object as the source lvalue, but with a different type.

Here the issue is that the unary & operator used in the description requires an lvalue, but the lvalue-to-rvalue conversion is applied to the operand when the target is an rvalue reference type.

It would seem that the lvalue-to-rvalue conversion should not be applied when the target of the cast is an rvalue reference type.

Proposed resolution (July, 2009):

  1. Change 5.2.10 [expr.reinterpret.cast] paragraph 1 as follows:

  2. The result of the expression reinterpret_cast<T>(v) is the result of converting the expression v to type T. If T is an lvalue reference type, the result is an lvalue; if T is an rvalue reference type, the result is an rvalue; otherwise, the result is an rvalue and the lvalue-to-rvalue (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 the expression v. Conversions that can be performed explicitly using reinterpret_cast are listed below. No other conversion can be performed explicitly using reinterpret_cast.
  3. Change 5.2.11 [expr.const.cast] paragraph 1 as follows:

  4. The result of the expression const_cast<T>(v) is of type T. If T is an lvalue reference type, the result is an lvalue; if T is an rvalue reference type, the result is an rvalue; otherwise, the result is an rvalue and the lvalue-to-rvalue (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 expression v. Conversions that can be performed explicitly using const_cast are listed below. No other conversion shall be performed explicitly using const_cast.



801. Casting away constness in a cast to rvalue reference type

Section: 5.2.11  [expr.const.cast]     Status: DR     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 58

[Voted into WP at October, 2009 meeting.]

The rules in 5.2.11 [expr.const.cast] paragraphs 8 and following, defining “casting away constness,” do not cover a cast to an rvalue reference type.

Proposed resolution (September, 2009):

Change 5.2.11 [expr.const.cast] paragraph 9 as follows:

Casting from an lvalue of type T1 to an lvalue of type T2 using a an lvalue reference cast, or casting from an expression of type T1 to an rvalue of type T2 using an rvalue reference cast, casts away constness if a cast from an rvalue of type “pointer to T1” to the type “pointer to T2” casts away constness.



803. sizeof an enumeration type with a fixed underlying type

Section: 5.3.3  [expr.sizeof]     Status: DR     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 70

[Voted into WP at October, 2009 meeting.]

There is no reason for the prohibition of using sizeof on “an enumeration type before all its enumerators have been declared” (5.3.3 [expr.sizeof] paragraph 1) if the underlying type of the enumeration is fixed.

Proposed resolution (July, 2009):

Change 5.3.3 [expr.sizeof] paragraph 1 as follows:

...The sizeof operator shall not be applied to an expression that has function or incomplete type, or to an enumeration type whose underlying type is not fixed before all its enumerators have been declared, or to the parenthesized name of such types, or to an lvalue that designates a bit-field...



672. Sequencing of initialization in new-expressions

Section: 5.3.4  [expr.new]     Status: DR     Submitter: Clark Nelson     Date: 11 January, 2008

[Voted into WP at October, 2009 meeting.]

Consider the following code, which uses double-checked locking (DCL):

    Widget* Widget::Instance() {
      if (pInstance == 0) {           // 1: first check
        lock<mutex> hold(mutW);       // 2: acquire lock
        if (pInstance == 0) {         // 3: second check
          pInstance = new Widget();   // 4: create and assign
        }
      }                               // 5: release lock
    }

We want this to be fully correct when pInstance is an atomic pointer to Widget. To get that result, we have to disallow any assignment to pInstance until after the new object is fully constructed. In other words, we want this to be an invalid transformation of line 4:

    pInstance = operator new(sizeof(Widget));
    new (pInstance) Widget;

I don't think it would be surprising if this were disallowed. For example, if the constructor were to throw an exception, I think many people would expect the variable not to be modified. I think the question is whether it's sufficiently clearly disallowed.

This could be clarified by stating (somewhere appropriate — probably either in 5.3.4 [expr.new] paragraph 16 or paragraph 22) that the initialization of the allocated object is sequenced before the value computation of the new-expression. Then by 5.17 [expr.ass] paragraph 1 (“In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.”), the initialization would have to be sequenced before the assignment.

This is probably not a problem for atomic<Widget*> because its operator= is a function, and function calls provide the necessary guarantees. But for the plain pointer assignment case, there's still a question about whether the sequencing of side effects is constrained as tightly as it should be. In fact, you don't even have to throw an exception from the constructor for there to be a question.

    struct X {
        static X* p;
        X();
    };

    X* X::p = new X;

When the constructor for X is invoked by this new-expression, would it be valid for X::p to be non-null? If the answer is supposed to be “no,” then I think the Standard should express that intent more clearly.

Proposed resolution (March, 2008):

Change 5.3.4 [expr.new] paragraph 22 as indicated:

Whether Initialization of the allocated object is sequenced before the value computation of the new-expression. It is unspecified whether the allocation function is called before evaluating the constructor arguments or after evaluating the constructor arguments but before entering the constructor is unspecified. It is also unspecified whether the arguments to a constructor are evaluated if the allocation function returns the null pointer or exits using an exception.

[Drafting note: The editor may wish to move paragraph 22 up to immediately follow paragraph 16 or 17. The paragraphs numbered 18-21 deal with the case where deallocation is done because initialization terminates with an exception, whereas paragraph 22 applies more to the initialization itself, described in paragraph 16.]

Notes from the September, 2008 meeting:

The proposed wording does not (but should) allow the call to the allocation function to occur in the middle of evaluating arguments for the constructor call.

Proposed resolution (July, 2009):

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

Whether the allocation function is called before evaluating the constructor arguments or after evaluating the constructor arguments but before entering the constructor is unspecified. The invocation of the allocation function is indeterminately sequenced with respect to the evaluations of expressions in the new-initializer. Initialization of the allocated object is sequenced before the value computation of the new-expression. It is also unspecified whether the arguments to a constructor expressions in the new-initializer are evaluated if the allocation function returns the null pointer or exits using an exception.

[Drafting note: the editor may wish to consider moving this paragraph to follow paragraph 15 or 16. Paragraphs 17-19 deal with the case where deallocation is done because initialization terminates with an exception, whereas this paragraph applies more to the initialization itself (described in paragraph 15).]




804. Deducing the type in new auto(x)

Section: 5.3.4  [expr.new]     Status: DR     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 71

[Voted into WP at October, 2009 meeting.]

The type of an allocated object wih the type specifier auto is determined by the rules of copy initialization, but the initialization applied will be direct initialization. This would affect classes which declare their copy constructor explicit, for instance. For consistency, use the same form of initiailization for the deduction as the new expression.

Proposed resolution (July, 2009):

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

If the auto type-specifier appears in the type-specifier-seq of a new-type-id or type-id of a new-expression, the new-expression shall contain a new-initializer of the form

The allocated type is deduced from the new-initializer as follows: Let (e) be e be the assignment-expression in the new-initializer and T be the new-type-id or type-id of the new-expression, then the allocated type is the type deduced for the variable x in the invented declaration (7.1.6.4 [dcl.spec.auto]):

[Example:...




930. alignof with incomplete array type

Section: 5.3.6  [expr.alignof]     Status: DR     Submitter: Alisdair Meredith     Date: 6 July, 2009

[Voted into WP at October, 2009 meeting.]

5.3.6 [expr.alignof] paragraph 1 currently says regarding alignof,

The operand shall be a type-id representing a complete effective object type or a reference to a complete effective object type.

This prohibits taking the alignment of an array type with an unknown bound. There doesn't appear to be any reason for this restriction.

Proposed resolution (July, 2009):

Change 5.3.6 [expr.alignof] paragraph 1 as follows:

The operand shall be a type-id representing a complete effective object type or an array thereof or a reference to a complete effective object type.



854. Left shift and unsigned extended types

Section: 5.8  [expr.shift]     Status: DR     Submitter: Daniel Krügler     Date: 5 April, 2009

[Voted into WP at October, 2009 meeting.]

According to 5.8 [expr.shift] paragraph 2,

The value of E1 << E2 is E1 (interpreted as a bit pattern) left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned type, the value of the result is E1 multiplied by the quantity 2 raised to the power E2, reduced modulo ULLONG_MAX+1 if E1 has type unsigned long long int, ULONG_MAX+1 if E1 has type unsigned long int, UINT_MAX+1 otherwise.

This specification does not allow for extended types with rank greater than long long; in particular, it says that the value of a shifted unsigned extended type is truncated as if it were the same width as an unsigned int.

It's unclear that the second sentence has any normative value; it might be better to relegate it to a note or omit it than to correct it.

Proposed resolution (July, 2009):

Change 5.8 [expr.shift] paragraphs 2-3 as follows:

The value of E1 << E2 is E1 (interpreted as a bit pattern) left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned type, the value of the result is E1 multiplied by the quantity 2 raised to the power E2 × 2E2, reduced modulo ULLONG_MAX+1 if E1 has type unsigned long long int, ULONG_MAX+1 if E1 has type unsigned long int, UINT_MAX+1 otherwise. [Note: the constants ULLONG_MAX, ULONG_MAX, and UINT_MAX are defined in the header <climits>. —end note] one more than the maximum value representable in the result type. Otherwise, if E1 has a signed type and nonnegative value, and E1 × 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 divided by the quantity 2 raised to the power E2 / 2E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.




587. Lvalue operands of a conditional expression differing only in cv-qualification

Section: 5.16  [expr.cond]     Status: DR     Submitter: Howard Hinnant     Date: 20 June 2006

[Voted into WP at October, 2009 meeting.]

Consider the following example:

    template <typename T>
    const T* f(bool b) {
        static T t1 = T();
        static const T t2 = T();
        return &(b ? t1 : t2);  // error?
    }

According to 5.16 [expr.cond], this function is well-formed if T is a class type and ill-formed otherwise. If the second and third operands of a conditional expression are lvalues of the same class type except for cv-qualification, the result of the conditional expression is an lvalue; if they are lvalues of the same non-class type except for cv-qualification, the result is an rvalue.

This difference seems gratuitous and should be removed.

Proposed resolution (June, 2009):

Change 5.16 [expr.cond] paragraph 3 as follows:

Otherwise, if the second and third operand have different types, and either has (possibly cv-qualified) class type, or if both are lvalues of the same type except for cv-qualification, an attempt is made to convert each of those operands to the type of the other. The process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows:




855. Incorrect comments in braced-init-list assignment example

Section: 5.17  [expr.ass]     Status: DR     Submitter: Daniel Krügler     Date: 5 April, 2009

[Voted into WP at October, 2009 meeting.]

5.17 [expr.ass] paragraph 9 has the following example:

    complex<double> z;
    z = { 1,2 };      // meaning z.operator=(1,2)
    z += { 1, 2 };    // meaning z.operator+=(1,2)

These comments make it look as if the assignment operator takes two arguments, which is obviously not the case. It would be better if the comments read something like

     // meaning z.operator=(complex<double>(1,2))

or even

    // meaning z.operator=({1,2}), resolves to
    // z.operator=(complex<double>(1,2)

Proposed resolution (July, 2009):

Change the example in 5.17 [expr.ass] paragraph 9 as follows:

[Example:

  complex<double> z;
  z = { 1,2 };        // meaning z.operator=({1,2})
  z += { 1, 2 };      // meaning z.operator+=({1,2})
  int a, b;
  a = b = { 1 };      // meaning a=b=1;
  a = { 1 } = b;      // syntax error

end example]




715. Class member access constant expressions

Section: 5.19  [expr.const]     Status: DR     Submitter: Steve Adamczyk     Date: 17 September, 2008

[Voted into WP at October, 2009 meeting.]

Bullet 12 of paragraph 2 of 5.19 [expr.const] says,

This wording needs to be clearer that the “effective literal type” provision applies only to the . form of member access and the “pointer to effective literal type” applies only to the -> form.

Proposed resolution (March, 2009):

Delete 5.19 [expr.const] paragraph 2 bullet 11:




721. Where must a variable be initialized to be used in a constant expression?

Section: 5.19  [expr.const]     Status: DR     Submitter: James Kanze     Date: 22 September, 2008

[Voted into WP at October, 2009 meeting.]

5.19 [expr.const] paragraph 2 allows an lvalue-to-rvalue conversion in a constant expression if it is applied to “an lvalue of effective integral type that refers to a non-volatile const variable or static data member initialized with constant expressions.” However, this does not require, as it presumably should, that the initialization occur in the same translation unit and precede the constant expression, nor that the static data member be initialized within the member-specification of its class.

Proposed resolution (March, 2009):

Change 5.19 [expr.const] paragraph 2, bullet 4, sub-bullet 1 as follows:

Additional note, June, 2009:

It has been suggested that the requirement that a static data member be initialized in the class definition is not actually needed but that static data members should be treated like other variable declarations -- a preceding definition with initialization should be sufficient. That is, given

    extern const int i;
    const int i = 5;
    struct S {
      static const int j;
    };
    const int S::j = 5;
    int a1[i];
    int a2[S::j];

there doesn't appear to be a good rationale for making a1 well-formed and a2 ill-formed. Some major implementations accept the declaration of a2 without error.

Proposed resolution (July, 2009):

Change 5.19 [expr.const] paragraph 2, bullet 4, sub-bullet 1 as follows:




806. Enumeration types in integral constant expressions

Section: 5.19  [expr.const]     Status: DR     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 50

[Voted into WP at October, 2009 meeting.]

According to 5.19 [expr.const] paragraph 2, bullet 4, sub-bullet 1, a non-volatile const variable or static data member initialized with constant expressions can be used in an integral constant expression only if it is “of effective integral type.” Unscoped enumeration types should also be accepted in such contexts.

Proposed resolution (September, 2009):

Change 5.19 [expr.const] paragraph 2, bullet 4, sub-bullet 1 as indicated:




717. Unintentional restrictions on the use of thread_local

Section: 7.1.1  [dcl.stc]     Status: DR     Submitter: Clark Nelson     Date: 17 September, 2008

N2800 comment US 36

[Voted into WP at October, 2009 meeting.]

The current wording unintentionally restricts the use of the thread_local specifier in two contexts: block-scope extern variable declarations and static data members. These restrictions are in conflict with 7.1.1 [dcl.stc] paragraph 1.

Proposed resolution (July, 2009):

Change 7.1.1 [dcl.stc] paragraph 4 as follows:

The thread_local specifier shall be applied only to the names of objects or references of namespace scope and, to the names of objects or references of block scope that also specify extern or static, and to the names of static data members. It specifies that the named object or reference has thread storage duration (3.7.2 [basic.stc.thread]).



809. Deprecation of the register keyword

Section: 7.1.1  [dcl.stc]     Status: DR     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 86

[Voted into WP at October, 2009 meeting.]

The register keyword serves very little function, offering no more than a hint that a note says is typically ignored. It should be deprecated in this version of the standard, freeing the reserved name up for use in a future standard, much like auto has been re-used this time around for being similarly useless.

Notes from the March, 2009 meeting:

The consensus of the CWG was in favor of deprecating register.

Proposed resolution (September, 2009):

  1. Change 7.1.1 [dcl.stc] paragraph 3 as follows:

  2. A register specifier is a hint to the implementation that the object so declared will be heavily used. [Note: the hint can be ignored and in most implementations it will be ignored if the address of the object is taken. This use is deprecated (see [depr.register]).end note]
  3. Add a new section following D.4 [depr.string]:

  4. register keyword [depr.register]

    The use of the register keyword as a storage-class-specifier is deprecated (see 7.1.1 [dcl.stc]).




940. Global anonymous unions

Section: 7.1.1  [dcl.stc]     Status: DR     Submitter: UK     Date: 14 July, 2009

N2800 comment UK 85

[Voted into WP at October, 2009 meeting.]

7.1.1 [dcl.stc] paragraph 1 refers to “global anonymous unions.” This reference should include anonymous unions declared in a named namespace, not just in global scope (cf 9.5 [class.union] paragraph 3).

Proposed resolution (September, 2009):

Change 7.1.1 [dcl.stc] paragraph 1 as follows:

If a storage-class-specifier appears in a decl-specifier-seq, there can be no typedef specifier in the same decl-specifier-seq and the init-declarator-list of the declaration shall not be empty (except for global an anonymous unions declared in a named namespace or in the global namespace, which shall be declared static (9.5))...



699. Must constexpr member functions be defined in the class member-specification?

Section: 7.1.5  [dcl.constexpr]     Status: DR     Submitter: Mike Miller     Date: 26 June, 2008

N2800 comment UK 49
N2800 comment JP 12
N2800 comment DE 23

[Voted into WP at October, 2009 meeting.]

According to 7.1.5 [dcl.constexpr] paragraph 1,

The constexpr specifier shall be applied only to the definition of an object, function, or function template, or to the declaration of a static data member of a literal type (3.9 [basic.types]).

As a result, a constexpr member function cannot be simply declared in the class member-specification and defined later; it must be defined in its initial declaration.

This restriction was not part of the initial proposal but was added during the CWG review. However, the original intent is still visible in some of the wording in 7.1.5 [dcl.constexpr]. For example, paragraph 2 refers to applying the constexpr specifier to the “declaration” and not the “definition” of a function or constructor. Although that is formally correct, as definitions are also declarations, it could be confusing. Also, the example in paragraph 6 reads,

    class debug_flag {
    public:
      explicit debug_flag(bool);
      constexpr bool is_on();    // error: debug_flag not literal type
      ...

when the proximate error is that is_on is only declared, not defined. There are also many occurrences of the constexpr specifier in the library clauses where the member function is only declared, not defined.

It's not clear how much simplification is gained by this restriction. There are reasons for defining ordinary inline functions outside the class member-specification (reducing the size and complexity of the class definition, separating interface from implementation, making the editing task easier if program evolution results in an inline function being made non-inline, etc.) that would presumably apply to constexpr member functions as well. It seems feasible to allow separate declaration and definition of a constexpr function; it would simply not be permitted to use it in a constant expression before the definition is seen (although it could presumably still be used in non-constant expressions in that region, like an ordinary inline function).

If the prohibition were relaxed to allow separate declaration and definition of constexpr member functions, some questions would need to be answered, such as whether the constexpr specifier must appear on both declaration and definition (the inline specifier need not). If it can be omitted in one or the other, there's a usability issue regarding the fact that constexpr implies const; the const qualifier would need to be specified explicitly in the declaration in which constexpr was omitted.

If the current restriction is kept, the library clauses should state in an introduction that a non-defining declaration of a constexpr member function should be considered “for exposition only” and not literal code.

Notes from the September, 2008 meeting:

In addition to the original issues described above, the question has arisen whether recursive constexpr functions are or should be permitted. Although they were originally desired by the proposers of the feature, they were prohibited out of an abundance of caution. However, the wording that specified the prohibition was changed during the review process, inadvertently permitting them.

The CWG felt that there are sufficient use cases for recursion that it should not be forbidden (although a new minimum for recursion depth should be added to Annex B [implimits]). If mutual recursion is to be allowed, forward declaration of constexpr functions must also be permitted (answering the original question in this issue). A call to an undefined constexpr function in the body of a constexpr function should be diagnosed when the outer constexpr function is invoked in a context requiring a constant expression; in all other contexts, a call to an undefined constexpr function should be treated as a normal runtime function call, just as if it had been invoked with non-constant arguments.

Proposed resolution (July, 2009):

  1. Change 5 [expr] paragraph 4 as follows:

  2. If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression appears where an integral constant expression is required (5.19 [expr.const]), in which case the program is ill-formed. [Note: most existing implementations of C++ ignore integer overflows. Treatment of division by zero, forming a remainder using a zero divisor, and all floating point exceptions vary among machines, and is usually adjustable by a library function. —end note]
  3. Add the indicated text to 5.19 [expr.const] paragraph 2:

  4. Change 7.1.5 [dcl.constexpr] paragraph 1 as follows:

  5. The constexpr specifier shall be applied only to the definition of an object, the declaration of a function, or function template, or to the declaration of a static data member of an effective literal type (3.9 [basic.types]). If any declaration of a function or function template has the constexpr specifier, then all its declarations shall contain the constexpr specifier. [Note: An explicit specialization can differ from the template declaration with respect to the constexpr specifier. —end note] [Note: function parameters cannot be declared constexpr. —end note] [Example:

      constexpr int square(int x);       //OK, declaration
      constexpr int square(int x) {      // OK
        return x * x;
      }
      constexpr int bufsz = 1024;        // OK, definition
      constexpr struct pixel {           // error: pixel is a type
        int x;
        int y;
        constexpr pixel(int);            // OK, declaration
      };
      constexpr pixel::pixel(int a)
        : x(square(a)), y(square(a)) { } //OK, definition
      constexpr pixel small(2);          // error: square not defined, so small(2)
                                         // not constant (5.19 [expr.const]), so constexpr not satisfied
      constexpr int square(int x) {      // OK, definition
        return x * x;
      }
      constexpr pixel large(4);          // OK, square defined
      int next(constexpr int x) {        // error, not for parameters
        return x + 1;
      }
      extern constexpr int memsz;        // error: not a definition
    

    end example]

  6. Add a new section following 17.6.4.5 [member.functions]:

  7. Implementations shall provide definitions for any non-defining declarations of constexpr functions and constructors within the associated header files.
  8. Add the following bullet to the list in B [implimits] paragraph 2:

(This resolution also resolves issue 695.)




862. Undefined behavior with enumerator value overflow

Section: 7.2  [dcl.enum]     Status: DR     Submitter: Daniel Krügler     Date: 7 April, 2009

[Voted into WP at October, 2009 meeting.]

The type of an enumerator that has no initializing value in an enumeration whose underlying type is not fixed is given by the third bullet of 7.2 [dcl.enum] paragraph 5:

the type of the initializing value is the same as the type of the initializing value of the preceding enumerator unless the incremented value is not representable in that type, in which case the type is an unspecified integral type sufficient to contain the incremented value.

This does not address the case in which there is no such type, meaning that it is apparently undefined behavior. Other cases in which an enumeration value is unrepresentable are made ill-formed (see the preceding paragraph for an enumeration with a fixed underlying type and the following paragraph for the case in which the minimum and maximum values cannot be represented by a single type). It would be better if this case were ill-formed as well, instead of causing undefined behavior.

Proposed resolution (July, 2009):

Change 7.2 [dcl.enum] paragraph 5, bullet 3 as follows:




921. Unclear specification of inline namespaces

Section: 7.3.1  [namespace.def]     Status: DR     Submitter: Michael Wong     Date: 19 June, 2009

[Voted into WP at October, 2009 meeting.]

According to 7.3.1 [namespace.def] paragraph 8,

Specifically, the inline namespace and its enclosing namespace are considered to be associated namespaces (3.4.2 [basic.lookup.argdep]) of one another, and a using-directive (7.3.4 [namespace.udir]) that names the inline namespace is implicitly inserted into the enclosing namespace.

There are two problems with this sentence. First, the concept of namespaces being associated with each other is undefined; 3.4.2 [basic.lookup.argdep] describes how namespaces are associated with types, not with other namespaces. Second, unlike unnamed namespaces, the location of the implicit using-directive is not specified.

Proposed resolution (July, 2009):

Change 7.3.1 [namespace.def] paragraph 8 as follows:

...Specifically, the inline namespace and its enclosing namespace are considered to be associated namespaces (3.4.2 [basic.lookup.argdep]) of one another both added to the set of associated namespaces used in argument-dependent lookup (3.4.2 [basic.lookup.argdep]) whenever one of them is, and a using-directive (7.3.4 [namespace.udir]) that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace (7.3.1.1 [namespace.unnamed]). Furthermore...



926. Inline unnamed namespaces

Section: 7.3.1.1  [namespace.unnamed]     Status: DR     Submitter: Michael Wong     Date: 29 June, 2009

[Voted into WP at October, 2009 meeting.]

In 7.3.1 [namespace.def] paragraph 1, an unnamed-namespace-definition is defined as

However, there is no provision for the inline keyword in the expansion of unnamed namespaces in 7.3.1.1 [namespace.unnamed] paragraph 1. Strictly interpreted, that would mean that the inline qualifier is ignored for unnamed namespaces.

Proposed resolution (September, 2009):

Change 7.3.1.1 [namespace.unnamed] paragraph 1 as follows:

An unnamed-namespace-definition behaves as if it were replaced by

    inlineopt namespace unique { /* empty body */ }
    using namespace unique ;
    namespace unique { namespace-body }

where inline appears if and only if it appears in the unnamed-namespace-definition, all occurrences of unique in a translation unit are replaced by the same identifier, and this identifier differs from all other identifiers in the entire program.87 [Example:...




713. Unclear note about cv-qualified function types

Section: 8.3.5  [dcl.fct]     Status: DR     Submitter: Doug Gregor     Date: 11 September, 2008

[Voted into WP at October, 2009 meeting.]

4.4 [conv.qual] paragraph 3 consists of a note reading,

[Note: Function types (including those used in pointer to member function types) are never cv-qualified (8.3.5 [dcl.fct]). —end note]

However, 8.3.5 [dcl.fct] paragraph 7 says,

A cv-qualifier-seq shall only be part of the function type...

This sounds like a contradiction, although formally it is not: a “function type with a cv-qualifier-seq” is not a “cv-qualified function type.” It would be helpful to make this distinction clearer.

Proposed resolution (March, 2009):

  1. Change 8.3.5 [dcl.fct] paragraph 7 as follows:

  2. A cv-qualifier-seq shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration. [Note: A function type that has a cv-qualifier-seq is not a cv-qualified type; there are no cv-qualified function types. —end note] The effect of a cv-qualifier-seq in a function declarator...
  3. Change 3.9.3 [basic.type.qualifier] paragraph 3 as follows:

  4. ...See 8.3.5 [dcl.fct] and 9.3.2 [class.this] regarding cv-qualified function types that have cv-qualifiers.



908. Deleted global allocation and deallocation functions

Section: 8.4  [dcl.fct.def]     Status: DR     Submitter: John Spicer     Date: 2 June, 2009

[Voted into WP at October, 2009 meeting.]

According to 8.4 [dcl.fct.def] paragraph 10, a deleted definition of a function must be its first declaration. It is not clear whether this requirement can be satisfied for the global allocation and deallocation functions. According to 3.7.4 [basic.stc.dynamic] paragraph 2, they are “implicitly declared in global scope in each translation unit of a program.” However, that does not specify where in the translation unit the declaration is considered to take place. This needs to be clarified.

Proposed resolution (July, 2009):

Change 8.4 [dcl.fct.def] paragraph 10 as follows:

...A deleted definition of a function shall be the first declaration of the function. An implicitly declared allocation or deallocation function (3.7.4 [basic.stc.dynamic]) shall not be defined as deleted. [Example:...



928. Defaulting a function that would be implicitly defined as deleted

Section: 8.4  [dcl.fct.def]     Status: DR     Submitter: Alisdair Meredith     Date: 1 July, 2009

[Voted into WP at October, 2009 meeting.]

8.4 [dcl.fct.def] paragraph 9 says,

A special member function that would be implicitly defined as deleted shall not be explicitly defaulted.

It would be more regular (and thus useful in generic programming) if such a member function were itself simply defined as deleted rather than being made ill-formed.

Proposed resolution (July, 2009):

  1. Change 8.4 [dcl.fct.def] paragraph 9 as follows:

  2. Only special member functions may be explicitly defaulted, and the implementation shall define them as if they had implicit definitions (12.1 [class.ctor], 12.4 [class.dtor], 12.8 [class.copy]). A special member function that would be implicitly defined as deleted shall not be explicitly defaulted. A special member function that would be implicitly defined as deleted may be explicitly defaulted only on its first declaration, in which case it is defined as deleted. A special member function is user-provided if...
  3. Change 12.1 [class.ctor] paragraph 6 as follows:

  4. A non-user-provided 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]). If the implicitly-defined default constructor is explicitly defaulted but the corresponding implicit declaration would have been deleted, the program is ill-formed. The implicitly-defined or explicitly-defaulted default constructor...
  5. Change 12.4 [class.dtor] paragraph 4 as follows:

  6. A program is ill-formed if the class for which a destructor is implicitly defined or explicitly defaulted has: if the implicitly-defined destructor is explicitly defaulted, but the corresponding implicit declaration would have been deleted.

  7. Change 12.8 [class.copy] paragraph 7 as follows:

  8. ...[Note: the copy constructor is implicitly defined even if the implementation elided its use (12.2 [class.temporary]). —end note] A program is ill-formed if the implicitly-defined copy constructor is explicitly defaulted, but the corresponding implicit declaration would have been deleted.
  9. Change 12.8 [class.copy] paragraph 12 as follows:

  10. A non-user-provided copy assignment operator is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type. A program is ill-formed if the implicitly-defined copy assignment operator is explicitly defaulted, but the corresponding implicit declaration would have been deleted.



737. Uninitialized trailing characters in string initialization

Section: 8.5.2  [dcl.init.string]     Status: DR     Submitter: James Kanze     Date: 26 October, 2008

[Voted into WP at October, 2009 meeting.]

The current specification of string initialization in 8.5.2 [dcl.init.string] leaves uninitialized all characters following the terminating '\0' of a character array with automatic storage duration. This is different from C99, in which string initialization is handled like aggregate initialization and all trailing characters are zeroed (6.7.8 paragraph 21).

(See also issue 694, in which we are considering following C99 in a somewhat similar case of zero-initializing trailing data.)

Proposed resolution (September, 2009):

Add a new paragraph following 8.5.2 [dcl.init.string] paragraph 2:

There shall not be more initializers than there are array elements. [Example:

  char cv[4] = "asdf";    // error

is ill-formed since there is no space for the implied trailing '\0'. —end example]

If there are fewer initializers than there are array elements, then each element not explicitly initialized shall be zero-initialized (8.5 [dcl.init]).




936. Array initialization with new string literals

Section: 8.5.2  [dcl.init.string]     Status: DR     Submitter: Alisdair Meredith     Date: 11 July, 2009

[Voted into WP at October, 2009 meeting.]

8.5.2 [dcl.init.string] paragraph 1 says,

A char array (whether plain char, signed char, or unsigned char), char16_t array, char32_t array, or wchar_t array can be initialized by a string-literal (optionally enclosed in braces) with no prefix, with a u prefix, with a U prefix, or with an L prefix, respectively...

This formulation does not allow for raw and UTF-8 literals.

Proposed resolution (July, 2009):

Change 8.5.2 [dcl.init.string] paragraph 1 as follows:

A char array (whether plain char, signed char, or unsigned char), char16_t array, char32_t array, or wchar_t array can be initialized by a string-literal (optionally enclosed in braces) with no prefix, with a u prefix, with a U prefix, or with an L prefix narrow character literal, char16_t string literal, char32_t string literal, or wide string literal, respectively; successive, or by an appropriately-typed string literal enclosed in braces. Successive characters of the string-literal value of the string literal initialize the members elements of the array. [Example: ...



589. Direct binding of class and array rvalues in reference initialization

Section: 8.5.3  [dcl.init.ref]     Status: DR     Submitter: Steve Adamczyk     Date: 26 July 2006

[Voted into WP at October, 2009 meeting.]

The resolutions of issues 391 and 450 say that the reference is “bound to” the class or array rvalue, but it does not say that the reference “binds directly” to the initializer, as it does for the cases that fall under the first bullet in 8.5.3 [dcl.init.ref] paragraph 5. However, this phrasing is important in determining the implicit conversion sequence for an argument passed to a parameter with reference type (13.3.3.1.4 [over.ics.ref]), where paragraph 2 says,

When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the underlying type of the reference according to 13.3.3.1 [over.best.ics]. Conceptually, this conversion sequence corresponds to copy-initializing a temporary of the underlying type with the argument expression.

The above-mentioned issue resolutions stated that no copy is to be made in such reference initializations, so the determination of the conversion sequence does not reflect the initialization semantics.

Simply using the “binds directly” terminology in the new wording may not be the right approach, however, as there are other places in the Standard that also give special treatment to directly-bound references. For example, the first bullet of 5.16 [expr.cond] paragraph 3 says,

If E2 is an lvalue: E1 can be converted to match E2 if E1 can be implicitly converted (clause 4 [conv]) to the type “reference to T2,” subject to the constraint that in the conversion the reference must bind directly (8.5.3 [dcl.init.ref]) to E1.

The effect of simply saying that a reference “binds directly” to a class rvalue can be seen in this example:

    struct B { };
    struct D: B { };
    D f();
    void g(bool x, const B& br) {
        x ? f() : br;   // result would be lvalue
    }

It is not clear that treating this conditional expression as an lvalue is a desirable outcome, even if the result of f() were to “bind directly” to the const B& reference.

Proposed resolution (June, 2009):

  1. Change 8.5.3 [dcl.init.ref] paragraph 5 as follows:

  2. A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

    In all cases except the last (i.e., creating and initializing a temporary from the initializer expression), the reference is said to bind directly to the initializer expression.

  3. Change 5.16 [expr.cond] paragraph 3 bullet 1 as follows:




656. Direct binding to the result of a conversion operator

Section: 8.5.3  [dcl.init.ref]     Status: DR     Submitter: Jason Merrill     Date: 23 October 2007

[Voted into WP at October, 2009 meeting.]

Consider the following example:

    struct A { };
    struct B : public A { };
    struct X {
       operator B();
    };
    X x;

    int main() {
       const A& r = x;
       return 0;
    }

It seems like the resolution of issue 391 doesn't actually cover this; X is not reference-compatible with A, so we go past the modified bullet (8.5.3 [dcl.init.ref] paragraph 5, bullet 2, sub-bullet 1), which reads:

If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound to the object represented by the rvalue (see 3.10 [basic.lval]) or to a sub-object within that object.

and hit

Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy initialization (8.5 [dcl.init]). The reference is then bound to the temporary.

which seems to require that we create an A temporary copied from the return value of X::operator B() rather than bind directly to the A subobject. I think that the resolution of issue 391 should cover this situation as well, and the EDG compiler seems to agree with me.

(See also issue 896.)

Proposed resolution (September, 2009):

  1. Change 8.5.3 [dcl.init.ref] paragraph 5 as follows:

  2. Editorial note: issue 589 makes edits to the top-level bullet preceding this one. The wording resulting from those edits should be changed for consistency with this wording so that the text there reads, “...in the first case and to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object).”

  3. Change 13.3 [over.match] paragraph 2, last bullet as follows:

  4. Change 13.3.1.6 [over.match.ref] paragraph 1 as follows:

  5. Under the conditions specified in 8.5.3 [dcl.init.ref], a reference can be bound directly to an lvalue or class rvalue that is the result of applying a conversion function to an initializer expression. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1 T” is the underlying type of the reference being initialized, and “cv S” is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:

(Note: this resolution also resolves issue 896.)




896. Rvalue references and rvalue-reference conversion functions

Section: 8.5.3  [dcl.init.ref]     Status: DR     Submitter: Steve Adamczyk     Date: 9 May, 2009

[Voted into WP at October, 2009 meeting.]

Consider the following example:

    struct A { } a;
    struct B {
      operator A&&() {
        return static_cast<A&&>(a);
      }
    };
    A&& r = B();

One would expect that r would be bound to the object returned by B::operator A&&(), i.e., a. However, the logic in 8.5.3 [dcl.init.ref] paragraph 5 requires that the result of the conversion function be copied to a temporary and r bound to the temporary.

Probably the way to address this is to add another top-level bullet between the first and second that would essentially mimic the first bullet except dealing with rvalue references: direct binding to reference-compatible rvalues or to the reference-compatible result of a conversion function. (Note that this should only apply to class rvalues; the creation of a temporary for non-class rvalues is necessary to have an object for the reference to bind to.)

(See also issue 656.)

Proposed resolution (September, 2009):

This issue is resolved by the resolution of issue 656.




703. Narrowing for literals that cannot be exactly represented

Section: 8.5.4  [dcl.init.list]     Status: DR     Submitter: Jason Merrill     Date: 2 July, 2008

[Voted into WP at October, 2009 meeting.]

Both of the following initializations are ill-formed because of narrowing, although they were previously well-formed:

    struct A { int i; } a = { 1.0 };
    struct B { float f; } b = { 1.1 };

The first one doesn't seem like a big problem, as there probably isn't much code that has this kind of aggregate initialization. The second might be of more concern, because 1.1 is not representable in either float or double. Is the resulting loss of precision a kind of narrowing that we want to diagnose?

Notes from the September, 2008 meeting:

The CWG agreed that the second initialization should not be a narrowing error; furthermore, this exemption should apply not only to literals but to any floating-point constant expression. Instead of the current formulation, requiring exact bidirectional convertibility, the Standard should only require that the initializer value be within the representable range of the target type.

Proposed resolution (July, 2009):

Change 8.5.4 [dcl.init.list] paragraph 6 as follows:

A narrowing conversion is an implicit conversion




865. Initializing a std::initializer_list

Section: 8.5.4  [dcl.init.list]     Status: DR     Submitter: James Widman     Date: 8 April, 2009

[Voted into WP at October, 2009 meeting.]

There are several problems with the wording of 8.5.4 [dcl.init.list] paragraph 4:

When an initializer list is implicitly converted to a std::initializer_list<E>, the object passed is constructed as if the implementation allocated an array of N elements of type E, where N is the number of elements in the initializer list. Each element of that array is initialized with the corresponding element of the initializer list converted to E, and the std::initializer_list<E> object is constructed to refer to that array. If a narrowing conversion is required to convert the element to E, the program is ill-formed.

First, an initializer list is not an expression, so it is not appropriate to refer to “implicitly convert[ing]” it, as is done in the first sentence.

Also, the conversion of the elements of the initializer list to the elements of the array is not specified to be either copy-initialization or direct-initialization. If this is intended to be viewed as an aggregate initialization, it would be copy-initialization, but that needs to be specified more clearly.

Finally, the initializer list can have nested initializer lists, so the references to converting the element also need to be cleaned up.

Proposed resolution (July, 2009):

Change 8.5.4 [dcl.init.list] paragraph 4 as follows:

When an initializer list is implicitly converted to a An object of type std::initializer_list<E> is constructed from an initializer list, the object passed is constructed as if the implementation allocated an array of N elements of type E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list converted to E, and the std::initializer_list<E> object is constructed to refer to that array. If a narrowing conversion is required to convert the element to E initialize any of the elements, the program is ill-formed. [Example:...



934. List-initialization of references

Section: 8.5.4  [dcl.init.list]     Status: DR     Submitter: Mike Miller     Date: 8 July, 2009

[Voted into WP at October, 2009 meeting.]

According to 8.5.4 [dcl.init.list] paragraph 3,

Otherwise, if T is a reference type, an rvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary.

This means, for an example like

    int i;
    const int& r1{ i };
    int&& r2{ i };

r1 is bound to a temporary containing the value of i, not to i itself, which seems surprising. Also, there's no prohibition here against binding the rvalue reference to an lvalue, as there is in 8.5.3 [dcl.init.ref] paragraph 5 bullet 2, so the initialization of r2 is well-formed, even though the corresponding non-list initialization int&& r3(i) is ill-formed.

There's also a question as to whether this bullet even applies to these examples. According to the decision tree in 8.5 [dcl.init] paragraph 16, initialization of a reference is dispatched to 8.5.3 [dcl.init.ref] in the first bullet, so these cases never make it to the third bullet sending the remaining braced-init-list cases to 8.5.4 [dcl.init.list]. If that's the correct interpretation, there's a problem with 8.5.3 [dcl.init.ref], since it doesn't deal with the braced-init-list cases, and the bullet in 8.5.4 [dcl.init.list] paragraph 3 dealing with references is dead code that's never used.

Proposed resolution (July, 2009):

  1. Move the third bullet of the list in 8.5 [dcl.init] paragraph 16 to the top of the list:

  2. Change 8.5.4 [dcl.init.list] paragraph 3, bullets 4 and 5, as follows:




874. Class-scope definitions of enumeration types

Section: 9.2  [class.mem]     Status: DR     Submitter: Daniel Krügler     Date: 16 April, 2009

[Voted into WP at October, 2009 meeting.]

According to 9.2 [class.mem] paragraph 1,

The enumerators of an enumeration (7.2 [dcl.enum]) defined in the class are members of the class... A member shall not be declared twice in the member-specification, except that a nested class or member class template can be declared and then later defined.

The enumerators of a scoped enumeration are not members of the containing class; the wording should be revised to apply only to unscoped enumerations.

The second part of the cited wording from 9.2 [class.mem] prohibits constructs like:

    class C {
      public:
        enum E: int;
      private:
        enum E: int { e0 };
    };

which might be useful in making the enumeration type, but not its enumerators, accessible.

Notes from the July, 2009 meeting:

According to 11.1 [class.access.spec] paragraph 4, the access must be the same for all declarations of a class member. The suggested usage given above violates that requirement: the second declaration of E declares the enumeration itself, not just the enumerators, to be private. The CWG did not feel that the utility of the suggested feature warranted the complexity of an exception to the general rule.

Proposed resolution (July, 2009):

  1. Change 9.2 [class.mem] paragraph 1 as follows:

  2. ...The enumerators of an unscoped enumeration (7.2 [dcl.enum]) defined in the class are members of the class... A member shall not be declared twice in the member-specification, except that a nested class or member class template can be declared and then later defined, and except that an enumeration can be first introduced with an opaque-enum-declaration and then later be redeclared with an enum-specifier.
  3. Change the example in 11.1 [class.access.spec] paragraph 4 as follows:

  4. When a member is redeclared within its class definition, the access specified at its redeclaration shall be the same as at its initial declaration. [Example:

      struct S {
        class A;
        enum E : int;
      private:
        class A { };          // error: cannot change access
        enum E : int { e0 };  // error: cannot change access
      };
    

    end example]




608. Determining the final overrider of a virtual function

Section: 10.3  [class.virtual]     Status: DR     Submitter: Mike Miller     Date: 7 December 2006

[Voted into WP at October, 2009 meeting.]

According to 10.3 [class.virtual] paragraph 2:

Then in any well-formed class, for each virtual function declared in that class or any of its direct or indirect base classes there is a unique final overrider that overrides that function and every other overrider of that function. The rules for member lookup (10.2 [class.member.lookup]) are used to determine the final overrider for a virtual function in the scope of a derived class but ignoring names introduced by using-declarations.

I think that description is wrong on at least a couple of counts. First, consider the following example:

    struct A { virtual void f(); };
    struct B: A { };
    struct C: A { void f(); };
    struct D: B, C { };

What is the “unique final overrider” of A::f() in D? According to 10.3 [class.virtual] paragraph 2, we determine that by looking up f in D using the lookup rules in 10.2 [class.member.lookup]. However, that lookup determines that f in D is ambiguous, so there is no “unique final overrider” of A::f() in D. Consequently, because “any well-formed class” must have such an overrider, D must be ill-formed.

Of course, we all know that D is not ill-formed. In fact, 10.3 [class.virtual] paragraph 10 contains an example that illustrates exactly this point:

struct A {
    virtual void f();
};
struct B1 : A {     // note non-virtual derivation
    void f();
};
struct B2 : A {
    void f();
};
struct D : B1, B2 { // D has two separate A subobjects
};

In class D above there are two occurrences of class A and hence two occurrences of the virtual member function A::f. The final overrider of B1::A::f is B1::f and the final overrider of B2::A::f is B2::f.

It appears that the requirement for a “unique final overrider” in 10.3 [class.virtual] paragraph 2 needs to say something about sub-objects. Whatever that “something” is, you can't just say “look up the name in the derived class using 10.2 [class.member.lookup].”

There's another problem with using the 10.2 [class.member.lookup] lookup to specify the final overrider: name lookup just looks up the name, while the overriding relationship is based not only on the name but on a matching parameter-type-list and cv-qualification. To illustrate this point:

    struct X {
        virtual void f();
    };
    struct Y: X {
        void f(int);
    };
    struct Z: Y { };

What is the “unique final overrider” of X::f() in A? Again, 10.3 [class.virtual] paragraph 2 says you're supposed to look up f in Z to find it; however, what you find is Y::f(int), not X::f(), and that's clearly wrong.

Proposed Resolution (December, 2006):

Change 10.3 [class.virtual] paragraph 2 as follows:

Then in any well-formed class, for each virtual function declared in that class or any of its direct or indirect base classes there is a unique final overrider that overrides that function and every other overrider of that function. The rules for member lookup (10.2 [class.member.lookup]) are used to determine the final overrider for a virtual function in the scope of a derived class but ignoring names introduced by using-declaration s. A virtual member function vf of a class C is a final overrider unless the most derived class (1.8 [intro.object]) of which C is a base class (if any) declares or inherits another member function that overrides vf. In a derived class, if a virtual member function of a base class subobject has more than one final overrider, the program is ill-formed.

Proposed resolution (July, 2009):

Change 10.3 [class.virtual] paragraph 2 as follows:

...Then in any well-formed class, for each virtual function declared in that class or any of its direct or indirect base classes there is a unique final overrider that overrides that function and every other overrider of that function. The rules for member lookup (10.2 [class.member.lookup]) are used to determine the final overrider for a virtual function in the scope of a derived class but ignoring names introduced by using-declarations. A virtual member function C::vf of a class object S is a final overrider unless the most derived class (1.8 [intro.object]) of which S is a base class subobject (if any) declares or inherits another member function that overrides vf. In a derived class, if a virtual member function of a base class subobject has more than one final overrider, the program is ill-formed. [Example: ... —end example] [Example:

    struct A { virtual void f(); };
    struct B: A { };
    struct C: A { void f(); };
    struct D: B, C { };    // OK; A::f and C::f are the final overriders
                           // for the B and C subobjects, respectively

end example]




257. Abstract base constructors and virtual base initialization

Section: 12.6.2  [class.base.init]     Status: DR     Submitter: Mike Miller     Date: 1 Nov 2000

[Voted into WP at October, 2009 meeting.]

Must a constructor for an abstract base class provide a mem-initializer for each virtual base class from which it is directly or indirectly derived? Since the initialization of virtual base classes is performed by the most-derived class, and since an abstract base class can never be the most-derived class, there would seem to be no reason to require constructors for abstract base classes to initialize virtual base classes.

It is not clear from the Standard whether there actually is such a requirement or not. The relevant text is found in 12.6.2 [class.base.init] paragraph 6:

All sub-objects representing virtual base classes are initialized by the constructor of the most derived class (1.8 [intro.object]). If the constructor of the most derived class does not specify a mem-initializer for a virtual base class V, then V's default constructor is called to initialize the virtual base class subobject. If V does not have an accessible default constructor, the initialization is ill-formed. A mem-initializer naming a virtual base class shall be ignored during execution of the constructor of any class that is not the most derived class.

This paragraph requires only that the most-derived class's constructor have a mem-initializer for virtual base classes. Should the silence be construed as permission for constructors of classes that are not the most-derived to omit such mem-initializers?

Christopher Lester, on comp.std.c++, March 19, 2004: If any of you reading this posting happen to be members of the above working group, I would like to encourage you to review the suggestion contained therein, as it seems to me that the final tenor of the submission is both (a) correct (the silence of the standard DOES mandate the omission) and (b) describes what most users would intuitively expect and desire from the C++ language as well.

The suggestion is to make it clearer that constructors for abstract base classes should not be required to provide initialisers for any virtual base classes they contain (as only the most-derived class has the job of initialising virtual base classes, and an abstract base class cannot possibly be a most-derived class).

For example:

struct A {
  A(const int i, const int j) {};
};

struct B1 : virtual public A {
  virtual void moo()=0;
  B1() {};   // (1) Look! not "B1() : A(5,6) {};"
};

struct B2 : virtual public A {
  virtual void cow()=0;
  B2() {};   // (2) Look! not "B2() : A(7,8) {};"
};

struct C : public B1, public B2 {
  C() : A(2,3) {};
  void moo() {};
  void cow() {};
};

int main() {
  C c;
  return 0;
};

I believe that, by not expressly forbidding it, the standard does (and should!) allow the above code. However, as the standard doesn't expressly allow it either (have I missed something?) there appears to be room for misunderstanding. For example, g++ version 3.2.3 (and maybe other versions as well) rejects the above code with messages like:

	In constructor `B1::B1()':
	no matching function for call to `A::A()'
	candidates are: A::A(const A&)
         	        A::A(int, int)

Fair enough, the standard is perhaps not clear enough. But it seems to be a shame that although this issue was first raised in 2000, we are still living with it today.

Note that we can work-around, and persuade g++ to compile the above by either (a) providing a default constructor A() for A, or (b) supplying default values for i and j in A(i,j), or (c) replace the construtors B1() and B2() with the forms shown in the two comments in the above example.

All three of these workarounds may at times be appropriate, but equally there are other times when all of these workarounds are particularly bad. (a) and (b) may be very bad if you are trying to enforce string contracts among objects, while (c) is just barmy (I mean why did I have to invent random numbers like 5, 6, 7 and 8 just to get the code to compile?).

So to to round up, then, my plea to the working group is: "at the very least, please make the standard clearer on this issue, but preferrably make the decision to expressly allow code that looks something like the above"

Proposed resolution (July, 2009):

  1. Add the indicated text (moved from paragraph 11) to the end of 12.6.2 [class.base.init] paragraph 7:

  2. ...The initialization of each base and member constitutes a full-expression. Any expression in a mem-initializer is evaluated as part of the full-expression that performs the initialization. A mem-initializer where the mem-initializer-id names a virtual base class is ignored during execution of a constructor of any class that is not the most derived class.
  3. Change 12.6.2 [class.base.init] paragraph 8 as follows:

  4. If a given non-static data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4 [class.abstract]), then

    [Note: An abstract class (10.4 [class.abstract]) is never a most derived class, thus its constructors never initialize virtual base classes, therefore the corresponding mem-initializers may be omitted. —end note] After the call to a constructor for class X has completed...

  5. Change 12.6.2 [class.base.init] paragraph 10 as follows:

  6. Initialization shall proceed proceeds in the following order:

    [Note: the declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. —end note]

  7. Remove all normative text in 12.6.2 [class.base.init] paragraph 11, keeping the example:

  8. All subobjects representing virtual base classes are initialized by the constructor of the most derived class (1.8 [intro.object]). If the constructor of the most derived class does not specify a mem-initializer for a virtual base class V, then V's default constructor is called to initialize the virtual base class subobject. If V does not have an accessible default constructor, the initialization is ill-formed. A mem-initializer naming a virtual base class shall be ignored during execution of the constructor of any class that is not the most derived class. [Example:...



888. Union member initializers

Section: 12.6.2  [class.base.init]     Status: DR     Submitter: Alisdair Meredith     Date: 6 May, 2009

[Voted into WP at October, 2009 meeting.]

12.6.2 [class.base.init] paragraph 5 forbids initializing multiple members of a union via mem-initializers:

If a ctor-initializer specifies more than one mem-initializer for the same member, for the same base class or for multiple members of the same union (including members of anonymous unions), the ctor-initializer is ill-formed.

However, there is no corresponding restriction against specifying brace-or-equal-initializers for multiple union members, nor for a non-overlapping pair of brace-or-equal-initializer and mem-initializer. This is presumably an oversight.

Proposed resolution (July, 2009):

  1. Change 9.5 [class.union] paragraph 1 as follows:

  2. ...If a union contains a non-static data member of reference type the program is ill-formed. At most one non-static data member of a union shall have a brace-or-equal-initializer. [Note:...
  3. Change 12.6.2 [class.base.init] paragraph 5 as follows:

  4. ...If a ctor-initializer specifies more than one mem-initializer for the same member, or for the same base class or for multiple members of the same union (including members of anonymous unions), the ctor-initializer is ill-formed.
  5. Change 12.6.2 [class.base.init] paragraph 8 as follows:

  6. ...An attempt to initialize more than one non-static data member of a union renders the program ill-formed. After the call to a constructor for class X has completed...



704. To which postfix-expressions does overload resolution apply?

Section: 13.3.1.1  [over.match.call]     Status: DR     Submitter: Jens Maurer     Date: 29 July, 2008

[Voted into WP at October, 2009 meeting.]

There are several problems with the phrasing of 13.3.1.1 [over.match.call] paragraphs 1 and 3. Paragraph 1 reads,

Recall from 5.2.2 [expr.call], that a function call is a postfix-expression, possibly nested arbitrarily deep in parentheses, followed by an optional expression-list enclosed in parentheses: Overload resolution is required if the postfix-expression is the name of a function, a function template (14.6.6 [temp.fct]), an object of class type, or a set of pointers-to-function.

Aside from the fact that directly addressing the reader (“Recall that...”) is stylistically incongruous with the rest of the Standard, as well as the fact that 5.2.2 [expr.call] doesn't mention parentheses at all, this wording does not cover member function calls: a member access expression isn't “the name” of anything. This should perhaps be reworded to refer to being either an id-expression or the id-expression in a member access expression. This could be either by using two lines in the “of the form” citation or in the discussion following the syntax reference.

In addition, paragraph 3 refers to “a postfix-expression of the form &F,” which is an oxymoron: &F is a unary-expression, not a postfix-expression. One possibility would be to explicitly include the parentheses needed in this case, i.e., “a postfix-expression of the form (&F)...”

Proposed resolution (September, 2009):

Replace the entirety of 13.3.1.1 [over.match.call] with the following two paragraphs:

In a function call (5.2.2 [expr.call])

if the postfix-expression denotes a set of overloaded functions and/or function templates, overload resolution is applied as specified in 13.3.1.1.1 [over.call.func]. If the postfix-expression denotes an object of class type, overload resolution is applied as specified in 13.3.1.1.2 [over.call.object].

If the postfix-expression denotes the address of a set of overloaded functions and/or function templates, overload resolution is applied using that set as described above. If the function selected by overload resolution is a non-static member function, the program is ill-formed. [Note: The resolution of the address of an overload set in other contexts is described in 13.4 [over.over]. —end note]




604. Argument list for overload resolution in copy-initialization

Section: 13.3.1.3  [over.match.ctor]     Status: DR     Submitter: Dawn Perchik     Date: 4 November 2006

[Voted into WP at October, 2009 meeting.]

According to 13.3.1.3 [over.match.ctor],

When objects of class type are direct-initialized (8.5 [dcl.init]), or copy-initialized from an expression of the same or a derived class type (8.5 [dcl.init])... [the] argument list is the expression-list within the parentheses of the initializer.

However, in copy initialization (using the “=” notation), there need be no parentheses. What is the argument list in that case?

Proposed resolution (June, 2009):

Change 13.3.1.3 [over.match.ctor] paragraph 1 as follows:

...The argument list is the expression-list or assignment-expression within the parentheses of the initializer initializer.



877. Viable functions and binding references to rvalues

Section: 13.3.2  [over.match.viable]     Status: DR     Submitter: Daniel Krügler     Date: 23 April, 2009

[Voted into WP at October, 2009 meeting.]

13.3.2 [over.match.viable] paragraph 3 says,

If the parameter has reference type, the implicit conversion sequence includes the operation of binding the reference, and the fact that a reference to non-const cannot be bound to an rvalue can affect the viability of the function (see 13.3.3.1.4 [over.ics.ref]).

This should say “lvalue reference to non-const,” as is correctly stated in 13.3.3.1.4 [over.ics.ref] paragraph 3.

Proposed resolution (July, 2009):

Change 13.3.2 [over.match.viable] paragraph 3 as follows:

If the parameter has reference type, the implicit conversion sequence includes the operation of binding the reference, and the fact that a an lvalue reference to non-const cannot be bound to an rvalue can affect the viability of the function (see 13.3.3.1.4 [over.ics.ref]).



879. Missing built-in comparison operators for pointer types

Section: 13.6  [over.built]     Status: DR     Submitter: Daniel Krügler     Date: 25 April, 2009

[Voted into WP at October, 2009 meeting.]

13.6 [over.built] paragraph 15 restricts the built-in comparison operators to

every T, where T is an enumeration type or pointer to effective object type

This omits both pointers to function types and pointers to void.

Proposed resolution (July, 2009):

  1. Add a new paragraph following 5.9 [expr.rel] paragraph 2:

  2. Pointers to void (after pointer conversions) can be compared, with a result defined as follows: If both pointers represent the same address or are both the null pointer value, the result is true if the operator is <= or >= and false otherwise; otherwise the result is unspecified.
  3. Change 5.10 [expr.eq] paragraph 1 as follows:

  4. ...Pointers to objects or functions of the same type (after pointer conversions) can be compared for equality...
  5. Change 13.6 [over.built] paragraph 15 as follows:

  6. For every T, where T is an enumeration type or, a pointer to effective object type, or std::nullptr_t, there exist candidate operator functions of the form...



840. Rvalue references as nontype template parameters

Section: 14.2  [temp.param]     Status: DR     Submitter: Steve Adamczyk     Date: 13 March, 2009

[Voted into WP at October, 2009 meeting.]

Nontype template parameters are currently allowed to have rvalue reference type (14.2 [temp.param] paragraph 4 bullet 3 just says “reference,” not “lvalue reference”). However, with the change of N2844 voted in (which prohibits rvalue references from binding to lvalues), I can't think of any way to specify a valid template argument for a parameter of rvalue reference type. If that's the case, should we restrict nontype template parameters to lvalue reference types?

Proposed resolution (July, 2009):

Change 14.2 [temp.param] paragraph 4, bullet 3 as follows:




929. What is a template alias?

Section: 14.6.7  [temp.alias]     Status: DR     Submitter: Alisdair Meredith     Date: 2 July, 2009

[Voted into WP at October, 2009 meeting.]

Although it is a reasonable assumption that a template-declaration in which the declaration is an alias-declaration declares a template alias, that is not said explicitly in 14.6.7 [temp.alias] nor, apparently, anywhere else.

Proposed resolution (September, 2009):

Change 14.6.7 [temp.alias] paragraph 1 as follows:

A template-declaration in which the declaration is an alias-declaration (clause 7) declares the identifier to be a template alias. A template alias declares template alias is a name for a family of types. The name of the template alias is a template-name.



730. Explicit specializations of members of non-template classes

Section: 14.8.3  [temp.expl.spec]     Status: DR     Submitter: Bronek Kozicki     Date: 3 October, 2008

N2800 comment DE 14

[Voted into WP at October, 2009 meeting.]

The list of entities that can be explicitly specialized in 14.8.3 [temp.expl.spec] paragraph 1 includes member templates of class templates but not member templates of non-template classes. This omission could lead to the conclusion that such member templates cannot be explicitly specialized. (Note, however, that paragraph 3 refers to “an explicit specialization for a member template of [a] class or class template.”)

Proposed resolution (July, 2009):

Change 14.8.3 [temp.expl.spec] paragraph 1 as follows:

An explicit specialization of any of the following:

can be declared...




884. Defining an explicitly-specialized static data member

Section: 14.8.3  [temp.expl.spec]     Status: DR     Submitter: Daniel Krügler     Date: 29 April, 2009

[Voted into WP at October, 2009 meeting.]

14.8.3 [temp.expl.spec] paragraphs 15-16 contain the following note:

[Note: there is no syntax for the definition of a static data member of a template that requires default initialization.
    template<> X Q<int>::x;
This is a declaration regardless of whether X can be default initialized (8.5 [dcl.init]). —end note]

While this note is still accurate, the C++0x list initialization syntax provides a way around the restriction, which could be useful if the class is not copyable or movable but has a default constructor. Perhaps the note should be updated to mention that possibility?

Proposed resolution (July, 2009):

Change 14.8.3 [temp.expl.spec] paragraphs 15-16 as follows:

An explicit specialization of a static data member of a template is a definition if the declaration includes an initializer; otherwise, it is a declaration. [Note: there is no syntax for the The definition of a static data member of a template that requires default initialization. must use a braced-init-list:

  template<> X Q<int>::x;      // declaration
  template<> X Q<int>::x ();   // error: declares a function
  template<> X Q<int>::x {};   // definition

This is a declaration regardless of whether X can be default initialized (8.5 [dcl.init]).end note]




657. Abstract class parameter in synthesized declaration

Section: 14.9.2  [temp.deduct]     Status: DR     Submitter: Mike Miller     Date: 31 October 2007

[Voted into WP at October, 2009 meeting.]

A customer of ours recently brought the following example to our attention. There's some question as to whether the Standard adequately addresses this example, and if it does, whether the outcome is what we'd like to see. Here's the example:

    struct Abs {
      virtual void x() = 0;
    };

    struct Der: public Abs {
      virtual void x();
    };

    struct Cnvt {
      template <typename F> Cnvt(F);
    };

    void foo(Cnvt a);
    void foo(Abs &a);

    void f() {
      Der d;
      Abs *a = &d;
      foo(*a);        // #1
      return 0;
    }

The question is how to perform overload resolution for the call at #1. To do that, we need to determine whether foo(Cnvt) is a viable function. That entails deciding whether there is an implicit conversion sequence that converts Abs (the type of *a in the call) to Cnvt (13.3.2 [over.match.viable] paragraph 3), and that involves a recursive invocation of overload resolution.

The initialization of the parameter of foo(Cnvt) is a case of copy-initialization of a class by user-defined conversion, so the candidate functions are the converting constructors of Cnvt (13.3.1.4 [over.match.copy] paragraph 1), of which there are two: the implicitly-declared copy constructor and the constructor template.

According to 14.8.1 [temp.inst] paragraph 8,

If a function template or a member function template specialization is used in a way that involves overload resolution, a declaration of the specialization is implicitly instantiated (14.9.3 [temp.over]).

Template argument deduction results in “synthesizing” (14.9.3 [temp.over] paragraph 1) (or “instantiating,” 14.8.1 [temp.inst] paragraph 8) the declaration

    Cnvt::Cnvt(Abs)

Because Abs is an abstract class, this declaration violates the restriction of 10.4 [class.abstract] paragraph 3 (“An abstract class shall not be used as a parameter type...”), and because a parameter of an abstract class type does not cause a deduction failure (it's not in the bulleted list in 14.9.2 [temp.deduct] paragraph 2), the program is ill-formed. This error is reported by both EDG and Microsoft compilers, but not by g++.

It seems unfortunate that the program would be rendered ill-formed by a semantic violation in a declaration synthesized solely for the purpose of overload resolution analysis; foo(Cnvt) would not be selected by overload resolution, so Cnvt::Cnvt(Abs) would not be instantiated.

There's at least some indication that a parameter with an abstract class type should be a deduction failure; an array element of abstract class type is a deduction failure, so one might expect that a parameter would be, also.

(See also issue 339; this question might be addressed as part of the direction described in the notes from the July, 2007 meeting.)

Notes from the June, 2008 meeting:

Paper N2634, adopted at the June, 2008 meeting, replaces the normative list of specific errors accepted as deduction failures by a general statement covering all “invalid types and expressions in the immediate context of the function type and its template parameter types,” so the code is now well-formed. However, the previous list is now a note, and the note should be updated to mention this case.

Proposed resolution (August, 2008):

Add a new bullet following the last bullet of the note in 14.9.2 [temp.deduct] paragraph 8 as follows:




876. Type references in rvalue reference deduction specification

Section: 14.9.2.1  [temp.deduct.call]     Status: DR     Submitter: Steve Adamczyk     Date: 20 April, 2009

[Voted into WP at October, 2009 meeting.]

14.9.2.1 [temp.deduct.call] paragraph 3 says,

If P is of the form T&&, where T is a template parameter, and the argument is an lvalue, the type A& is used in place of A for type deduction.

The type references in that sentence are inconsistent with the normal usage in the Standard; they should instead refer to “an rvalue reference to a cv-unqualified template parameter” and “lvalue reference to A.”

Proposed resolution (July, 2009):

Change 14.9.2.1 [temp.deduct.call] paragraph 3 as follows:

If P is a cv-qualified type, the top level cv-qualifiers of P's type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction. If P is of the form T&&, where T is a template parameter, an rvalue reference to a cv-unqualified template parameter and the argument is an lvalue, the type A& “lvalue reference to A is used in place of A for type deduction.



601. Type of literals in preprocessing expressions

Section: 16.1  [cpp.cond]     Status: DR     Submitter: Daveed Vandevoorde     Date: 23 October 2006

[Voted into WP at October, 2009 meeting.]

The description of preprocessing expressions in 16.1 [cpp.cond] paragraph 4 says,

The resulting tokens comprise the controlling constant expression which is evaluated according to the rules of 5.19 using arithmetic that has at least the ranges specified in 18.3 [support.limits], except that all signed and unsigned integer types act as if they have the same representation as, respectively, intmax_t or uintmax_t (18.3.2).

However, this does not address the type implicitly assigned to integral literals. For example, in an implementation where int is 32 bits and long long is 64 bits, is a literal like 0xffffffff signed or unsigned? WG14 adopted DR 265 to deal with this issue in the essentially-identical wording in C99; we should probably follow suit for C++.

Proposed Resolution (July, 2009):

Change 16.1 [cpp.cond] paragraph 4 as follows:

...and then each preprocessing token is converted into a token. The resulting tokens comprise the controlling constant expression which is evaluated according to the rules of 5.19 [expr.const] using arithmetic that has at least the ranges specified in 18.3 [support.limits], except that. For the purposes of this token conversion and evaluation all signed and unsigned integer types act as if they have the same representation as, respectively, intmax_t or uintmax_t (18.4.2 [stdinth])[Footnote: Thus on an implementation where std::numeric_limits<int>::max() is 0x7FFF and std::numeric_limits<unsigned int>::max() is 0xFFFF, the integer literal 0x8000 is signed and positive within a #if expression even though it is unsigned in translation phase 7 (2.2 [lex.phases]). —end footnote]. This includes interpreting character literals...



618. Casts in preprocessor conditional expressions

Section: 16.1  [cpp.cond]     Status: DR     Submitter: Martin Sebor     Date: 12 February 2007

[Voted into WP at October, 2009 meeting.]

16.1 [cpp.cond] paragraph 1 states,

The expression that controls conditional inclusion shall be an integral constant expression except that: it shall not contain a cast...

The prohibition of casts is vacuous and misleading: as pointed out in the footnote in that paragraph,

Because the controlling constant expression is evaluated during translation phase 4, all identifiers either are or are not macro names — there simply are no keywords, enumeration constants, and so on.

As a result, there can be no casts, which require either keywords or identifiers that resolve to types in order to be recognized as casts. The wording on casts should be removed and replaced by a note recognizing this implication.

Notes from the April, 2007 meeting:

The CWG agreed with this suggested resolution; however, the reference is in the “Preprocessing Directives” clause, which WG21 intends to keep in as close synchronization as possible with the corresponding wording in the C Standard. Any change here must therefore be done in consultation with WG14. Clark Nelson will fulfill this liaison function.

It was also noted that the imminent introduction of constexpr also has the potential for a similar kind of confusion, so the proposed resolution should address both casts and constexpr.

Proposed resolution (July, 2009):

Change 16.1 [cpp.cond] paragraph 1 as follows:

The expression that controls conditional inclusion shall be an integral constant expression except that: it shall not contain a cast; identifiers (including those lexically identical to keywords)...



626. Preprocessor string literals

Section: 16.3.2  [cpp.stringize]     Status: DR     Submitter: Gennaro Prota     Date: 12 September 2006

[Voted into WP at October, 2009 meeting.]

Clause 16 [cpp] refers in several places to “character string literals” without specifying whether they are narrow or wide strings. For instance, what kind of string does the # operator (16.3.2 [cpp.stringize]) produce?

16.4 [cpp.line] paragraph 1 says,

The string literal of a #line directive, if present, shall be a character string literal.

Is “character string literal” intended to mean a narrow string literal? (Also, there is no string-literal mentioned in the grammatical descriptions of #line; paragraph 4 reads,

which is apparently intended to suggest a string literal but does not use the term.)

16.8 [cpp.predefined] should also specify what kind of character string literals are produced by the various string-valued predefined macros.

Notes from the July, 2007 meeting:

The CWG affirmed that all the string literals mentioned in Clause 16 [cpp] are intended to be narrow strings.

Proposed resolution (September, 2008)

  1. Change the footnote in 16 [cpp] paragraph 1 as follows:

  2. Thus, preprocessing directives are commonly called “lines.” These “lines” have no other syntactic significance, as all white space is equivalent except in certain situations during preprocessing (see the # character string literal creation operator in 16.3.2 [cpp.stringize], for example).
  3. Change 16.3.2 [cpp.stringize] paragraph 2 as follows:

  4. If, in the replacement list, a parameter is immediately preceded by a # preprocessing token, both are replaced by a single character ordinary string literal (2.14.5 [lex.string]) preprocessing token that contains the spelling of the preprocessing token sequence for the corresponding argument... Otherwise, the original spelling of each preprocessing token in the argument is retained in the character ordinary string literal, except for special handling for producing the spelling of string literals and character literals: a \ character is inserted before each " and \ character of a character literal or string literal (including the delimiting " characters). If the replacement that results is not a valid character ordinary string literal, the behavior is undefined. The character ordinary string literal corresponding to an empty argument is "". The order of evaluation of # and ## operators is unspecified.
  5. Change 16.3.5 [cpp.scope] paragraph 6 as follows:

  6. To illustrate the rules for creating character ordinary string literals and concatenating tokens, the sequence... or, after concatenation of the character ordinary string literals...
  7. Change 16.4 [cpp.line] paragraph 1 as follows:

  8. The string literal of a #line directive, if present, shall be a character an ordinary string literal.
  9. Change 16.4 [cpp.line] paragraph 4 as follows:

  10. ...and changes the presumed name of the source file to be the contents of the character ordinary string literal.
  11. Change 16.8 [cpp.predefined] paragraph 1 as follows:

  12. __DATE__

    __FILE__

    ...

    __TIME__

Notes from the September, 2008 meeting:

The proposed resolution will be discussed with the C Committee before proceeding, as it is expected that the next revision of the C Standard will also adopt new forms of string literals.

Additional notes (May, 2009):

At its most recent meeting, the C Committee decided to keep the existing term, “character string literal.”

One possibility for maintaining compatible phraseology with the C Standard would be to replace the occurrences of “ordinary string literal” in 2.14.5 [lex.string] with “character string literal,” instead of the extensive set of changes above.

Another possibility would be to leave the references in clause 16 [cpp] unchanged and just insert a prefatory comment near the beginning that every occurrence of “character string literal” refers to a string-literal with no prefix. (The use of “ordinary string literal” in the preceding edits is problematic in that the phrase includes raw string literals as well as unprefixed literals.)

Proposed resolution (July, 2009):

  1. Change 16.3.2 [cpp.stringize] paragraph 2 as follows:

  2. A character string literal is a string-literal with no prefix. If, in the replacement list, a parameter is immediately preceded by a # preprocessing token...
  3. Change the fifteenth bullet of Annex B [implimits] paragraph 2 as follows:




831. Limit on recursively nested template instantiations

Section: B  [implimits]     Status: DR     Submitter: DE     Date: 3 March, 2009

N2800 comment DE 25

[Voted into WP at October, 2009 meeting.]

The limit of 17 recursively-nested template instantiations is too small for modern programming practices such as template metaprogramming. It is unclear, however, whether this is a useful metric; see this paper for an example that honors the limit but results in over 750 billion instantiations.

Notes from the July, 2009 meeting:

The consensus of the CWG was to increase the limit to 1024.

Proposed resolution (September, 2009):

Change B [implimits], the fourth bullet from the end, as follows:






Issues with "WP" Status


598. Associated namespaces of overloaded functions and function templates

Section: 3.4.2  [basic.lookup.argdep]     Status: WP     Submitter: Mike Miller     Date: 27 September 2006

[Voted into the WP at the March, 2009 meeting.]

The resolution of issue 33 added the following wording in 3.4.2 [basic.lookup.argdep]:

In addition, if the argument is the name or address of a set of overloaded functions and/or function templates, its associated classes and namespaces are the union of those associated with each of the members of the set: the namespace in which the function or function template is defined and the classes and namespaces associated with its (non-dependent) parameter types and return type.

This wording is self-contradictory: although it claims that the treatment of overload sets is intended to be “the union of those associated with each of the members of the set,” it says that the namespace of which each function or function template is a member is to be considered an associated namespace. That is different from the case of a non-overloaded function argument; in that case, because only the type of the argument is considered, the namespace of which the function is a member is not an associated namespace. This should be rectified so that overloaded and unoverloaded functions really are treated the same.

Proposed resolution (June, 2008):

Change 3.4.2 [basic.lookup.argdep] paragraph 2 as follows:

...In addition, if the argument is the name or address of a set of overloaded functions and/or function templates, its associated classes and namespaces are the union of those associated with each of the members of the set: the namespace in which the function or function template is defined and, i.e., the classes and namespaces associated with its (non-dependent) parameter types and return type.



571. References declared const

Section: 3.5  [basic.link]     Status: WP     Submitter: Dave Abrahams     Date: 31 March 2006

[Voted into the WP at the March, 2009 meeting.]

According to 3.5 [basic.link] paragraph 3,

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

It is not possible to declare a reference to be const.

Proposed resolution (March, 2008):

Change 3.5 [basic.link] paragraph 3 as indicated (note addition of punctuation in the first bullet):

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




757. Types without linkage in declarations

Section: 3.5  [basic.link]     Status: WP     Submitter: John Spicer     Date: 23 December, 2008

[Voted into WP at July, 2009 meeting.]

Paper N2657, adopted at the June, 2008 meeting, removed the prohibition of local and unnamed types as template arguments. As part of the change, 3.5 [basic.link] paragraph 8 was modified to read,

A type without linkage shall not be used as the type of a variable or function with linkage, unless

Because a type without linkage can only be named as a dependent type, there are still some potentially useful things that cannot be done:

    template <class T> struct A {
      friend void g(A, T);  // this can't be defined later
      void h(T);  // this cannot be explicitly specialized
    };

    template <class T> void f(T) {
      A<T> at;
      g(at, (T)0);
    }

    enum { e };

    void g(A<decltype(e)>, decltype(e)){}  // not allowed

    int main() {
      f(e);
    }

These deficiencies could be addressed by allowing types without linkage to be used as the type of a variable or function, but with the requirement that any such entity that is used must also be defined in the same translation unit. This would allow issuing a compile-time, instead of a link-time, diagnostic if the definition were not provided, for example. It also seems to be easier to implement than the current rules.

Proposed resolution (March, 2009):

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

...A type without linkage shall not be used as the type of a variable or function with linkage, unless

[Note: in other words, a type without linkage contains a class or enumeration that cannot be named outside its translation unit. An entity with external linkage declared using such a type could not correspond to any other entity in another translation unit of the program and thus is not permitted must be defined in the translation unit if it is used. Also note that classes with linkage may contain members whose types do not have linkage, and that typedef names are ignored in the determination of whether a type has linkage. —end note] [Example:

    void f() {
      struct A { int x; };    // no linkage
      extern A a;             // ill-formed
      typedef A B;
      extern B b;             // ill-formed
    }

end example]

[Example:

    template <class T> struct A {
      // in A<X>, the following is allowed because the type with no linkage
      // X is named using template parameter T.
      friend void f(A, T){}
    };

    template <class T> void g(T t) {
      A<T> at;
      f(at, t);
    }

    int main() {
      class X {} x;
      g(x);
    }

    template <typename T> struct B {
        void g(T){}
        void h(T);
        friend void i(B, T){}
    };

    void f() {
        struct A { int x; };  // no linkage
        A a = {1};
        B<A> ba;              // declares B<A>::g(A) and B<A>::h(A)
        ba.g(a);              // OK
        ba.h(a);              // error: B<A>::h(A) not defined in the translation unit
        i(ba, a);             // OK
    }

end example]

[Drafting note: issue 527 also changes part of the same text.]




685. Integral promotion of enumeration ignores fixed underlying type

Section: 4.5  [conv.prom]     Status: WP     Submitter: Alberto Ganesh Barbati     Date: 6 January, 2008

[Voted into WP at July, 2009 meeting.]

According to 4.5 [conv.prom] paragraph 2,

An rvalue of an unscoped 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 int, unsigned long int, long long int, or unsigned long long int.

This wording may have surprising behavior in this case:

    enum E: long { e };

    void f(int);
    void f(long);

    void g() {
        f(e);    // Which f is called?
    }

Intuitively, as the programmer has explicitly expressed preference for long as the underlying type, he/she might expect f(long) to be called. However, if long and int happen to have the same size, then e is promoted to int (as it is the first type in the list that can represent all values of E) and f(int) is called instead.

According to 7.2 [dcl.enum] the underlying type of an enumeration is always well-defined for both the fixed and the non-fixed cases, so it makes sense simply to promote to the underlying type unless such a type would itself require promotion.

Suggested resolution:

In 4.5 [conv.prom] paragraph 2, replace all the text from “An rvalue of an unscoped enumeration type” through the end of the paragraph with the following:

An rvalue of an unscoped enumeration type (7.2 [dcl.enum]) is converted to an rvalue of its underlying type if it is different from char16_t, char32_t, wchar_t, or has integer conversion rank greater than or equal to int. Otherwise, it is converted to an rvalue of the first of the following types that can represent all the values of its underlying type: int, unsigned int, long int, unsigned long int, long long int, or unsigned long long int.

(Note that this wording no longer needs to mention extended integer types as special cases.)

Proposed resolution (August, 2008):

Move the following text from 4.5 [conv.prom] paragraph 2 into a separate paragraph, making the indicated changes, and add the following new paragraph after it:

An rvalue of an unscoped enumeration type whose underlying type is not fixed (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 int, unsigned long int, long long int, or unsigned long long int. If none of the types in that list can represent all the values of the enumeration, an rvalue of an unscoped enumeration type can be converted to an rvalue of the extended integer type with lowest integer conversion rank (4.13 [conv.rank]) greater than the rank of long long in which all the values of the enumeration can be represented. If there are two such extended types, the signed one is chosen.

An rvalue of an unscoped enumeration type whose underlying type is fixed (7.2 [dcl.enum]) can be converted to an rvalue of its underlying type. Moreover, if integral promotion can be applied to its underlying type, an rvalue of an unscoped enumeration type whose underlying type is fixed can also be converted to an rvalue of the promoted underlying type.




707. Undefined behavior in integral-to-floating conversions

Section: 4.9  [conv.fpint]     Status: WP     Submitter: Alberto Ganesh Barbati     Date: 2 Aug, 2008

[Voted into WP at July, 2009 meeting.]

The current wording of 4.9 [conv.fpint] paragraph 2 does not specify what should happen when converting an integer value that is outside the representable range of the target floating point type. The C99 Standard covers this case explicitly in 6.3.1.4 paragraph 2:

When a value of integer type is converted to a real floating type, if the value being converted can be represented exactly in the new type, it is unchanged. If the value being converted is in the range of values that can be represented but cannot be represented exactly, the result is either the nearest higher or nearest lower representable value, chosen in an implementation-defined manner. If the value being converted is outside the range of values that can be represented, the behavior is undefined.

While the current C++ specification requires defined behavior in all cases, the C specification allows for use of NaNs and traps, if those are needed for efficiency.

Notes from the September, 2008 meeting:

The CWG agreed that the C approach should be adopted.

Proposed resolution (March, 2009):

Change 4.9 [conv.fpint] paragraph 2 as indicated:

An rvalue of an integer type or of an unscoped enumeration type can be converted to an rvalue of a floating point type. The result is exact if possible. Otherwise If the value being converted is in the range of values that can be represented but cannot be represented exactly, it is an implementation-defined choice of either the next lower or higher representable value. [Note: loss of precision occurs if the integral value cannot be represented exactly as a value of the floating type. —end note] If the value being converted is outside the range of values that can be represented, the behavior is undefined. If the source type is bool, the value false is converted to zero and the value true is converted to one.



438. Possible flaw in wording for multiple accesses to object between sequence points

Section: 5  [expr]     Status: WP     Submitter: Jason Merrill     Date: 29 Oct 2003

Lisa Lippincott mentioned this case to me:

  A[0] = 0;
  A[A[0]] = 1;

This seems to use the old value of A[0] other than to calculate the new value, which is said to be undefined, but it also seems reasonable, since the old value is used in order to select the object to modify, so there's no ordering ambiguity.

Steve Adamczyk: the ordering rule referred to is in 5 [expr] paragraph 4.

Notes from the March 2004 meeting:

Clark Nelson mentions that the C committee may have done something on this.

Note (July, 2009):

This issue was resolved by the adoption of the “sequenced before” wording.




720. Need examples of lambda-expressions

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 20 September, 2008

N2800 comment DE 9

[Voted into the WP at the July, 2009 meeting as part of N2927.]

There is not a single example of a lambda-expression in their specification. The Standard would be clearer if a few judiciously-chosen examples were added.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




750. Implementation constraints on reference-only closure objects

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 10 December, 2008

N2800 comment US 73

[Voted into the WP at the July, 2009 meeting as part of N2927.]

Consider an example like:

    void f(vector<double> vec) {
      double x, y, z;
      fancy_algorithm(vec, [&]() { /* use x, y, and z in various ways */ });
    }

5.1.2 [expr.prim.lambda] paragraph 8 requires that the closure class for this lambda will have three reference members, and paragraph 12 requires that it be derived from std::reference_closure, implying two additional pointer members. Although 8.3.2 [dcl.ref] paragraph 4 allows a reference to be implemented without allocation of storage, current ABIs require that references be implemented as pointers. The practical effect of these requirements is that the closure object for this lambda expression will contain five pointers. If not for these requirements, however, it would be possible to implement the closure object as a single pointer to the stack frame, generating data accesses in the function-call operator as offsets relative to the frame pointer. The current specification is too tightly constrained.

Lawrence Crowl:

The original intent was that the reference members could be omitted from the closure object by an implementation. The problem we had was that we want the call to f in

    extern f(std::reference_closure<void()>);
    extern f(std::function<void()>);
    f([&](){});

to unambiguously bind to the reference_closure; using reference_closure can be an order of magnitude faster than using function.

(See also issue 751.)

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927. (See also document PL22.16/09-0035 = WG21 N2845, which partially addressed this issue by the removal of std::reference_clossure.)




751. Deriving from closure classes

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 11 December, 2008

[Voted into the WP at the July, 2009 meeting as part of N2927.]

During the discussion of issue 750, it was suggested that an implementation might be permitted to omit fields in the closure object of a lambda expression if the implementation does not need them to address the corresponding automatic variables. If permitted, this implementation choice might be visible to the program via inheritance. Consider:

    void f() {
      int const N = 10;
      typedef decltype([&N](){}) F;
      struct X: F {
        void n() { float z[N]; } // Error?
      };
    }

If it is implementation-defined or unspecified whether the reference member F::N will exist, then it is unknown whether the the reference to N in X::n() will be an error (because lookup finds F::N, which is private) or well-formed (because there is no F::N, so the reference is to the local automatic variable).

If implementations can omit fields, the implementation dependency might be addressed by either treating the lookup “as if” the fields existed, even if they are not present in the object layout, or by defining the names of the fields in the closure class to be unique identifiers, similar to the names of unnamed namespaces (7.3.1.1 [namespace.unnamed]).

Another suggestion was made that derivation from a closure class should be prohibited, at least for now. However, it was pointed out that inheritance is frequently used to give stateless function objects some state, suggesting a use case along the lines of:

    template<class T> struct SomeState: T {
      // ...
    };
    template<class F, typename T< void algo(T functor, ...) {
      SomeState<T< state(functor);
      ...
    }

    ... algo([](int a){ return 2*a; }) ...

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




752. Name lookup in nested lambda-expressions

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 10 December, 2008

N2800 comment US 31

[Voted into the WP at the July, 2009 meeting as part of N2927.]

How does name binding work in nested lambda-expressions? For example,

    void f1() {
      float v;
      []() { return [v]() { return v; } }
    }

    void f2() {
      float v;
      [v]() { return [v]() { return v; } }
    }

According to 5.1.2 [expr.prim.lambda] paragraph 3,

A name in the lambda-capture shall be in scope in the context of the lambda expression, and shall be this or shall refer to a local variable or reference with automatic storage duration.

One possible interpretation is that the lambda expression in f1 is ill-formed because v is used in the compound-statement of the outer lambda expression but does not appear in its effective capture set. However, the appearance of v in the inner lambda-capture is not a “use” in the sense of 3.2 [basic.def.odr] paragraph 2, because a lambda-capture is not an expression, and it's not clear whether the reference in the inner lambda expression's return expression should be considered a use of the automatic variable or of the member of the inner lambda expression's closure object.

Similarly, the lambda expression in f2 could be deemed to be ill-formed because the reference to v in the inner lambda expression's lambda-capture would refer to the field of the outer lambda-expression's closure object, not to a local automatic variable; however, it's not clear whether the inner lambda expression should be evaluated in situ or as part of the generated operator() member of the outer lambda expression's closure object.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




753. Array names in lambda capture sets

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 10 December, 2008

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The current specification does not adequately describe what happens when an array name is part of the effective capture set of a lambda expression. 5.1.2 [expr.prim.lambda] paragraph 13 says that the array member of the closure object is direct-initialized by the local array; however, 8.5 [dcl.init] paragraph 16 says that such an initialization is ill-formed. There are several possibilities for handling this problem:

  1. This results in an array member of the closure object, which is initialized by copying each element, along the lines of 12.8 [class.copy] paragraph 8.

  2. This results in a pointer member of the closure object, initialized to point to the first element of the array (i.e., the array lvalue decays to a pointer rvalue).

  3. This is ill-formed.

  4. This results in a reference-to-array member of the closure object, initialized to refer to the array, regardless of whether & was used or not.

  5. This is ill-formed unless the capture is “by reference.”

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




754. Lambda expressions in default arguments of block-scope function declarations

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 10 December, 2008

[Voted into the WP at the July, 2009 meeting as part of N2927.]

Is a lambda expression permitted in a default argument expression for a block-scope function declaration? For example,

    void g() {
      void f(std::reference_closure<void()> rc = []() {});
      f();
    }

This was not discussed in either the Evolution Working Group nor in the Core Working Group, and it is possible that some of the same implementation difficulties that led to prohibiting use of automatic variables in such default argument expressions (8.3.6 [dcl.fct.default] paragraph 7) might also apply to closure objects, even though they are not automatic variables.

(See also issue 772.)

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




756. Dropping cv-qualification on members of closure objects

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 15 December, 2008

[Voted into the WP at the July, 2009 meeting as part of N2927.]

Consider the following example:

    void f() {
      int const N = 10;
      [=]() mutable { N = 30; }  // Okay: this->N has type int, not int const.
      N = 20;  // Error.
    }

That is, the N that is a member of the closure object is not const, even though the captured variable is const. This seems strange, as capturing is basically a means of capturing the local environment in a way that avoids lifetime issues. More seriously, the change of type means that the results of decltype, overload resolution, and template argument deduction applied to a captured variable inside a lambda expression can be different from those in the scope containing the lambda expression, which could be a subtle source of bugs.

On the other hand, the copying involved in capturing has uses beyond avoiding lifetime issues (taking snapshots of values, avoiding data races, etc.), and the value of a cv-qualified object is not cv-qualified.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




759. Destruction of closure objects

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 22 January, 2009

N2800 comment UK 39

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The specification of closure objects is missing a couple of important points regarding their destruction. First, although 5.1.2 [expr.prim.lambda] paragraph 11 mentions other implicitly-declared special member functions, it is silent on the destructor, leading to questions about whether the closure class has one or not.

Second, nothing is said about the timing of the destruction of a closure object: is it normally destroyed at the end of the full-expression to which the lambda expression belongs, and is its lifetime extended if the closure object is bound to a reference? These questions would be addressed if paragraph 2 defined the closure object as a temporary instead of just as an rvalue. (It should be noted that 5.2.3 [expr.type.conv] also does not define the conceptually-similar T() as a temporary.)

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927. (The question regarding the failure of 5.2.3 [expr.type.conv] failing to categorize T() as a temporary was split off into a separate issue; see issue 943.)




761. Inferred return type of closure object call operator

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 5 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

According to 5.1.2 [expr.prim.lambda] paragraph 10, the following lambda expressions are ill-formed because the return types of the generated operator() functions are an array type and a function type, respectively:

    void f() {
      []{ return ""; };
      []{ return f; };
    }

It would seem reasonable to expect the array-to-pointer and function-to-pointer decay to apply to these return values and hence to the inferred return type of operator().

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




762. Name lookup in the compound-statement of a lambda expression

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: John Spicer     Date: 5 February, 2009

N2800 comment US 29
N2800 comment US 30

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The current wording of 5.1.2 [expr.prim.lambda] is not clear as to how name lookup is to be performed for names appearing in the compound-statement of a lambda expression. Consider, for example:

    int fac(int n) {
      return [=]{ return n <= 1 ? 1 : n*operator()(n-1); }();
    }

There is no operator() in scope in the context of the lambda expression. Consequently, according to bullet 5 of paragraph 10, the reference to operator() is not transformed to the class member access syntax but appears untransformed in the closure object's function call operator, where presumably it is interpreted as a recursive call to itself.

A similar question (although it does not involve name lookup per se) arises with respect to use of this in the compound-statement of a lambda expression that does not appear in the body of a non-static member function; for example,

    void f() {
      float v;
      [v]() { return v+this->v; }
    }

this cannot refer to anything except the closure object, so are the two references to v equivalent?

The crux of this question is whether the lookups for names in the compound-statement are done in the context of the lambda expression or from the call operator of the closure object. The note at the end of paragraph 10 bullet 5 would tend to support the latter interpretation:

[Note: Reference to captured variables or references within the compound-statement refer to the data members of F. —end note]

Another possible interpretation of the current wording is that there are two distinct compound-statements in view: the compound-statement that is part of the lambda-expression and the body of the closure object's function call operator that is “obtained from” the former. If this is the intended interpretation, one way of addressing the issues regarding the operator() example above would be to state that it is an error if the lookup of a name in the compound-statement fails, making the example ill-formed.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




763. Is a closure object's operator() inline?

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 6 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

A lambda expression appearing in local scope presumably creates a local class (in the sense of 9.8 [class.local]) as the type of the closure object, because that class is “considered to be defined at the point where the lambda expression occurs” (5.1.2 [expr.prim.lambda] paragraph 7), and in the absence of any indication to the contrary that class must satisfy the restrictions of 9.8 [class.local] on local classes. One such restriction is that all its member functions must be defined within the class definition, making them inline. However, nothing is said about whether the function call operator for a non-local closure class is inline, and even for the local case it would be better if the specification were explicit.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




764. Capturing unused variables in a lambda expression

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Steve Adamczyk     Date: 6 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

5.1.2 [expr.prim.lambda] paragraph 5 says,

The compound-statement of a lambda expression shall use (3.2 [basic.def.odr]) an automatic variable or reference from the context where the lambda expression appears only if the name of the variable or reference is a member of the effective capture set...

The reference to 3.2 [basic.def.odr] makes clear that the technical meaning of “use” is in view here, and that the names of variables can appear without being captured if they are constants used as values or if they are unevaluated operands.

There appears to be a disconnect with the preceding paragraph, however, in the description of which variables are implicitly captured by a capture-default:

for each name v that appears in the lambda expression and denotes a local variable or reference with automatic storage duration in the context where the lambda expression appears and that does not appear in the capture-list or as a parameter name in the lambda-parameter-declaration-list...

It would be more consistent if only variables that were required by paragraph 5 to be captured were implicitly captured, i.e., if “that appears in the lambda expression” were replaced by “that is used (3.2 [basic.def.odr]) in the compound-statement of the lambda expression.” For example,

    struct A {
      A();
      A(const A&);
      ~A();
    };
    void f() {
      A a;
      int i = [=]() { return sizeof a; }();
    }

Here, a will be captured (and copied), even though it is not “used” in the lambda expression.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




766. Where may lambda expressions appear?

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 6 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

According to 5.1.2 [expr.prim.lambda] paragraph 7, the appearance of a lambda expression results in the definition of a class “considered to be defined at the point where the lambda expression occurs.” It is not clear whether that means that a lambda expression cannot appear at any point where it is not permitted to define a class type. For example, 8.3.5 [dcl.fct] paragraph 10 says, “Types shall not be defined in return or parameter types.” Does that mean that a function declaration like

    void f(int a[sizeof ([]{ return 0; })]);

is ill-formed, because the parameter type defines the closure class for the lambda expression? (Issue 686 lists many contexts in which type definitions are prohibited. Each of these should be examined to see whether a lambda expression should be allowed or prohibited there.)

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




767. void and other unnamed lambda-parameters

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 5 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The grammar in 5.1.2 [expr.prim.lambda] for lambda-parameter specifies that a declarator must be present, i.e., that all lambda-parameters must be named. This also has the effect of prohibiting a lambda like [](void){}. It is not clear that there is a good reason for these restrictions; programmers could reasonably expect that lambda-parameters were like ordinary function parameters in these regards.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




768. Ellipsis in a lambda parameter list

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Daveed Vandevoorde     Date: 5 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The grammar in 5.1.2 [expr.prim.lambda] for lambda-parameter-declaration does not allow for an ellipsis. Is this a desirable restriction?

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




769. Initialization of closure objects

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Mike Miller     Date: 9 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

5.1.2 [expr.prim.lambda] paragraph 13 says simply,

The closure object is initialized by direct-initializing each member N of F with the local variable or reference named N; the member t is initialized with this.

The mechanism for this initialization is not specified. In particular, does the closure class have a default constructor that performs this initialization?

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




771. Move-construction of reference members of closure objects

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Jonathan Caves     Date: 9 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

According to 5.1.2 [expr.prim.lambda] paragraph 11, the closure class “has a public move constructor that performs a member-wise move.” Although the terms “move constructor” and “member-wise move” are not currently defined (see issue 680), this presumably means that a lambda like [&i]{} results in a closure class similar to:

    class F {
        int& i;
    public:
        F(&& other):
            i(std::move(other.i)) { }
        // etc.
    };

This constructor is ill-formed because it attempts to initialize an lvalue reference to non-const int with the rvalue returned by std::move.

It is not clear whether this should be handled by:

  1. Not generating the move constructor.

  2. Generating the declaration of the move constructor but only defining it (and giving the corresponding error) if the move constructor would be used, similar to the handling of other implicitly-defined special member functions.

  3. Generating the move constructor but copy-constructing any reference members.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




772. capture-default in lambdas in local default arguments

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Steve Adamczyk     Date: 10 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

Assuming that it is permitted to use a lambda as a default argument in a block-scope function declaration (see issue 754), it is presumably ill-formed for such a lambda expression to refer to a local automatic variable (8.3.6 [dcl.fct.default] paragraph 7). What does this mean for capture-defaults? For example,

    void f() {
      int i = 1;
      void f(int = ([i]() { return i; })());  // Definitely an error
      void g(int = ([i]() { return 0; })());  // Probably an error
      void h(int = ([=]() { return i; })());  // Definitely an error
      void o(int = ([=]() { return 0; })());  // Okay?
      void p(int = ([]() { return sizeof i; })());  // Presumably okay
    }

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




774. Can a closure class be a POD?

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: John Spicer     Date: 11 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The current wording does not state under what conditions, if ever, a closure class is a POD. It should either be explicitly unspecified or definitively stated that a closure class is never a POD, to allow implementations freedom to determine the contents of closure classes.

Notes from the March, 2009 meeting:

A closure class is neither standard-layout nor trivial.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




775. Capturing references to functions

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Steve Adamczyk     Date: 12 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

According to 5.1.2 [expr.prim.lambda] paragraph 8, the “object type” of a captured function is the type to which the reference refers. That's clearly wrong when the captured reference is a reference to a function, because the resulting data member of the closure class will have a function type:

    void f() { }
    void g() {
      void (&fr)() = f;
      [fr]{};   // Oops...
    }

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




779. Rvalue reference members of closure objects?

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Mike Miller     Date: 26 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

5.1.2 [expr.prim.lambda] paragraph 8, bullet 2, says of members of a closure class,

if the element is of the form & N, the data member has the name N and type “reference to object type of N

Is an implementation free to use an rvalue reference as the type of this member, as only a “reference” is specified? (See issue 771; the move constructor would be well-formed if the reference member were an rvalue reference.)

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




782. Lambda expressions and argument-dependent lookup

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: Mike Miller     Date: 1 March, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

Functions and function objects behave differently with respect to argument-dependent lookup. In particular, the associated namespaces of a function's parameters and return types, but not the namespace in which the function is declared, are associated namespaces of the function; the exact opposite is true of a function object. The Committee should consider rectifying that disparity; however, in the absence of such action, an explicit decision should be made as to whether lambdas are more function-like or object-like with respect to argument-dependent lookup. For example:

    namespace M {
      struct S { };
    }
    namespace N {
      void func(M::S);
      struct {
        void operator()(M::S);
      } fn_obj;
      const auto& lambda = [](M::S){};
    }
    void g() {
      f(N::func);    // assoc NS == M, not N
      f(N::fn_obj);  // assoc NS == N, not M
      f(N::lambda);  // assoc NS == ??
    }

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




796. Lifetime of a closure object with members captured by reference

Section: 5.1.2  [expr.prim.lambda]     Status: WP     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 40

5.1.2 [expr.prim.lambda] paragraph 13 ties the effective lifetime of a closure object with members captured by reference to the innermost block scope in which the lambda appears, rather than to the lifetime of the objects to which the references are bound. This seems too restrictive.

Notes from the March, 2009 meeting:

Making the suggested change would be problematic for an implementation in which the “reference members” were actually implemented using offsets from a captured stack pointer and in which nested blocks were pushed onto the stack (to optimize space for large local objects, for example).

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




665. Problems in the specification of dynamic_cast

Section: 5.2.7  [expr.dynamic.cast]     Status: WP     Submitter: Daniel Krügler     Date: 1 December 2007

[Voted into the WP at the March, 2009 meeting.]

At least one implementation accepts the following example as well-formed (returning a null pointer at runtime), although others reject it at compile time:

    struct A { virtual ~A(); };
    struct B: private A { } b;
    A* pa = dynamic_cast<A*>(&b);

Presumably the intent of 5.2.7 [expr.dynamic.cast] paragraph 5 is that all up-casts (converting from derived to base) are to be handled at compile time, regardless of whether the class involved is polymorphic or not:

If T is “pointer to cv1 B” and v has type “pointer to cv2 D” such that B is a base class of D, the result is a pointer to the unique B subobject of the D object pointed to by v. Similarly, if T is “reference to cv1 B” and v has type cv2 D such that B is a base class of D, the result is the unique B subobject of the D object referred to by v... In both the pointer and reference cases, cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2, and B shall be an accessible unambiguous base class of D.

One explanation for the implementation that accepts the example at compile time is that the final sentence is interpreted as part of the condition for the applicability of this paragraph, so that this case falls through into the description of runtime checking that follows. This (mis-)interpretation is buttressed by the example in paragraph 9, which reads in significant part:

    class A { virtual void f(); };
    class B { virtual void g(); };
    class D : public virtual A, private B {};
    void g() {
        D d;
        B* bp;
        bp = dynamic_cast<B*>(&d); // fails
    }

The “fails” comment is identical to the commentary on the lines in the example where the run-time check fails. If the interpretation that paragraph 5 is supposed to apply to all up-casts, presumably this comment should change to “ill-formed,” or the line should be removed from the example altogether.

It should be noted that this interpretation (that the example is ill-formed and the runtime check applies only to down-casts and cross-casts) rejects some programs that could plausibly be accepted and actually work at runtime. For example,

    struct B { virtual ~B(); };
    struct D: private virtual B { };

    void test(D* pd) {
        B* pb = dynamic_cast<B*>(pd); // #1
    }

    struct D2: virtual B, virtual D {};

    void demo() {
        D2 d2;
        B* pb = dynamic_cast<B*>(&d2); // #2
        test(&d2); // #3
    }

According to the interpretation that paragraph 5 applies, line #1 is ill-formed. However, converting from D2 to B (line #2) is well-formed; if the alternate interpretation were applied, the conversion in line #1 could succeed when applied to d2 (line #3).

One final note: the wording in 5.2.7 [expr.dynamic.cast] paragraph 8 is incorrect:

The run-time check logically executes as follows:

All uses of T in this paragraph treat it as if it were a class type; in fact, T is the type to which the expression is being cast and thus is either a pointer type or a reference type, not a class type.

Proposed resolution (June, 2008):

  1. Change 5.2.7 [expr.dynamic.cast] paragraph 5 as follows:

  2. ...In both the pointer and reference cases, cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2, and B shall be an accessible unambiguous base class of D the program is ill-formed if cv2 is greater cv-qualification than cv1 or if B is an inaccessible or ambiguous base class of D.
  3. Change the comment in the example in 5.2.7 [expr.dynamic.cast] paragraph 9 as follows:

  4.     bp = dynamic_cast<B*>(&d);     // fails ill-formed (not a run-time check)
    
  5. Change 5.2.7 [expr.dynamic.cast] paragraph 8 as follows:

  6. The If C is the class type to which T points or refers, the run-time check logically executes as follows:




658. Defining reinterpret_cast for pointer types

Section: 5.2.10  [expr.reinterpret.cast]     Status: WP     Submitter: Dave Abrahams     Date: 4 November 2007

[Voted into the WP at the March, 2009 meeting.]

For years I've noticed that people will write code like this to get the address of an object's bytes:

  void foo(long* p) {
      char* q = reinterpret_cast<char*>(p);  // #1
      // do something with the bytes of *p by using q
  }

When in fact the only portable way to do it according to the standard is:

  void foo(long* p) {
      char* q = static_cast<char*>(static_cast<void*>(p));  // #2
      // do something with the bytes of *p by using q
  }

I thought reinterpret_cast existed so that vendors could provide some weird platform-specific things. However, recently Peter Dimov pointed out to me that if we substitute a class type for long above, reinterpret_cast is required to work as expected by 9.2 [class.mem] paragraph 18:

A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa.

So there isn't a whole lot of flexibility to do something different and useful on non-class types. Are there any implementations for which #1 actually fails? If not, I think it would be a good idea to nail reinterpret_cast down so that the standard says it does what people (correctly) think it does in practice.

Proposed resolution (March, 2008):

Change 5.2.10 [expr.reinterpret.cast] paragraph 7 as indicated:

A pointer to an object can be explicitly converted to a pointer to an object of different type. When an rvalue v of type “pointer to T1” is converted to the type “pointer to cv T2,” the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (3.9 [basic.types]) and the alignment requirements of T2 are no stricter than those of T1. Except that cConverting an rvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, t. The result of any other such a pointer conversion is unspecified.



805. Which exception to throw for overflow in array size calculation

Section: 5.3.4  [expr.new]     Status: WP     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 72
N2800 comment UK 192

[Voted into WP at July, 2009 meeting as part of N2932.]

Throwing std::length_error (5.3.4 [expr.new] paragraph 7) for an attempt to allocate a too-large array brings in too much of the Standard library. A simpler exception, like std::bad_alloc, should be thrown instead.

Notes from the March, 2009 meeting:

The CWG was in favor of throwing an exception derived from std::bad_alloc. This would be upwardly compatible; it would be harmless for programs that currently catch std::bad_alloc, but would allow programs to treat the calculation overflow case separately if they wish.




599. Deleting a null function pointer

Section: 5.3.5  [expr.delete]     Status: WP     Submitter: Martin Sebor     Date: 3 October 2006

[Voted into WP at July, 2009 meeting.]

The requirements for the operand of the delete operators are given in 5.3.5 [expr.delete] paragraph 2:

In either alternative, the value of the operand of delete may be a null pointer value. If it is not a null pointer value, in the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a subobject (1.8 [intro.object]) representing a base class of such an object (clause 10 [class.derived]). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete shall be the pointer value which resulted from a previous array new-expression. If not, the behavior is undefined.

There are no restrictions on the type of a null pointer, only on a pointer that is not null. That seems wrong.

Proposed resolution (June, 2008):

Change 5.3.5 [expr.delete] paragraph 1 as follows:

...The operand shall have a pointer to object type, or a class type having a single non-explicit conversion function (12.3.2 [class.conv.fct]) to a pointer to object type...

Proposed resolution (September, 2008):

  1. Change 5.3.5 [expr.delete] paragraph 1 as follows:

  2. ...The operand shall have a pointer to object type, or a class type having a single non-explicit conversion function (12.3.2) to a pointer to object type. [Footnote: This implies that an object cannot be deleted using a pointer of type void* because void is not an object type. —end footnote] ...
  3. Delete the footnote at the end of 5.3.5 [expr.delete] paragraph 3:

  4. ...if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined. [Footnote: This implies that an object cannot be deleted using a pointer of type void* because there are no objects of type void. —end footnote]



556. Conflicting requirements for acceptable aliasing

Section: 5.17  [expr.ass]     Status: WP     Submitter: Mike Miller     Date: 30 January 2006

[Voted into the WP at the March, 2009 meeting.]

There appear to be two different specifications for when aliasing is permitted. One is in 3.10 [basic.lval] paragraph 15:

If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined

There is also a much more restrictive specification in 5.17 [expr.ass] paragraph 8:

If the value being stored in an object is accessed from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is undefined.

This affects, for example, the definedness of operations on union members: when may a value be stored into one union member and accessed via another.

It should be noted that this conflict existed in C90 and is unchanged in C99 (see, for example, section 6.5 paragraph 7 and section 6.5.16.1 paragraph 3 of ISO/IEC 9899:1999, which directly parallel the sections cited above).

Notes from the October, 2006 meeting:

This issue is based on a misunderstanding of the intent of the wording in 5.17 [expr.ass] paragraph 8. Instead of being a general statement about aliasing, it's describing the situation in which the source of the value being assigned is storage that overlaps the storage of the target object. The proposed resolution should make that clearer rather than changing the specification.

Proposed resolution (June, 2008):

Add the following note at the end of 5.17 [expr.ass] paragraph 8:

If the value being stored in an object is accessed from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is undefined. [Note: This restriction applies to the relationship between the left and right sides of the assignment operation; it is not a statement about how the target of the assignment may be aliased in general. See 3.10 [basic.lval]. —end note]



652. Compile-time evaluation of floating-point expressions

Section: 5.19  [expr.const]     Status: WP     Submitter: Jens Maurer     Date: 3 October 2007

[Voted into the WP at the March, 2009 meeting.]

It was the intention of the constexpr proposal that implementations be required to evaluate floating-point expressions at compile time. This intention is not reflected in the actual wording of 5.19 [expr.const] paragraph 2, bullet 5:

This restriction has the effect of forbidding the use of floating-point expressions in integral constant expressions.

Proposed resolution (June, 2008):

Delete bullet 6 of 5.19 [expr.const] paragraph 2:

Notes from the June, 2008 meeting:

The CWG agreed with the intent of this issue, that floating-point calculations should be permitted in constant expressions, but acknowledged that this opens the possibility of differing results between compile time and run time. Such issues should be addressed non-normatively, e.g., via a “recommended practice” note like that of C99's 6.4.4.2 or in a technical report.

Proposed resolution (August, 2008):

  1. Delete bullet 6 of 5.19 [expr.const] paragraph 2:

  2. Add a new paragraph after 5.19 [expr.const] paragraph 3:

  3. [Note: Although in some contexts constant expressions must be evaluated during program translation, others may be evaluated during program execution. Since this International Standard imposes no restrictions on the accuracy of floating-point operations, it is unspecified whether the evaluation of a floating-point expression during translation yields the same result as the evaluation of the same expression (or the same operations on the same values) during program execution. [Footnote: Nonetheless, implementations are encouraged to provide consistent results, irrespective of whether the evaluation was actually performed during translation or during program execution. —end footnote] [Example:

      bool f() {
        char array[1 + int(1 + 0.2 - 0.1 - 0.1)];  // Must be evaluated during translation
        int size = 1 + int(1 + 0.2 - 0.1 - 0.1);   // May be evaluated at runtime
        return sizeof(array) == size;
      }
    

    It is unspecified whether the value of f() will be true or false. —end example] —end note]




569. Spurious semicolons at namespace scope should be allowed

Section: 7  [dcl.dcl]     Status: WP     Submitter: Matt Austern     Date: 20 March 2006

[Voted into the WP at the March, 2009 meeting.]

The grammar in 7 [dcl.dcl] paragraph 1 says that a declaration-seq is either declaration or declaration-seq declaration. Some declarations end with semicolons and others (e.g. function definitions and namespace declarations) don't. This means that users who put a semicolon after every declaration are technically writing ill-formed code. The trouble is that in this respect the standard is out of sync with reality. It's convenient to allow semicolons after every declaration, and there's no implementation difficulty in doing so. All existing compilers accept this, except in extra-pedantic mode. When all implementations disagree with the standard, it's time for the standard to change.

Suggested resolution:

In the grammar in 7 [dcl.dcl] paragraph 11, change the second line in the definition of declaration-seq to

Proposed resolution (October, 2006):

  1. Add the indicated lines to the grammar definitions in 7 [dcl.dcl] paragraph 1:

  2. declaration:

    ...

    static_assert-declaration:


    empty-declaration:
      ;
  3. Add the following as a new paragraph after 7 [dcl.dcl] paragraph 4:

  4. An empty-declaration has no effect.



576. Typedefs in function definitions

Section: 7.1.3  [dcl.typedef]     Status: WP     Submitter: Jon Caves     Date: 21 April 2006

[Voted into the WP at the March, 2009 meeting.]

7.1.3 [dcl.typedef] paragraph 1 says,

The typedef specifier shall not be used in a function-definition (8.4 [dcl.fct.def])...

Does this mean that the following is ill-formed?

    void f() {
        typedef int INT;
    }

Proposed resolution (March, 2008):

Change 7.1.3 [dcl.typedef] paragraph 1 as follows:

...The typedef specifier shall not be used in a function-definition (8.4 [dcl.fct.def]), and it shall not be combined in a decl-specifier-seq with any other kind of specifier except a type-specifier, and it shall not be used in the declaration of a function parameter nor in the decl-specifier-seq of a function-definition (8.4 [dcl.fct.def])...

Proposed resolution (September, 2008):

Change 7.1.3 [dcl.typedef] paragraph 1 as follows:

...The typedef specifier shall not be used in a function-definition (8.4 [dcl.fct.def]), and it shall not be combined in a decl-specifier-seq with any other kind of specifier except a type-specifier, and it shall be used neither in the decl-specifier-seq of a parameter-declaration (8.3.5 [dcl.fct]) nor in the decl-specifier-seq of a function-definition (8.4 [dcl.fct.def]).



711. auto with braced-init-list

Section: 7.1.6.4  [dcl.spec.auto]     Status: WP     Submitter: Jason Merrill     Date: 27 August, 2008

[Voted into WP at July, 2009 meeting.]

One effect of the initializer-list proposal is that now we allow

    auto x = { 1, 2, 3 };  // decltype(x) is std::initializer_list<int>

but not

    auto ar[3] = { 1, 2, 3 };  // ill-formed

This seems unfortunate, as the code for the first could also support the second. Incidentally, I also failed to update the text in 7.1.6.4 [dcl.spec.auto] paragraph 3 which forbids the use of auto with braced-init-lists, so technically the first form above is currently ill-formed but has defined semantics.

Bjarne Stroustrup:

Is this the thin edge of a wedge? How about

    vector<auto> v = { 1, 2, 3 };

and

    template<class T> void f(vector<T>& v);
    f({1, 2, 3 });

(See also issue 625.)

Proposed resolution (March, 2009):

Change 7.1.6.4 [dcl.spec.auto] paragraph 3 as follows:

...The decl-specifier-seq shall be followed by one or more init-declarators, each of which shall have a non-empty initializer. of either of the following forms:

[Drafting note: This change does not address the original issue of the inability to use auto with an array initializer, only the secondary issue of permitted the braced-init-list. The CWG explicitly decided not to support the array case.]




746. Use of auto in new-expressions

Section: 7.1.6.4  [dcl.spec.auto]     Status: WP     Submitter: Jason Merrill     Date: 18 November, 2008

N2800 comment UK 95

[Voted into WP at July, 2009 meeting.]

In listing the acceptable contexts in which the auto specifier may appear, 7.1.6.4 [dcl.spec.auto]) paragraph 4 mentions “the type-specifier-seq in a new-type-id” but not the type-id in the parenthesized form; that is, new auto (42) is well-formed but new (auto) (42) is not. This seems an unnecessary restriction, as well as contradicting 5.3.4 [expr.new] paragraph 2:

If the auto type-specifier appears in the type-specifier-seq of a new-type-id or type-id of a new-expression...

Proposed resolution (March, 2009):

Change 7.1.6.4 [dcl.spec.auto] paragraph 4 as follows:

The auto type-specifier can also be used in declaring an object in the condition of a selection statement (6.4 [stmt.select]) or an iteration statement (6.5 [stmt.iter]), in the type-specifier-seq in a the new-type-id or type-id of a new-expression (5.3.4 [expr.new]), in a for-range-declaration...



628. The values of an enumeration with no enumerator

Section: 7.2  [dcl.enum]     Status: WP     Submitter: Gennaro Prota     Date: 15 March 2007

N2800 comment UK 96

[Voted into the WP at the March, 2009 meeting.]

According to 7.2 [dcl.enum] paragraph 6, the underlying type of an enumeration with an empty enumeration-list is determined as if the enumeration-list contained a single enumerator with value 0. Paragraph 7, which specifies the values of an enumeration and the minimum size of bit-field needed represent those values needs a similar provision for empty enumeration-lists.

Proposed resolution (March, 2008):

Add the indicated sentence to the end of 7.2 [dcl.enum] paragraph 5:

...It is possible to define an enumeration that has values not defined by any of its enumerators. If the enumerator-list is empty, the values of the enumeration are as if the enumeration had a single enumerator with value 0.



564. Agreement of language linkage or linkage-specifications?

Section: 7.5  [dcl.link]     Status: WP     Submitter: Daveed Vandevoorde     Date: 8 March 2006

[Voted into the WP at the March, 2009 meeting.]

The wording of 7.5 [dcl.link] paragraph 5 is suspect:

If two declarations of the same function or object specify different linkage-specifications (that is, the linkage-specifications of these declarations specify different string-literals), the program is ill-formed if the declarations appear in the same translation unit, and the one definition rule (3.2) applies if the declarations appear in different translation units.

But what if only one of the declarations has a linkage-specification, while the other is left with the default C++ linkage? Shouldn't this restriction be phrased in terms of the functions’ or objects’ language linkage rather than linkage-specifications?

(Additional note [wmm]: Is the ODR the proper vehicle for enforcing this requirement? This is dealing with declarations, not necessarily definitions. Shouldn't this say “ill-formed, no diagnostic required” instead of some vague reference to the ODR?)

Proposed resolution (June, 2008):

Change 7.5 [dcl.link] paragraph 5 as follows:

If two declarations of the same function or object declare functions with the same name and parameter-type-list (8.3.5 [dcl.fct]) to be members of the same namespace or declare objects with the same name to be members of the same namespace specify different linkage-specifications (that is, the linkage-specifications of these declarations specify different string-literals) and the declarations give the names different language linkages, the program is ill-formed if the declarations appear in the same translation unit, and the one definition rule (3.2 [basic.def.odr]) applies; no diagnostic is required if the declarations appear in different translation units.



815. Parameter pack expansion inside attributes

Section: 7.6.1  [dcl.attr.grammar]     Status: WP     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 103

[Voted into WP at July, 2009 meeting as N2933.]

Parameter packs should be expanded inside attributes. For example, it would be useful to specify the alignment of each element in a pack expansion using a parallel pack expansion.




816. Diagnosing violations of [[final]]

Section: 7.6.4  [dcl.attr.final]     Status: WP     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 108

[Voted into WP at July, 2009 meeting.]

According to 7.6.4 [dcl.attr.final] paragraph 2, overriding a virtual function with the [[final]] attribute renders a program ill-formed, but no diagnostic is required. This is easily diagnosable and a diagnostic should be required in this case.

Notes from the March, 2009 meeting:

This specification was a deliberate decision on the part of the EWG; the general rule was that it should be possible to ignore attributes without changing the meaning of a program. However, the consensus of the CWG was that violation of the [[final]] attribute should require a diagnostic.

Proposed resolution (March, 2009):

Change 7.6.4 [dcl.attr.final] paragraph 2 as follows:

If a virtual member function f in some class B is marked final and in a class D derived from B a function D::f overrides B::f, the program is ill-formed; no diagnostic required. [Footnote: If an implementation does not emit a diagnostic it should execute the program as if final were not present. —end footnote]



770. Ambiguity in late-specified return type

Section: 8  [dcl.decl]     Status: WP     Submitter: Daveed Vandevoorde     Date: 9 February, 2009

[Voted into the WP at the July, 2009 meeting as part of N2927.]

It is currently unspecified whether a declaration like

    f() -> struct S { };

should be parsed as a declaration of f whose return type is a class definition (which will be ill-formed according to 7.1.6 [dcl.type] paragraph 3) or as a definition of f whose return type is an elaborated-type-specifier.

Proposed resolution (June, 2009):

See document PL22.16/09-0117 = WG21 N2927.




732. Late-specified return types in function definitions

Section: 8.4  [dcl.fct.def]     Status: WP     Submitter: Daniel Krügler     Date: 7 October, 2008

N2800 comment DE 13

[Voted into the WP at the July, 2009 meeting as part of N2927.]

The grammar in 8.4 [dcl.fct.def] paragraph 2 incorrectly excludes late-specified return types and should be corrected.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




645. Are bit-field and non-bit-field members layout compatible?

Section: 9.2  [class.mem]     Status: WP     Submitter: Alan Stokes     Date: 9 Aug 2007

[Voted into the WP at the March, 2009 meeting.]

The current wording defining a “common initial sequence” in 9.2 [class.mem] paragraph 17 does not address the case in which one member is a bit-field and the corresponding member is not:

Two standard-layout structs share a common initial sequence if corresponding members have layout-compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members.

Presumably the intent was something like, “(and, if one of the pair is a bit-field, the other is also a bit-field of the same width).”

Proposed Resolution (September, 2008):

Change 9.2 [class.mem] paragraph 18 as follows:

... Two standard-layout structs share a common initial sequence if corresponding members have layout-compatible types (and, for bit-fields, the same widths) and either neither member is a bit-field or both are bit-fields with the same widths for a sequence of one or more initial members.



714. Static const data members and braced-init-lists

Section: 9.4.2  [class.static.data]     Status: WP     Submitter: Steve Adamczyk     Date: 15 September, 2008

[Voted into WP at July, 2009 meeting.]

The recent changes in the handling of initialization have not touched the requirement that the in-class initializer for a const static data member must be of the form = assignment-expression and not a braced-init-list. It would be more consistent and general to allow the braced form as well.

Proposed resolution (March, 2009):

  1. Change 5.19 [expr.const] paragraph 3 as follows:

  2. ...as enumerator initializers (7.2 [dcl.enum]), as static member initializers (9.4.2 [class.static.data]), and as integral or enumeration non-type template arguments (14.5 [temp.type]).
  3. Change 9.4.2 [class.static.data] paragraph 3 as follows:

  4. If a static data member is of const effective literal type, its declaration in the class definition can specify a brace-or-equal-initializer with an in which every initializer-clause that is an assignment-expression is a integral constant expression. A static data member of effective literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer with an in which every initializer-clause that is an assignment-expression is a integral constant expression. [Note: In both these cases, the member may appear in integral constant expressions. end note] The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.

[Drafting note: this change also corrects an editorial error resulting from overlapping changes that inadvertently retained the original restriction that only members of integral type could be initialized inside the class definition.]




716. Specifications that should apply only to non-static union data members

Section: 9.5  [class.union]     Status: WP     Submitter: Mike Miller     Date: 17 September, 2008

[Voted into WP at July, 2009 meeting.]

Unions are no longer forbidden to have static data members; however, much of the wording of 9.5 [class.union] (and possibly other places in the Standard) is still written with that assumption and refers only to “data members” when clearly non-static data members are in view. From paragraph 1, for example:

In a union, at most one of the data members can be active at any time... The size of a union is sufficient to contain the largest of its data members...

Proposed resolution (March, 2009):

  1. Change the footnote in 3.9.3 [basic.type.qualifier] paragraph 1 as follows:

  2. The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and non-static data members of unions.
  3. Change 3.10 [basic.lval] paragraph 15 bullet 6 as follows:

  4. Change 5.9 [expr.rel] paragraph 2 bullet 5 as follows:

  5. Change 7.6.2 [dcl.align] paragraph 8 as follows:

  6. [Note: the alignment of a union type can be strengthened by applying the alignment attribute to any non-static data member of the union. —end note]
  7. Change 8.5.1 [dcl.init.aggr] paragraph 15 as follows:

  8. When a union is initialized with a brace-enclosed initializer, the braces shall only contain an initializer-clause for the first non-static data member of the union...
  9. Change 9.5 [class.union] paragraph 1 as follows:

  10. In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time. [Note: one special guarantee is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout structs that share a common initial sequence (9.2 [class.mem]), and if an object of this standard-layout union type contains one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of standard-layout struct members; see 9.2 [class.mem]. —end note] The size of a union is sufficient to contain the largest of its non-static data members. Each non-static data member is allocated as if it were the sole member of a struct. A union can have...



939. Explicitly checking virtual function overriding

Section: 10.3  [class.virtual]     Status: WP     Submitter: FI/US     Date: 14 July, 2009

N2800 comment FI 1
N2800 comment US 41

[Voted into WP at July, 2009 meeting as N2928.]

There should be a way to detect errors in overriding a virtual function.

Proposed resolution (July, 2009):

This issue is resolved by paper PL22.16/09-0118 = WG21 N2928.




650. Order of destruction for temporaries bound to the returned value of a function

Section: 12.2  [class.temporary]     Status: WP     Submitter: Mike Miller     Date: 14 Aug 2007

[Voted into the WP at the March, 2009 meeting.]

In describing the order of destruction of temporaries, 12.2 [class.temporary] paragraphs 4-5 say,

There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression...

The second context is when a reference is bound to a temporary... A temporary bound to the returned value in a function return statement (6.6.3 [stmt.return]) persists until the function exits.

The following example illustrates the issues here:

    struct S {
        ~S();
    };

    S& f() {
        S s;            // #1
        return
            (S(),       // #2
             S());      // #3
    }

If the return type of f() were simply S instead of S&, the two temporaries would be destroyed at the end of the full-expression in the return statement in reverse order of their construction, followed by the destruction of the variable s at block-exit, i.e., the order of destruction of the S objects would be #3, #2, #1.

Because the temporary #3 is bound to the returned value, however, its lifetime is extended beyond the end of the full-expression, so that S object #2 is destroyed before #3.

There are two problems here. First, it is not clear what “until the function exits” means. Does it mean that the temporary is destroyed as part of the normal block-exit destructions, as described in 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.

Or is the point of destruction for #3 after the destruction of the “constructed objects... that are declared [emphasis mine] in that scope” (because temporary #3 was not “declared”)? I.e., should #3 be destroyed before or after #1?

The other problem is that, according to the recollection of one of the participants responsible for this wording, the intent was not to extend the lifetime of #3 but simply to emphasize that its lifetime ended before the function returned, i.e., that the result of f() could not be used without causing undefined behavior. This is also consistent with the treatment of this example by many implementations; MSVC++, g++, and EDG all destroy #3 before #2.

Suggested resolution:

Change 12.2 [class.temporary] paragraph 5 as indicated:

A The lifetime of a temporary bound to the returned value in a function return statement (6.6.3 [stmt.return]) persists until the function exits is not extended; it is destroyed at the end of the full-expression in the return statement.

Proposed resolution (June, 2008):

Change 12.2 [class.temporary] paragraph 5 as follows (converting the running text into a bulleted list and making the indicated edits to the wording):

... The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except: as specified below.

The destruction of a temporary whose lifetime is not extended...




542. Value initialization of arrays of POD-structs

Section: 12.6  [class.init]     Status: WP     Submitter: Alisdair Meredith     Date: 27 October 2005

[Voted into the WP at the March, 2009 meeting.]

12.6 [class.init] paragraph 2 says,

When an array of class objects is initialized (either explicitly or implicitly), the constructor shall be called for each element of the array, following the subscript order;

That implies that, given

    struct POD {
      int x;
    };

    POD data[10] = {};

this should call the implicitly declared default ctor 10 times, leaving 10 uninitialized ints, rather than value initialize each member of data, resulting in 10 initialized ints (which is required by 8.5.1 [dcl.init.aggr] paragraph 7).

I suggest rephrasing along the lines:

When an array is initialized (either explicitly or implicitly), each element of the array shall be initialized in turn, following the subscript order;

This would allow for PODs and other classes with a dual nature under value/default initialization, and cover copy initialization for arrays too.

Proposed resolution (October, 2006):

Change 12.6 [class.init] paragraph 3 as follows:

When an array of class objects is initialized (either explicitly or implicitly) and the elements are initialized by constructor, the constructor shall be called for each element of the array, following the subscript order; see 8.3.4 [dcl.array].



653. Copy assignment of unions

Section: 12.8  [class.copy]     Status: WP     Submitter: Jens Maurer     Date: 3 October 2007

[Voted into WP at July, 2009 meeting.]

How does copy assignment for unions work? For example,

  union U {
    int a;
    float b;
  };

  void f() {
    union U u = { 5 };
    union U v;
    v = u;    // what happens here?
  }

9.5 [class.union] is silent on the issue, therefore it seems that 12.8 [class.copy] applies. There is no special case for unions, thus paragraph 13 (memberwise assignment of subobjects) seems to apply. That would seem to imply these actions in the compiler-generated copy assignment operator:

  v.a = u.a;
  v.b = u.b;

And this is just wrong. For example, the lifetime of v.a ends once the second assignment reuses the memory of v.a.

We should probably prescribe “memcpy” copying for unions (both for the copy constructor and the assignment operator) unless the user provided his own special member function.

Proposed resolution (March, 2008):

  1. Change 12.8 [class.copy] paragraph 8 as follows:

  2. The implicitly-defined or explicitly-defaulted copy constructor for a non-union class X performs a memberwise copy of its subobjects...
  3. Add a new paragraph after 12.8 [class.copy] paragraph 8:

  4. The implicitly-defined or explicitly-defaulted copy constructor for a union X where all members have a trivial copy constructor copies the object representation (3.9 [basic.types]) of X. [Note: The behavior is undefined if X is not a trivial type. —end note]
  5. Change 12.8 [class.copy] paragraph 13 as follows:

  6. The implicitly-defined or explicitly-defaulted copy assignment operator for a non-union class X performs memberwise assignment of its subobjects...
  7. Add a new paragraph after 12.8 [class.copy] paragraph 13:

  8. The implicitly-defined or explicitly-defaulted copy assignment operator for a union X where all members have a trivial copy assignment operator copies the object representation (3.9 [basic.types]) of X. [Note: The behavior is undefined if X is not a trivial type. —end note]

Notes from the September, 2008 meeting:

The proposed wording needs to be updated to reflect the changes adopted in papers N2757 and N2762, resolving issue 683, which require “no non-trivial” special member functions instead of “a trivial” function. Also, the notes regarding undefined behavior are incorrect, because the member functions involved are defined as deleted when there are non-trivial members.

Proposed resolution (October, 2008):

  1. Change 12.8 [class.copy] paragraph 8 as follows:

  2. The implicitly-defined or explicitly-defaulted copy constructor for a non-union class X performs a memberwise copy of its subobjects...
  3. Add a new paragraph following 12.8 [class.copy] paragraph 8:

  4. The implicitly-defined or explicitly-defaulted copy constructor for a union X copies the object representation (3.9 [basic.types]) of X.
  5. Change 12.8 [class.copy] paragraph 13 as follows:

  6. The implicitly-defined or explicitly-defaulted copy assignment operator for a non-union class X performs memberwise assignment of its subobjects...
  7. Add a new paragraph following 12.8 [class.copy] paragraph 13:

  8. The implicitly-defined or explicitly-defaulted copy assignment operator for a union X copies the object representation (3.9 [basic.types]) of X.



680. What is a move constructor?

Section: 12.8  [class.copy]     Status: WP     Submitter: Steve Adamczyk     Date: 3 March, 2008

N2800 comment US 33

[Voted into the WP at the July, 2009 meeting as part of N2927.]

Although the term “move constructor” appears multiple times in the library clauses and is referenced in the newly-added text for the lambda feature, it is not defined anywhere.

Notes from the June, 2008 meeting:

The only reference to “move constructor” in the core language clauses of the Standard is in 5.1.2 [expr.prim.lambda] paragraph 10; there are no semantic implications of the term. This issue will be addressed by using a function signature instead of the term, thus allowing the library section to provide a definition that is appropriate for its needs.

Proposed resolution (July, 2009)

See document PL22.16/09-0117 = WG21 N2927.




641. Overload resolution and conversion-to-same-type operators

Section: 13.3.2  [over.match.viable]     Status: WP     Submitter: Nathan Sidwell     Date: 2 Aug 2007

[Voted into the WP at the March, 2009 meeting.]

12.3.2 [class.conv.fct] paragraph 1 says,

A conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to (possibly cv-qualified) void.

At what point is this enforced, and how is it enforced?

  1. Does such a user-declared conversion operator participate in overload resolution? Or is it never entered into the overload set?
  2. If it does participate in overload resolution, what happens if it is selected? Is the program ill-formed (and diagnostic required), or is it silently ignored? The above wording doesn't really make it clear.

Consider this test case:

    struct abc;

    struct xyz {
       xyz();

       xyz(xyz &);

       operator xyz& (); // #1
       operator abc& (); // #2
    };

    struct abc : xyz {};

    void foo(xyz &);

    void bar() {
             foo (xyz ());
    }

If such conversion functions are part of the overload set, #1 is a better conversion than #2 to convert the temporary xyz object to a non-const reference required for foo's operand. If such conversion functions are not part of the overload set, then #2 would be selected, and AFAICT the program would be well formed.

If the conversion functions are not part of the overload set, then it would seem one cannot take their address. For instance, adding the following line to the above test case would find no suitable function:

    xyz &(xyz::*ptr) () = &xyz::operator xyz &;

Notes from the October, 2007 meeting:

The intent of 12.3.2 [class.conv.fct] paragraph 1 is that overload resolution not be attempted at all for the listed cases; that is, if the target type is void, the object's type, or a base of the object's type, the conversion is done directly without considering any conversion functions. Consequently, the questions about whether the conversion function is part of the overload set or not are moot. The wording will be changed to make this clearer.

Proposed Resolution (October, 2007):

Change the footnote in 12.3.2 [class.conv.fct] paragraph 1 as follows:

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

Additional note (March, 2008):

A slight change to the example above indicates that there is a need for a normative change as well as the clarification of the rationale in the October, 2007 proposed resolution. If the declaration of foo were changed to

    void foo(const xyz&);

with the current wording, the call foo(xyz()) would be interpreted as foo(xyz().operator abc&()) instead of binding the parameter directly to the rvalue, which is clearly wrong.

Proposed resolution (March, 2008):

  1. Change the footnote in 12.3.2 [class.conv.fct] paragraph 1 as described in the October, 2007 proposed resolution.

  2. Change 8.5.3 [dcl.init.ref] paragraph 5 as follows:

  3. A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

    [Drafting note: this resolution makes the example in the issue description ill-formed.]




495. Overload resolution with template and non-template conversion functions

Section: 13.3.3  [over.match.best]     Status: WP     Submitter: Nathan Sidwell     Date: 20 Dec 2004

[Voted into WP at July, 2009 meeting.]

The overload resolution rules for ranking a template against a non-template function differ for conversion functions in a surprising way. 13.3.3 [over.match.best] lists four checks, the last three concern this report. For the non-conversion operator case, checks 2 and 3 are applicable, whereas for the conversion operator case checks 3 and 4 are applicable. Checks 2 and 4 concern the ranking of argument and return value conversion sequences respectively. Check 3 concerns only the templatedness of the functions being ranked, and will prefer a non-template to a template. Notice that this check happens after argument conversion sequence ranking, but before return value conversion sequence ranking. This has the effect of always selecting a non-template conversion operator, as the following example shows:

    struct C
    {
      inline operator int () { return 1; }
      template <class T> inline operator T () { return 0; }
    };

    inline long f (long x) { return x; }

    int
    main (int argc, char *argv[])
    {
      return f (C ());
    }

The non-templated C::operator int function will be selected, rather than the apparently better C::operator long<long> instantiation. This is a surprise, and resulted in a bug report where the user expected the template to be selected. In addition some C++ compilers have implemented the overload ranking as if checks 3 and 4 were transposed.

Is this ordering accidental, or is there a rationale?

Notes from the April, 2005 meeting:

The CWG agreed that the template/non-template distinction should be the final tie-breaker.

Proposed resolution (March, 2007):

In the second bulleted list of 13.3.3 [over.match.best] paragraph 1, move the second and third bullets to the end of the list, to read as follows:




702. Preferring conversion to std::initializer_list

Section: 13.3.3.2  [over.ics.rank]     Status: WP     Submitter: Jason Merrill     Date: 2 July, 2008

[Voted into WP at July, 2009 meeting.]

We need another bullet in 13.3.3.2 [over.ics.rank], along the lines of:

This is necessary to make the following example work:

    #include <initializer_list>

    struct string {
      string (const char *) {}
      template <class Iter> string (Iter, Iter);
    };

    template <class T, class U>
    struct pair {
      pair (T t, U u) {}
    };

    template<class T, class U>
    struct map {
      void insert (pair<T,U>);
      void insert (std::initializer_list<pair<T,U> >) {}
    };

    int main() {
      map<string,string> m;
      m.insert({ {"this","that"}, {"me","you"} });
    }

Proposed resolution (March, 2009):

Add a new top-level bullet at the end of the current list in 13.3.3.2 [over.ics.rank] paragraph 3:




749. References to function types with a cv-qualifier or ref-qualifier

Section: 13.6  [over.built]     Status: WP     Submitter: Alberto Ganesh Barbati     Date: 9 December, 2008

[Voted into WP at July, 2009 meeting.]

13.6 [over.built] paragraph 7 posits the existence of built-in candidate operator* functions “for every function type T.” However, only non-static member function types can contain a cv-qualifier or ref-qualifier (8.3.5 [dcl.fct] paragraph 7), and a reference to such a type cannot be initialized (5.2.5 [expr.ref] paragraph 4, bullet 3, sub-bullet 2). (See also _N2914_.14.10.4 [concept.support] paragraph 10, which disallows references to function types with cv-qualifiers but is silent on ref-qualifiers.)

Proposed resolution (March, 2009):

  1. Change 13.6 [over.built] paragraph 7 as follows:

  2. For every function type T that does not have cv-qualifiers or a ref-qualifier, there exist candidate operator functions of the form
  3. Change _N2914_.14.10.4 [concept.support] paragraph 7 as follows:

  4. Requires: for every type T that is an object type, a function type that does not have cv-qualifiers or a ref-qualifier, or cv void, a concept map PointeeType<T> is implicitly defined in namespace std.
  5. Change _N2914_.14.10.4 [concept.support] paragraph 11 as follows:

  6. Requires: for every type T that is an object type, a function type that does not have cv-qualifiers or a ref-qualifier, or a reference type, a concept map ReferentType<T> is implicitly defined in namespace std.



588. Searching dependent bases of classes local to function templates

Section: 14.7.2  [temp.dep]     Status: WP     Submitter: James Widman     Date: 21 June 2006

[Voted into the WP at the March, 2009 meeting.]

14.7.2 [temp.dep] paragraph 3 reads,

In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.

This wording applies only to definitions of class templates and members of class templates. That would make the following program ill-formed (but it probably should be well-formed):

    struct B{ void f(int); };

    template<class T> struct D: B { };

    template<class T> void g() {
       struct B{ void f(); };
       struct A: D<T> {
           B m;
       };
       A a;
       a.m.f(); // Presumably, we want ::g()::B::f(), not ::B::f(int)
    }

    int main () {
       g<int>();
       return 0;
    }

I suspect the wording should be something like

In the definition of a class template or a class defined (directly or indirectly) within the scope of a class template or function template, if a base class...

That should also include deeply nested classes in templates, local classes of non-template member functions of member classes of class templates, etc.

Proposed resolution (October, 2006):

Change 14.7.2 [temp.dep] paragraph 3 as follows:

In the definition of a class or class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.



499. Throwing an array of unknown size

Section: 15.1  [except.throw]     Status: WP     Submitter: Mike Miller     Date: 19 Jan 2005

[Voted into the WP at the March, 2009 meeting.]

According to 15.1 [except.throw] paragraph 3,

The type of the throw-expression shall not be an incomplete type, or a pointer to an incomplete type other than (possibly cv-qualified) void.

This disallows cases like the following, because str has an incomplete type (an array of unknown size):

    extern const char str[];
    void f() {
        throw str;
    }

The array-to-pointer conversion is applied to the operand of throw, so there's no problem creating the exception object, which is the reason for the restriction on incomplete types. I believe this case should be permitted.

Notes from the April, 2005 meeting:

The CWG agreed that the example should be permitted. Note that the reference to throw-expression in the cited text is incorrect; a throw-expression includes the throw keyword and is always of type void. This wording problem is addressed in the proposed resolution for issue 475.

Proposed resolution (October, 2006)

Change 15.1 [except.throw] paragraph 3 as indicated:

...The type of the throw-expression shall not If the type of the exception object would be an incomplete type, or a pointer to an incomplete type other than (possibly cv-qualified) void the program is ill-formed...



668. Throwing an exception from the destructor of a local static object

Section: 15.5.1  [except.terminate]     Status: WP     Submitter: Daniel Krügler     Date: 16 December 2007

[Voted into the WP at the March, 2009 meeting.]

The destruction of local static objects occurs at the same time as that of non-local objects (3.6.3 [basic.start.term] paragraph 1) and the execution of functions registered with std::atexit (paragraph 3). According to 15.5.1 [except.terminate] paragraph 1, std::terminate is called if a destructor for a non-local object or a function registered with std::atexit exits via an exception, but the Standard is silent about the result of throwing an exception from a destructor for a local static object. Presumably this is an oversight and the same rules should apply to destruction of local static objects.

Proposed resolution (September, 2008):

Change 15.5.1 [except.terminate] paragraph 1, fourth bullet as indicated, and add an additional bullet to follow it:






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.3.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.6.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.6.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.6.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.6.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.6.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.4 [class.friend]) not otherwise visible may be found.

With the current definition of name, argument-dependent lookup apparently does not apply to function-notation calls to overloaded operators.

Another related question is whether a template-id is a name or not and thus would trigger an argument-dependent lookup. Personally, I have always viewed a template-id as a name, just like operator+.

Proposed Resolution (November, 2006):

  1. Change 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.3 [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.5 [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.4 [class.friend] ) not otherwise visible may be found... the set of declarations found by the lookup of the function name [includes] the set of declarations found in the... classes associated with the argument types.
The most straightforward reading of this wording is that if a function of namespace scope (as opposed to a class member function) is declared as a friend in a class, and that class is an associated class in a function call, the friend function will be part of the overload set, even if it is not visible to normal lookup.

Consider the following example:

    namespace A {
	class S;
    };
    namespace B {
	void f(A::S);
    };
    namespace A {
	class S {
	    int i;
	    friend void B::f(S);
	};
    }
    void g() {
	A::S s;
	f(s); // should find B::f(A::S)
    }
This example would seem to satisfy the criteria from 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.4 [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.4 [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.4 [class.friend]).
    • All names except those of (possibly overloaded) functions and function templates are ignored.




403. Reference to a type as a template-id

Section: 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.8.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.8.2 [temp.explicit]) or explicitly specialized (14.8.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.3 [temp.names] ) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class or function template.

There do not seem to be any circumstances in which use of a non-member template function would be well-formed as the id-expression of a class member access expression.

Proposed Resolution (November, 2006):

Change 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.3 [temp.names]) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class or function template...



305. Name lookup in destructor call

Section: 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.7.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.4.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.4.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.4.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.local])) 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.local])) 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.4.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.3.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.3.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]