Document number:  PL22.16/09-0126 = WG21 N2936
Date:  2009-08-03
Project:  Programming Language C++
Reference:  ISO/IEC IS 14882:2003
Reply to:  William M. Miller
 Edison Design Group, Inc.
 wmm@edg.com


C++ Standard Core Language Active Issues, Revision 65


This document contains the C++ core language issues on which the Committee (J16 + WG21) has not yet acted, that is, issues with status "Ready," "Review," "Drafting," and "Open."

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:

Section references in this document reflect the section numbering of document PL22.16/09-0104 = WG21 N2914.

The purpose of these documents is to record the disposition of issues that have come before the Core Language Working Group of the ANSI (INCITS PL22.16) and ISO (WG21) C++ Standard Committee.

Some issues represent potential defects in the ISO/IEC IS 14882:2003 document and corrected defects in the earlier ISO/IEC 14882:1998 document; others refer to text in the working draft for the next revision of the C++ language, informally known as C++0x, and not to any Standard text. Issues are not necessarily formal ISO Defect Reports (DRs). While some issues will eventually be elevated to DR status, others will be disposed of in other ways. (See Issue Status below.)

The most current public version of this document can be found at http://www.open-std.org/jtc1/sc22/wg21. Requests for further information about these documents should include the document number, reference ISO/IEC 14882:2003, and be submitted to the InterNational Committee for Information Technology Standards (INCITS), 1250 Eye Street NW, Suite 200, Washington, DC 20005, USA.

Information regarding how to obtain a copy of the C++ Standard, join the Standard Committee, or submit an issue can be found in the C++ FAQ at http://www.comeaucomputing.com/csc/faq.html. Public discussion of the C++ Standard and related issues occurs on newsgroup comp.std.c++.


Revision History

Issue status

Issues progress through various statuses as the Core Language Working Group and, ultimately, the full PL22.16 and WG21 committees deliberate and act. For ease of reference, issues are grouped in these documents by their status. Issues have one of the following statuses:

Open: The issue is new or the working group has not yet formed an opinion on the issue. If a Suggested Resolution is given, it reflects the opinion of the issue's submitter, not necessarily that of the working group or the Committee as a whole.

Drafting: Informal consensus has been reached in the working group and is described in rough terms in a Tentative Resolution, although precise wording for the change is not yet available.

Review: Exact wording of a Proposed Resolution is now available for an issue on which the working group previously reached informal consensus.

Ready: The working group has reached consensus that the issue is a defect in the Standard, the Proposed Resolution is correct, and the issue is ready to forward to the full Committee for ratification as a proposed defect report.

DR: The full Committee has approved the item as a proposed defect report. The Proposed Resolution in an issue with this status reflects the best judgment of the Committee at this time regarding the action that will be taken to remedy the defect; however, the current wording of the Standard remains in effect until such time as a Technical Corrigendum or a revision of the Standard is issued by ISO.

TC1: A DR issue included in Technical Corrigendum 1. TC1 is a revision of the Standard issued in 2003.

CD1: A DR issue not resolved in TC1 but included in Committee Draft 1. CD1 was advanced for balloting at the September, 2008 WG21 meeting.

WP: A DR issue whose resolution is reflected in the current Working Paper. The Working Paper is a draft for a future version of the Standard.

Dup: The issue is identical to or a subset of another issue, identified in a Rationale statement.

NAD: The working group has reached consensus that the issue is not a defect in the Standard. A Rationale statement describes the working group's reasoning.

Extension: The working group has reached consensus that the issue is not a defect in the Standard but is a request for an extension to the language. The working group expresses no opinion on the merits of an issue with this status; however, the issue will be maintained on the list for possible future consideration as an extension proposal.

Concepts: The issue relates to the “Concepts” proposal that was removed from the working paper at the Frankfurt (July, 2009) meeting and hence is no longer under consideration.


Issues with "Ready" Status


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

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

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




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

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

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: ready     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 13

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: ready     Submitter: Alisdair Meredith     Date: 7 July, 2009

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



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

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

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:




861. Unintended ambiguity in inline namespace lookup

Section: 3.4.3.2  [namespace.qual]     Status: ready     Submitter: Michael Wong     Date: 7 April, 2009

The algorithm for namespace-qualified lookup is given in 3.4.3.2 [namespace.qual] paragraph 2:

Given X::m (where X is a user-declared namespace), or given ::m (where X is the global namespace), let S be the set of all declarations of m in X and in the transitive closure of all namespaces nominated by using-directives in X and its used namespaces, except that using-directives that nominate non-inline namespaces (7.3.1 [namespace.def]) are ignored in any namespace, including X, directly containing one or more declarations of m.

Consider the following example:

    namespace A {
        inline namespace B {
            namespace C {
                int i;
            }
            using namespace C;
        }
        int i;
    }

    int j = A::i;     // ambiguous

The transitive closure includes B because it is inline, and it includes C because there is no declaration of i in B. As a result, A::i finds both the i declared in A and the one declared in C, and the lookup is ambiguous.

This result is apparently unintended.

Proposed resolution (July, 2009):

  1. Change 7.3.1 [namespace.def] paragraph 9 as follows:

  2. These properties are transitive: if a namespace N contains an inline namespace M, which in turn contains an inline namespace O, then the members of O can be used as though they were members of M or N. The transitive closure of all inline namespaces in N is the inline namespace set of N. The set of namespaces consisting of the innermost non-inline namespace enclosing an inline namespace O, together with any intervening inline namespaces, is the enclosing namespace set of O.
  3. Change 3.4.3.2 [namespace.qual] paragraph 2 as follows:

  4. Given X::m (where X is a user-declared namespace), or given ::m (where X is the global namespace), let S' be the set of all declarations of m in X and in the inline namespace set of X (7.3.1 [namespace.def]). If S' is not empty, S is S'; otherwise, let S be the set of all declarations of m in X and in the transitive closure of all namespaces nominated by using-directives in X and its used namespaces, except that using-directives that nominate non-inline namespaces (7.3.1 [namespace.def]) are ignored in any namespace, including X, directly containing one or more declarations of m. No namespace is searched more than once...



527. Problems with linkage of types

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

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




882. Defining main as deleted

Section: 3.6.1  [basic.start.main]     Status: ready     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: ready     Submitter: Michael Wong     Date: 12 February, 2009

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



883. std::memcpy vs std::memmove

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

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



695. Compile-time calculation errors in constexpr functions

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

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: ready     Submitter: Beman Dawes     Date: 5 March, 2009

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: ready     Submitter: Jason Merrill     Date: 1 April, 2009

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:




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

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

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.



799. Can reinterpret_cast be used to cast an operand to its own type?

Section: 5.2.10  [expr.reinterpret.cast]     Status: ready     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 55

The note in 5.2.10 [expr.reinterpret.cast] paragraph 2 says,

Subject to the restrictions in this section, an expression may be cast to its own type using a reinterpret_cast operator.

However, there is nothing in the normative text that permits this conversion, and paragraph 1 forbids any conversion not explicitly permitted.

Proposed resolution (July, 2009):

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

  2. The reinterpret_cast operator shall not cast away constness (5.2.11 [expr.const.cast]). [Note: Subject to the restrictions in this section, an expression may be cast to its own type using a reinterpret_cast operator. —end note] An expression of pointer or pointer-to-member type can be explicitly converted to its own type; such a cast yields the value of its operand.
  3. Change 5.2.10 [expr.reinterpret.cast] paragraph 10 as follows:

  4. An rvalue of type “pointer to member of X of type T1” can be explicitly converted to an rvalue of a different type “pointer to member of Y of type T2” if...



842. Casting to rvalue reference type

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

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.



803. sizeof an enumeration type with a fixed underlying type

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

N2800 comment UK 70

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: ready     Submitter: Clark Nelson     Date: 11 January, 2008

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: ready     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 71

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: ready     Submitter: Alisdair Meredith     Date: 6 July, 2009

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: ready     Submitter: Daniel Krügler     Date: 5 April, 2009

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: ready     Submitter: Howard Hinnant     Date: 20 June 2006

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: ready     Submitter: Daniel Krügler     Date: 5 April, 2009

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: ready     Submitter: Steve Adamczyk     Date: 17 September, 2008

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: ready     Submitter: James Kanze     Date: 22 September, 2008

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:




717. Unintentional restrictions on the use of thread_local

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

N2800 comment US 36

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



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

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

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

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: ready     Submitter: Daniel Krügler     Date: 7 April, 2009

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:




812. Duplicate names in inline namespaces

Section: 7.3.1  [namespace.def]     Status: ready     Submitter: JP     Date: 3 March, 2009

N2800 comment JP 14

It is not clear from the specification in 7.3.1 [namespace.def] paragraph 8 how a declaration in an inline namespace should be handled if the name is the same as one in the containing namespace or in an parallel inline namespace. For example:

  namespace Q {
    inline namespace V1 {
      int i;
      int j;
    }
    inline namespace V2 {
      int j;
    }
    int i;
  }
  int Q::i = 1;   // Q::i or Q::V1::i?
  int Q::j = 2;   // Q::V1::j or Q::V2::j?

Proposed resolution (July, 2009):

This issue is resolved by the resolution of issue 861.




919. Contradictions regarding inline namespaces

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

According to 7.3.1 [namespace.def] paragraph 8,

Members of an inline namespace can be used in most respects as though they were members of the enclosing namespace... Furthermore, each member of the inline namespace can subsequently be explicitly instantiated (14.8.2 [temp.explicit]) or explicitly specialized (14.8.3 [temp.expl.spec]) as though it were a member of the enclosing namespace.

However, that assertion is contradicted for class template specializations by 9 [class] paragraph 11:

If a class-head contains a nested-name-specifier, the class-specifier shall refer to a class that was previously declared directly in the class or namespace to which the nested-name-specifier refers...

It is also contradicted for function template specializations by 3.4.3.2 [namespace.qual] paragraph 6:

In a declaration for a namespace member in which the declarator-id is a qualified-id, given that the qualified-id for the namespace member has the form the unqualified-id shall name a member of the namespace designated by the nested-name-specifier.

Proposed resolution (July, 2009):

  1. Change 9 [class] paragraph 11 as follows:

  2. If a class-head contains a nested-name-specifier, the class-specifier shall refer to a class that was previously declared directly in the class or namespace to which the nested-name-specifier refers, or in an element of the inline namespace set (7.3.1 [namespace.def]) of that namespace (i.e., neither not merely inherited nor or introduced by a using-declaration), and the class-specifier shall appear in a namespace enclosing the previous declaration.
  3. Change 3.4.3.2 [namespace.qual] paragraph 6 as follows:

  4. In a declaration for a namespace member in which the declarator-id is a qualified-id, given that the qualified-id for the namespace member has the form

    the unqualified-id shall name a member of the namespace designated by the nested-name-specifier, or of an element of the inline namespace (7.3.1 [namespace.def]) of that namespace. [Example:...

(Note: this resolution depends on the resolution for issue 861.)




921. Unclear specification of inline namespaces

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

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



920. Interaction of inline namespaces and using-declarations

Section: 8.3  [dcl.meaning]     Status: ready     Submitter: Michael Wong     Date: 19 June, 2009

According to 8.3 [dcl.meaning] paragraph 1,

When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers (or of an inline namespace within that scope (7.3.1 [namespace.def])), and the member shall not have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id.

This would appear to make the following example ill-formed, even though it would be well-formed if the using-declaration were omitted:

    namespace A {
      inline namespace B {
        template <class T> void foo() { }
     }
     using B::foo;
    }
    template void A::foo<int>();

This seems strange.

Proposed resolution (July, 2009):

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

...When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers (or, in the case of a namespace, of an element of the inline namespace within that scope set of that namespace (7.3.1 [namespace.def])), and; the member shall not merely have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id. [Note:...

(Note: this resolution depends on the resolution of issue 861.)




713. Unclear note about cv-qualified function types

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

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: ready     Submitter: John Spicer     Date: 2 June, 2009

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: ready     Submitter: Alisdair Meredith     Date: 1 July, 2009

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.



936. Array initialization with new string literals

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

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: ready     Submitter: Steve Adamczyk     Date: 26 July 2006

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:




703. Narrowing for literals that cannot be exactly represented

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

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: ready     Submitter: James Widman     Date: 8 April, 2009

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: ready     Submitter: Mike Miller     Date: 8 July, 2009

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: ready     Submitter: Daniel Krügler     Date: 16 April, 2009

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: ready     Submitter: Mike Miller     Date: 7 December 2006

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: ready     Submitter: Mike Miller     Date: 1 Nov 2000

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: ready     Submitter: Alisdair Meredith     Date: 6 May, 2009

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



604. Argument list for overload resolution in copy-initialization

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

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: ready     Submitter: Daniel Krügler     Date: 23 April, 2009

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: ready     Submitter: Daniel Krügler     Date: 25 April, 2009

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: ready     Submitter: Steve Adamczyk     Date: 13 March, 2009

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:




730. Explicit specializations of members of non-template classes

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

N2800 comment DE 14

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: ready     Submitter: Daniel Krügler     Date: 29 April, 2009

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: ready     Submitter: Mike Miller     Date: 31 October 2007

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: ready     Submitter: Steve Adamczyk     Date: 20 April, 2009

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: ready     Submitter: Daveed Vandevoorde     Date: 23 October 2006

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: ready     Submitter: Martin Sebor     Date: 12 February 2007

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: ready     Submitter: Gennaro Prota     Date: 12 September 2006

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:






Issues with "Review" Status


554. Definition of “declarative region” and “scope”

Section: 3.3  [basic.scope]     Status: review     Submitter: Gabriel Dos Reis     Date: 29 December 2005

The various uses of the term “declarative region” throughout the Standard indicate that the term is intended to refer to the entire block, class, or namespace that contains a given declaration. For example, 3.3 [basic.scope] paragraph 2 says, in part:

[Example: in

    int j = 24;
    int main()
    {
        int i = j, j;
        j = 42;
    }

The declarative region of the first j includes the entire example... The declarative region of the second declaration of j (the j immediately before the semicolon) includes all the text between { and }...

However, the actual definition given for “declarative region” in 3.3 [basic.scope] paragraph 1 does not match this usage:

Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity.

Because (except in class scope) a name cannot be used before it is declared, this definition contradicts the statement in the example and many other uses of the term throughout the Standard. As it stands, this definition is identical to that of the scope of a name.

The term “scope” is also misused. The scope of a declaration is defined in 3.3 [basic.scope] paragraph 1 as the region in which the name being declared is valid. However, there is frequent use of the phrase “the scope of a class,” not referring to the region in which the class's name is valid but to the declarative region of the class body, and similarly for namespaces, functions, exception handlers, etc. There is even a mention of looking up a name “in the scope of the complete postfix-expression” (3.4.5 [basic.lookup.classref] paragraph 3), which is the exact inverse of the scope of a declaration.

This terminology needs a thorough review to make it logically consistent. (Perhaps a discussion of the scope of template parameters could also be added to section 3.3 [basic.scope] at the same time, as all other kinds of scopes are described there.)

Proposed resolution (November, 2006):

  1. Change 3.3 [basic.scope] paragraph 1 as follows:

  2. Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity a statement, block, function declarator, function-definition, class, handler, template-declaration, template-parameter-list of a template template-parameter, or namespace. In general, each particular name is valid may be used as an unqualified name to refer to the entity of its declaration or to the label only within some possibly discontiguous portion of program text called its scope. To determine the scope of a declaration...
  3. Change 3.3 [basic.scope] paragraph 3 as follows:

  4. The names declared by a declaration are introduced into the scope in which the declaration occurs declarative region that directly encloses the declaration, except that declaration-statements, function parameter names in the declarator of a function-definition, exception-declarations (3.3.3 [basic.scope.local]), the presence of a friend specifier (11.4 [class.friend]), certain uses of the elaborated-type-specifier (7.1.6.3 [dcl.type.elab]), and using-directives (7.3.4 [namespace.udir]) alter this general behavior.
  5. Change 3.3.3 [basic.scope.local] paragraphs 1-3 and add a new paragraph 4 before the existing paragraph 4 as follows:

  6. A name declared in a block (6.3 [stmt.block]) is local to that block. Its potential scope begins at its point of declaration (3.3.2 [basic.scope.pdecl]) and ends at the end of its declarative region. The declarative region of a name declared in a declaration-statement is the directly enclosing block (6.3 [stmt.block]). Such a name is local to the block.

    The potential scope declarative region of a function parameter name (including one appearing in the declarator of a function-definition or in a lambda-parameter-declaration-clause) or of a function-local predefined variable in a function definition (8.4 [dcl.fct.def]) begins at its point of declaration. If the function has a function-try-block the potential scope of a parameter or of a function-local predefined variable ends at the end of the last associated handler, otherwise it ends at the end of the outermost block of the function definition. A parameter name is the entire function definition or lambda-expression. Such a name is local to the function definition and shall not be redeclared in the any outermost block of the function definition nor in the outermost block of any handler associated with a function-try-block function-body (including handlers of a function-try-block) or lambda-expression.

    The name in a catch exception-declaration The declarative region of a name declared in an exception-declaration is its entire handler. Such a name is local to the handler and shall not be redeclared in the outermost block of the handler.

    The potential scope of any local name begins at its point of declaration (3.3.2 [basic.scope.pdecl]) and ends at the end of its declarative region.

  7. Change 3.3.5 [basic.funscope] as indicated:

  8. Labels (6.1 [stmt.label]) have function scope and may be used anywhere in the function in which they are declared except in members of local classes (9.8 [class.local]) of that function. Only labels have function scope.
  9. Change 6.7 [stmt.dcl] paragraph 1 as follows:

  10. A declaration statement introduces one or more new identifiers names into a block; it has the form

    [Note: If an identifier a name introduced by a declaration was previously declared in an outer block, the outer declaration is hidden for the remainder of the block, after which it resumes its force (3.3.11 [basic.scope.hiding]). end note]

[Drafting notes: This resolution deals almost exclusively with the unclear definition of “declarative region.” I've left the ambiguous use of “scope” alone for now. However sections 3.3.x all have headings reading “xxx scope,” but they don't mean the scope of a declaration but the different kinds of declarative regions and their effects on the scope of declarations contained therein. To me, it looks like most of 3.4 should refer to “declarative region” and not to “scope.”

The change to 6.7 fixes an “identifier” misuse (e.g., extern T operator+(T,T); at block scope introduces a name but not an identifier) and removes normative redundancy.]




555. Pseudo-destructor name lookup

Section: 3.4  [basic.lookup]     Status: review     Submitter: Krzysztof Zelechowski     Date: 26 January 2006

The Standard does not completely specify how to look up the type-name(s) in a pseudo-destructor-name (5.2 [expr.post] paragraph 1, 5.2.4 [expr.pseudo]), and what information it does have is incorrect and/or in the wrong place. Consider, for instance, 3.4.5 [basic.lookup.classref] paragraphs 2-3:

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. If the type of the object expression is of pointer to scalar type, the unqualified-id is looked up in the context of the complete postfix-expression.

If the unqualified-id is ~type-name, and the type of the object expression is of a class type C (or of pointer to a class type C), the type-name is looked up in the context of the entire postfix-expression and in the scope of class C. The type-name shall refer to a class-name. If type-name is found in both contexts, the name shall refer to the same class type. If the type of the object expression is of scalar type, the type-name is looked up in the scope of the complete postfix-expression.

There are at least three things wrong with this passage with respect to pseudo-destructors:

  1. A pseudo-destructor call (5.2.4 [expr.pseudo]) is not a “class member access”, so the statements about scalar types in the object expressions are vacuous: the object expression in a class member access is required to be a class type or pointer to class type (5.2.5 [expr.ref] paragraph 2).

  2. On a related note, the lookup for the type-name(s) in a pseudo-destructor name should not be described in a section entitled “Class member access.”

  3. Although the class member access object expressions are carefully allowed to be either a class type or a pointer to a class type, paragraph 2 mentions only a “pointer to scalar type” (disallowing references) and paragraph 3 deals only with a “scalar type,” presumably disallowing pointers (although it could possibly be a very subtle way of referring to both non-class pointers and references to scalar types at once).

The other point at which lookup of pseudo-destructors is mentioned is 3.4.3 [basic.lookup.qual] paragraph 5:

If a pseudo-destructor-name (5.2.4 [expr.pseudo]) contains a nested-name-specifier, the type-names are looked up as types in the scope designated by the nested-name-specifier.

Again, this specification is in the wrong location (a pseudo-destructor-name is not a qualified-id and thus should not be treated in the “Qualified name lookup” section).

Finally, there is no place in the Standard that describes the lookup for pseudo-destructor calls of the form p->T::~T() and r.T::~T(), where p and r are a pointer and reference to scalar, respectively. To the extent that it gives any guidance at all, 3.4.5 [basic.lookup.classref] deals only with the case where the ~ immediately follows the . or ->, and 3.4.3 [basic.lookup.qual] deals only with the case where the pseudo-destructor-name contains a nested-name-specifier that designates a scope in which names can be looked up.

See document J16/06-0008 = WG21 N1938 for further discussion of this and related issues, including 244, 305, 399, and 414.

Proposed resolution (June, 2008):

  1. Add a new paragraph following 5.2 [expr.post] paragraph 2 as follows:

  2. When a postfix-expression is followed by a dot . or arrow -> operator, the interpretation depends on the type T of the expression preceding the operator. If the operator is ., T shall be a scalar type or a complete class type; otherwise, T shall be a pointer to a scalar type or a pointer to a complete class type. When T is a (pointer to) a scalar type, the postfix-expression to which the operator belongs shall be a pseudo-destructor call (5.2.4 [expr.pseudo]); otherwise, it shall be a class member access (5.2.5 [expr.ref]).
  3. Change 5.2.4 [expr.pseudo] paragraph 2 as follows:

  4. The left-hand side of the dot operator shall be of scalar type. The left-hand side of the arrow operator shall be of pointer to scalar type. This scalar type The type of the expression preceding the dot operator, or the type to which the expression preceding the arrow operator points, is the object type...
  5. Change 5.2.5 [expr.ref] paragraph 2 as follows:

  6. For the first option (dot) the type of the first expression (the object expression) shall be “class object” (of a complete type) is a class type. For the second option (arrow) the type of the first expression (the pointer expression) shall be “pointer to class object” (of a complete type) is a pointer to a class type. In these cases, the id-expression shall name a member of the class or of one of its base classes.
  7. Add a new paragraph following 3.4 [basic.lookup] paragraph 2 as follows:

  8. In a pseudo-destructor-name that does not include a nested-name-specifier, the type-names are looked up as types in the context of the complete expression.
  9. Delete the last sentence of 3.4.5 [basic.lookup.classref] paragraph 2:

  10. 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, the unqualified-id is looked up in the scope of class C. If the type of the object expression is of pointer to scalar type, the unqualified-id is looked up in the context of the complete postfix-expression.



373. Lookup on namespace qualified name in using-directive

Section: 3.4.6  [basic.lookup.udir]     Status: review     Submitter: Clark Nelson     Date: 15 August 2002

Is this case valid? G++ compiles it.

namespace X {
  namespace Y {
    struct X {
      void f()
      {
        using namespace X::Y;
        namespace Z = X::Y;
      }
    };
  }
}

The relevant citation from the standard is 3.4.6 [basic.lookup.udir]: "When looking up a namespace-name in a using-directive or namespace-alias-definition, only namespace names are considered." This statement could reasonably be interpreted to apply only to the last element of a qualified name, and that's the way EDG and Microsoft seem to interpret it.

However, since a class can't contain a namespace, it seems to me that this interpretation is, shall we say, sub optimal. If the X qualifiers in the above example are interpreted as referring to the struct X, an error of some sort is inevitable, since there can be no namespace for the qualified name to refer to. G++ apparently interprets 3.4.6 [basic.lookup.udir] as applying to nested-name-specifiers in those contexts as well, which makes a valid interpretation of the test possible.

I'm thinking it might be worth tweaking the words in 3.4.6 [basic.lookup.udir] to basically mandate the more useful interpretation. Of course a person could argue that the difference would matter only to a perverse program. On the other hand, namespaces were invented specifically to enable the building of programs that would otherwise be considered perverse. Where name clashes are concerned, one man's perverse is another man's real world.

Proposed Resolution (November, 2006):

Change 3.4.6 [basic.lookup.udir] paragraph 1 as follows:

When looking up a namespace-name in a using-directive or namespace-alias-definition, In a using-directive or namespace-alias-definition, during the lookup for a namespace-name or for a name in a nested-name-specifier, only namespace names are considered.



792. Effects of std::quick_exit

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

N2800 comment US 24

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::atexit(int) (18.5 [support.start.term])) and hence without destroying does not destroy any objects with automatic storage duration (12.4 [class.dtor])...


853. Support for relaxed pointer safety

Section: 3.7.4.3  [basic.stc.dynamic.safety]     Status: review     Submitter: Jens Maurer     Date: 3 April, 2009

According to 20.8.13.7 [util.dynamic.safety] paragraph 16, when std::get_pointer_safety() returns std::pointer_safety::relaxed,

pointers that are not safely derived will be treated the same as pointers that are safely derived for the duration of the program.

However, 3.7.4.3 [basic.stc.dynamic.safety] paragraph 4 says unconditionally that

If a pointer value that is not a safely-derived pointer value is dereferenced or deallocated, and the referenced complete object is of dynamic storage duration and has not previously been declared reachable (20.8.13.7 [util.dynamic.safety]), the behavior is undefined.

This is a contradiction: the library clause attempts to constrain undefined behavior, which by definition is unconstrained.

Proposed resolution (July, 2009):

Change 3.7.4.3 [basic.stc.dynamic.safety] paragraph 4 as follows to define the terms “strict pointer safety” and “relaxed pointer safety,” which could then be used by the library clauses to achieve the desired effect:

An implementation may have relaxed pointer safety, in which case the validity of a pointer value does not depend on whether it is a safely-derived pointer value or not. Alternatively, an implementation may have strict pointer safety, in which case if If a pointer value that is not a safely-derived pointer value is dereferenced or deallocated, and the referenced complete object is of dynamic storage duration and has not previously been declared reachable (20.8.13.7 [util.dynamic.safety]), the behavior is undefined. [Note: this is true even if the unsafely-derived pointer value might compare equal to some safely-derived pointer value. —end note] It is implementation-defined whether an implementation has relaxed or strict pointer safety.



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

Section: 3.8  [basic.life]     Status: review     Submitter: Mike Miller     Date: 27 September 2006

An lvalue referring to an out-of-lifetime non-POD class objects can be used in limited ways, subject to the restrictions in 3.8 [basic.life] paragraph 6:

if the original object will be or was of a non-POD class type, the program has undefined behavior if:

There are at least a couple of questionable things in this list. First, there is no “implicit conversion to a reference to a base class,” as assumed by the second bullet. Presumably this is intended to say that the lvalue is bound to a reference to a base class, and the cross-reference should be to 8.5.3 [dcl.init.ref], not to 4.10 [conv.ptr] (which deals with pointer conversions). However, even given that adjustment, it is not clear why it is forbidden to bind a reference to a non-virtual base class of an out-of-lifetime object, as that is just an address offset calculation. (Binding to a virtual base, of course, would require access to the value of the object and thus cannot be done outside the object's lifetime.)

The third bullet also appears questionable. It's not clear why static_cast is discussed at all here, as the only permissible static_cast conversions involving reference types and non-POD classes are to references to base or derived classes and to the same type, modulo cv-qualification; if implicit “conversion” to a base class reference is forbidden in the second bullet, why would an explicit conversion be permitted in the third? Was this intended to refer to reinterpret_cast? Also, is there a reason to allow char types but disallow array-of-char types (which are more likely to be useful than a single char)?

Proposed resolution (March, 2008):

  1. Change 3.8 [basic.life] paragraph 5 as follows:

  2. ...If the object will be or was of a non-trivial class type, the program has undefined behavior if:

  3. Change 3.8 [basic.life] paragraph 6 as follows:

  4. ...if the original object will be or was of a non-trivial class type, the program has undefined behavior if:

[Drafting notes: Paragraph 5 was changed to track the changes to paragraph 6. See also the resolution for issue 658.]




572. Standard conversions for non-built-in types

Section: 4  [conv]     Status: review     Submitter: Jens Maurer     Date: 6 April 2006

4 [conv] paragraph 1 says,

Standard conversions are implicit conversions defined for built-in types.

However, enumeration types (which take part in the integral promotions) and class types (which take part in the lvalue-to-rvalue conversion) are not “built-in” types, so the definition of “standard conversions” is wrong.

Proposed resolution (October, 2006):

Change 4 [conv] paragraph 1 as follows:

Standard conversions are implicit conversions defined for built-in types with built-in meaning...



240. Uninitialized values and undefined behavior

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

4.1 [conv.lval] paragraph 1 says,

If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.

I think there are at least three related issues around this specification:

  1. Presumably assigning a valid value to an uninitialized object allows it to participate in the lvalue-to-rvalue conversion without undefined behavior (otherwise the number of programs with defined behavior would be vanishingly small :-). However, the wording here just says "uninitialized" and doesn't mention assignment.

  2. There's no exception made for unsigned char types. The wording in 3.9.1 [basic.fundamental] was carefully crafted to allow use of unsigned char to access uninitialized data so that memcpy and such could be written in C++ without undefined behavior, but this statement undermines that intent.

  3. It's possible to get an uninitialized rvalue without invoking the lvalue-to-rvalue conversion. For instance:

            struct A {
                int i;
                A() { } // no init of A::i
            };
            int j = A().i;  // uninitialized rvalue
    

    There doesn't appear to be anything in the current IS wording that says that this is undefined behavior. My guess is that we thought that in placing the restriction on use of uninitialized objects in the lvalue-to-rvalue conversion we were catching all possible cases, but we missed this one.

In light of the above, I think the discussion of uninitialized objects ought to be removed from 4.1 [conv.lval] paragraph 1. Instead, something like the following ought to be added to 3.9 [basic.types] paragraph 4 (which is where the concept of "value" is introduced):

Any use of an indeterminate value (5.3.4 [expr.new], 8.5 [dcl.init], 12.6.2 [class.base.init]) of any type other than char or unsigned char results in undefined behavior.

John Max Skaller:

A().i had better be an lvalue; the rules are wrong. Accessing a member of a structure requires it be converted to an lvalue, the above calculation is 'as if':

    struct A {
        int i;
        A *get() { return this; }
    };
    int j = (*A().get()).i;

and you can see the bracketed expression is an lvalue.

A consequence is:

    int &j= A().i; // OK, even if the temporary evaporates

j now refers to a 'destroyed' value. Any use of j is an error. But the binding at the time is valid.

Proposed Resolution (November, 2006):

  1. Add the indicated words to 3.9 [basic.types] paragraph 4:

    ... For trivial types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values. Any use of an indeterminate value (5.3.4 [expr.new], 8.5 [dcl.init], 12.6.2 [class.base.init]) of a type other than unsigned char results in undefined behavior.
  2. Change 4.1 [conv.lval] paragraph 1 as follows:

    If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.

Additional note (May, 2008):

The C committee is dealing with a similar issue in their DR336. According to this analysis, they plan to take almost the opposite approach to the one described above by augmenting the description of their version of the lvalue-to-rvalue conversion. The CWG did not consider that access to an unsigned char might still trap if it is allocated in a register and needs to reevaluate the proposed resolution in that light. See also issue 129.




858. Example binding an rvalue reference to an lvalue

Section: 5  [expr]     Status: review     Submitter: Daniel Krügler     Date: 6 April, 2009

The adoption of paper N2844 made it ill-formed to attempt to bind an rvalue reference to an lvalue, but the example in 5 [expr] paragraph 6 was overlooked in making this change:

    struct A { };
    A&& operator+(A, A);
    A&& f();

    A a;
    A&& ar = a;

The last line should be changed to use something like static_cast<A&&>(a).

(See also issue 847.)

Proposed resolution (July, 2009):

Change the example in 5 [expr] paragraph 6 as follows:

[Example:

  struct A { };
  A&& operator+(A, A);
  A&& f();

  A a;
  A&& ar = static_cast<A&&>(a);

The expressions f() and a + a are rvalues of type A. The expression ar is an lvalue of type A. —end example]




800. Safely-derived pointers and object pointers converted from function pointers

Section: 5.2.10  [expr.reinterpret.cast]     Status: review     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 57

It is not specified under what conditions an object pointer created by converting a function pointer, as described in 5.2.10 [expr.reinterpret.cast] paragraph 8, will be safely-derived, particularly in light of the conditionally-supported, implementation-defined nature of such conversions.

Notes from the March, 2009 meeting:

If this is to be addressed, the result should not be as suggested, i.e., a requirement for implementation documentation appearing only in a note. At the least, such a requirement must be in normative text.

Proposed resolution (July, 2009):

This issue should be closed as NAD

The definition of “safely-derived pointer” is clearly and exclusively formulated in terms of pointers to objects. So no implementation is required to maintain safe pointer derivation through conversion to and from a function-pointer type.

On the other hand, any garbage-collecting implementation is free to treat function pointers the same as object pointers for purposes of collection. This would provide the effect of safe pointer derivation through function-pointer types. An implementation is even free to document this behavior, if it so chooses.

However, converting a pointer to a dynamically-allocated object into a function pointer would be a very strange and almost always pointless and unsafe thing to do. There is no need for the standard to encourage this sort of behavior, even to the extent of adding a note mentioning the possibility.




342. Terminology: "indirection" versus "dereference"

Section: 5.3  [expr.unary]     Status: review     Submitter: Jason Merrill     Date: 7 Oct 2001

Split off from issue 315.

Incidentally, another thing that ought to be cleaned up is the inconsistent use of "indirection" and "dereference". We should pick one.

Proposed resolution (December, 2006):

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

  2. The unary * operator performs indirection dereferences a pointer value: the expression to which it is applied shall be a pointer...
  3. Change 8.3.4 [dcl.array] paragraph 8 as follows:

  4. The results are added and indirection applied values are added and the result is dereferenced to yield an array (of five integers), which in turn is converted to a pointer to the first of the integers.
  5. Change 8.3.5 [dcl.fct] paragraph 9 as follows:

  6. The binding of *fpi(int) is *(fpi(int)), so the declaration suggests, and the same construction in an expression requires, the calling of a function fpi, and then using indirection through dereferencing the (pointer) result to yield an integer. In the declarator (*pif)(const char*, const char*), the extra parentheses are necessary to indicate that indirection through dereferencing a pointer to a function yields a function, which is then called.
  7. Change the index for * and “dereferencing” no longer to refer to “indirection.”

[Drafting note: 26.6.9 [template.indirect.array] requires no change. Many more places in the current wording use “dereferencing” than “indirection.”]




292. Deallocation on exception in new before arguments evaluated

Section: 5.3.4  [expr.new]     Status: review     Submitter: Andrei Iltchenko     Date: 26 Jun 2001

According to the C++ Standard section 5.3.4 [expr.new] paragraph 21 it is unspecified whether the allocation function is called before evaluating the constructor arguments or after evaluating the constructor arguments but before entering the constructor.

On top of that paragraph 17 of the same section insists that

If any part of the object initialization described above [Footnote: This may include evaluating a new-initializer and/or calling a constructor.] terminates by throwing an exception and a suitable deallocation function is found, the deallocation function is called to free the memory in which the object was being constructed... If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object's memory to be freed...

Now suppose we have:

  1. An implementation that always evaluates the constructor arguments first (for a new-expression that creates an object of a class type and has a new-initializer) and calls the allocation function afterwards.
  2. A class like this:
        struct  copy_throw  {
           copy_throw(const copy_throw&)
           {   throw  std::logic_error("Cannot copy!");   }
           copy_throw(long, copy_throw)
           {   }
           copy_throw()
           {   }
        };
    
  3. And a piece of code that looks like the one below:
        int  main()
        try  {
           copy_throw   an_object,     /* undefined behaviour */
              * a_pointer = ::new copy_throw(0, an_object);
           return  0;
        }
        catch(const std::logic_error&)
        {   }
    

Here the new-expression '::new copy_throw(0, an_object)' throws an exception when evaluating the constructor's arguments and before the allocation function is called. However, 5.3.4 [expr.new] paragraph 17 prescribes that in such a case the implementation shall call the deallocation function to free the memory in which the object was being constructed, given that a matching deallocation function can be found.

So a call to the Standard library deallocation function '::operator delete(void*)' shall be issued, but what argument is an implementation supposed to supply to the deallocation function? As per 5.3.4 [expr.new] paragraph 17 - the argument is the address of the memory in which the object was being constructed. Given that no memory has yet been allocated for the object, this will qualify as using an invalid pointer value, which is undefined behaviour by virtue of 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 4.

Suggested resolution:

Change the first sentence of 5.3.4 [expr.new] paragraph 17 to read:

If the memory for the object being created has already been successfully allocated and any part of the object initialization described above...

Proposed resolution (March, 2008):

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

If any part of the object initialization described above [Footnote: ...] terminates by throwing an exception, storage has been obtained for the object, and a suitable deallocation function can be found, the deallocation function is called...



236. Explicit temporaries and integral constant expressions

Section: 5.19  [expr.const]     Status: review     Submitter: Mike Miller     Date: 19 Jul 2000

Does an explicit temporary of an integral type qualify as an integral constant expression? For instance,

    void* p = int();    // well-formed?

It would appear to be, since int() is an explicit type conversion according to 5.2.3 [expr.type.conv] (at least, it's described in a section entitled "Explicit type conversion") and type conversions to integral types are permitted in integral constant expressions (5.19 [expr.const]). However, this reasoning is somewhat tenuous, and some at least have argued otherwise.

Note (March, 2008):

This issue should be closed as NAD as a result of the rewrite of 5.19 [expr.const] in conjunction with the constexpr proposal.




378. Wording that says temporaries are declared

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

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

On exit from a scope (however accomplished), destructors (12.4 [class.dtor]) are called for all constructed objects with automatic storage duration (3.7.3 [basic.stc.auto]) (named objects or temporaries) that are declared in that scope.

It refers to objects "that are declared" but the text in parenthesis also mentions temporaries, which cannot be declared. I think that text should be removed.

This is related to issue 276.

Proposed Resolution (November, 2006):

This issue is resolved by the resolution of issue 276.




539. Constraints on type-specifier-seq

Section: 7.1.6  [dcl.type]     Status: review     Submitter: Mike Miller     Date: 5 October 2005

The constraints on type-specifiers given in 7.1.6 [dcl.type] paragraphs 2 and 3 (at most one type-specifier except as specified, at least one type-specifier, no redundant cv-qualifiers) are couched in terms of decl-specifier-seqs and declarations. However, they should also apply to constructs that are not syntactically declarations and that are defined to use type-specifier-seqs, including 5.3.4 [expr.new], 6.6 [stmt.jump], 8.1 [dcl.name], and 12.3.2 [class.conv.fct].

Proposed resolution (March, 2008):

Change 7.1.6 [dcl.type] paragraph 3 as follows:

At In a complete type-specifier-seq or in a complete decl-specifier-seq of a declaration, at least one type-specifier that is not a cv-qualifier is required in a declaration shall appear unless it the declaration declares a constructor, destructor or conversion function.

(Note: paper N2546, voted into the Working Draft in February, 2008, addresses part of this issue.)




482. Qualified declarators in redeclarations

Section: 8.3  [dcl.meaning]     Status: review     Submitter: Daveed Vandevoorde     Date: 03 Nov 2004

According to 8.3 [dcl.meaning] paragraph 1,

A declarator-id shall not be qualified except for the definition of a member function (9.3 [class.mfct]) or static data member (9.4 [class.static]) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or the definition of a previously declared explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (11.4 [class.friend]). When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers...

This restriction prohibits examples like the following:

    void f();
    void ::f();        // error: qualified declarator

    namespace N {
      void f();
      void N::f() { }  // error: qualified declarator
    }

There doesn't seem to be any good reason for disallowing such declarations, and a number of implementations accept them in spite of the Standard's prohibition. Should the Standard be changed to allow them?

Notes from the April, 2006 meeting:

In discussing issue 548, the CWG agreed that the prohibition of qualified declarators inside their namespace should be removed.

Proposed resolution (October, 2006):

Remove the indicated words from 8.3 [dcl.meaning] paragraph 1:

...An unqualified-id occurring in a declarator-id shall be a simple identifier except for the declaration of some special functions (12.3 [class.conv], 12.4 [class.dtor], 13.5 [over.oper]) and for the declaration of template specializations or partial specializations (). A declarator-id shall not be qualified except for the definition of a member function (9.3 [class.mfct]) or static data member (9.4 [class.static]) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or the definition of a previously declared explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (11.4 [class.friend]). When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers, and the member shall not have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id...

[Drafting note: The omission of “outside of its class” here does not give permission for redeclaration of class members; that is still prohibited by 9.2 [class.mem] paragraph 1. The removal of the enumeration of the kinds of declarations in which a qualified-id can appear does allow a typedef declaration to use a qualified-id, which was not permitted before; if that is undesirable, the prohibition can be reinstated here.]




547. Partial specialization on member function types

Section: 8.3.5  [dcl.fct]     Status: review     Submitter: Peter Dimov     Date: 04 November 2005

The following example appears to be well-formed, with the partial specialization matching the type of Y::f(), even though it is rejected by many compilers:

    template<class T> struct X;

    template<class R> struct X< R() > {
    };

    template<class F, class T> void test(F T::* pmf) {
        X<F> x;
    }

    struct Y {
        void f() {
        }
    };

    int main() {
        test( &Y::f );
    }

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

A cv-qualifier-seq shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration. The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. In the latter case, the cv-qualifiers are ignored.

This specification makes it impossible to write a partial specialization for a const member function:

    template<class R> struct X<R() const> {
    };

A template argument is not one of the permitted contexts for cv-qualification of a function type. This restriction should be removed.

Notes from the April, 2006 meeting:

During the meeting the CWG was of the opinion that the “R() const” specialization would not match the const member function even if it were allowed and so classified the issue as NAD. Questions have been raised since the meeting, however, suggesting that the template argument in the partial specialization would, in fact, match the type of a const member function (see, for example, the very similar usage via typedefs in 9.3 [class.mfct] paragraph 9). The issue is thus being left open for renewed discussion at the next meeting.

Proposed resolution (June, 2008):

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

A cv-qualifier-seq shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration, or the top-level function type of a type-id that is a template-argument for a type template-parameter. The effect... A ref-qualifier shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration, or the top-level function type of a type-id that is a template-argument for a type template-parameter. The return type...



906. Which special member functions can be defaulted?

Section: 8.4  [dcl.fct.def]     Status: review     Submitter: Daveed Vandevoorde     Date: 27 May, 2009

The only restriction placed on the use of “=default” in 8.4 [dcl.fct.def] paragraph 9 is that a defaulted function must be a special member function. However, there are many variations of declarations of special member functions, and it's not clear which of those should be able to be defaulted. Among the possibilities:

Presumably, you should only be able to default a function if it is declared compatibly with the implicit declaration that would have been generated.

Proposed resolution (July, 2009):

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

A function definition of the form:

is called an explicitly-defaulted definition. Only a 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]). that

may be explicitly defaulted, and if it is explicitly defaulted on its first declaration, it shall be public and it shall not be explicit. [Note: This implies that parameter types, return type, and cv-qualifiers must match. —end note] A special member function...




694. Zero- and value-initialization of union objects

Section: 8.5  [dcl.init]     Status: review     Submitter: Clark Nelson     Date: 14 May, 2008

The C committee is considering changing the definition of zero-initialization of unions to guarantee that the bytes of the entire union are set to zero before assigning 0, converted to the appropriate type, to the first member. The argument (summarized here) is for backward compatibility. The C++ Committee may want to consider the same change.

Proposed resolution (August, 2008):

Change bullet 3 of 8.5 [dcl.init] paragraph 5 (in the first list, dealing with zero-initialization) as follows:

[Drafting notes: Ask a C liaison about the progress of WG14 paper N1311, which deals with this issue. Since the adoption of WG21 paper N2544, unions may have static data members, hence the change to refer to the first non-static data member and the deletion of the footnote.]

Notes from the September, 2008 meeting:

It was observed that padding bytes in structs are zero-initialized in C, so if we are changing the treatment of unions in this way we should consider adding the C behavior for padding bytes at the same time. In particular, using memcmp to compare structs only works reliably if the padding bytes are zero-initialized.




355. Global-scope :: in nested-name-specifier

Section: 9  [class]     Status: review     Submitter: Clark Nelson     Date: 16 May 2002

In looking at a large handful of core issues related to elaborated-type-specifiers and the naming of classes in general, I discovered an odd fact. It turns out that there is exactly one place in the grammar where nested-name-specifier is not immediately preceded by "::opt": in class-head, which is used only for class definitions. So technically, this example is ill-formed, and should evoke a syntax error:

  struct A;
  struct ::A { };

However, all of EDG, GCC and Microsoft's compiler accept it without a qualm. In fact, I couldn't get any of them to even warn about it.

Suggested resolution:

It would simplify the grammar, and apparently better reflect existing practice, to factor the global-scope operator into the rule for nested-name-specifier.

Proposed resolution (November, 2006):

  1. In 3.4.3 [basic.lookup.qual] paragraph 6, change the grammar snippet as follows:

  2. Delete 5.1.1 [expr.prim.general] paragraph 4 (“The operator :: followed by...”). [Drafting note: It's covered by paragraph 8 (type, lvalue-ness, member-ness, reference to 3.4.3.2 [namespace.qual]) and 3.4.3.2 [namespace.qual] (qualified lookup for namespace members).]

  3. Change the grammar in 5.1.1 [expr.prim.general] paragraph 7 as follows (deleting the :: forms from qualified-id and adding :: as a new production for nested-name-specifier):

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

    A nested-name-specifier that names designates a namespace (7.3 [basic.namespace]), followed by the name of a member of that namespace...
  5. Change 5.1.1 [expr.prim.general] paragraph 10 as follows:

    In a qualified-id, if the id-expression unqualified-id is a conversion-function-id...
  6. In 5.2 [expr.post] paragraph 1, change the grammar as follows:

  7. In 5.2.4 [expr.pseudo] paragraph 2, change the grammar snippet as follows:

  8. In 7.1.6.2 [dcl.type.simple] paragraph 1, change the grammar as follows:

  9. In 7.1.6.3 [dcl.type.elab] before paragraph 1, change the grammar as follows:

  10. In 7.1.6.3 [dcl.type.elab] paragraph 1, change the grammar snippet as follows:

  11. In 7.3.2 [namespace.alias] paragraph 1, change the grammar as follows:

  12. In 7.3.3 [namespace.udecl] paragraph 1, change the grammar as follows:

  13. In 7.3.4 [namespace.udir] before paragraph 1, change the grammar as follows:

  14. In 8 [dcl.decl] paragraph 4, change the grammar as follows:

  15. In 8.3.3 [dcl.mptr] paragraph 1, change the grammar snippet as follows:

  16. In 9.2 [class.mem] before paragraph 1, change the grammar as follows:

  17. In 10 [class.derived] paragraph 1, change the grammar as follows:

  18. In 12.6.2 [class.base.init] paragraph 1, change the grammar as follows:

  19. In 14.7 [temp.res] paragraph 3, change the grammar as follows:

[Drafting notes: gcc 4.1.1 rejects the example in the issue description. I still think it's a good idea to make the grammar more uniform, and there ought to be nothing special about the global scope operator. However, there is a slight change in effective grammar with these modification: all places that require a non-optional nested-name-specifier used to required at least one named level of nesting. With these changes, "::" is a valid nested-name-specifier (that denotes the global scope). Any such use needed to protect against non-class (i.e. namespace) scopes in its semantic description anyway, which also covers the "::" case.]




905. Explicit defaulted copy constructors and trivial copyability

Section: 9  [class]     Status: review     Submitter: Daveed Vandevoorde     Date: 27 May, 2009

It is presumably possible to declare a defaulted copy constructor to be explicit. Should that render a class not trivially copyable, even though the copy constructor is trivial? That is, does being “trivally copyable” mean that copy initialization, and not just direct initialization, is possible?

A related question is whether the specification of triviality should require that the copy constructor and copy assignment operator must be public. (With the advent of “=default” it is possible to make them non-public, which was not the case when these definitions were crafted.)

Proposed resolution (July, 2009):

This issues is resolved by the resolution of issue 906.




512. Union members with user-declared non-default constructors

Section: 9.5  [class.union]     Status: review     Submitter: Alisdair Meredith     Date: 19 Mar 2005

Can a member of a union be of a class that has a user-declared non-default constructor? The restrictions on union membership in 9.5 [class.union] paragraph 1 only mention default and copy constructors:

An object of a class with a non-trivial default constructor (12.1 [class.ctor]), a non-trivial copy constructor (12.8 [class.copy]), a non-trivial destructor (12.4 [class.dtor]), or a non-trivial copy assignment operator (13.5.3 [over.ass], 12.8 [class.copy]) cannot be a member of a union...

(12.1 [class.ctor] paragraph 11 does say, “a non-trivial constructor,” but it's not clear whether that was intended to refer only to default and copy constructors or to any user-declared constructor. For example, 12.2 [class.temporary] paragraph 3 also speaks of a “non-trivial constructor,” but the cross-references there make it clear that only default and copy constructors are in view.)

Note (March, 2008):

This issue was resolved by the adoption of paper J16/08-0054 = WG21 N2544 (“Unrestricted Unions”) at the Bellevue meeting.




462. Lifetime of temporaries bound to comma expressions

Section: 12.2  [class.temporary]     Status: review     Submitter: Steve Adamczyk     Date: April 2004

Split off from issue 86.

Should binding a reference to the result of a "," operation whose second operand is a temporary extend the lifetime of the temporary?

  const SFileName &C = ( f(), SFileName("abc") );

Notes from the March 2004 meeting:

We think the temporary should be extended.

Proposed resolution (October, 2004):

Change 12.2 [class.temporary] paragraph 2 as indicated:

... In all these cases, the temporaries created during the evaluation of the expression initializing the reference, except the temporary that is the overall result of the expression [Footnote: For example, if the expression is a comma expression (5.18 [expr.comma]) and the value of its second operand is a temporary, the reference is bound to that temporary.] and to which the reference is bound, are destroyed at the end of the full-expression in which they are created and in the reverse order of the completion of their construction...

[Note: this wording partially resolves issue 86. See also issue 446.]

Notes from the April, 2005 meeting:

The CWG suggested a different approach from the 10/2004 resolution, leaving 12.2 [class.temporary] unchanged and adding normative wording to 5.18 [expr.comma] specifying that, if the result of the second operand is a temporary, that temporary is the result of the comma expression as well.

Proposed Resolution (November, 2006):

Add the indicated wording to 5.18 [expr.comma] paragraph 1:

... The type and value of the result are the type and value of the right operand; the result is an lvalue if its right operand is an lvalue, and is a bit-field if its right operand is an lvalue and a bit-field. If the value of the right operand is a temporary (12.2 [class.temporary]), the result is that temporary.



111. Copy constructors and cv-qualifiers

Section: 12.8  [class.copy]     Status: review     Submitter: Jack Rouse     Date: 4 May 1999

Jack Rouse: In 12.8 [class.copy] paragraph 8, the standard includes the following about the copying of class subobjects in such a constructor:

But there can be multiple copy constructors declared by the user with differing cv-qualifiers on the source parameter. I would assume overload resolution would be used in such cases. If so then the passage above seems insufficient.

Mike Miller: I'm more concerned about 12.8 [class.copy] paragraph 7, which lists the situations in which an implicitly-defined copy constructor can render a program ill-formed. Inaccessible and ambiguous copy constructors are listed, but not a copy constructor with a cv-qualification mismatch. These two paragraphs taken together could be read as requiring the calling of a copy constructor with a non-const reference parameter for a const data member.

Proposed Resolution (November, 2006):

This issue is resolved by the proposed resolution for issue 535.




535. Copy construction without a copy constructor

Section: 12.8  [class.copy]     Status: review     Submitter: Mike Miller     Date: 7 October 2005

Footnote 112 (12.8 [class.copy] paragraph 2) says,

Because a template constructor is never a copy constructor, the presence of such a template does not suppress the implicit declaration of a copy constructor. Template constructors participate in overload resolution with other constructors, including copy constructors, and a template constructor may be used to copy an object if it provides a better match than other constructors.

However, many of the stipulations about copy construction are phrased to refer only to “copy constructors.” For example, 12.8 [class.copy] paragraph 14 says,

A program is ill-formed if the copy constructor... for an object is implicitly used and the special member function is not accessible (clause 11 [class.access]).

Does that mean that using an inaccessible template constructor to copy an object is permissible, because it is not a “copy constructor?” Obviously not, but each use of the term “copy constructor” in the Standard should be examined to determine if it applies strictly to copy constructors or to any constructor used for copying. (A similar issue applies to “copy assignment operators,” which have the same relationship to assignment operator function templates.)

Proposed Resolution (February, 2008):

  1. Change 3.2 [basic.def.odr] paragraph 2 as follows:

  2. ... [Note: this covers calls to named functions (5.2.2 [expr.call]), operator overloading (clause 13 [over]), user-defined conversions (12.3.2 [class.conv.fct]), allocation function for placement new (5.3.4 [expr.new]), as well as non-default initialization (8.5 [dcl.init]). A copy constructor selected to copy class objects is used even if the call is actually elided by the implementation (12.8 [class.copy]). —end note] ... A copy-assignment function for a class An assignment operator function in a class is used by an implicitly-defined copy-assignment function for another class as specified in 12.8 [class.copy]...
  3. Delete 12.1 [class.ctor] paragraphs 10 and 11:

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

    A union member shall not be of a class type (or array thereof) that has a non-trivial constructor.

  5. Replace the “example” in 12.2 [class.temporary] paragraph 1 with a note as follows:

  6. [Example: even if the copy constructor is not called, all the semantic restrictions, such as accessibility (clause 11 [class.access]), shall be satisfied. —end example] [Note: This includes accessibility (clause 11 [class.access]) for the constructor selected. —end note]
  7. Change 12.8 [class.copy] paragraph 7 as follows:

  8. A non-user-provided copy constructor is implicitly defined if it is used to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type (3.2 [basic.def.odr]). [Footnote: See 8.5 [dcl.init] for more details on direct and copy initialization. —end footnote] [Note: the copy constructor is implicitly defined even if the implementation elided its use (12.2 [class.temporary]) the copy operation (12.8 [class.copy]). —end note] A program is ill-formed if the class for which a copy constructor is implicitly defined or explicitly defaulted has:

    • a non-static data member of class type (or array thereof) with an inaccessible or ambiguous copy constructor, or

    • a base class with an inaccessible or ambiguous copy constructor.

    Before the non-user-provided copy constructor for a class is implicitly defined...

  9. Change 12.8 [class.copy] paragraph 8 as follows:

  10. ...Each subobject is copied in the manner appropriate to its type:

    [Drafting note: 8.5 [dcl.init] paragraph 15 requires “unambiguous” and 13.3 [over.match] paragraph 3 requires “accessible,” thus no need for normative text here.]

  11. Change 12.8 [class.copy] paragraph 12 as follows:

  12. 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 it is used (3.2 [basic.def.odr]). A program is ill-formed if the class for which a copy assignment operator is implicitly defined or explicitly defaulted has: a non-static data member of const or reference type.

    • a non-static data member of const type, or

    • a non-static data member of reference type, or

    • a non-static data member of class type (or array thereof) with an inaccessible copy assignment operator, or

    • a base class with an inaccessible copy assignment operator.

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

  14. ... Each subobject is assigned in the manner appropriate to its type:

  15. Delete 12.8 [class.copy] paragraph 14:

  16. A program is ill-formed if the copy constructor or the copy assignment operator for an object is implicitly used and the special member function is not accessible (clause 11 [class.access]). [Note: Copying one object into another using the copy constructor or the copy assignment operator does not change the layout or size of either object. —end note]
  17. Change 12.8 [class.copy] paragraph 15 as follows:

  18. When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor selected for the copy operation and/or the destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. [Footnote: Because only one object is destroyed instead of two, and one copy constructor is not executed, there is still one object destroyed for each one constructed. —end footnote] This elision...
  19. Change 13.3.3.1.2 [over.ics.user] paragraph 4 as follows:

  20. A conversion of an expression of class type to the same class type is given Exact Match rank, and a conversion of an expression of class type to a base class of that type is given Conversion rank, in spite of the fact that a copy constructor (i.e., a user-defined conversion function) is called for those cases.
  21. Change 15.1 [except.throw] paragraph 3 as follows:

  22. A throw-expression initializes a temporary object, called the exception object, the type of which by copy-initialization (8.5 [dcl.init]). The type of that temporary object is determined...
  23. Change 15.1 [except.throw] paragraph 5 as follows:

  24. When the thrown object is a class object, the copy constructor selected for the copy-initialization and the destructor shall be accessible, even if the copy operation is elided (12.8 [class.copy]).
  25. Change 15.3 [except.handle] paragraphs 16-17 as follows:

  26. When the exception-declaration specifies a class type, a copy constructor copy-initialization (8.5 [dcl.init]) is used to initialize either the object declared in the exception-declaration or, if the exception-declaration does not specify a name, a temporary object of that type. The object shall not have an abstract class type. The object is destroyed when the handler exits, after the destruction of any automatic objects initialized within the handler. The copy constructor selected for the copy-initialization and the destructor shall be accessible in the context of the handler, even if the copy operation is elided (12.8 [class.copy]). If the copy constructor and destructor are implicitly declared (12.8 [class.copy]), such a use in the handler causes these functions to be implicitly defined; otherwise, the program shall provide a definition for these functions.

    The copy constructor and destructor associated with the object shall be accessible even if the copy operation is elided (12.8 [class.copy]).

  27. Change the footnote in 15.5.1 [except.terminate] paragraph 1 as follows:

  28. [Footnote: For example, if the object being thrown is of a class with a copy constructor type, std::terminate() will be called if that copy constructor the constructor selected to copy the object exits with an exception during a throw. —end footnote]

(This resolution also resolves issue 111.)

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

Notes from February, 2008 meeting:

These changes overlap those that will be made when concepts are added. This issue will be maintained in “review” status until the concepts proposal is adopted and any conflicts will be resolved at that point.




847. Error in rvalue reference deduction example

Section: 14.9.2.1  [temp.deduct.call]     Status: review     Submitter: Steve Adamczyk     Date: 27 March, 2009

The adoption of paper N2844 made it ill-formed to attempt to bind an rvalue reference to an lvalue. However, the example in 14.9.2.1 [temp.deduct.call] paragraph 3 still reflects the previous specification:

    template <typename T> int f(T&&);
    int i;
    int j = f(i);        // calls f<int&>(i)
    template <typename T> int g(const T&&);
    int k;
    int n = g(k);        // calls g<int>(k)

The last line of that example is now ill-formed, attempting to bind the const int&& parameter of g to the lvalue k.

Proposed resolution (July, 2009):

Replace the example in 14.9.2.1 [temp.deduct.call] paragraph 3 with:

    template<typename T> int f(T&&);
    template<typename T> int g(const T&&);
    int i;
    int n1 = f(i);    // calls f<int&>(int&)
    int n2 = f(0);    // calls f<int>(int&&)
    int n3 = g(i);    // error: would call g<int>(const int&&), which would
                      // bind an rvalue reference to an lvalue

(See also issue 858.)




692. Partial ordering of variadic class template partial specializations

Section: 14.9.2.5  [temp.deduct.type]     Status: review     Submitter: Doug Gregor     Date: 16 April, 2008

14.9.2.5 [temp.deduct.type] paragraph 22 describes how we cope with partial ordering between two function templates that differ because one has a function parameter pack while the other has a normal function parameter. However, this paragraph was meant to apply to template parameter packs as well, e.g., to help with partial ordering of class template partial specializations:

   template <class T1, class ...Z> class S; // #1
   template <class T1, class ...Z> class S<T1, const Z&...> {}; // #2
   template <class T1, class T2> class S<T1, const T2&> {};; // #3
   S<int, const int&> s; // both #2 and #3 match; #3 is more specialized

(See also issue 818.)

Proposed resolution (March, 2009):

Change 14.9.2.5 [temp.deduct.type] paragraphs 9-10 as follows (and add the example above to paragraph 9):

If P has a form that contains <T> or <i>, then each argument Pi of the respective template argument list of P is compared with the corresponding argument Ai of the corresponding template argument list of A. If the template argument list of P contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context. If Pi is a pack expansion, then the pattern of Pi is compared with each remaining argument in the template argument list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by Pi. During partial ordering (14.9.2.4 [temp.deduct.partial]), if Ai was originally a pack expansion and Pi is not a pack expansion, or if P does not contain a template argument corresponding to Ai, argument deduction fails.

Similarly, if P has a form that contains (T), then each parameter type Pi of the respective parameter-type-list of P is compared with the corresponding parameter type Ai of the corresponding parameter-type-list of A. If the parameter-declaration corresponding to Pi is a function parameter pack, then the type of its declarator-id is compared with each remaining parameter type in the parameter-type-list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. During partial ordering (14.9.2.4 [temp.deduct.partial]), if Ai was originally a function parameter pack and Pi is not a function parameter pack, or if P does not contain a function parameter type corresponding to Ai, argument deduction fails. [Note: A function parameter pack can only occur at the end of a parameter-declaration-list (8.3.5 [dcl.fct]). —end note]






Issues with "Drafting" Status


690. The dynamic type of an rvalue reference

Section: 1.3  [intro.defs]     Status: drafting     Submitter: Eelis van der Weegen     Date: 7 April, 2008

N2800 comment FR 5

According to 1.3 [intro.defs], “dynamic type,”

The dynamic type of an rvalue expression is its static type.

This is not true of an rvalue reference, which can be bound to an object of a class type derived from the reference's static type.

Proposed resolution (June, 2008):

Change 1.3 [intro.defs], “dynamic type,” as follows:

the type of the most derived object (1.8 [intro.object]) to which the lvalue denoted by an lvalue or an rvalue-reference (clause 5 [expr]) expression refers. [Example: if a pointer (8.3.1 [dcl.ptr]) p whose static type is “pointer to class B” is pointing to an object of class D, derived from B (clause 10 [class.derived]), the dynamic type of the expression *p is “D.” References (8.3.2 [dcl.ref]) are treated similarly. —end example] The dynamic type of an rvalue expression that is not an rvalue reference is its static type.

Notes from the June, 2008 meeting:

Because expressions have an rvalue reference type only fleetingly, immediately becoming either lvalues or rvalues and no longer references, the CWG expressed a desire for a different approach that would somehow describe an rvalue that resulted from an rvalue reference instead of using the concept of an expression that is an rvalue reference, as above. This approach could also be used in the resolution of issue 664.

Additional note (August, 2008):

This issue, along with issue 664, indicates that rvalue references have more in common with lvalues than with other rvalues: they denote particular objects, thus allowing object identity and polymorphic behavior. That suggests that these issues may be just the tip of the iceberg: restrictions on out-of-lifetime access to objects, the aliasing rules, and many other specifications are written to apply only to lvalues, on the assumption that only lvalues refer to specific objects. That assumption is no longer valid with rvalue references.

This suggests that it might be better to classify all rvalue references, not just named rvalue references, as lvalues instead of rvalues, and then just change the reference binding, overload resolution, and template argument deduction rules to cater to the specific kind of lvalues that are associated with rvalue references.

Additional note, May, 2009:

Another place in the Standard where the assumption is made that only lvalues can have dynamic types that differ from their static types is 5.2.8 [expr.typeid] paragraph 2.

(See also issues 846 and 863.)




612. Requirements on a conforming implementation

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

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.




785. “Execution sequence” is inappropriate phraseology

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

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

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




740. Incorrect note on data races

Section: 1.10  [intro.multithread]     Status: drafting     Submitter: Wolf Lammen     Date: 3 November, 2008

1.10 [intro.multithread] paragraph 12 says,

A visible side effect A on an object M with respect to a value computation B of M satisfies the conditions:

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

The note here suggests that, except in the case of a data race, visible side effects to value computation can always be determined. But unsequenced and indeterminately sequenced side effects on the same object create ambiguities with respect to a later value computation as well. So the wording needs to be revisited, see the following examples.

    int main(){
      int i = 0;
      i = // unsequenced side effect A
      i++; // unsequenced side effect B
      return i; // value computation C
    }

According to the definition in the draft, both A and B are visible side effects to C. However, there is no data race, because (paragraph 14) a race involves at least two threads. So the note in paragraph 12 is logically false.

The model introduces the special case of indeterminately sequenced side effects, that leave open what execution order is taken in a concrete situation. If the execution paths access the same data, unpredictable results are possible, just as it is the case with data races. Whereas data races constitute undefined behavior, indeterminatedly sequenced side effects on the same object do not. As a consequence of this disparity, indeterminately sequenced execution occasionally needs exceptional treatment.

    int i = 0;
    int f(){
      return
      i = 1; // side effect A
    }
    int g(){
      return
      i = 2; // side effect B
    }
    int h(int, int){
      return i; // value computation C
    }
    int main(){
      return h(f(),g()); // function call D returns 1 or 2?
    }

Here, either A or B is the visible side effect on the value computation C, but you cannot tell which (cf. 1.9 [intro.execution] paragraph 16). Although an ambiguity is present, it is neither because of a data race, nor is the behavior undefined, in total contradiction to the note.




786. Definition of “thread”

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

N2800 comment US 17

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




787. Unnecessary lexical undefined behavior

Section: 2.2  [lex.phases]     Status: drafting     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 9
N2800 comment UK 12

There are several instances of undefined behavior in lexical processing:

These would be more appropriately handled as conditionally-supported behavior, requiring implementations either to document their handling of these constructs or to issue a diagnostic.

Additional note, March, 2009:

The undefined behavior referred to above regarding universal-character-names is the result of the considerations described in the C99 Rationale, section 5.2.1, in the part entitled “UCN models.” Three different models for support of UCNs are described, each involving different conversions between UCNs and wide characters and/or at different times during program translation. Implementations, as well as the specification in a language standard, can employ any of the three, but it must be impossible for a well-defined program to determine which model was actually employed by implementation. The implication of this “equivalence principle” is that any construct that would give different results under the different models must be classified as undefined behavior. For example, an apparent UCN resulting from a line-splice would be recognized as a UCN by an implementation in which all wide characters were translated immediately into UCNs, as described in C++ phase 1, but would not be recognized as a UCN by another implementation in which all UCNs were translated immediately into wide characters (a possibility mentioned parenthetically in C++ phase 1).

There are additional implications for this “equivalence principle” beyond the ones identified in the UK CD comments. See also issue 578; presumably a string like the one in that issue should also be described as having undefined behavior. Also, because C++'s model introduces backslash characters as part of UCNs for any character outside the basic source character set, any header-name that contains such a character (e.g., #include "@.h") will have undefined behavior in C++. This is also the reason that UCNs are translated into wide characters inside raw strings: two of the three models articulated in the C99 Rationale translate to or from UCNs in phase 1, before raw strings are recognized as tokens in phase 3, so raw strings cannot treat UCNs differently from the way they are treated in other contexts. See also issue 789 for similar points regarding trigraphs.




789. Deprecating trigraphs

Section: 2.4  [lex.trigraph]     Status: drafting     Submitter: UK     Date: 3 March, 2009

N2800 comment UK 11

Trigraphs are a complicated solution to an old problem, that cause more problems than they solve in the modern environment. Unexpected trigraphs in string literals and occasionally in comments can be very confusing for the non-expert. They should be deprecated.

Notes from the March, 2009 meeting:

IBM, at least, uses trigraphs in its header files in conditional compilation directives to select character-set dependent content in a character-set independent fashion and would thus be negatively affected by the removal of trigraphs. One possibility that was discussed was to avoid expanding trigraphs inside character string literals, which is the context that causes most surprise and confusion, but still to support them in the rest of the program text. Specifying that approach, however, would be challenging because trigraphs are replaced in phase 1, before character strings are recognized in phase 3. See also the similar discussion of universal-character-names in issue 787.

The consensus of the CWG was that trigraphs should be deprecated.




369. Are new/delete identifiers or preprocessing-op-or-punc?

Section: 2.5  [lex.pptoken]     Status: drafting     Submitter: Martin v. Loewis     Date: 30 July 2002

2.5 [lex.pptoken] paragraph 2 specifies that there are 5 categories of tokens in phases 3 to 6. With 2.13 [lex.operators] paragraph 1, it is unclear whether new is an identifier or a preprocessing-op-or-punc; likewise for delete. This is relevant to answer the question whether

#define delete foo

is a well-formed control-line, since that requires an identifier after the define token.

(See also issue 189.)




189. Definition of operator and punctuator

Section: 2.13  [lex.operators]     Status: drafting     Submitter: Mike Miller     Date: 20 Dec 1999

The nonterminals operator and punctuator in 2.7 [lex.token] are not defined. There is a definition of the nonterminal operator in 13.5 [over.oper] paragraph 1, but it is apparent that the two nonterminals are not the same: the latter includes keywords and multi-token operators and does not include the nonoverloadable operators mentioned in paragraph 3.

There is a definition of preprocessing-op-or-punc in 2.13 [lex.operators] , with the notation that

Each preprocessing-op-or-punc is converted to a single token in translation phase 7 (2.1).
However, this list doesn't distinguish between operators and punctuators, it includes digraphs and keywords (can a given token be both a keyword and an operator at the same time?), etc.

Suggested resolution:


  1. Change 13.5 [over.oper] to use the term overloadable-operator.
  2. Change 2.7 [lex.token] to use the term operator-token instead of operator (since there are operators that are keywords and operators that are composed of more than one token).
  3. Change 2.13 [lex.operators] to define the nonterminals operator-token and punctuator.

Additional note (April, 2005):

The resolution for this problem should also address the fact that sizeof and typeid (and potentially others like decltype that may be added in the future) are described in some places as “operators” but are not listed in 13.5 [over.oper] paragraph 3 among the operators that cannot be overloaded.

(See also issue 369.)




790. Concatenation of raw and non-raw string literals

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

N2800 comment JP 5

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.




834. What is an “ordinary string literal”?

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

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.



932. UCNs in closing delimiters of raw string literals

Section: 2.14.5  [lex.string]     Status: drafting     Submitter: Alisdair Meredith     Date: 7 July, 2009

Since members of the basic source character set can be written inside a string using a universal character name, it is not clear whether a UCN that represents ']' or one of the characters in the terminating d-char-sequence should be interpreted as that character or as an attempt to “escape” that character and prevent its interpretation as part of the terminating sequence of a raw character string.

Notes from the July, 2009 meeting:

The CWG supported a resolution in which the d-char-sequence of a raw string literal is considered to be outside the literal and thus, by 2.3 [lex.charset] paragraph 2, could not contain a UCN designating a member of the basic source character set.




633. Specifications for variables that should also apply to references

Section: 3  [basic]     Status: drafting     Submitter: Alisdair Meredith     Date: 17 May 2007

N2800 comment UK 22

There are a number of specifications in the Standard that should also apply to references. For example:

A number of other examples could be cited. A thorough review is needed to make sure that references are completely specified.

Proposed resolution (March, 2008):

  1. Change 2.2 [lex.phases] paragraph 1, number 9 as follows:

    1. All external object and function entity references are resolved. Library components are linked to satisfy external references to functions and objects entities not defined in the current translation...

  2. Change 3.3 [basic.scope] paragraph 4, bullet 2 as follows:

  3. Change 3.3.2 [basic.scope.pdecl] paragraph 9 as follows:

  4. Function declarations at block scope and object or reference declarations with the extern specifier at block scope refer to declarations that are members of an enclosing namespace...
  5. Change 3.3.11 [basic.scope.hiding] paragraph 2 as follows:

  6. A class name (9.1 [class.name]) or enumeration name (7.2 [dcl.enum]) can be hidden by the name of an object, reference, function, or enumerator declared in the same scope. If a class or enumeration name and an object, reference, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the object, reference, function, or enumerator name is visible.
  7. Change 3.4.1 [basic.lookup.unqual] paragraph 14 as follows:

  8. If a variable or reference member of a namespace is defined outside of the scope of its namespace then any name used that appears 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...
  9. Change 3.4.3 [basic.lookup.qual] paragraph 1 as follows:

  10. ...During the lookup for a name preceding the :: scope resolution operator, object, reference, function, and enumerator names are ignored...
  11. Change 3.4.3.2 [namespace.qual] paragraph 5 as follows:

  12. During the lookup of a qualified namespace member name, if the lookup finds more than one declaration of the member, and if one declaration introduces a class name or enumeration name and the other declarations either introduce the same object, the same reference, the same enumerator or a set of functions, the non-type name hides the class or enumeration name if and only if the declarations are from the same namespace; otherwise (the declarations are from different namespaces), the program is ill-formed.
  13. Change 3.5 [basic.link] paragraph 6 as follows:

  14. The name of a function declared in block scope, and the name of an object or reference declared by a block scope extern declaration, have linkage...
  15. Change 3.5 [basic.link] paragraph 8 as follows:

  16. ...A type without linkage shall not be used as the type of a variable, reference, or function with linkage, unless the variable or function that entity has extern "C" linkage...
  17. Change 3.5 [basic.link] paragraph 10 as follows:

  18. ...the types specified by all declarations referring to a given object, reference, or function shall be identical...
  19. Change 3.6.1 [basic.start.main] paragraph 1 as follows:

  20. ...Dynamic initialization of an object or reference is either ordered or unordered. Definitions of explicitly specialized class template static data members have ordered initialization. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) have unordered initialization. Other objects and references defined in namespace scope have ordered initialization. Objects and references 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 and references with unordered initialization and for objects and references defined in different translation units. An unordered initialization is indeterminately sequenced with respect to every other dynamic initialization. [Note: 8.5.1 [dcl.init.aggr] describes the order in which aggregate members are initialized. The initialization of local static objects and references is described in 6.7 [stmt.dcl]. —end note]
  21. Change 3.6.1 [basic.start.main] paragraph 3 as follows:

  22. It is implementation-defined whether or not the dynamic initialization (8.5 [dcl.init], 9.4 [class.static], 12.1 [class.ctor], 12.6.1 [class.expl.init]) of an object or reference of namespace scope is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first use of any function, or object, or reference defined in the same translation unit as the object or reference to be initialized. [Footnote: An object or reference defined in namespace scope having initialization with side-effects must be initialized even if it is not used (3.7.1). —end footnote]
  23. Change 3.7.1 [basic.stc.static] paragraph 3 as follows:

  24. The keyword static can be used to declare a local variable or reference with static storage duration. [Note: 6.7 [stmt.dcl] describes the their initialization of local static variables; 3.6.3 [basic.start.term] describes the their destruction of local static variables. —end note]
  25. Change 5.1.1 [expr.prim.general] paragraph 4 as follows:

  26. ...The result is an lvalue if the entity is a function, or variable, or reference... [Note: the use of :: allows a type, an object, a function, an enumerator, or a namespace an entity declared in the global namespace to be referred to even if its identifier name has been hidden (3.4.3 [basic.lookup.qual]). —end note]
  27. Change 5.1.1 [expr.prim.general] paragraph 7 as follows:

  28. ...The result is an lvalue if the entity is a function, variable, reference, or data member.
  29. Change 5.1.1 [expr.prim.general] paragraph 8 as follows:

  30. ...The result is an lvalue if the member is a function, or a variable, or reference.
  31. Change 6.5.1 [stmt.while] paragraph 2 as follows:

  32. ...The object or reference created in a condition is destroyed and created with each iteration of the loop...
  33. Change 6.7 [stmt.dcl] paragraph 2 as follows:

  34. Variables and references with automatic storage duration (3.7.3 [basic.stc.auto]) are initialized each time their declaration-statement is executed...
  35. Change 6.7 [stmt.dcl] paragraph 3 as follows:

  36. ...A program that jumps from a point where a local variable or reference with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has it is a variable with trivial type (3.9 [basic.types]) and is declared without an initializer (8.5 [dcl.init])...
  37. Change 6.7 [stmt.dcl] paragraph 4 as follows:

  38. The zero-initialization (8.5 [dcl.init]) of all local objects with static storage duration (3.7.1 [basic.stc.static]) is performed before any other initialization takes place. When initialized with a constant expression, a local reference with static storage duration or a A local object of trivial or literal type (3.9 [basic.types]) with static storage duration initialized with constant-expressions is initialized before its block is first entered. An implementation is permitted to perform early initialization of other local objects with static storage duration under the same conditions that an implementation is permitted to statically initialize an object with static storage duration in namespace scope (3.6.2 [basic.start.init]). Otherwise such an object or reference is initialized the first time control passes through its declaration; such an object or reference is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control re-enters the declaration (recursively) while the object or reference is being initialized, the behavior is undefined...
  39. Change 7.1.1 [dcl.stc] paragraphs 2-7 as follows:

  40. The register specifier shall be applied only to names of objects and references declared in a block (6.3 [stmt.block]) or to function parameters (8.4 [dcl.fct.def]). It specifies that the named object or reference has automatic storage duration (3.7.3 [basic.stc.auto]). An object or reference declared without a storage-class-specifier at block scope or declared as a function parameter has automatic storage duration by default.

    A register specifier is a hint to the implementation that the object or reference 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. —end note]

    The static specifier can be applied only to names of objects, references, and functions and to anonymous unions (9.5 [class.union]). There can be no static function declarations within a block, nor any static function parameters. A static specifier used in the declaration of an object or reference declares the object entity to have static storage duration (3.7.1 [basic.stc.static]). A static specifier can be used in declarations of class members; 9.4 [class.static] describes its effect. For the linkage of a name declared with a static specifier, see 3.5 [basic.link].

    The extern specifier can be applied only to the names of objects, references, and functions. The extern specifier cannot be used in the declaration of class members or function parameters. For the linkage of a name declared with an extern specifier, see 3.5 [basic.link]. [Note: The extern keyword can also be used in explicit-instantiations and linkage-specifications, but it is not a storage-class-specifier in such contexts. —end note]

    A name declared in a namespace scope without a storage-class-specifier has external linkage unless it has internal linkage because of a previous declaration and provided it is not declared const. Objects declared const and not explicitly declared extern have internal linkage.

    The linkages implied by successive declarations for a given entity shall agree. That is, within a given scope, each declaration declaring the same object or reference name or the same overloading of a function name shall imply the same linkage. Each function in a given set of overloaded functions can have a different linkage, however...

  41. Change 7.1.6.4 [dcl.spec.auto] paragraph 1 as follows:

  42. The auto type-specifier signifies that the type of an object or reference being declared shall be deduced from its initializer...
  43. Change 7.1.6.4 [dcl.spec.auto] paragraph 3 as follows:

  44. Otherwise, the type of the object or reference is deduced from its initializer. The name of the object entity being declared shall not appear in the initializer expression. This use of auto is allowed when declaring objects and references in a block (6.3 [stmt.block]), in namespace scope (3.3.6 [basic.scope.namespace]), and in a for-init-statement (6.5.3 [stmt.for]).
  45. Change 7.1.6.4 [dcl.spec.auto] paragraph 4 as follows:

  46. The auto type-specifier can also be used in declaring an object or reference in the condition of a selection statement...
  47. Change 7.1.6.4 [dcl.spec.auto] paragraphs 6-7 as follows:

  48. Once the type of a declarator-id has been determined according to 8.3 [dcl.meaning], the type of the declared variable or reference using the declarator-id is determined from the type of its initializer using the rules for template argument deduction. Let T be the type that has been determined for a variable or reference identifier d. Obtain P from T by replacing the occurrences of auto with a new invented type template parameter U. Let A be the type of the initializer expression for d. The type deduced for the variable d is then the deduced type determined using the rules of template argument deduction from a function call (14.9.2.1 [temp.deduct.call]), where P is a function template parameter type and A is the corresponding argument type. If the deduction fails, the declaration is ill-formed.

    If the list of declarators contains more than one declarator, the type of each declared variable entity is determined as described above...

  49. Change 7.3.1.1 [namespace.unnamed] paragraph 2 as follows:

  50. The use of the static keyword is deprecated when declaring objects and references in a namespace scope (see annex D [depr]); the unnamed-namespace provides a superior alternative.
  51. Change 7.3.4 [namespace.udir] paragraph 6 as follows:

  52. ...[Note: in particular, the name of an object, reference, function or enumerator does not hide the name of a class or enumeration declared in a different namespace...
  53. Change 8 [dcl.decl] paragraph 1 as follows:

  54. A declarator declares a single object, reference, function, or type, within a declaration...
  55. Change 8 [dcl.decl] paragraph 2 as follows:

  56. ...The specifiers indicate the type, storage class or other properties of the objects, functions or typedefs entities being declared. The declarators specify the names of these objects, functions or typedefs, entities and (optionally) modify the type of the specifiers with operators such as * (pointer to) and () (function returning)...
  57. Change 8.1 [dcl.name] paragraph 1 as follows:

  58. ...This can be done with a type-id, which is syntactically a declaration for an object, reference, or function of that type that omits the name of the object or function entity...
  59. Change 8.5 [dcl.init] paragraph 2 as follows:

  60. Automatic, register, static, and external variables and references of namespace scope can be initialized by arbitrary expressions involving literals and previously declared variables and functions...
  61. Change 8.5 [dcl.init] paragraph 4 as follows:

  62. The order of initialization of static objects and references is described in 3.6 [basic.start] and 6.7 [stmt.dcl].
  63. Delete the last bullet of 8.5 [dcl.init] paragraph 4, first list (zero-initialization) and replace the semicolon with a period in the preceding bullet:

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

  65. A variable An entity declared to be a T& or T&&, that is, “reference to type T” (8.3.2 [dcl.ref]), shall be initialized by an object, or function, of type T or by an object that can be converted into a T...
  66. Change 9.1 [class.name] paragraph 2 as follows:

  67. ...If a class name is declared in a scope where an object, reference, function, or enumerator of the same name is also declared, then when both declarations are in scope, the class can be referred to only using an elaborated-type-specifier (3.4.4 [basic.lookup.elab])...
  68. Change 9.4.2 [class.static.data] paragraph 6 as follows:

  69. Static data members are initialized and destroyed exactly like non-local objects and references (3.6.2 [basic.start.init], 3.6.3 [basic.start.term]).
  70. Change 9.8 [class.local] paragraph 1 as follows:

  71. ...Declarations in a local class can use only type names, static variables and references with static storage duration, extern variables and functions, and enumerators from the enclosing scope...
  72. Change 10.2 [class.member.lookup] paragraph 4 as follows:

  73. ...[Note: Looking up a name in an elaborated-type-specifier (3.4.4 [basic.lookup.elab]) or base-specifier (clause 10 [class.derived]), for instance, ignores all non-type declarations, while looking up a name in a nested-name-specifier (3.4.3 [basic.lookup.qual]) ignores function, object, reference, and enumerator declarations...
  74. Change 14 [temp] paragraph 5 as follows:

  75. A class template shall not have the same name as any other template, class, function, object, reference, enumeration, enumerator, namespace, or type in the same scope...
  76. Change 14.9 [temp.fct.spec] paragraph 2 as follows:

  77. Each function template specialization instantiated from a template has its own copy of any static variable or reference...

[Drafting notes: This resolution depends on the part of the resolution for issue 485 that adds references to the list of “entities.” It is also partly resolved by the proposed resolution for issue 570. No change is proposed to the text in 7.5 [dcl.link], hence reference names continue to have no language linkage, and prohibitions against conflicting linkage specifications do not apply to reference declarations.]

Notes from the September, 2008 meeting:

The CWG expressed interest in an approach that would define “variable” to include both objects and references and to use that for both this issue and issue 570.




676. static_assert-declarations and general requirements for declarations

Section: 3.1  [basic.def]     Status: drafting     Submitter: Alisdair Meredith     Date: 12 February, 2008

3.1 [basic.def] makes statements about declarations that do not appear to apply to static_assert-declarations. For example, paragraph 1 says,

A declaration (clause 7 [dcl.dcl]) introduces names into a translation unit or redeclares names introduced by previous declarations. A declaration specifies the interpretation and attributes of these names.

What name is being declared or described by a static_assert-declaration?

Also, paragraph 2 lists the kinds of declarations that are not definitions, and a static_assert-declaration is not among them. Is it intentional that static_assert-declarations are definitions?




570. Are references subject to the ODR?

Section: 3.2  [basic.def.odr]     Status: drafting     Submitter: Dave Abrahams     Date: 2 April 2006

N2800 comment UK 26

3.2 [basic.def.odr] paragraph 1 says,

No translation unit shall contain more than one definition of any variable, function, class type, enumeration type or template.

This says nothing about references. Is it permitted to define a reference more than once in a single translation unit? (The list in paragraph 5 of things that can have definitions in multiple translation units does not include references.)

Proposed resolution (March, 2008):

  1. Change 3.2 [basic.def.odr] paragraph 1 as follows:

  2. No translation unit shall contain more than one definition of any variable, reference, function, class type, enumeration type or template.
  3. Change 3.2 [basic.def.odr] paragraph 2 as follows:

  4. ...An object, reference, or non-overloaded function whose name appears as a potentially-evaluated expression is used unless it is an object that satisfies the requirements for appearing in a constant expression...
  5. Change 3.2 [basic.def.odr] paragraph 3 as follows:

  6. Every program shall contain exactly one definition of every non-inline function, or object, or reference that is used in that program...

(Note: this resolution also resolves part of issue 633.)

Notes from the September, 2008 meeting:

The CWG expressed interest in an approach that would define “variable” to include both objects and references and to use that for both this issue and issue 633.




678. Language linkage of member function parameter types and the ODR

Section: 3.2  [basic.def.odr]     Status: drafting     Submitter: James Widman     Date: 15 February, 2008

I thought this case would result in undefined behavior according to 3.2 [basic.def.odr]:

    // t.h:
    struct A { void (*p)(); };

    // t1.cpp:
    #include "t.h" // A::p is a pointer to C++ func

    // t2.cpp:
    extern "C" {
    #include "t.h" // A::p is a pointer to C func
    }

...but I don't see how any of the bullets in the list in paragraph 5 apply.




712. Are integer constant operands of a conditional-expression “used?”

Section: 3.2  [basic.def.odr]     Status: drafting     Submitter: Mike Miller     Date: 9 September, 2008

In describing static data members initialized inside the class definition, 9.4.2 [class.static.data] paragraph 3 says,

The member shall still be defined in a namespace scope if it is used in the program...

The definition of “used” is in 3.2 [basic.def.odr] paragraph 1:

An object or non-overloaded function whose name appears as a potentially-evaluated expression is used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19 [expr.const]) and the lvalue-to-rvalue conversion (4.1 [conv.lval]) is immediately applied.

Now consider the following example:

    struct S {
      static const int a = 1;
      static const int b = 2;
    };
    int f(bool x) {
      return x ? S::a : S::b;
    }

According to the current wording of the Standard, this example requires that S::a and S::b be defined in a namespace scope. The reason for this is that, according to 5.16 [expr.cond] paragraph 4, the result of this conditional-expression is an lvalue and the lvalue-to-rvalue conversion is applied to that, not directly to the object, so this fails the “immediately applied” requirement. This is surprising and unfortunate, since only the values and not the addresses of the static data members are used. (This problem also applies to the proposed resolution of issue 696.)




481. Scope of template parameters

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

N2800 comment FR 16

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.




642. Definition and use of “block scope” and “local scope”

Section: 3.3.3  [basic.scope.local]     Status: drafting     Submitter: Alisdair Meredith     Date: 6 Aug 2007

The Standard uses the terms “block scope” and “local scope” interchangeably, but the former is never formally defined. Would it be better to use only one term consistently? “Block scope” seems to be more frequently used.

Notes from the October, 2007 meeting:

The CWG expressed a preference for the term “local scope.”

Proposed resolution (February, 2008):

  1. Change the note in 3.3.2 [basic.scope.pdecl] paragraph 9 as follows:

  2. [Note: friend declarations refer to functions or classes that are members of the nearest enclosing namespace, but they do not introduce new names into that namespace (7.3.1.2 [namespace.memdef]). Function declarations at block local scope and object declarations with the extern specifier at block local scope refer to declarations that are members of an enclosing namespace, but they do not introduce new names into that scope. —end note]
  3. Change the example in 3.4.1 [basic.lookup.unqual] paragraph 6 as follows:

  4. ...
    // 1) outermost block local scope of A::n::f, before the use of i
    ...
  5. Change the example in 3.4.1 [basic.lookup.unqual] paragraph 8 as follows:

  6. ...
    // 1) outermost block local scope of M::N::X::f, before the use of i
    ...
  7. Change 3.4.1 [basic.lookup.unqual] paragraph 11 as follows:

  8. During the lookup for a name used as a default argument (8.3.6 [dcl.fct.default]) in a function parameter-declaration-clause or used in the expression of a mem-initializer for a constructor (12.6.2 [class.base.init]), the function parameter names are visible and hide the names of entities declared in the block local, class or namespace scopes containing the function declaration...
  9. Change 3.4.1 [basic.lookup.unqual] paragraph 12 as follows:

  10. During the lookup of a name used in the constant-expression of an enumerator-definition, previously declared enumerators of the enumeration are visible and hide the names of entities declared in the block local, class, or namespace scopes containing the enum-specifier.
  11. Change 3.4.2 [basic.lookup.argdep] paragraph 3 as follows:

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

    then Y is empty. Otherwise...

  13. Change 3.5 [basic.link] paragraph 6 as follows:

  14. The name of a function declared in block local scope, and the name of an object declared by a block local scope extern declaration, have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block local scope declaration declares that same entity and receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed. Otherwise, if no matching entity is found, the block local scope entity receives external linkage...
  15. Change 3.5 [basic.link] paragraph 7 as follows:

  16. When a block local 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...
  17. Change 3.6.3 [basic.start.term] paragraph 1 as follows:

  18. Destructors (12.4 [class.dtor]) for initialized objects of static storage duration (declared at block local scope or at namespace scope) are called as a result...
  19. Change 7.1.1 [dcl.stc] paragraph 2 as follows:

  20. ...An object declared without a storage-class-specifier at block local scope or declared as a function parameter has automatic storage duration by default.
  21. Change 7.1.2 [dcl.fct.spec] paragraph 3 as follows (cf 7.1.6.4 [dcl.spec.auto] paragraph 3):

  22. ...The inline specifier shall not appear on a block scope function declaration when declaring a function in a block...
  23. Change 9.5 [class.union] paragraph 3 as follows:

  24. Anonymous unions declared in a named namespace or in the global namespace shall be declared static. Anonymous unions declared at block scope in a block shall be declared with any storage class allowed for a block local-scope variable, or with no storage class...
  25. Change 20.8.12 [unique.ptr] paragraph 1 as follows:

  26. Template The class template unique_ptr stores a pointer to an object and deletes that object using the associated deleter when it is itself destroyed (such as when leaving block local scope (6.7 [stmt.dcl])).
  27. Change 30.4.3 [thread.lock] paragraph 1 as follows:

  28. A lock is an object that holds a reference to a mutex and may unlock the mutex during the lock's destruction (such as when leaving block local scope)...
  29. Change Appendix B [implimits] paragraph 2, bullet 8 as follows:

  30. Change C.1.7 [diff.class], reference to 9.1 [class.name] as follows:

  31. ...If the hidden name is at block local scope, either the type or the struct tag has to be renamed.
  32. Change D.9.1 [auto.ptr] paragraph 1 as follows:

  33. Template auto_ptr stores a pointer to an object obtained via new and deletes that object when it itself is destroyed (such as when leaving block local scope 6.7 [stmt.dcl]).

Notes from the September, 2008 meeting:

Reevaluating the relative prevalence of the two terms (including the fact that new uses of “block scope” are being introduced, e.g., in both the lambda and thread-local wording) led to CWG reversing its previous preference for “local scope.” The resolution will need to add a definition of “block scope” and should change the title of 3.3.3 [basic.scope.local].




490. Name lookup in friend declarations

Section: 3.4.1  [basic.lookup.unqual]     Status: drafting     Submitter: Ben Hutchings     Date: 7 Dec 2004

When 3.4.1 [basic.lookup.unqual] paragraph 10 says,

In a friend declaration naming a member function, a name used in the function declarator and not part of a template-argument in a template-id is first looked up in the scope of the member function's class. If it is not found, or if the name is part of a template-argument in a template-id, the look up is as described for unqualified names in the definition of the class granting friendship.

what does “in the scope of the member function's class” mean? Does it mean that only members of the class and its base classes are considered? Or does it mean that the same lookup is to be performed as if the name appeared in the member function's class? Implementations vary in this regard. For example:

     struct s1;

     namespace ns {
         struct s1;
     }

     struct s2 {
         void f(s1 &);
     };

     namespace ns {
         struct s3 {
             friend void s2::f(s1 &);
         };
     }

Microsoft Visual C++ and Comeau C++ resolve s1 in the friend declaration to ns::s1 and issue an error, while g++ resolves it to ::s1 and accepts the code.

Notes from the April, 2005 meeting:

The phrase “looked up in the scope of [a] class” occurs frequently throughout the Standard and always refers to the member name lookup described in 10.2 [class.member.lookup]. This is the first interpretation mentioned above (“only members of the class and its base classes”), resolving s1 to ns::s1. A cross-reference to 10.2 [class.member.lookup] will be added to 3.4.1 [basic.lookup.unqual] paragraph 10 to make this clearer.

In discussing this question, the CWG noticed another problem: the text quoted above applies to all template-arguments appearing in the function declarator. The intention of this rule, however, is that only template-arguments in the declarator-id should ignore the member function's class scope; template-arguments used elsewhere in the function declarator should be treated like other names. For example:

     template<typename T> struct S;
     struct A {
       typedef int T;
       void foo(S<T>);
     };
     template <typename T> struct B {
       friend void A::foo(S<T>);  // i.e., S<A::T>
     };



225. Koenig lookup and fundamental types

Section: 3.4.2  [basic.lookup.argdep]     Status: drafting     Submitter: Derek Inglis     Date: 26 Jan 2000

In discussing issue 197, the question arose as to whether the handling of fundamental types in argument-dependent lookup is actually what is desired. This question needs further discussion.




705. Suppressing argument-dependent lookup via parentheses

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

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



156. Name lookup for conversion functions

Section: 3.4.5  [basic.lookup.classref]     Status: drafting     Submitter: Derek Inglis     Date: 18 Aug 1999

Paragraph 7 of 3.4.5 [basic.lookup.classref] says,

If the id-expression is a conversion-function-id, its conversion-type-id shall denote the same type in both the context in which the entire postfix-expression occurs and in the context of the class of the object expression (or the class pointed to by the pointer expression).
Does this mean that the following example is ill-formed?
    struct A { operator int(); } a;
    void foo() {
      typedef int T;
      a.operator T(); // 1) error T is not found in the context
		      // of the class of the object expression?
    }
The second bullet in paragraph 1 of 3.4.3.1 [class.qual] says,
a conversion-type-id of an operator-function-id is looked up both in the scope of the class and in the context in which the entire postfix-expression occurs and shall refer to the same type in both contexts
How about:
    struct A { typedef int T; operator T(); };
    struct B : A { operator T(); } b;
    void foo() {
      b.A::operator T(); // 2) error T is not found in the context
			 // of the postfix-expression?
    }
Is this interpretation correct? Or was the intent for this to be an error only if T was found in both scopes and referred to different entities?

If the intent was for these to be errors, how do these rules apply to template arguments?

    template <class T1> struct A { operator T1(); }
    template <class T2> struct B : A<T2> {
      operator T2();
      void foo() {
	T2 a = A<T2>::operator T2(); // 3) error? when instantiated T2 is not
				     // found in the scope of the class
	T2 b = ((A<T2>*)this)->operator T2(); // 4) error when instantiated?
      }
    }

(Note bullets 2 and 3 in paragraph 1 of 3.4.3.1 [class.qual] refer to postfix-expression. It would be better to use qualified-id in both cases.)

Erwin Unruh: The intent was that you look in both contexts. If you find it only once, that's the symbol. If you find it in both, both symbols must be "the same" in some respect. (If you don't find it, its an error).

Mike Miller: What's not clear to me in these examples is whether what is being looked up is T or int. Clearly the T has to be looked up somehow, but the "name" of a conversion function clearly involves the base (non-typedefed) type, not typedefs that might be used in a definition or reference (cf 3 [basic] paragraph 7 and 12.3 [class.conv] paragraph 5). (This is true even for types that must be written using typedefs because of the limited syntax in conversion-type-ids — e.g., the "name" of the conversion function in the following example

    typedef void (*pf)();
    struct S {
	operator pf();
    };
is S::operator void(*)(), even though you can't write its name directly.)

My guess is that this means that in each scope you look up the type named in the reference and form the canonical operator name; if the name used in the reference isn't found in one or the other scope, the canonical name constructed from the other scope is used. These names must be identical, and the conversion-type-id in the canonical operator name must not denote different types in the two scopes (i.e., the type might not be found in one or the other scope, but if it's found in both, they must be the same type).

I think this is all very vague in the current wording.




682. Missing description of lookup of template aliases

Section: 3.4.5  [basic.lookup.classref]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 1 March, 2008

3.4.5 [basic.lookup.classref] does not mention template aliases as the possible result of the lookup but should do so.




426. Identically-named variables, one internally and one externally linked, allowed?

Section: 3.5  [basic.link]     Status: drafting     Submitter: Steve Adamczyk     Date: 2 July 2003

An example in 3.5 [basic.link] paragraph 6 creates two file-scope variables with the same name, one with internal linkage and one with external.

  static void f();
  static int i = 0;                       //1
  void g() {
          extern void f();                // internal linkage
          int i;                          //2: i has no linkage
          {
                  extern void f();        // internal linkage
                  extern int i;           //3: external linkage
          }
  }

Is this really what we want? C99 has 6.2.2.7/7, which gives undefined behavior for having an identifier appear with internal and external linkage in the same translation unit. C++ doesn't seem to have an equivalent.

Notes from October 2003 meeting:

We agree that this is an error. We propose to leave the example but change the comment to indicate that line //3 has undefined behavior, and elsewhere add a normative rule giving such a case undefined behavior.

Proposed resolution (October, 2005):

Change 3.5 [basic.link] paragraph 6 as indicated:

...Otherwise, if no matching entity is found, the block scope entity receives external linkage. If, within a translation unit, the same entity is declared with both internal and external linkage, the behavior is undefined.

[Example:

    static void f();
    static int i = 0;            // 1
    void g () {
        extern void f ();        // internal linkage
        int i;                   // 2: i has no linkage
        {
            extern void f ();    // internal linkage
            extern int i;        // 3: external linkage
        }
    }

There are three objects named i in this program. The object with internal linkage introduced by the declaration in global scope (line //1 ), the object with automatic storage duration and no linkage introduced by the declaration on line //2, and the object with static storage duration and external linkage introduced by the declaration on line //3. Without the declaration at line //2, the declaration at line //3 would link with the declaration at line //1. But because the declaration with internal linkage is hidden, //3 is given external linkage, resulting in a linkage conflict.end example]

Notes frum the April 2006 meeting:

According to 3.5 [basic.link] paragraph 9, the two variables with linkage in the proposed example are not “the same entity” because they do not have the same linkage. Some other formulation will be needed to describe the relationship between those two variables.

Notes from the October 2006 meeting:

The CWG decided that it would be better to make a program with this kind of linkage mismatch ill-formed instead of having undefined behavior.




735. Missing case in specification of safely-derived pointers

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

N2800 comment DE 3

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?




350. signed char underlying representation for objects

Section: 3.9  [basic.types]     Status: drafting     Submitter: Noah Stein     Date: 16 April 2002

Sent in by David Abrahams:

Yes, and to add to this tangent, 3.9.1 [basic.fundamental] paragraph 1 states "Plain char, signed char, and unsigned char are three distinct types." Strangely, 3.9 [basic.types] paragraph 2 talks about how "... the underlying bytes making up the object can be copied into an array of char or unsigned char. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value." I guess there's no requirement that this copying work properly with signed chars!

Notes from October 2002 meeting:

We should do whatever C99 does. 6.5p6 of the C99 standard says "array of character type", and "character type" includes signed char (6.2.5p15), and 6.5p7 says "character type". But see also 6.2.6.1p4, which mentions (only) an array of unsigned char.

Proposed resolution (April 2003):

Change 3.8 [basic.life] paragraph 5 bullet 3 from

to

Change 3.8 [basic.life] paragraph 6 bullet 3 from

to

Change the beginning of 3.9 [basic.types] paragraph 2 from

For any object (other than a base-class subobject) of POD type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7 [intro.memory]) making up the object can be copied into an array of char or unsigned char.

to

For any object (other than a base-class subobject) of POD type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7 [intro.memory]) making up the object can be copied into an array of byte-character type.

Add the indicated text to 3.9.1 [basic.fundamental] paragraph 1:

Objects declared as characters (char) shall be large enough to store any member of the implementation's basic character set. If a character from this set is stored in a character object, the integral value of that character object is equal to the value of the single character literal form of that character. It is implementation-defined whether a char object can hold negative values. Characters can be explicitly declared unsigned or signed. Plain char, signed char, and unsigned char are three distinct types, called the byte-character types. A char, a signed char, and an unsigned char occupy the same amount of storage and have the same alignment requirements (3.9 [basic.types]); that is, they have the same object representation. For byte-character types, all bits of the object representation participate in the value representation. For unsigned byte-character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types. In any particular implementation, a plain char object can take on either the same values as a signed char or an unsigned char; which one is implementation-defined.

Change 3.10 [basic.lval] paragraph 15 last bullet from

to

Notes from October 2003 meeting:

It appears that in C99 signed char may have padding bits but no trap representation, whereas in C++ signed char has no padding bits but may have -0. A memcpy in C++ would have to copy the array preserving the actual representation and not just the value.

March 2004: The liaisons to the C committee have been asked to tell us whether this change would introduce any unnecessary incompatibilities with C.

Notes from October 2004 meeting:

The C99 Standard appears to be inconsistent in its requirements. For example, 6.2.6.1 paragraph 4 says:

The value may be copied into an object of type unsigned char [n] (e.g., by memcpy); the resulting set of bytes is called the object representation of the value.

On the other hand, 6.2 paragraph 6 says,

If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one.

Mike Miller will investigate further.




619. Completeness of array types

Section: 3.9  [basic.types]     Status: drafting     Submitter: Steve Clamage     Date: 16 February 2007

Is the following example well-formed?

    struct S {
        static char a[5];
    };
    char S::a[];    // Unspecified bound in definition

3.5 [basic.link] paragraph 10 certainly makes allowance for declarations to differ in the presence or absence of a major array bound. However, 3.1 [basic.def] paragraph 5 says that

A program is ill-formed if the definition of any object gives the object an incomplete type (3.9 [basic.types]).

3.9 [basic.types] paragraph 7 says,

The declared type of an array object might be an array of unknown size and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points (“array of unknown bound of T” and “array of N T”) are different types.

This wording appears to make no allowance for the C concept of “composite type;” instead, each declaration is said to have its own type. By this interpretation, the example is ill-formed, because the type declared by the definition of S::a is incomplete.

If the example is intended to be well-formed, the Standard needs explicit wording stating that an omitted array bound in a declaration is implicitly taken from that of a visible declaration of that object, if any.

Notes from the April, 2007 meeting:

The CWG agreed that this usage should be permitted.

Proposed resolution (June, 2008):

  1. Change 8.3.4 [dcl.array] paragraph 1 as follows:

  2. ...If Except as noted below, if the constant expression is omitted, the type of the identifier of D is “derived-declarator-type-list array of unknown bound of T,” an incomplete object type...
  3. Change 8.3.4 [dcl.array] paragraph 3 as follows:

  4. When several “array of” specifications are adjacent, a multidimensional array is created; only the first of the constant expressions that specify the bounds of the arrays can may be omitted only for the first member of the sequence. [Note: this elision is useful for function parameters of array types, and when the array is external and the definition, which allocates storage, is given elsewhere. —end note] In addition to declarations in which an incomplete object type is allowed, an array bound may be omitted in the declaration of a function parameter (8.3.5 [dcl.fct]). The first constant-expression can An array bound may also be omitted when the declarator is followed by an initializer (8.5 [dcl.init]). In this case the bound is calculated from the number of initial elements (say, N) supplied (8.5.1 [dcl.init.aggr]), and the type of the identifier of D is “array of N T.” Furthermore, if there is a visible declaration of the name declared by the declarator-id (if any) in which the bound was specified, an omitted array bound is taken to be the same as in that earlier declaration.

Notes from the September, 2008 meeting:

The proposed resolution does not capture the result favored by the CWG: array bound information should be accumulated across declarations within the same scope, but a block extern declaration in a nested scope should not inherit array bound information from the outer declaration. (This is consistent with the treatment of default arguments in function declarations.) For example:

    int a[5];
    void f() {
        extern int a[];
        sizeof(a);
    }

Although there was some confusion about the C99 wording dealing with this case, it is probably well-formed in C99. However, it should be ill-formed in C++, because we want to avoid the concept of “compatible types” as it exists in C.




636. Dynamic type of objects and aliasing

Section: 3.10  [basic.lval]     Status: drafting     Submitter: Gabriel Dos Reis     Date: 23 May 2007

The aliasing rules given in 3.10 [basic.lval] paragraph 10 rely on the concept of “dynamic type.” The problem is that the dynamic type of an object often cannot be determined (or even sufficiently constrained) at the point at which an optimizer needs to be able to determine whether aliasing might occur or not. For example, consider the function

    void foo(int* p, double* q) {
        *p = 42;
        *q = 3.14;
    }

An optimizer, on the basis of the existing aliasing rules, might decide that an int* and a double* cannot refer to the same object and reorder the assignments. This reordering, however, could result in undefined behavior if the function foo is called as follows:

   void goo() {
      union {
         int i; 
         double d;
      } t;

      t.i = 12;

      foo(&t.i, &t.d);

      cout << t.d << endl;
   };

Here, the reference to t.d after the call to foo will be valid only if the assignments in foo are executed in the order in which they were written; otherwise, the union will contain an int object rather than a double.

One possibility would be to require that if such aliasing occurs, it be done only via member names and not via pointers.

Notes from the July, 2007 meeting:

This is the same issue as C's DR236. The CWG expressed a desire to address the issue the same way C99 does. The issue also occurs in C++ when placement new is used to end the lifetime of one object and start the lifetime of a different object occupying the same storage.




617. Lvalue-to-rvalue conversions of uninitialized char objects

Section: 4.1  [conv.lval]     Status: drafting     Submitter: Alan Stokes     Date: 6 February 2007

According to 4.1 [conv.lval] paragraph 1, applying the lvalue-to-rvalue conversion to any uninitialized object results in undefined behavior. However, character types are intended to allow any data, including uninitialized objects and padding, to be copied (hence the statements in 3.9.1 [basic.fundamental] paragraph 1 that “For character types, all bits of the object representation participate in the value representation” and in 3.10 [basic.lval] paragraph 15 that char and unsigned char types can alias any object). The lvalue-to-rvalue conversion should be permitted on uninitialized objects of character type without evoking undefined behavior.




170. Pointer-to-member conversions

Section: 4.11  [conv.mem]     Status: drafting     Submitter: Mike Stump     Date: 16 Sep 1999

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

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

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

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

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

(See also issue 794.)




536. Problems in the description of id-expressions

Section: 5.1.1  [expr.prim.general]     Status: drafting     Submitter: Mike Miller     Date: 13 October 2005

There are at least a couple of problems in the description of the various id-expressions in 5.1.1 [expr.prim.general]:

  1. Paragraph 4 embodies an incorrect assumption about the syntax of qualified-ids:

    The operator :: followed by an identifier, a qualified-id, or an operator-function-id is a primary-expression.

    The problem here is that the :: is actually part of the syntax of qualified-id; consequently, “:: followed by... a qualified-id” could be something like “:: ::i,” which is ill-formed. Presumably this should say something like, “A qualified-id with no nested-name-specifier is a primary-expression.”

  2. More importantly, some kinds of id-expressions are not described by 5.1.1 [expr.prim.general]. The structure of this section is that the result, type, and lvalue-ness are specified for each of the cases it covers:

    This treatment leaves unspecified all the non-identifier unqualified-ids (operator-function-id, conversion-function-id, and template-id), as well as (perhaps) “:: template-id” (it's not clear whether the “:: followed by a qualified-id” case is supposed to apply to template-ids or not). Note also that the proposed resolution of issue 301 slightly exacerbates this problem by removing the form of operator-function-id that contains a tmeplate-argument-list; as a result, references like “::operator+<X>” are no longer covered in 5.1.1 [expr.prim.general].




743. Use of decltype in a nested-name-specifier

Section: 5.1.1  [expr.prim.general]     Status: drafting     Submitter: Jaakko Järvi     Date: 12 November, 2008

N2800 comment JP 8

The grammar for nested-name-specifier in 5.1.1 [expr.prim.general] paragraph 7 does not allow decltype to be used in a qualified-id. This could be useful for cases like:

   auto vec = get_vec();
   decltype(vec)::value_type v = vec.first();



760. this inside a nested class of a non-static member function

Section: 5.1.1  [expr.prim.general]     Status: drafting     Submitter: Mike Miller     Date: 3 February, 2009

this is a keyword and thus not subject to ordinary name lookup. That makes the interpretation of examples like the following somewhat unclear:

    struct outer {
      void f() {
        struct inner {
          int a[sizeof(*this)];  // #1
        };
      }
    };

According to 5.1.1 [expr.prim.general] paragraph 3,

The keyword this shall be used only inside a non-static class member function body (9.3 [class.mfct]) or in a brace-or-equal-initializer for a non-static data member.

Should the use of this at #1 be interepreted as a well-formed reference to outer::f()'s this or as an ill-formed attempt to refer to a this for outer::inner?

One possible interpretation is that the intent is as if this were an ordinary identifier appearing as a parameter in each non-static member function. (This view applies to the initializers of non-static data members as well if they are considered to be rewritten as mem-initializers in the constructor body.) Under this interpretation, the prohibition against using this in other contexts simply falls out of the fact that name lookup would fail to find this anywhere else, so the reference in the example is well-formed. (Implementations vary in their treatment of this example, so clearer wording is needed, whichever way the interpretation goes.)




722. Can nullptr be passed to an ellipsis?

Section: 5.2.2  [expr.call]     Status: drafting     Submitter: Alisdair Meredith     Date: 25 September, 2008

The current wording of 5.2.2 [expr.call] paragraph 7 is:

After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or effective class type, the program is ill-formed.

It's not clear whether this is intended to exclude anything other than void, but the effect is to disallow passing nullptr to ellipsis. That seems unnecessary.




731. Omitted reference qualification of member function type

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

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

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.




573. Conversions between function pointers and void*

Section: 5.2.10  [expr.reinterpret.cast]     Status: drafting     Submitter: Steve Adamczyk     Date: 13 April 2006

The resolution to issue 195 makes “converting a pointer to a function into a pointer to an object type or vice versa” conditionally-supported behavior. In doing so, however, it overlooked the fact that void is not an “object type” (3.9 [basic.types] paragraph 9). The wording should be amended to allow conversion to and from void* types.

Proposed resolution (June, 2008):

  1. Change 3.9.2 [basic.compound] paragraph 4 as follows:

  2. Objects of cv-qualified (3.9.3 [basic.type.qualifier]) or cv-unqualified type void* (pointer to void) A pointer to cv-qualified or cv-unqualified void can be used to point to objects of unknown type. A void* shall be able to hold any object pointer and is thus considered to be an object pointer type, although it is not a pointer to object type (because void is not an object type). A cv-qualified or cv-unqualified (3.9.3 [basic.type.qualifier]) An object of type cv void* shall have the same representation and alignment requirements as a cv-qualified or cv-unqualified cv char*.
  3. Change 4.10 [conv.ptr] paragraph 1 as follows:

  4. ...A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of pointer to object or pointer to function object pointer or function pointer type...
  5. Change 5.2.10 [expr.reinterpret.cast] paragraph 7 as follows:

  6. A pointer to an object An object pointer can be explicitly converted to a pointer to an object an object pointer of different type. Except that converting an rvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types or void and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.
  7. Change 5.2.10 [expr.reinterpret.cast] paragraph 8 as follows:

  8. Converting a pointer to a function into a pointer to an object type a function pointer to an object pointer or vice versa is conditionally-supported...

[Drafting note: 14.2 [temp.param] paragraph 4 was not changed and thus continues to allow only pointers to objects, not object pointers, as non-type template parameters.]




734. Are unique addresses required for namespace-scope variables?

Section: 5.2.10  [expr.reinterpret.cast]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 15 October, 2008

Consider the following example:

    static const char test1 = 'x';
    static const char test2 = 'x';
    bool f() {
        return &test1 != &test2;
    }

Is f() allowed to return false? Can a smart optimizer alias these two variables, taking advantage of the fact that they are const, initialized to the same value, and thus can never be different in a well-defined program?

The C++ Standard doesn't explicitly specify address allocation of objects except as members of arrays and classes, so the answer would appear to be that such an implementation would be conforming.

This situation appears to have been the inadvertent result of the resolution of issue 73. Prior to that change, 5.10 [expr.eq] said,

Two pointers of the same type compare equal if and only if they... both point to the same object...

That resolution introduced the current wording,

Notes from the March, 2009 meeting:

The CWG agreed that this aliasing should not be permitted in a conforming implementation.

Two pointers of the same type compare equal if and only if... both represent the same address.



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

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

N2800 comment UK 58

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.




232. Is indirection through a null pointer undefined behavior?

Section: 5.3.1  [expr.unary.op]     Status: drafting     Submitter: Mike Miller     Date: 5 Jun 2000

At least a couple of places in the IS state that indirection through a null pointer produces undefined behavior: 1.9 [intro.execution] paragraph 4 gives "dereferencing the null pointer" as an example of undefined behavior, and 8.3.2 [dcl.ref] paragraph 4 (in a note) uses this supposedly undefined behavior as justification for the nonexistence of "null references."

However, 5.3.1 [expr.unary.op] paragraph 1, which describes the unary "*" operator, does not say that the behavior is undefined if the operand is a null pointer, as one might expect. Furthermore, at least one passage gives dereferencing a null pointer well-defined behavior: 5.2.8 [expr.typeid] paragraph 2 says

If the lvalue expression is obtained by applying the unary * operator to a pointer and the pointer is a null pointer value (4.10 [conv.ptr]), the typeid expression throws the bad_typeid exception (18.7.4 [bad.typeid]).

This is inconsistent and should be cleaned up.

Bill Gibbons:

At one point we agreed that dereferencing a null pointer was not undefined; only using the resulting value had undefined behavior.

For example:

    char *p = 0;
    char *q = &*p;

Similarly, dereferencing a pointer to the end of an array should be allowed as long as the value is not used:

    char a[10];
    char *b = &a[10];   // equivalent to "char *b = &*(a+10);"

Both cases come up often enough in real code that they should be allowed.

Mike Miller:

I can see the value in this, but it doesn't seem to be well reflected in the wording of the Standard. For instance, presumably *p above would have to be an lvalue in order to be the operand of "&", but the definition of "lvalue" in 3.10 [basic.lval] paragraph 2 says that "an lvalue refers to an object." What's the object in *p? If we were to allow this, we would need to augment the definition to include the result of dereferencing null and one-past-the-end-of-array.

Tom Plum:

Just to add one more recollection of the intent: I was very happy when (I thought) we decided that it was only the attempt to actually fetch a value that creates undefined behavior. The words which (I thought) were intended to clarify that are the first three sentences of the lvalue-to-rvalue conversion, 4.1 [conv.lval]:

An lvalue (3.10 [basic.lval]) of a non-function, non-array type T can be converted to an rvalue. If T is an incomplete type, a program that necessitates this conversion is ill-formed. If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.

In other words, it is only the act of "fetching", of lvalue-to-rvalue conversion, that triggers the ill-formed or undefined behavior. Simply forming the lvalue expression, and then for example taking its address, does not trigger either of those errors. I described this approach to WG14 and it may have been incorporated into C 1999.

Mike Miller:

If we admit the possibility of null lvalues, as Tom is suggesting here, that significantly undercuts the rationale for prohibiting "null references" -- what is a reference, after all, but a named lvalue? If it's okay to create a null lvalue, as long as I don't invoke the lvalue-to-rvalue conversion on it, why shouldn't I be able to capture that null lvalue as a reference, with the same restrictions on its use?

I am not arguing in favor of null references. I don't want them in the language. What I am saying is that we need to think carefully about adopting the permissive approach of saying that it's all right to create null lvalues, as long as you don't use them in certain ways. If we do that, it will be very natural for people to question why they can't pass such an lvalue to a function, as long as the function doesn't do anything that is not permitted on a null lvalue.

If we want to allow &*(p=0), maybe we should change the definition of "&" to handle dereferenced null specially, just as typeid has special handling, rather than changing the definition of lvalue to include dereferenced nulls, and similarly for the array_end+1 case. It's not as general, but I think it might cause us fewer problems in the long run.

Notes from the October 2003 meeting:

See also issue 315, which deals with the call of a static member function through a null pointer.

We agreed that the approach in the standard seems okay: p = 0; *p; is not inherently an error. An lvalue-to-rvalue conversion would give it undefined behavior.

Proposed resolution (October, 2004):

(Note: the resolution of issue 453 also resolves part of this issue.)

  1. Add the indicated words to 3.10 [basic.lval] paragraph 2:

    An lvalue refers to an object or function or is an empty lvalue (5.3.1 [expr.unary.op]).
  2. Add the indicated words to 5.3.1 [expr.unary.op] paragraph 1:

    The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points, if any. If the pointer is a null pointer value (4.10 [conv.ptr]) or points one past the last element of an array object (5.7 [expr.add]), the result is an empty lvalue and does not refer to any object or function. An empty lvalue is not modifiable. If the type of the expression is “pointer to T,” the type of the result is “T.” [Note: a pointer to an incomplete type (other than cv void) can be dereferenced. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to an rvalue, see 4.1 [conv.lval].—end note]
  3. Add the indicated words to 4.1 [conv.lval] paragraph 1:

    If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, or if the lvalue is an empty lvalue (5.3.1 [expr.unary.op]), a program that necessitates this conversion has undefined behavior.
  4. Change 1.9 [intro.execution] as indicated:

    Certain other operations are described in this International Standard as undefined (for example, the effect of dereferencing the null pointer division by zero).

Note (March, 2005):

The 10/2004 resolution interacts with the resolution of issue 73. We added wording to 3.9.2 [basic.compound] paragraph 3 to the effect that a pointer containing the address one past the end of an array is considered to “point to” another object of the same type that might be located there. The 10/2004 resolution now says that it would be undefined behavior to use such a pointer to fetch the value of that object. There is at least the appearance of conflict here; it may be all right, but it at needs to be discussed further.

Notes from the April, 2005 meeting:

The CWG agreed that there is no contradiction between this direction and the resolution of issue 73. However, “not modifiable” is a compile-time concept, while in fact this deals with runtime values and thus should produce undefined behavior instead. Also, there are other contexts in which lvalues can occur, such as the left operand of . or .*, which should also be restricted. Additional drafting is required.




901. Deleted operator delete

Section: 5.3.4  [expr.new]     Status: drafting     Submitter: John Spicer     Date: 20 May, 2009

It is not clear from 5.3.4 [expr.new] whether a deleted operator delete is referenced by a new-expression in which there is no initialization or in which the initialization cannot throw an exception, rendering the program ill-formed. (The question also arises as to whether such a new-expression constitutes a “use” of the deallocation function in the sense of 3.2 [basic.def.odr].)

Notes from the July, 2009 meeting:

The rationale for defining a deallocation function as deleted would presumably be to prevent such objects from being freed. Treating the new-expression as a use of such a deallocation function would mean that such objects could not be created in the first place. There is already an exemption from freeing an object if “a suitable deallocation function [cannot] be found;” a deleted deallocation function should be treated similarly.




806. Enumeration types in integral constant expressions

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

N2800 comment UK 50

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.




809. Deprecation of the register keyword

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

N2800 comment UK 86

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.




810. Block-scope thread_local variables should be implicitly static

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

N2800 comment UK 87

According to 7.1.1 [dcl.stc] paragraph 4,

The thread_local specifier shall be applied only to the names of objects or references of namespace scope and to the names of objects or references of block scope that also specify static.

Why require two keywords, where one on its own becomes ill-formed? thread_local should imply static in this case, and the combination of keywords should be banned rather than required. This would also eliminate the one of two exceptions documented in paragraph 1.

Notes from the July, 2009 meeting:

The consensus of the CWG was that thread_local should imply static, as suggested, but that the combination should still be allowed (it is needed, for example, for thread-local static data members).




940. Global anonymous unions

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

N2800 comment UK 85

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




765. Local types in inline functions with external linkage

Section: 7.1.2  [dcl.fct.spec]     Status: drafting     Submitter: Mike Miller     Date: 6 February, 2009

7.1.2 [dcl.fct.spec] paragraph 4 specifies that local static variables and string literals appearing in the body of an inline function with external linkage must be the same entities in every translation unit in the program. Nothing is said, however, about whether local types are likewise required to be the same.

Although a conforming program could always have determined this by use of typeid, recent changes to C++ (allowing local types as template type arguments, lambda expression closure classes) make this question more pressing.

Notes from the July, 2009 meeting:

The types are intended to be the same.




407. Named class with associated typedef: two names or one?

Section: 7.1.3  [dcl.typedef]     Status: drafting     Submitter: Clark Nelson     Date: 31 March 2003

Here's an example:

  typedef struct S { ... } S;
  void fs(S *x) { ... }

The big question is, to what declaration does the reference to identifier S actually refer? Is it the S that's declared as a typedef name, or the S that's declared as a class name (or in C terms, as a struct tag)? (In either case, there's clearly only one type to which it could refer, since a typedef declaration does not introduce a new type. But the debugger apparently cares about more than just the identity of the type.)

Here's a classical, closely related example:

  struct stat { ... };
  int stat();
  ... stat( ... ) ...

Does the identifier stat refer to the class or the function? Obviously, in C, you can't refer to the struct tag without using the struct keyword, because it is in a different name space, so the reference must be to the function. In C++, the reference is also to the function, but for a completely different reason.

Now in C, typedef names and function names are in the same name space, so the natural extrapolation would be that, in the first example, S refers to the typedef declaration, as it would in C. But C++ is not C. For the purposes of this discussion, there are two important differences between C and C++

The first difference is that, in C++, typedef names and class names are not in separate name spaces. On the other hand, according to section 3.3.11 [basic.scope.hiding] (Name hiding), paragraph 2:

A class name (9.1) or enumeration name (7.2) can be hidden by the name of an object, function, or enumerator declared in the same scope. If a class or enumeration name and an object, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the object, function, or enumerator name is visible.

Please consider carefully the phrase I have highlighted, and the fact that a typedef name is not the name of an object, function or enumerator. As a result, this example:

  struct stat { ... };
  typedef int stat;

Which would be perfectly legal in C, is disallowed in C++, both implicitly (see the above quote) and explicitly (see section 7.1.3 [dcl.typedef] (The typedef specifier), paragraph 3):

In a given scope, a typedef specifier shall not be used to redefine the name of any type declared in that scope to refer to a different type. Similarly, in a given scope, a class or enumeration shall not be declared with the same name as a typedef-name that is declared in that scope and refers to a type other than the class or enumeration itself.

From which we can conclude that in C++ typedef names do not hide class names declared in the same scope. If they did, the above example would be legal.

The second difference is that, in C++, a typedef name that refers to a class is a class-name; see 7.1.3 [dcl.typedef] paragraph 4:

A typedef-name that names a class is a class-name(9.1). If a typedef-name is used following the class-key in an elaborated-type-specifier (7.1.5.3) or in the class-head of a class declaration (9), or is used as the identifier in the declarator for a constructor or destructor declaration (12.1, 12.4), the program is ill-formed.

This implies, for instance, that a typedef-name referring to a class can be used in a nested-name-specifier (i.e. before :: in a qualified name) or following ~ to refer to a destructor. Note that using a typedef-name as a class-name in an elaborated-type-specifier is not allowed. For example:

  struct X { };
  typedef struct X X2;
  X x; // legal
  X2 x2; // legal
  struct X sx; // legal
  struct X2 sx2; // illegal

The final relevant piece of the standard is 7.1.3 [dcl.typedef] paragraph 2:

In a given scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers.

This of course is what allows the original example, to which let us now return:

  typedef struct S { ... } S;
  void fs(S *x) { ... }

The question, again is, to which declaration of S does the reference actually refer? In C, it would clearly be to the second, since the first would be accessible only by using the struct keyword. In C++, if typedef names hid class names declared in the same scope, the answer would be the same. But we've already seen that typedef names do not hide class names declared in the same scope.

So to which declaration does the reference to S refer? The answer is that it doesn't matter. The second declaration of S, which appears to be a declaration of a typedef name, is actually a declaration of a class name (7.1.3 [dcl.typedef] paragraph 4), and as such is simply a redeclaration. Consider the following example:

  typedef int I, I;
  extern int x, x;
  void f(), f();

To which declaration would a reference to I, x or f refer? It doesn't matter, because the second declaration of each is really just a redeclaration of the thing declared in the first declaration. So to save time, effort and complexity, the second declaration of each doesn't add any entry to the compiler's symbol table.

Note (March, 2005):

Matt Austern: Is this legal?

    struct A { };
    typedef struct A A;
    struct A* p;

Am I right in reading the standard [to say that this is ill-formed]? On the one hand it's a nice uniform rule. On the other hand, it seems likely to confuse users. Most people are probably used to thinking that 'typedef struct A A' is a null operation, and, if this code really is illegal, it would seem to be a gratuitous C/C++ incompatibility.

Mike Miller: I think you're right. 7.1.3 [dcl.typedef] paragraph 1:

A name declared with the typedef specifier becomes a typedef-name.

7.1.3 [dcl.typedef] paragraph 2:

In a given non-class scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers.

After the typedef declaration in the example, the name X has been “redefined” — it is no longer just a class-name, it has been “redefined” to be a typedef-name (that, by virtue of the fact that it refers to a class type, is also a class-name).

John Spicer: In C, and originally in C++, an elaborated-type-specifier did not consider typedef names, so “struct X* x” would find the class and not the typedef.

When C++ was changed to make typedefs visible to elaborated-type-specifier lookups, I believe this issue was overlooked and inadvertantly made ill-formed.

I suspect we need add text saying that if a given scope contains both a class/enum and a typedef, that an elaborated type specifier lookup finds the class/enum.

Mike Miller: I'm a little uncomfortable with this approach. The model we have for declaring a typedef in the same scope as a class/enum is redefinition, not hiding (like the “struct stat” hack). This approach seems to assume that the typedef hides the class/enum, which can then be found by an elaborated-type-specifier, just as if it were hidden by a variable, function, or enumerator.

Also, this approach reduces but doesn't eliminate the incompatibility with C. For example:

    struct S { };
    {
        typedef struct S S;
        struct S* p;        // still ill-formed
    }

My preference would be for something following the basic principle that declaring a typedef-name T in a scope where T already names the type designated by the typedef should have no effect on whether an elaborated-type-specifier in that or a nested scope is well-formed or not. Another way of saying that is that a typedef-name that designates a same-named class or enumeration in the same or a containing scope is transparent with respect to elaborated-type-specifiers.

John Spicer: This strikes me as being a rather complicated solution. When we made the change to make typedefs visible to elaborated-type-specifiers we did so knowing it would make some C cases ill-formed, so this does not bother me. We've lived with the C incompatibility for many years now, so I don't personally feel a need to undo it. I also don't like the fact that you have to essentially do the old-style elaborated-type-specifier lookup to check the result of the lookup that found the typedef.

I continue to prefer the direction I described earlier where if a given scope contains both a class/enum and a typedef, that an elaborated-type-specifier lookup finds the class/enum.

Notes from the April, 2005 meeting:

The CWG agreed with John Spicer's approach, i.e., permitting a typedef-name to be used in an elaborated-type-specifier only if it is declared in the same scope as the class or enumeration it names.




700. Constexpr member functions of class templates

Section: 7.1.5  [dcl.constexpr]     Status: drafting     Submitter: Jens Maurer     Date: 27 June, 2008

7.1.5 [dcl.constexpr] paragraph 5 applies only to “the instantiated template specialization of a constexpr function template;” it should presumably apply to non-template member functions of a class template, as well.

Notes from the September, 2008 meeting:

This question is more involved than it might appear. For example, a constexpr member function is implicitly const; if the constexpr specifier is ignored, does that make the member function non-const? Also, should this provision apply only to dependent expressions in the function? Should it be an error if no constexpr function can be instantiated from the template, along the lines of the permission given in 14.7 [temp.res] paragraph 8 for an implementation to diagnose a template definition from which no valid specialization can be instantiated?

Notes from the July, 2009 meeting:

The consensus of the CWG was that an “ignored” constexpr specifier in this case simply means that the specialization is not constexpr, not that it is not const. The CWG also decided not to address the question of non-dependent expressions that render a function template specialization non-constexpr, leaving it to quality of implementation whether a (warning) diagnostic is issued in such cases.




837. Constexpr functions and return braced-init-list

Section: 7.1.5  [dcl.constexpr]     Status: drafting     Submitter: Mike Miller     Date: 11 March, 2009

The body of a constexpr function is required by 7.1.5 [dcl.constexpr] paragraph 3 to be of the form

However, there does not seem to be any good reason for prohibiting the alternate return syntax involving a braced-init-list. The restriction should be removed.




860. Explicit qualification of constexpr member functions

Section: 7.1.5  [dcl.constexpr]     Status: drafting     Submitter: Daniel Krügler     Date: 6 April, 2009

7.1.5 [dcl.constexpr] paragraph 6 says,

A constexpr specifier for a non-static member function that is not a constructor declares that member function to be const (9.3.1 [class.mfct.non-static]).

Is a const qualifier on such a member function redundant or ill-formed?

Notes from the July, 2009 meeting:

The CWG agreed that a const qualifier on a constexpr member function is simply redundant and not an error.




892. Missing requirements for constexpr constructors

Section: 7.1.5  [dcl.constexpr]     Status: drafting     Submitter: Alisdair Meredith     Date: 8 May, 2009

The rules for constexpr constructors are missing some necessary requirements. In particular, there is no requirement that a brace-or-equal-initializer for a non-static data member be a constant expression, and the requirement for constexpr constructors for initializing non-static data members applies only to members named in a mem-initializer, allowing a non-constexpr default constructor to be invoked.




625. Use of auto as a template-argument

Section: 7.1.6.4  [dcl.spec.auto]     Status: drafting     Submitter: John Spicer     Date: 9 March 2007

The auto specifier can be used only in certain contexts, as specified in 7.1.6.4 [dcl.spec.auto] paragraphs 2-3:

Otherwise (auto appearing with no type specifiers other than cv-qualifiers), the auto type-specifier signifies that the type of an object being declared shall be deduced from its initializer. The name of the object being declared shall not appear in the initializer expression.

This use of auto is allowed when declaring objects in a block (6.3 [stmt.block]), in namespace scope (3.3.6 [basic.scope.namespace]), and in a for-init-statement (6.5.3 [stmt.for]). The decl-specifier-seq shall be followed by one or more init-declarators, each of which shall have a non-empty initializer of either of the following forms:

It was intended that auto could be used only at the top level of a declaration, but it is not clear whether this wording is sufficient to forbid usage like the following:

    template <class T> struct A {};
    template <class T> void f(A<T> x) {}

    void g()
    {
        f(A<short>());

        A<auto> x = A<short>();
    }

Notes from the February, 2008 meeting:

It was agreed that the example should be ill-formed.




926. Inline unnamed namespaces

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

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.




138. Friend declaration name lookup

Section: 7.3.1.2  [namespace.memdef]     Status: drafting     Submitter: Martin von Loewis     Date: 14 Jul 1999

7.3.1.2 [namespace.memdef] paragraph 3 says,

If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace... When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered.
It is not clear from this passage how to determine whether an entity is "first declared" in a friend declaration. One question is whether a using-declaration influences this determination. For instance:
    void foo();
    namespace A{
      using ::foo;
      class X{
	friend void foo();
      };
    }
Is the friend declaration a reference to ::foo or a different foo?

Part of the question involves determining the meaning of the word "synonym" in 7.3.3 [namespace.udecl] paragraph 1:

A using-declaration introduces a name into the declarative region in which the using-declaration appears. That name is a synonym for the name of some entity declared elsewhere.
Is "using ::foo;" the declaration of a function or not?

More generally, the question is how to describe the lookup of the name in a friend declaration.

John Spicer: When a declaration specifies an unqualified name, that name is declared, not looked up. There is a mechanism in which that declaration is linked to a prior declaration, but that mechanism is not, in my opinion, via normal name lookup. So, the friend always declares a member of the nearest namespace scope regardless of how that name may or may not already be declared there.

Mike Miller: 3.4.1 [basic.lookup.unqual] paragraph 7 says:

A name used in the definition of a class X outside of a member function body or nested class definition shall be declared in one of the following ways:... [Note: when looking for a prior declaration of a class or function introduced by a friend declaration, scopes outside of the innermost enclosing namespace scope are not considered.]
The presence of this note certainly implies that this paragraph describes the lookup of names in friend declarations.

John Spicer: It most certainly does not. If that section described the friend lookup it would yield the incorrect results for the friend declarations of f and g below. I don't know why that note is there, but it can't be taken to mean that that is how the friend lookup is done.

    void f(){}
    void g(){}
    class B {
        void g();
    };
    class A : public B {
        void f();
        friend void f(); // ::f not A::f
        friend void g(); // ::g not B::g
    };

Mike Miller: If so, the lookups for friend functions and classes behave differently. Consider the example in 3.4.4 [basic.lookup.elab] paragraph 3:

    struct Base {
        struct Data;         // OK: declares nested Data
        friend class Data;   // OK: nested Data is a friend
    };

If the friend declaration is not a reference to ::foo, there is a related but separate question: does the friend declaration introduce a conflicting (albeit "invisible") declaration into namespace A, or is it simply a reference to an as-yet undeclared (and, in this instance, undeclarable) A::foo? Another part of the example in 3.4.4 [basic.lookup.elab] paragraph 3 is related:

    struct Data {
        friend struct Glob;  // OK: Refers to (as yet) undeclared Glob
                             // at global scope.
    };

John Spicer: You can't refer to something that has not yet been declared. The friend is a declaration of Glob, it just happens to declare it in a such a way that its name cannot be used until it is redeclared.

(A somewhat similar question has been raised in connection with issue 36. Consider:

    namespace N {
        struct S { };
    }
    using N::S;
    struct S;          // legal?

According to 9.1 [class.name] paragraph 2,

A declaration consisting solely of class-key identifier ; is either a redeclaration of the name in the current scope or a forward declaration of the identifier as a class name.

Should the elaborated type declaration in this example be considered a redeclaration of N::S or an invalid forward declaration of a different class?)

(See also issues 95, 136, 139, 143, 165, and 166, as well as paper J16/00-0006 = WG21 N1229.)




341. extern "C" namespace member function versus global variable

Section: 7.5  [dcl.link]     Status: drafting     Submitter: Steve Adamczyk     Date: 1 Mar 2002

Here's an interesting case:

  int f;
  namespace N {
    extern "C" void f () {}
  }
As far as I can tell, this is not precluded by the ODR section (3.2 [basic.def.odr]) or the extern "C" section (7.5 [dcl.link]). However, I believe many compilers do not do name mangling on variables and (more-or-less by definition) on extern "C" functions. That means the variable and the function in the above end up having the same name at link time. EDG's front end, g++, and the Sun compiler all get essentially the same error, which is a compile-time assembler-level error because of the duplicate symbols (in other words, they fail to check for this, and the assembler complains). MSVC++ 7 links the program without error, though I'm not sure how it is interpreted.

Do we intend for this case to be valid? If not, is it a compile time error (required), or some sort of ODR violation (no diagnostic required)? If we do intend for it to be valid, are we forcing many implementations to break binary compatibility by requiring them to mangle variable names?

Personally, I favor a compile-time error, and an ODR prohibition on such things in separate translation units.

Notes from the 4/02 meeting:

The working group agreed with the proposal. We feel a diagnostic should be required for declarations within one translation unit. We also noted that if the variable in global scope in the above example were declared static we would still expect an error.

Relevant sections in the standard are 7.5 [dcl.link] paragraph 6 and 3.5 [basic.link] paragraph 9. We feel that the definition should be written such that the entities in conflict are not "the same entity" but merely not allowed together.

Additional note (September, 2004)

This problem need not involve a conflict between a function and a variable; it can also arise with two variable declarations:

    int x;
    namespace N {
        extern "C" int x;
    }

Proposed resolution (March, 2008):

Change 7.5 [dcl.link] paragraph 6 as follows:

At most one function with a particular name can have C language linkage. Two declarations for a function with C language linkage with the same function name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same function. Two declarations for an object with C language linkage with the same name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same object. A function or object with C linkage shall not be declared with the same name (clause 3 [basic]) as an object or reference declared in global scope, unless both declarations denote the same object; no diagnostic is required if the declarations appear in different translation units. [Note: because of the one definition rule (3.2 [basic.def.odr]), only Only one definition for a function or object with C linkage may appear in the program (see 3.2 [basic.def.odr]); that is, implies that such a function or object must not be defined in more than one namespace scope. For example,

    int x;
    namespace A {
      extern "C" int f();
      extern "C" int g() { return 1; }
      extern "C" int h();
      extern "C" int x();               // ill-formed: same name as global-scope object x
    }

    namespace B {
      extern "C" int f();               // A::f and B::f refer
                                        // to the same function
      extern "C" int g() { return 1; }  // ill-formed, the function g
                                        // with C language linkage
                                        // has two definitions
    }

    int A::f() { return 98; }           // definition for the function f
                                        // with C language linkage
    extern "C" int h() { return 97; }
                                        // definition for the function h
                                        // with C language linkage
                                        // A::h and ::h refer to the same function

end note]

Notes from the September, 2008 meeting:

It should also be possible to declare references with C name linkage (although the meaning the first sentence of 7.5 [dcl.link] paragraph 1 with respect to the meaning of such a declaration is not clear), which would mean that the changed wording should refer to declaring “the same entity” instead of “the same object.” The formulation here would probably benefit from the approach currently envisioned for issues 570 and 633, in which “variable” is defined as being either an object or a reference.




814. Attribute to indicate that a function throws nothing

Section: 7.6  [dcl.attr]     Status: drafting     Submitter: US     Date: 3 March, 2009

N2800 comment US 40

A function with an exception-specification of throw() must be given a catch(...) clause to enforce its contract, i.e., to call std::unexpected() if it exits with an exception. It would be useful to have an attribute indicating that the function really does throw nothing and thus that the catch(...) clause need not be generated.




817. Meaning of [[final]] applied to a class definition

Section: 7.6.4  [dcl.attr.final]     Status: drafting     Submitter: US     Date: 3 March, 2009

N2800 comment US 42

According to 7.6.4 [dcl.attr.final] paragraph 1, the [[final]] attribute applied to a class is just a shorthand notation for marking each of the class's virtual functions as [[final]]. This is different from the similar usage in other languages, where it means that the class so marked cannot be used as a base class. This discrepancy is confusing, and the definition used by the other languages is more useful.

Notes from the March, 2009 meeting:

The intent of the [[final]] attribute is as an aid in optimization, to avoid virtual function calls when the final overrider is known. It is possible to use the [[final]] attribute to prevent derivation by marking the destructor as [[final]]; in fact, as most polymorphic classes will, as a matter of good programming practice, have a virtual destructor, marking the class as [[final]] will have the effect of preventing derivation.

Nonetheless, the general consensus of the CWG was to change the meaning of class [[final]] to parallel the usage in other languages.




374. Can explicit specialization outside namespace use qualified name?

Section: 8.3  [dcl.meaning]     Status: drafting     Submitter: Steve Adamczyk     Date: 23 August 2002

This case is nonstandard by 8.3 [dcl.meaning] paragraph 1 (there is a requirement that the specialization first be declared within the namespace before being defined outside of the namespace), but probably should be allowed:

  namespace NS1 {
    template<class T>
    class CDoor {
    public:
      int mtd() { return 1; }
    };
  }
  template<> int NS1::CDoor<char>::mtd()
  {
    return 0;
  }

Notes from October 2002 meeting:

There was agreement that we wanted to allow this.




453. References may only bind to “valid” objects

Section: 8.3.2  [dcl.ref]     Status: drafting     Submitter: Gennaro Prota     Date: 18 Jan 2004

8.3.2 [dcl.ref] paragraph 4 says:

A reference shall be initialized to refer to a valid object or function. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the "object" obtained by dereferencing a null pointer, which causes undefined behavior ...]

What is a "valid" object? In particular the expression "valid object" seems to exclude uninitialized objects, but the response to Core Issue 363 clearly says that's not the intent. This is an example (overloading construction on constness of *this) by John Potter, which I think is supposed to be legal C++ though it binds references to objects that are not initialized yet:

 struct Fun {
    int x, y;
    Fun (int x, Fun const&) : x(x), y(42) { }
    Fun (int x, Fun&) : x(x), y(0) { }
  };
  int main () {
    const Fun f1 (13, f1);
    Fun f2 (13, f2);
    cout << f1.y << " " << f2.y << "\n";
  }

Suggested resolution: Changing the final part of 8.3.2 [dcl.ref] paragraph 4 to:

A reference shall be initialized to refer to an object or function. From its point of declaration on (see 3.3.2 [basic.scope.pdecl]) its name is an lvalue which refers to that object or function. The reference may be initialized to refer to an uninitialized object but, in that case, it is usable in limited ways (3.8 [basic.life], paragraph 6) [Note: On the other hand, a declaration like this:
    int & ref = *(int*)0;
is ill-formed because ref will not refer to any object or function ]

I also think a "No diagnostic is required." would better be added (what about something like int& r = r; ?)

Proposed Resolution (October, 2004):

(Note: the following wording depends on the proposed resolution for issue 232.)

Change 8.3.2 [dcl.ref] paragraph 4 as follows:

A reference shall be initialized to refer to a valid object or function. If an lvalue to which a reference is directly bound designates neither an existing object or function of an appropriate type (8.5.3 [dcl.init.ref]), nor a region of memory of suitable size and alignment to contain an object of the reference's type (1.8 [intro.object], 3.8 [basic.life], 3.9 [basic.types]), the behavior is undefined. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” empty lvalue obtained by dereferencing a null pointer, which causes undefined behavior. As does not designate an object or function. Also, as described in 9.6 [class.bit], a reference cannot be bound directly to a bit-field. ]

The name of a reference shall not be used in its own initializer. Any other use of a reference before it is initialized results in undefined behavior. [Example:

  int& f(int&);
  int& g();

  extern int& ir3;
  int* ip = 0;

  int& ir1 = *ip;     // undefined behavior: null pointer
  int& ir2 = f(ir3);  // undefined behavior: ir3 not yet initialized
  int& ir3 = g();
  int& ir4 = f(ir4);  // ill-formed: ir4 used in its own initializer
end example]

Rationale: The proposed wording goes beyond the specific concerns of the issue. It was noted that, while the current wording makes cases like int& r = r; ill-formed (because r in the initializer does not "refer to a valid object"), an inappropriate initialization can only be detected, if at all, at runtime and thus "undefined behavior" is a more appropriate treatment. Nevertheless, it was deemed desirable to continue to require a diagnostic for obvious compile-time cases.

It was also noted that the current Standard does not say anything about using a reference before it is initialized. It seemed reasonable to address both of these concerns in the same wording proposed to resolve this issue.

Notes from the April, 2005 meeting:

The CWG decided that whether to require an implementation to diagnose initialization of a reference to itself should be handled as a separate issue (504) and also suggested referring to “storage” instead of “memory” (because 1.8 [intro.object] defines an object as a “region of storage”).

Proposed Resolution (April, 2005):

(Note: the following wording depends on the proposed resolution for issue 232.)

Change 8.3.2 [dcl.ref] paragraph 4 as follows:

A reference shall be initialized to refer to a valid object or function. If an lvalue to which a reference is directly bound designates neither an existing object or function of an appropriate type (8.5.3 [dcl.init.ref]), nor a region of storage of suitable size and alignment to contain an object of the reference's type (1.8 [intro.object], 3.8 [basic.life], 3.9 [basic.types]), the behavior is undefined. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” empty lvalue obtained by dereferencing a null pointer, which causes undefined behavior. As does not designate an object or function. Also, as described in 9.6 [class.bit], a reference cannot be bound directly to a bit-field. ]

Any use of a reference before it is initialized results in undefined behavior. [Example:

  int& f(int&);
  int& g();

  extern int& ir3;
  int* ip = 0;

  int& ir1 = *ip;     // undefined behavior: null pointer
  int& ir2 = f(ir3);  // undefined behavior: ir3 not yet initialized
  int& ir3 = g();
  int& ir4 = f(ir4);  // undefined behavior: ir4 used in its own initializer
end example]

Note (February, 2006):

The word “use” in the last paragraph of the proposed resolution was intended to refer to the description in 3.2 [basic.def.odr] paragraph 2. However, that section does not define what it means for a reference to be “used,” dealing only with objects and functions. Additional drafting is required to extend 3.2 [basic.def.odr] paragraph 2 to apply to references.

Additional note (May, 2008):

The proposed resolution for issue 570 adds wording to define “use” for references.




701. When is the array-to-pointer conversion applied?

Section: 8.3.4  [dcl.array]     Status: drafting     Submitter: Eelis van der Weegen     Date: 13 July, 2008

Paragraph 7 of 8.3.4 [dcl.array] says,

If E is an n-dimensional array of rank i × j × ... × k, then E appearing in an expression is converted to a pointer to an (n - 1)-dimensional array with rank j × ... × k.

This formulation does not allow for the existence of expressions in which the array-to-pointer conversion does not occur (as specified in clause 5 [expr] paragraph 9). This paragraph should be no more than a note, if it appears at all, and the wording should be corrected.




393. Pointer to array of unknown bound in template argument list in parameter

Section: 8.3.5  [dcl.fct]     Status: drafting     Submitter: Mark Mitchell     Date: 12 Dec 2002

EDG rejects this code:

  template <typename T>
  struct S {};

  void f (S<int (*)[]>);
G++ accepts it.

This is another case where the standard isn't very clear:

The language from 8.3.5 [dcl.fct] is:

If the type of a parameter includes a type of the form "pointer to array of unknown bound of T" or "reference to array of unknown bound of T," the program is ill-formed.
Since "includes a type" is not a term defined in the standard, we're left to guess what this means. (It would be better if this were a recursive definition, the way a type theoretician would do it: )

Notes from April 2003 meeting:

We agreed that the example should be allowed.




818. Function parameter packs in non-final positions

Section: 8.3.5  [dcl.fct]     Status: drafting     Submitter: US     Date: 3 March, 2009

N2800 comment US 45

8.3.5 [dcl.fct] paragraph 13 requires that a parameter pack, if present, must appear at the end of the parameter list. This restriction is not necessary when template argument deduction is not needed and is inconsistent with the way pack expansions are handled. It should be removed.

(See also issue 692.)




777. Default arguments and parameter packs

Section: 8.3.6  [dcl.fct.default]     Status: drafting     Submitter: Michael Wong     Date: 13 February, 2009

8.3.6 [dcl.fct.default] paragraph 4 says,

In a given function declaration, all parameters subsequent to a parameter with a default argument shall have default arguments supplied in this or previous declarations.

It is not clear whether this applies to parameter packs or not. For example, is the following well-formed?

    template <typename... T> void f(int i = 0, T ...args) { }

Note for comparison the corresponding wording in 14.2 [temp.param] paragraph 11 regarding template parameter packs:

If a template-parameter of a class template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack.



915. Deleted specializations of member function templates

Section: 8.4  [dcl.fct.def]     Status: drafting     Submitter: Daveed Vandevoorde     Date: 12 June, 2009

It is not clear whether the following definition of an explicit specialization of a member function template is permitted or not:

    template <typenanme T> struct S {
      template <typename U> void f();
    };
    template <> template <typename U>
      void S<int>::f() = delete;

Is the explicit specialization the “first declaration” of the member function template?

(See also issue 845.)

Notes from the July, 2009 meeting:

The intent is that this usage should be supported.




508. Non-constructed value-initialized objects

Section: 8.5  [dcl.init]     Status: drafting     Submitter: Alisdair Meredith     Date: 18 Mar 2005

According to the definition of value initialization (8.5 [dcl.init] paragraph 5), non-union class types without user-declared constructors are value-initialized by value-initializing each of their members rather than by executing the (generated) default constructor. However, a number of other items in the Standard are described in relationship to the execution of the constructor:

Proposed resolution (October, 2005):

Add the indicated words to 8.5 [dcl.init] paragraph 6:

A program that calls for default-initialization or value-initialization of an entity of reference type is ill-formed. If T is a cv-qualified type, the cv-unqualified version of T is used for these definitions of zero-initialization, default-initialization, and value-initialization. Even when value-initialization of an object does not call that object's constructor, the object is deemed to have been fully constructed once its initialization is complete and thus subject to provisions of this International Standard applying to “constructed” objects, objects “for which the constructor has completed execution,” etc.

Notes from April, 2006 meeting:

There was some concern about whether this wording covered (or needed to cover) cases where an object is “partially constructed.” Another approach might be simply to define value initialization to be “construction.” Returned to “drafting” status for further investigation.




615. Incorrect description of variables that can be initialized

Section: 8.5  [dcl.init]     Status: drafting     Submitter: comp.std.c++     Date: 30 January 2007

8.5 [dcl.init] paragraph 2 reads,

Automatic, register, static, and external variables of namespace scope can be initialized by arbitrary expressions involving literals and previously declared variables and functions.

Both “automatic” and “static” are used to describe storage durations, “register” is a storage class specifier which indicates the object has automatic storage duration, “external” describes linkage, and “namespace scope” is a kind of scope. Automatic, register, static and external, together with namespace scope, are used to restrict the “variables.”

Register objects are only a sub-set of automatic objects and thus the word “register” is redundant and should be elided. If register objects are to be emphasized, they should be mentioned like “Automatic (including register)...”

Variables having namespace scope can never be automatic; they can only be static, with either external or internal linkage. Therefore, there are in fact no “automatic variables of namespace scope,” and the “static” in “static variables of namespace scope” is useless.

In fact, automatic and static variables already compose all variables with either external linkage or not, and thus the “external” becomes redundant, too, and the quoted sentence seems to mean that all variables of namespace scope can be initialized by arbitrary expressions. But this is not true because not all internal variables of namespace scope can. Therefore, the restrictive “external” is really necessary, not redundant.

As a result, the erroneous restrictive “automatic, register, static” should be removed and the quoted sentence may be changed to:

External variables of namespace scope can be initialized by arbitrary expressions involving literals and previously declared variables and functions.

Notes from the April, 2007 meeting:

This sentence is poorly worded, but the analysis given in the issue description is incorrect. The intent is simply that the storage class of a variable places no restrictions on the kind of expression that can be used to initialize it (in contrast to C, where variables of static storage duration can only be initialized by constant expressions).

Proposed resolution (June, 2008):

Change 8.5 [dcl.init] paragraph 2 as follows:

Automatic, register, static, and external variables of namespace scope Variables of automatic, thread, and static storage duration can be initialized by arbitrary expressions involving literals and previously declared variables and functions...

Notes from the September, 2008 meeting:

The existing wording is intended to exclude block-scope extern declarations but to allow initializers in all other forms of variable declarations. The best way to phrase that is probably to say that all variable definitions (except for function parameters, where the initializer syntax is used for default arguments) can have arbitrary expressions as initializers, regardless of storage duration.




869. Uninitialized thread_local objects

Section: 8.5  [dcl.init]     Status: drafting     Submitter: Daniel Krügler     Date: 14 April, 2009

8.5 [dcl.init] paragraph 11 says,

If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, a non-static object has indeterminate value.

This is inaccurate, because objects with thread storage duration are zero-initialized (3.6.2 [basic.start.init] paragraph 2).




886. Member initializers and aggregates

Section: 8.5.1  [dcl.init.aggr]     Status: drafting     Submitter: Daniel Krügler     Date: 5 May, 2009

The current wording of 8.5.1 [dcl.init.aggr] paragraph 1 does not consider brace-or-equal-initializers on members as affecting whether a class type is an aggregate or not. Because in-class member initializers are essentially syntactic sugar for mem-initializers, and the presence of a user-provided constructor disqualifies a class from being an aggregate, presumably the same should hold true of member initializers.




737. Uninitialized trailing characters in string initialization

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

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




233. References vs pointers in UDC overload resolution

Section: 8.5.3  [dcl.init.ref]     Status: drafting     Submitter: Matthias Meixner     Date: 9 Jun 2000

There is an inconsistency in the handling of references vs pointers in user defined conversions and overloading. The reason for that is that the combination of 8.5.3 [dcl.init.ref] and 4.4 [conv.qual] circumvents the standard way of ranking conversion functions, which was probably not the intention of the designers of the standard.

Let's start with some examples, to show what it is about:

    struct Z { Z(){} };

    struct A {
       Z x;

       operator Z *() { return &x; }
       operator const Z *() { return &x; }
    };

    struct B {
       Z x;

       operator Z &() { return x; }
       operator const Z &() { return x; }
    };

    int main()
    {
       A a;
       Z *a1=a;
       const Z *a2=a; // not ambiguous

       B b;
       Z &b1=b;
       const Z &b2=b; // ambiguous
    }

So while both classes A and B are structurally equivalent, there is a difference in operator overloading. I want to start with the discussion of the pointer case (const Z *a2=a;): 13.3.3 [over.match.best] is used to select the best viable function. Rule 4 selects A::operator const Z*() as best viable function using 13.3.3.2 [over.ics.rank] since the implicit conversion sequence const Z* -> const Z* is a better conversion sequence than Z* -> const Z*.

So what is the difference to the reference case? Cv-qualification conversion is only applicable for pointers according to 4.4 [conv.qual]. According to 8.5.3 [dcl.init.ref] paragraphs 4-7 references are initialized by binding using the concept of reference-compatibility. The problem with this is, that in this context of binding, there is no conversion, and therefore there is also no comparing of conversion sequences. More exactly all conversions can be considered identity conversions according to 13.3.3.1.4 [over.ics.ref] paragraph 1, which compare equal and which has the same effect. So binding const Z* to const Z* is as good as binding const Z* to Z* in terms of overloading. Therefore const Z &b2=b; is ambiguous. [13.3.3.1.4 [over.ics.ref] paragraph 5 and 13.3.3.2 [over.ics.rank] paragraph 3 rule 3 (S1 and S2 are reference bindings ...) do not seem to apply to this case]

There are other ambiguities, that result in the special treatment of references: Example:

    struct A {int a;};
    struct B: public A { B() {}; int b;};

    struct X {
       B x;
       operator A &() { return x; }
       operator B &() { return x; }
    };

    main()
    {
       X x;
       A &g=x; // ambiguous
    }

Since both references of class A and B are reference compatible with references of class A and since from the point of ranking of implicit conversion sequences they are both identity conversions, the initialization is ambiguous.

So why should this be a defect?

So overall I think this was not the intention of the authors of the standard.

So how could this be fixed? For comparing conversion sequences (and only for comparing) reference binding should be treated as if it was a normal assignment/initialization and cv-qualification would have to be defined for references. This would affect 8.5.3 [dcl.init.ref] paragraph 6, 4.4 [conv.qual] and probably 13.3.3.2 [over.ics.rank] paragraph 3.

Another fix could be to add a special case in 13.3.3 [over.match.best] paragraph 1.




656. Direct binding to the result of a conversion operator

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

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




664. Direct binding of references to non-class rvalue references

Section: 8.5.3  [dcl.init.ref]     Status: drafting     Submitter: Eric Niebler     Date: 1 December 2007

According to 8.5.3 [dcl.init.ref] paragraph 5, a reference initialized with a reference-compatible rvalue of class type binds directly to the object. A reference-compatible non-class rvalue reference, however, is first copied to a temporary and the reference binds to that temporary, not to the target of the rvalue reference. This can cause problems when the result of a forwarding function is used in such a way that the address of the result is captured. For example:

    struct ref {
        explicit ref(int&& i): p(&i) { }
        int* p;
    };

    int&& forward(int&& i) {
        return i;
    }

    void f(int&& i) {
        ref r(forward(i));
        // Here r.p is a dangling pointer, pointing to a defunct int temporary
    }

A formulation is needed so that rvalue references are treated like class and array rvalues.

Notes from the February, 2008 meeting:

You can't just treat scalar rvalues like class and array rvalues, because they might not have an associated object. However, if you have an rvalue reference, you know that there is an object, so probably the best way to address this issue is to specify somehow that binding a reference to an rvalue reference does not introduce a new temporary.

(See also issues 690 and 846.)




896. Rvalue references and rvalue-reference conversion functions

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

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




924. alias-declaration as a class member

Section: 9.2  [class.mem]     Status: drafting     Submitter: Alisdair Meredith     Date: 23 June, 2009

The grammar for member-declaration in 9.2 [class.mem] does not include a production for the alias-declaration form of typedef declarations, meaning that something like

    struct S {
      using UINT = unsigned int;
    };

is ill-formed. This seems like an oversight.




741. “plain” long long bit-fields

Section: 9.6  [class.bit]     Status: drafting     Submitter: Mike Miller     Date: 7 November, 2008

The type long long is missing from the list of bit-field types in 9.6 [class.bit] paragraph 3 for which the implementation can choose the signedness. This was presumably an oversight. (If that is the case, we may want to reconsider the handling of 4.5 [conv.prom] paragraph 3: a long long bit-field that the implementation treats as unsigned will — pending the outcome of issue 739 — still promote to signed long long, which can lead to unexpected results for bit-fields with the same number of bits as long long.)




696. Use of block-scope constants in local classes

Section: 9.8  [class.local]     Status: drafting     Submitter: Steve Adamczyk     Date: 29 May, 2008

According to 9.8 [class.local] paragraph 1,

Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators from the enclosing scope.

This would presumably make both of the members of S2 below ill-formed:

    void test () {
      const int local_const = 7;
      struct S2 {
        int member:local_const;
        void f() { int j = local_const; }
      };
    }

Should there be an exception to this rule for constant values? Current implementations seem to accept the reference to local_const in the bit-field declaration but not in the member function definition. Should they be the same or different?

Notes from the September, 2008 meeting:

The CWG agreed that both uses of local_const in the example above should be accepted. The intent of the restriction was to avoid the need to pass a frame pointer into local class member functions, so uses of local const variables as values should be permitted.

Proposed resolution (March, 2009):

Change 9.8 [class.local] paragraph 1 as follows:

Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators shall not use (3.2 [basic.def.odr]) an automatic variable or reference from the enclosing scope. [Example:
  int x;
  void f() {
    static int s ;
    int x;
    extern int g();
    const int c = 42;
    struct local {
      int g() { return x; }     // error: x has automatic storage duration
      int h() { return s; }     // OK
      int k() { return ::x; }   // OK
      int l() { return g(); }   // OK
      int m() { return c; }     // OK
    };
  }

  local* p = 0;                 // error: local not in scope
end example]

Notes from the July, 2009 meeting:

This proposed resolution relies on the definition of “use” in 3.2 [basic.def.odr]. The CWG was concerned about cases in which it might not be possible to immediately determine whether a reference to a local automatic variable constitutes a “use” or not, such as in overload resolution, conditional expressions, dependent contexts, etc. To address this concern, the CWG expressed support for an approach in which a reference to a local automatic variable in a nested class or lambda body would enter the expression as an rvalue, which would reduce the complexity of the problem.




580. Access in template-parameters of member and friend definitions

Section: 11  [class.access]     Status: drafting     Submitter: John Spicer     Date: 16 May 2006

The resolution of issue 372 leaves unclear whether the following are well-formed or not:

    class C {
        typedef int I;                // private
        template <int> struct X;
        template <int> friend struct Y;
    }

    template <C::I> struct C::X { };  // C::I accessible to member?

    template <C::I> struct Y { };     // C::I accessible to friend?

Presumably the answer to both questions is “yes,” but the new wording does not address template-parameters.

Proposed resolution (June, 2008):

Change 11 [class.access] paragraph 6 as follows:

...For purposes of access control, the base-specifiers of a class, the template-parameters of a template-declaration, and the definitions of class members that appear outside of the class definition are considered to be within the scope of that class...

Notes from the September, 2008 meeting:

The proposed resolution preserves the word “scope” as a holdover from the original specification prior to issue 372, which intended to change access determination from a scope-based model to an entity-based model. The resolution should eliminate all references to scope and simply use the entity-based model.

(See also issue 718.)




472. Casting across protected inheritance

Section: 11.5  [class.protected]     Status: drafting     Submitter: Mike Miller     Date: 16 Jun 2004

Does the restriction in 11.5 [class.protected] apply to upcasts across protected inheritance, too? For instance,

    struct B {
        int i;
    };
    struct I: protected B { };
    struct D: I {
        void f(I* ip) {
            B* bp = ip;    // well-formed?
            bp->i = 5;     // aka "ip->i = 5;"
        }
    };

I think the rationale for the 11.5 [class.protected] restriction applies equally well here — you don't know whether ip points to a D object or not, so D::f can't be trusted to treat the protected B subobject consistently with the policies of its actual complete object type.

The current treatment of “accessible base class” in 11.2 [class.access.base] paragraph 4 clearly makes the conversion from I* to B* well-formed. I think that's wrong and needs to be fixed. The rationale for the accessibility of a base class is whether “an invented public member” of the base would be accessible at the point of reference, although we obscured that a bit in the reformulation; it seems to me that the invented member ought to be considered a non-static member for this purpose and thus subject to 11.5 [class.protected].

(See also issues 385 and 471.).

Notes from October 2004 meeting:

The CWG tentatively agreed that casting across protective inheritance should be subject to the additional restriction in 11.5 [clas