| Document number: | PL22.16/11-0007 = WG21 N3237 |
| Date: | 2011-02-28 |
| Project: | Programming Language C++ |
| Reference: | ISO/IEC IS 14882:2003 |
| Reply to: | William M. Miller |
| Edison Design Group, Inc. | |
| wmm@edg.com |
This document contains the C++ core language issues that have been categorized as Defect Reports by the Committee (J16 + WG21), that is, issues with status "DR," "WP," "CD1," "CD2," and "TC1," along with their proposed resolutions. ONLY RESOLUTIONS FOR ISSUES WITH TC1 STATUS ARE PART OF THE INTERNATIONAL STANDARD FOR C++. The other issues are provided for informational purposes only, as an indication of the intent of the Committee. They should not be considered definitive until or unless they appear in an approved Technical Corrigendum or revised International Standard for C++.
This document is part of a group of related documents that together describe the issues that have been raised regarding the C++ Standard. The other documents in the group are:
For more information, including a description of the meaning of the issue status codes and instructions on reporting new issues, please see the Active Issues List.
Section references in this document reflect the section numbering of document PL22.16/10-0215 = WG21 N3225.
[Voted into the WP at the November, 2010 meeting as paper N3146.]
The list of identifier characters specified in the C++ standard annex _N2691_.E [extendid] and the C99 standard annex D are different. The C99 standard includes more characters.
The C++ standard says that the characters are from "ISO/IEC PDTR 10176" while the C99 standard says "ISO/IEC TR 10176". I'm guessing that the PDTR is an earlier draft of the TR.
Should the list in the C++ standard be updated?
Tom Plum: In my opinion, the "identifier character" issue has not been resolved with certainty within SC22.
One critical difference in C99 was the decision to allow a compiler to accept more characters than are given in the annex. This allows for future expansion.
The broader issue concerns the venue in which the "identifier character" issue will receive ongoing resolution.
Notes from 10/00 meeting:
The core language working group expressed a strong preference (13/0/5 in favor/opposed/abstaining) that the list of identifier characters should be extensible, as is the case in C99. However, the fact that this topic is under active discussion by other bodies was deemed sufficient reason to defer any changes to the C++ specification until the situation is more stable.
Notes from October, 2005 meeting:
The working group expressed interest in the kind of approach taken by XML 1.1, in which the definition of an identifier character is done by excluding large ranges of the Unicode character set and accepting any character outside those ranges, rather than by affirmatively designating each identifier character in each language. As noted above, consideration of this issue was previously deferred pending other related standardization efforts. Clark Nelson will investigate whether these have reached a point at which progress on this issue in C++ is now possible.
Additional note (May, 2008):
Issue 663 also deals with this appendix, and the proposed resolution there is to update the table to reflect the newest available technical report, ISO/IEC TR 10176:2003. That resolution might be seen as sufficient for this issue, as well. However, that approach does not address several of the concerns mentioned in the discussion above: coordination with WG14, the extensibility of the list of identifiers, the alternative approach used in the XML specification, etc.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment GB 6There are core issues surrounding the undefined behavior of dereferencing a null pointer. It appears the intent is that dereferencing is well defined, but using the result of the dereference will yield undefined behavior. This topic is too confused to be the reference example of undefined behavior, or should be stated more precisely if it is to be retained.
(See also issue 232.)
Proposed resolution (September, 2010):
Change 1.9 [intro.execution] paragraph 4 as follows:
Certain other operations are described in this International Standard as undefined (for example, the effect of dereferencing the null pointer attempting to modify a const object). [Note:...
[Voted into the WP at the November, 2010 meeting.]
N3092 comment GB 7The current wording of 1.9 [intro.execution] paragraph 6 could be read as saying that any signal would leave the program in an unspecified state after completing.
Proposed resolution (August, 2010):
Change 1.9 [intro.execution] paragraph 6 as follows:
When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects which are neither
of type volatile std::sig_atomic_t nor
lock-free atomic objects (29.4 [atomics.lockfree])
are unspecified during the execution of the signal handler, and the value of any object not in either of these two categories that is modified by the handler becomes undefined.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 13“Raw” strings are still only Pittsburgh-rare strings: the reversion in phase 3 only applies to an r-char-sequence. It should apply to the entire raw string literal.
Proposed resolution (August, 2010):
Change 2.2 [lex.phases] paragraph 1 phase 1 as follows:
...(An implementation may use any internal encoding, so long as an actual extended character encountered in the source file, and the same extended character expressed in the source file as a universal-character-name (i.e., using the \uXXXX notation), are handled equivalently except where this replacement is reverted in a raw string literal.).)
Change 2.2 [lex.phases] paragraph 1 phase 3 as follows:
...[Example: see the handling of < within a #include preprocessing directive. —end example] Within the r-char-sequence of a raw string literal, any transformations performed in phases 1 and 2 (trigraphs, universal-character-names, and line splicing) are reverted.
Change 2.2 [lex.phases] paragraph 1 phase 5 as follows:
Each source character set member and universal-character-name in a character literal or a string literal, as well as each escape sequence and universal-character-name in a character literal or a non-raw string literal, is converted to the corresponding member of the execution character set (2.14.3 [lex.ccon], 2.14.5 [lex.string]); if there is no corresponding member, it is converted to an implementation-defined member other than the null (wide) character.
Change 2.3 [lex.charset] paragraph 2 as follows:
...Additionally, if the hexadecimal value for a universal-character-name outside the c-char-sequence, s-char-sequence, or r-char-sequence of a character or string literal corresponds to a control character (in either of the ranges 0x000x1F or 0x7F0x9F, both inclusive) or to a character in the basic source character set, the program is ill-formed. [Footnote: A sequence of characters resembling a universal-character-name in an r-char-sequence (2.14.5 [lex.string]) does not form a universal-character-name. —end footnote]
Change 2.5 [lex.pptoken] paragraph 3 as follows:
If the input stream has been parsed into preprocessing tokens up to a given character:
if If the next character begins a sequence of characters that could be the prefix and initial double quote of a raw string literal, such as R", the next preprocessing token shall be a raw string literal;. Between the initial and final double quote characters of the raw string, any transformations performed in phases 1 and 2 (trigraphs, universal-character-names, and line splicing) are reverted; this reversion shall apply before any d-char, r-char, or delimiting parenthesis is identified. The raw string literal is defined as the shortest sequence of characters that matches the raw-string pattern
encoding-prefixopt R raw-string
otherwise Otherwise, the next preprocessing token is the longest sequence of characters that could constitute a preprocessing token, even if that would cause further lexical analysis to fail.
Delete footnote 24 in 2.14.5 [lex.string] paragraph 2:
Use of characters with trigraph equivalents in a d-char-sequence may produce unintended results.
Insert the following examples after 2.14.5 [lex.string] paragraph 4:
[Example: The raw string
R"a( )\ a" )a"is equivalent to "\n)\\\na\"\n". The raw string
R"(??)"is equivalent to "\?\?". The raw string
R"#( )??=" )#"is equivalent to "\n)\?\?=\"\n". —end example]
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 15Passing a name qualified by the global scope operator :: as a template argument can inadvertently trigger recognition of the <: digraph, causing a syntax error. This should be handled by a lexical rule similar to the special treament given to >> so that <:: would be recognized as an open angle-bracket followed by the scope-resolution operator.
Proposed resolution (August, 2010):
Insert a bullet into the list in 2.5 [lex.pptoken] paragraph 3 as follows:
If the input stream has been parsed into preprocessing tokens up to a given character:
if the next character begins a sequence of characters that could be the prefix and initial double quote of a raw string literal, such as R", the next preprocessing token shall be a raw string literal;
otherwise, if the next three characters are <:: and the subsequent character is neither : nor >, the < is treated as a preprocessor token by itself (and not as the first character of the alternative token <:);
otherwise, the next preprocessing token is the longest sequence...
[Voted into the WP at the November, 2010 meeting as paper N3146.]
N3092 comment CA 24“Combining characters should not appear as the first character of an identifier.” [Reference: ISO/IEC TR 10176:2003 (Annex A)] This is not reflected in FCD.
Restrictions on the first character of an identifier are not observed as recommended in TR 10176:2003. The inclusion of digits (outside of those in the basic character set) under identifer-nondigit is implied by FCD.
It is implied that only the “main listing” from Annex A is included for C++. That is, the list ends with the Special Characters section. This is not made explicit in FCD. Existing practice in C++03 as well as WG 14 (C, as of N1425) and WG 4 (COBOL, as of N4315) is to include a list in a normative Annex.
Specify width sensitivity as implied by C++03: \uFF21 is not the same as A. Case sensitivity is already stated in 2.11 [lex.name].
Notes from the August, 2010 meeting:
CWG expressed interest in an approach by which all characters except for those in specifically-excluded categories would be acceptable identifier characters. This suggestion will be brought up with WG14 as a liaison matter in hopes of agreeing on a uniform approach between C and C++.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment DE 3It is not sufficiently clear that std::nullptr_t is a distinct type and neither a pointer type nor a pointer-to-member type. Add a note in 2.14.7 [lex.nullptr] stating that, preferably with cross-references to the normative statements in 3.9 [basic.types].
Proposed resolution (September, 2010):
Change 2.14.7 [lex.nullptr] paragraph 1 as follows:
The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t. [Note: std::nullptr_t is a distinct type that is neither a pointer type nor a pointer to member type; rather, a prvalue of this type is a null pointer constant and can be converted to a null pointer value or null member pointer value. See 4.10 [conv.ptr] and 4.11 [conv.mem]. —end note]
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 17In general, the parameter type of a literal operator must be the same as the argument passed to it. That is not the case for a user-defined-character-literal, where the argument could inadvertently match a literal operator intended for use with user-defined-integer-literals:
typedef unsigned long long ULL;
int operator "" X(ULL);
int i = 'c'X; // operator"" X(ULL('c'))
Suggested resolution: Add the following phrase to the description in paragraph 6:
S shall contain a literal operator whose parameter type is the same as the type of ch.
Proposed resolution (August, 2010):
Change 2.14.8 [lex.ext] paragraph 6 as follows:
If L is a user-defined-character-literal, let ch
be the literal without its ud-suffix. The
S shall contain a literal operator (13.5.8 [over.literal]) whose only parameter has the type of ch and
the literal L is treated as a call of the form
operator "" X (ch )
[Voted into the WP at the November, 2010 meeting as paper N3214.]
N3092 comment US 19It is not always clear when an occurrence of the word “use” is intended as an implicit reference to 3.2 [basic.def.odr] and when it is simply the ordinary English meaning. This could be addressed by:
Replacing all the non-technical appearances of the word “use” with synonyms such as “occurrence” or “appearance” or “reference to.”
Following each occurrence of the word “use” that is intended in the technical sense with a cross-reference to 3.2 [basic.def.odr].
Replacing all the technical occurrences of the word “use” with a different word or phrase, such as “odr-use,” that would unambiguously indicate the intent.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 24One of the critieria for giving a name internal linkage is “a variable that is explicitly declared const and neither explicitly declared extern nor previously declared to have external linkage.” This should presumably apply to variables declared constexpr as well.
Proposed resolution (August, 2010):
Change 3.5 [basic.link] paragraph 3 bullet 2 as follows:
a variable that is explicitly declared const or constexpr and neither explicitly declared extern nor previously declared to have external linkage; or
[Voted into the WP at the November, 2010 meeting.]
N3092 comment DE 4It is odd that, in the following example, N has no linkage but g has external linkage:
namespace {
namespace N // has no linkage
{
void g(); // has external linkage
}
}
Proposed resolution (August, 2010):
Change 3.5 [basic.link] paragraph 4 as follows:
An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage. All other namespaces have external linkage. A name having namespace scope that has not been given internal linkage above has external linkage the same linkage as the enclosing namespace if it is the name of
a variable, unless it has internal linkage; or
a function, unless it has internal linkage; or
a named class (Clause 9 [class]), or an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage purposes (7.1.3 [dcl.typedef]); or
a named enumeration (7.2 [dcl.enum]), or an unnamed enumeration defined in a typedef declaration in which the enumeration has the typedef name for linkage purposes (7.1.3 [dcl.typedef]); or
an enumerator belonging to an enumeration with external linkage; or
a template, unless it is a function template that has internal linkage (Clause 14 [temp]); or.
a namespace (7.3 [basic.namespace]), unless it is declared within an unnamed namespace.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment GB 18The example in 3.8 [basic.life] paragraph 9 reads,
struct B {
B();
~B();
};
const B b;
void h() {
b.~B();
new (&b) const B; // undefined behavior
}
Assuming that the placement new is intended to use the operator defined in the Standard library, the new-expression is ill-formed, because there is no implicit conversion from “pointer to const B” to void*.
Proposed resolution (August, 2010):
Change the example in 3.8 [basic.life] paragraph 9 as follows:
...
new (const_cast<B *>(&b)) const B; // undefined behavior
...
[Voted into the WP at the November, 2010 meeting.]
3.10 [basic.lval] paragraph 7 says,
Whenever an lvalue appears in a context where an rvalue is expected, the lvalue is converted to an rvalue
That is not correct in the context of an attempt to bind an rvalue reference to an lvalue (8.5.3 [dcl.init.ref]).
Proposed resolution (October, 2009):
Change 3.10 [basic.lval] paragraph 7 as follows:
Whenever an lvalue appears in a context where an rvalue is expected and an lvalue is not explicitly prohibited (as, for example, in 8.5.3 [dcl.init.ref]), the lvalue it is converted to an rvalue; see 4.1 [conv.lval], 4.2 [conv.array], and 4.3 [conv.func].
Notes from the March, 2010 meeting:
This resolution needs to be reconsidered in light of the new expression taxonomy.
Proposed resolution (September, 2010):
Change 3.10 [basic.lval] paragraph 2 as follows:
Whenever a glvalue appears in a context where a prvalue is expected, the glvalue is converted to a prvalue; see 4.1 [conv.lval], 4.2 [conv.array], and 4.3 [conv.func]. [Note: An attempt to bind an rvalue reference to an lvalue is not such a context; see 8.5.3 [dcl.init.ref]. —end note]
[Voted into the WP at the November, 2010 meeting.]
N3092 comment JP 1One of the bullets in the note in 5 [expr] paragraph 6 says that an expression is an xvalue if it is:
a class member access expression designating a non-static data member in which the object expression is an xvalue
This is incorrect if the type of the non-static data member is a reference type (cf 5.2.5 [expr.ref] paragraph 4.)
Proposed resolution (September, 2010):
Change 5 [expr] paragraph 6 bullet 3 as follows:
a class member access expression designating a non-static data member of non-reference type in which the object expression is an xvalue, or
[Voted into the WP at the November, 2010 meeting.]
5.1.2 [expr.prim.lambda] paragraph 4 bullet 1 says,
if the compound-statement if [sic] of the form
{ return attribute-specifieropt expression ; }
the type of the returned expression...
The problem (besides the typo “if”) is that the attribute-specifier for a return statement precedes, rather than following, the keyword (6 [stmt.stmt] paragraph 1).
Proposed resolution (September, 2010):
Change 5.1.2 [expr.prim.lambda] paragraph 4 bullet 1 as follows:
if the compound-statement is of the form
the type of the returned expression after lvalue-to-rvalue conversion (4.1 [conv.lval]), array-to-pointer conversion (4.2 [conv.array]), and function-to-pointer conversion (4.3 [conv.func]);
[Voted into the WP at the November, 2010 meeting.]
N3092 comment CH 4The adoption of paper N3067 at the March, 2010 meeting moved the position of the optional attribute-specifier in a function declarator from immediately following the parameter-declaration-clause to after the exception-specification. However, the grammar in 5.1.2 [expr.prim.lambda] paragraph 1 and the verbal description in paragraph 5 still have the attribute-specifier in a lambda-declarator at its old position. These should be updated to reflect the new function declarator syntax.
Proposed resolution (August, 2010):
Change the grammar in 5.1.2 [expr.prim.lambda] paragraph 1 as follows:
Change 5.1.2 [expr.prim.lambda] paragraph 5 as follows:
...Any attribute-specifiers appearing immediately after the lambda-expression's parameter-declaration-clause appertain An attribute-specifier in a lambda-declarator appertains to the type of the corresponding function call operator...
[Voted into the WP at the November, 2010 meeting.]
5.2.2 [expr.call] paragraph 7 should mention a non-trivial move constructor as well, for consistency with “trivially copyable.”
Proposed resolution (September, 2010):
Change 5.2.2 [expr.call] paragraph 7 as follows:
...Passing a potentially-evaluated argument of class type (Clause 9 [class]) with having a non-trivial copy constructor, a non-trivial move constructor, or a non-trivial destructor, with no corresponding parameter is conditionally-supported, with implementation-defined semantics. If the argument...
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 32According to 5.2.5 [expr.ref] paragraph 5,
If E2 is a non-static data member or a non-static member function, the program is ill-formed if the class of which E2 is directly a member is an ambiguous base (10.2 [class.member.lookup]) of the naming class (11.2 [class.access.base]) of E2.
This does not cover the following case:
struct A { int i; };
struct B: A { };
struct C: A, B { };
void f(C* p) {
p->A::i; // Should be ambiguous
}
Notes (August, 2010):
The example in the FCD National Body comment is incorrect: it is missing the A:: in the next-to-last line.
The ambiguity actually is covered in the Standard but in an unexpected location: 11.2 [class.access.base] paragraph 6:
If a class member access operator, including an implicit “this->,” is used to access a non-static data member or non-static member function, the reference is ill-formed if the left operand (considered as a pointer in the “.” operator case) cannot be implicitly converted to a pointer to the naming class of the right operand.
An explanatory note, including a cross-reference to 11.2 [class.access.base], should be added to 5.2.5 [expr.ref] paragraph 6.
Proposed resolution (September, 2010):
Change 5.2.5 [expr.ref] paragraph 5 as follows:
If E2 is a non-static data member or a non-static member function, the program is ill-formed if the class of which E2 is directly a member is an ambiguous base (10.2 [class.member.lookup]) of the naming class (11.2 [class.access.base]) of E2. [Note: The program is also ill-formed if the naming class is an ambiguous base of the class type of the object expression; see 11.2 [class.access.base]. —end note]
[Voted into the WP at the November, 2010 meeting.]
According to 5.2.9 [expr.static.cast] paragraph 7, static_cast can be used to perform the inverse of any standard conversion sequence except the lvalue-to-rvalue, array-to-pointer, function-to-pointer, and boolean conversions. The null pointer and null pointer-to-member conversions should also be listed — it should not be permitted to cast a pointer or pointer-to-member to either integral type or std::nullptr_t.
Proposed resolution (October, 2010):
Change 4.10 [conv.ptr] paragraph 1 as follows:
...A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of pointer to object or pointer to function type. Such a conversion is called a null pointer conversion. Two null pointer values...
Change 4.11 [conv.mem] paragraph 1 as follows:
A null pointer constant (4.10 [conv.ptr]) can be converted to a pointer to member type; the result is the null member pointer value of that type and is distinguishable from any pointer to member not created from a null pointer constant. Such a conversion is called a null member pointer conversion. Two null member pointer values...
Change 5.2.9 [expr.static.cast] paragraph 7 as follows:
The inverse of any standard conversion sequence (Clause 4 [conv]), other than the not containing an lvalue-to-rvalue (4.1 [conv.lval]), array-to-pointer (4.2 [conv.array]), function-to-pointer (4.3 [conv.func]), and null pointer (4.10 [conv.ptr]), null member pointer (4.11 [conv.mem]), or boolean (4.12 [conv.bool]) conversions, can be performed explicitly using static_cast. A program is ill-formed...
[Voted into the WP at the November, 2010 meeting.]
5.2.11 [expr.const.cast] paragraph 1 refers to casting to an rvalue reference to function type. This was an accidental edit in the application of the value categories and should be removed.
Proposed resolution (October, 2010):
Change 5.2.11 [expr.const.cast] paragraph 1 as follows:
If T is an lvalue reference to object type or an rvalue reference to function type, the result is an lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue and the lvalue-to-rvalue (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...[Drafting note: with this change to the wording, an attempt to cast a function lvalue to either an lvalue or rvalue reference will be ill-formed because the function-to-pointer conversion will be applied to the operand and the resulting operand base type will be incompatible with the target base type.]
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 33The resolution of issue 983 restored an error, inadvertently removed by the resolution of issue 39, for the formation of a member of an ambiguous base class. For example:
struct B { int i; };
struct I1: B { };
struct I2: B { };
struct D: I1, I2 { };
int B::* pm = &D::i; // Originally and again ambiguous
This error is not necessary, because the result of taking the address of a member of an ambiguous base class is a pointer to a member of that class; an actual ambiguity would occur only if that pointer to a base class member is converted to a pointer to a member of the derived class. (See issue 203, which suggests changing the result of taking the address of a member to reflect the naming class of the member instead of the class of which it is directly a member; if that change were made, the ambiguity error would be needed.)
The resolution of issue 983 should be reverted.
Proposed resolution (August, 2010):
Change 5.3.1 [expr.unary.op] paragraph 3 as follows:
...If the operand is a qualified-id naming a non-static member m of some class C with type T, the result has type “pointer to member of class C of type T and is a prvalue designating C::m; the program is ill formed if C is an ambiguous base (10.2) of the class designated by the nested-name-specifier of the qualified-id. Otherwise...
Change 10.2 [class.member.lookup] paragraph 13 as follows:
[Note: Even if the result of name lookup is unambiguous, use of a name found in multiple subobjects might still be ambiguous (4.11 [conv.mem], 5.2.5 [expr.ref], 5.3.1 [expr.unary.op], 11.2 [class.access.base]). —end note]...
[Voted into the WP at the November, 2010 meeting.]
N3092 comment GB 24The return type of the sizeof operator is defined as being of type std::size_t, defined in library clause 18.2 [support.types]. This, in turn, says that size_t is defined in the C standard, which in turn says that size_t is defined as the type of the result of the sizeof operator!
The C definition of sizeof returns an implementation-defined unsigned integer type, recommended not to have “an integer conversion rank greater than signed long int, unless the implementation supports objects large enough to make this necessary.”
Proposed resolution (September, 2010):
Add the following three paragraphs after 18.2 [support.types] paragraph 4:
The type ptrdiff_t is an implementation-defined signed integer type that can hold the difference of two subscripts in an array object, as described in 5.7 [expr.add].
The type size_t is an implementation-defined unsigned integer type that is large enough to contain the size in bytes of any object.
[Note: It is recommended that implementations choose types for ptrdiff_t and size_t whose integer conversion ranks (4.13 [conv.rank]) are no greater than that of signed long int unless a larger size is necessary to contain all the possible values. —end note]
[Voted into the WP at the November, 2010 meeting.]
Recent changes have added the requirement (5.3.4 [expr.new] paragraph 7),
If the value of that expression is such that the size of the allocated object would exceed the implementation-defined limit, no storage is obtained and the new-expression terminates by throwing an exception of a type that would match a handler (15.3 [except.handle]) of type std::bad_array_new_length (18.6.2.2 [new.badlength]).
Given this checking, is there any current reason for the statement in the preceding paragraph,
If the value of the expression is negative, the behavior is undefined.
Presumably for most negative expressions on most platforms, a negative value would result in a too-large request anyway, and even if not the check could easily be expanded to look explicitly for a negative value in addition to a too-large request.
Proposed resolution (September, 2010):
Change 5.3.4 [expr.new] paragraphs 6 and 7 as follows:
...If the value of the expression is negative, the behavior is undefined. [Example: given the definition int n = 42, new float[n][5] is well-formed (because n is the expression of a noptr-new-declarator), but new float[5][n] is ill-formed (because n is not a constant expression). If n is negative, the effect of new float[n][5] is undefined. —end example]
When the value of the expression in a noptr-new-declarator is zero, the allocation function is called to allocate an array with no elements. If the value of that expression is less than zero or such that the size of the allocated object would exceed the implementation-defined limit, no storage is obtained and the new-expression terminates by throwing an exception of a type that would match a handler (15.3 [except.handle]) of type std::bad_array_new_length (18.6.2.2 [new.badlength]).
Change 18.6.2.2 [new.badlength] paragraph 1 as follows:
The class bad_array_new_length defines the type of objects thrown as exceptions by the implementation to report an attempt to allocate an array of size less than zero or greater than an implementation-defined limit (5.3.4 [expr.new]).
[Voted into the WP at the November, 2010 meeting.]
According to 5.3.5 [expr.delete] paragraph 2,
...in the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a subobject (1.8 [intro.object]) representing a base class of such an object (Clause 10 [class.derived]). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete shall be the pointer value which resulted from a previous array new-expression.79 If not, the behavior is undefined.
The second part of this specification makes it clear that an array object being deleted must have been allocated via new. However, the first part, for the non-array object, completely omits this vital requirement, requiring only that it not be an array.
The corresponding requirement for an argument to a deallocation function is found in 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 3:
...the value supplied to operator delete(void*) in the standard library shall be one of the values returned by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, const std::nothrow_t&) in the standard library, and the value supplied to operator delete[](void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[](std::size_t) or operator new[](std::size_t, const std::nothrow_t&) in the standard library.
This correctly states the required provenance of the pointer, but it does so using “shall,” which is inappropriate for a runtime requirement. This wording should be recast in terms of undefined behavior if the requirement is not met.
Proposed resolution (October, 2010):
Change 5.3.5 [expr.delete] paragraph 2 as follows:
If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, and the converted operand is used in place of the original operand for the remainder of this section. In either alternative, the value of the operand of delete may be a null pointer value. If it is not a null pointer value, in In the first alternative (delete object), the value of the operand of delete shall may be a null pointer value, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject (1.8 [intro.object]) representing a base class of such an object (Clause 10 [class.derived]). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete shall may be the a null pointer value or a pointer value which that resulted from a previous array new-expression.79 If not, the behavior is undefined. [Note: this means that the syntax of the delete-expression must match the type of the object allocated by new new, not the syntax of the new-expression. —end note]...
Change 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 3 as follows:
...Otherwise, the behavior is undefined if the value supplied to operator delete(void*) in the standard library shall be is not one of the values returned by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, const std::nothrow_t&) in the standard library, and the behavior is undefined if the value supplied to operator delete[](void*) in the standard library shall be is not one of the values returned by a previous invocation of either operator new[](std::size_t) or operator new[](std::size_t, const std::nothrow_t&) in the standard library.
[Voted into the WP at the November, 2010 meeting as paper N3204.]
N3092 comment FI 17Destructors should by default be noexcept. Such a rule should be obeyed even for cases where a destructor is defaulted. Then a throwing destructor would need to be declared noexcept(false), and the resulting code breakage is acceptable.
Notes from the August, 2010 meeting:
CWG agreed with the suggested direction.
(Duplicate of issue 1147.)
[Voted into the WP at the November, 2010 meeting as paper N3218.]
N3092 comment DE 8In the definition of “potential constant expression” in 5.19 [expr.const] paragraph 6, it is unclear how “arbitrary” the substitution of the function parameters is. Does it mean “there exists a value for which the result is a constant expression” or does it mean “for all possible values, the result needs to be a constant expression?” Example:
constexpr int f(int x){ return x + 1; }
is a constant expression under the first interpretation, but not under the second (because overflow occurs for x == INT_MAX, cf 5.19 [expr.const] paragraph 2 bullet 5). The answer also affects expressions such as:
constexpr int f2(bool v) { return v ? throw 0 : 0; }
constexpr int f3(bool v) { return v && (throw 0, 0); }
See also issue 1129.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment GB 25It does not appear to be clearly enough stated that the example
constexpr int f() { return 42 + 84; }
const int sz = f();
int a[sz];
is equivalent to
const int sz = 42 + 84;
int a[sz];
Proposed resolution (August, 2010):
Change 5.19 [expr.const] paragraph 1 as follows:
Certain contexts require expressions that satisfy additional requirements as detailed in this sub-clause; other contexts have different semantics depending on whether or not an expression satisfies these requirements. Such expressions Expressions that satisfy these requirements are called constant expressions. [Note: Those Constant expressions can be evaluated during translation. —end note]
[Voted into the WP at the November, 2010 meeting as paper N3218.]
N3092 comment GB 26It is not clear how overload resolution is performed inside the body of a constexpr function. In particular, if the function is invoked with parameters such that an expression evaluates to an integral 0, does that make the expression eligible for the null pointer conversion and thus potentially select a different overloaded function than in invocations in which the expression has a non-zero value? (Suggested answer: no.)
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 39The current wording of 7.1 [dcl.spec] paragraph 1 says,
The optional attribute-specifier in a decl-specifier-seq appertains to the type determined by the decl-specifier-seq (8.3 [dcl.meaning]).
However, decl-specifier-seq is a recursive production. The intent is that the attribute-specifier appertains to the type determined by the top-level decl-specifier-seq, but this makes it sound as if it appertains to the one that directly contains the attribute-specifier.
Proposed resolution (August, 2010):
Change 7.1 [dcl.spec] paragraph 1 as follows:
The optional attribute-specifier in a decl-specifier-seq appertains to the type determined by the decl-specifier-seq preceding decl-specifiers (8.3 [dcl.meaning]).
[Voted into the WP at the November, 2010 meeting.]
N3092 comment GB 29A constexpr function is not permitted to return via an exception. This should be recognised, and a function declared constexpr without an explicit exception specification should be treated as if declared noexcept(true) rather than the usual noexcept(false). For a function template declared constexpr without an explicit exception specification, it should be considered noexcept(true) if and only if the constexpr keyword is respected on a given instantiation.
See also issue 1125.
Notes from the August, 2010 meeting:
The premise is not correct: an exception is forbidden only when a constexpr function is invoked in a context that requires a constant expression. Used as an ordinary function, it can throw.
Proposed resolution (August, 2010):
Change 5.3.7 [expr.unary.noexcept] paragraph 3 bullet 1 as follows:
[Voted into the WP at the November, 2010 meeting.]
According to 14.2 [temp.names] paragraph 7,
A template-id that names a template alias specialization is a type-name.
However, the grammar for type-name in 7.1.6.2 [dcl.type.simple] does not include a production for simple-template-id.
Proposed resolution (September, 2010):
Change the definition of type-name in 7.1.6.2 [dcl.type.simple] paragraph 1 as follows:
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 40The description of decltype does not specify whether the type of a parameter is the declared type or the type as adjusted in 8.3.5 [dcl.fct] paragraph 5:
auto f(int a[])->decltype(a); // ill-formed or int*?
auto g(const int i)->decltype(i); // int or const int?
Suggested resolution: Clarify the wording to indicate that the type of a parameter is after the array- and function-to-pointer decay but before the removal of cv-qualification.
Proposed resolution (August, 2010):
Change 8.3.5 [dcl.fct] paragraph 5 as follows:
...After producing the list of parameter types, several transformations take place upon these types to determine the function type. Any any top-level cv-qualifiers modifying a parameter type is are deleted when forming the function type. [Example: the type void(*)(const int) becomes void(*)(int) —end example] Such cv-qualifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. If a storage-class-specifier modifies a parameter type, the specifier is deleted. [Example: register char* becomes char* —end example] Such storage-class-specifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list. [Note: This transformation does not affect the types of the parameters. For example, int(*)(const int p, decltype(p)*) and int(*)(int, const int*) are identical types. —end note]
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 41The current wording disallows use of typedef-names in elaborated-type-specifiers. This prohibition should also apply to template aliases:
struct A { };
template<typename T> using X = A;
struct X<int>* p2; // ill-formed
Proposed resolution (August, 2010):
Change 7.1.6.3 [dcl.type.elab] paragraph 2 as follows:
...If the identifier resolves to a typedef-name or the simple-template-id resolves to an alias template specialization, the elaborated-type-specifier is ill-formed. [Note:...
[Note: this wording assumes the change from “template alias” to “alias template” requested by comment FI 11 on FCD N3092.]
[Voted into the WP at the November, 2010 meeting.]
N3092 comment FI 6Although 7.3.1.1 [namespace.unnamed] states that the use of the static keyword for declaring variables in namespace scope is deprecated because the unnamed namespace provides a superior alternative, it is unlikely that the feature will be removed at any point in the foreseeable future, especially in light of C compatibility concerns. The Committee should consider removing the deprecation.
Proposed resolution (August, 2010):
Delete 7.3.1.1 [namespace.unnamed] paragraph 2:
The use of the static keyword is deprecated when declaring variables in a namespace scope (see annex D [depr]); the unnamed-namespace provides a superior alternative.
Delete D.2 [depr.static]:
D.2 static keyword [depr.static] The use of the static keyword is deprecated when declaring objects in namespace scope (see 3.3.6 [basic.scope.namespace]).
[Voted into the WP at the November, 2010 meeting.]
Here's an interesting case:
int f;
namespace N {
extern "C" void f () {}
}
As far as I can tell, this is not precluded by the ODR section
(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.
Proposed resolution (February, 2010):
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 or reference with C language linkage with the same name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same object or reference. An entity with C language linkage shall not be declared with the same name as an entity in global scope, unless both declarations denote the same object or reference; no diagnostic is required if the declarations appear in different translation units. [Note: because of the one definition rule (3.2 [basic.def.odr]), only Only one definition for a function or object an entity with C linkage may appear in the program (see 3.2 [basic.def.odr]); that is, implies that such a function or object an entity must not be defined in more than one namespace scope. —end note] For example, [Example:
int x; namespace A { extern "C" int f(); extern "C" int g() { return 1; } extern "C" int h(); extern "C" int x(); // ill-formed: same name as global-scope object x } namespace B { extern "C" int f(); // A::f and B::f refer // to the same function extern "C" int g() { return 1; } // ill-formed, the function g // with C language linkage // has two definitions } int A::f() { return 98; } // definition for the function f // with C language linkage extern "C" int h() { return 97; } // definition for the function h // with C language linkage // A::h and ::h refer to the same function—end note example]
Additional note (February, 2010):
The proposed wording above does not cover the case where two different entities with C linkage are declared in different namespaces, only the case where one of the entities is in global scope.
Proposed resolution (August, 2010):
Change 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 a variable with C language linkage with the same name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same object variable. An entity with C language linkage shall not be declared with the same name as an entity in global scope, unless both declarations denote the same entity; no diagnostic is required if the declarations appear in different translation units. A variable with C language linkage shall not be declared with the same name as a function with C language linkage (ignoring the namespace names that qualify the respective names); no diagnostic is required if the declarations appear in different translation units. [Note: because of the one definition rule (3.2 [basic.def.odr]), only Only one definition for a function or object an entity with a given name with C language linkage may appear in the program (see 3.2 [basic.def.odr]); that is, implies such a function or object an entity must not be defined in more than one namespace scope. —end note] For example, [Example:
int x; namespace A { extern "C" int f(); extern "C" int g() { return 1; } extern "C" int h(); extern "C" int x(); // ill-formed: same name as global-scope object x } namespace B { extern "C" int f(); // A::f and B::f refer // to the same function extern "C" int g() { return 1; } // ill-formed, the function g // with C language linkage // has two definitions } int A::f() { return 98; } // definition for the function f // with C language linkage extern "C" int h() { return 97; } // definition for the function h // with C language linkage // A::h and ::h refer to the same function—end note example]
[Note to editor: please consider reformatting the comments in the example.]
[Voted into the WP at the November, 2010 meeting as part of paper N3190.
The current syntax requires that multiple attributes that appertain to the same entity be grouped into a single attribute-specifier. The migration from existing vendor-specific attributes would be easier if the syntax allowed multiple attribute-specifiers at each location where an attribute-specifier currently appears.
Proposed resolution (October, 2010):
Replace every occurrence of attribute-specifier with attribute-specifier-seq except in 7.6.1 [dcl.attr.grammar] paragraphs 1, 3, and 6 (but including paragraph 4).
Insert the following production at the beginning of the grammar of 7.6.1 [dcl.attr.grammar] paragraph 1:
[Voted into the WP at the November, 2010 meeting.]
The Standard explicitly bans alignment attributes for function parameters (7.6.2 [dcl.align] paragraph 1), but it is silent regarding the “parameter” of an exception handler. This should be clarified one way or the other.
Proposed resolution (October, 2010):
Change 7.6.2 [dcl.align] paragraph 1 as follows:
...The attribute may be followed by an ellipsis. The attribute may be applied to a variable that is neither a function parameter nor declared with the register storage class specifier and to a class data member that is not a bit-field or to a class data member, but it shall not be applied to a bit-field, a function parameter, the formal parameter of a catch clause (15.3 [except.handle]), or a variable declared with the register storage class specifier. The attribute may also be applied to the declaration of a class or enumeration type.
[Voted into the WP at the November, 2010 meeting as part of paper N3206.]
The meaning of the [[base_check]] and [[hiding]] attributes is defined in terms of hiding as described in 3.3.10 [basic.scope.hiding]. In that section, however, hiding is orthogonal to overriding: practically by definition, a function that overrides a base class virtual function also hides it. According to the current specification, the [[override]] and [[hiding]] attributes would always need to be specified together on every overriding function in a [[base_check]] class. This is presumably unintended, so the current wording should be amended so that [[override]] implies [[hiding]] or some such.
[Voted into the WP at the November, 2010 meeting.]
It seems strange that it is possible to call a function with an explict argument of {} but that it is not possible to specify that same argument as a default in a function declaration.
Rationale (August, 2010):
This was previously considered and rejected by EWG.
Note (October, 2010):
Additional discussion has indicated a potential willingness to revisit this question.
Proposed resolution (November, 2010):
See paper N3217.
[Voted into the WP at the November, 2010 meeting.]
In 8.3.5 [dcl.fct] paragraph 2, the type of a function declarator with a trailing-return-type is said to be
“function of (parameter-declaration-clause) cv-qualifier-seqopt ref-qualifieropt returning type-id”.
This formulation incorrectly omits the derived-declarator-type-list modifier for the type, and it should refer to “the trailing-type-specifier-seq of the trailing-return-type” as the return type instead of type-id (which is left over from before the introduction of trailing-return-type).
Proposed resolution (September, 2010):
Change 8.3.5 [dcl.fct] paragraph 2 as follows:
...T shall be the single type-specifier auto. The type of the declarator-id in D is “derived-declarator-type-list function of (parameter-declaration-clause) cv-qualifier-seqopt ref-qualifieropt returning type-id trailing-return-type”. The optional...
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 478.4.2 [dcl.fct.def.default] paragraph 4 says,
A special member function is user-provided if it is user-declared and not explicitly defaulted on its first declaration. A user-provided explicitly-defaulted function is defined at the point where it is explicitly defaulted...
The second sentence should say “user-declared” instead of “user-provided.”
Proposed resolution (September, 2010):
Change 8.4.2 [dcl.fct.def.default] paragraph 4 as follows:
...A special member function is user-provided if it is user-declared and not explicitly defaulted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed. [Note:...[Drafting note: the suggestion in the NB comment is incorrect. The proposed resolution clarifies the intent.]
[Voted into the WP at the November, 2010 meeting.]
The C committee is considering changing the definition of zero-initialization of unions to guarantee that the bytes of the entire union are set to zero before assigning 0, converted to the appropriate type, to the first member. The argument (summarized here) is for backward compatibility. The C++ Committee may want to consider the same change.
Proposed resolution (August, 2008):
Change bullet 3 of 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.
Additional notes (February, 2010):
The C Committee has changed its approach to this question and is no longer using a two-phase process; in C, zero-initialization is specific to program start-up and thus is not appropriate for use with thread-local storage. See C paper N1387.
Proposed resolution (October, 2010):
Change 8.5 [dcl.init] paragraph 5 as follows:
To zero-initialize an object or reference of type T means:
if T is a scalar type (3.9 [basic.types]), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T;103
if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized, and padding is initialized to zero bits;
if T is a (possibly cv-qualified) union type, the object's first non-static named data member is zero-initialized, and padding is initialized to zero bits;
if T is an array type, each element is zero-initialized;
if T is a reference type, no initialization is performed.
[Voted into the WP at the November, 2010 meeting.]
Issue 990 added the following text to 8.5.4 [dcl.init.list] paragraph 3:Otherwise, if the initializer list has no elements and T is an aggregate, each of the members of T is initialized from an empty initializer list. [Example:...
A better way to handle this would be to delete that bullet and change 8.5.1 [dcl.init.aggr] paragraph 7 as follows:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be value-initialized (8.5 [dcl.init]) initialized from an empty initializer list (8.5.4 [dcl.init.list]).
This makes { } less of a special case and makes the following example work:
struct A {
A(std::initializer_list<int>);
};
struct B {
int i;
A a;
};
B b = { 1 };
Proposed resolution (August, 2010):
Delete 8.5.4 [dcl.init.list] paragraph 3 bullet 2, includeing the example:
Change 8.5.1 [dcl.init.aggr] paragraph 7 as follows:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be value-initialized (8.5 [dcl.init]) initialized from an empty initializer list (8.5.4 [dcl.init.list]).
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 48The requirement that an rvalue reference must be bound to an rvalue is found in 8.5.3 [dcl.init.ref] paragraph 5 bullet 2:
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference and the initializer expression shall be an rvalue or have a function type.
This is not quite correct, as it is phrased in terms of the value category of the initializer expression itself rather than that of the result of any conversions applied to the initializer. It should be permitted to bind an rvalue reference to a temporary created from an lvalue, for instance, or to the rvalue result of a conversion function for an lvalue object of class type. Also, it should not be permitted to bind an rvalue reference to the lvalue result of a conversion function for a class rvalue.
Proposed resolution (August, 2010):
Change 8.5.3 [dcl.init.ref] paragraph 5 as follows:
A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:
If the reference is an lvalue reference and the initializer expression
is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be implicitly converted to an lvalue of type “cv3 T3,” where “cv1 T1” is reference-compatible with “cv3 T3”105 (this conversion is selected by enumerating the applicable conversion functions (13.3.1.6 [over.match.ref]) and choosing the best one through overload resolution (13.3 [over.match])),
then the reference is bound to the initializer expression lvalue in the first case and to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object). [Note: the usual 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 not needed, and therefore are suppressed, when such direct bindings to lvalues are done. —end note]
[Example:
double d = 2.0; double& rd = d; // rd refers to d const double& rcd = d; // rcd refers to d struct A { }; struct B : A { operator int&(); } b; A& ra = b; // ra refers to A subobject in b const A& rca = b; // rca refers to A subobject in b int& ir = B(); // ir refers to the result of B::operator int&—end example]
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference and the initializer expression shall be an rvalue or have a function type. [Example:
double& rd2 = 2.0; // error: not an lvalue and reference not const int i = 2; double& rd3 = i; // error: type mismatch and reference not const double&& rd4 = i; // error: rvalue reference cannot bind to lvalue—end example]
If the initializer expression
is an xvalue, class prvalue, array prvalue or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be implicitly converted to an xvalue, class prvalue, or function lvalue of type “cv3 T3,” where “cv1 T1” is reference-compatible with “cv3 T3”,
then the reference is bound to the value of the initializer expression in the first case and to the result of the conversion in the second case (or, in either case, to an appropriate base class subobject). In the second case, if the reference is an rvalue reference and the second standard conversion sequence of the user-defined conversion sequence includes an lvalue-to-rvalue conversion, the program is ill-formed.
If T1 is a function type, then
if T2 is the same type as T1, the reference is bound to the initializer expression lvalue;
if T2 is a class type and the initializer expression can be implicitly converted to an lvalue of type T1 (this conversion is selected by enumerating the applicable conversion functions (13.3.1.6 [over.match.ref]) and choosing the best one through overload resolution (13.3 [over.match])), the reference is bound to the function lvalue that is the result of the conversion;
otherwise, the program is ill-formed.
Otherwise, if T2 is a class type and
the initializer expression is an rvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or
T1 is not reference-related to T2 and the initializer expression can be implicitly converted to an rvalue of type “cv3 T3” (this conversion is selected by enumerating the applicable conversion functions (13.3.1.6 [over.match.ref]) and choosing the best one through overload resolution (13.3 [over.match])),
then the reference is bound to the initializer expression rvalue in the first case and to the object that is the result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object).
[Example:
struct A { }; struct B : A { } b; extern B f(); const A& rca = f(); // bound to the A subobject of the B rvalue. A&& rcb rra = f(); // same as above struct X { operator B(); operator int&(); } x; const A& r = x; // bound to the A subobject of the result of the conversion int&& rri = static_cast<int&&>(i); // bound directly to i B&& rrb = x; // bound directly to the result of operator B int&& rri2 = X(); // error: lvalue-to-rvalue conversion applied to result of operator int&—end example]
If the initializer expression is an rvalue, with T2 an array type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound to the object represented by the rvalue (see 3.10 [basic.lval]).
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. If T1 is reference-related to T2, cv1 must shall be the same cv-qualification as, or greater cv-qualification than, cv2; otherwise, the program is ill-formed. If T1 is reference-related to T2 and the reference is an rvalue reference, the initializer expression shall not be an lvalue. [Example:
const double& rcd2 = 2; // rcd2 refers to temporary with value 2.0 double&& rcd3 rrd = 2; // rcd3 rrd refers to temporary with value 2.0 const volatile int cvi = 1; const int& r = cvi; // error: type qualifiers dropped double&& rrd2 = d; // error: copying lvalue of related type double&& rrd3 = i; // rrd3 refers to temporary with value 2.0—end example]
In all cases except the last (i.e., creating and initializing a temporary from the initializer expression), the reference is said to bind directly to the initializer expression.
This resolution also resolves issue 1139.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 49The current wording of 8.5.3 [dcl.init.ref] paragraph 5 does not specify direct binding of an rvalue reference to a scalar xvalue; instead, it requires creation of a temporary and binding the rvalue reference to the temporary.
Proposed resolution (August, 2010):
This issue is resolved by the resolution of 1138.
[Voted into the WP at the November, 2010 meeting.]
9.2 [class.mem] paragraph 13 requires that all enumerators of a member enumeration type have names different from that of the containing class. This is only necessary for an unscoped enumeration; scoped enumerators are not in the scope of the containing class.
Proposed resolution (September, 2010):
Change 9.2 [class.mem] paragraph 14 bullet 4 as follows:
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 52The current wording of 9.3 [class.mfct] paragraph 7 allows friend declarations to name member functions “after their class has been defined.” This appears to prohibit a friend declaration in a nested class defined inside its containing class that names a member function of the containing class, because the containing class is still considered to be incomplete at that point.
Proposed resolution (September, 2010):
Change 9.3 [class.mfct] paragraph 7 as follows:
Member Previously-declared member functions may be mentioned in friend declarations after their class has been defined.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 56Access declarations were deprecated in the 1998 standard and have no benefits over using-declarations. They should be removed in C++0x.
Proposed resolution (August, 2010):
Delete 11.3 [class.access.dcl].
Delete D.3 [depr.access.dcl].
Delete the following production from the grammar in 9.2 [class.mem] paragraph 1:
...Except when used to declare friends (11.4) or to introduce the name of a member of a base class into a derived class (7.3.3, 11.3), member-declarations declare members of the class...
Delete 7.3.3 [namespace.udecl] paragraph 19:
[Note: use of access-declarations (11.3 [class.access.dcl]) is deprecated; member using-declarations provide a better alternative. —end note]
[Voted into the WP at the November, 2010 meeting.]
The Standard does not define the type of a destructor call. Although that is not of any practical importance, it should do so as a matter of completeness. (5.2.4 [expr.pseudo] paragraph 1 defines the type of a pseudo-destructor call as void.)
Proposed resolution (September, 2010):
Change 5.2.2 [expr.call] paragraph 3 as follows:
The If the postfix-expression designates a destructor (12.4 [class.dtor]), the type of the function call expression is void; otherwise, the type of the function call expression is the return type of the statically chosen function (i.e., ignoring the virtual keyword), even if the type of the function actually called is different. This type shall be a complete object type, a reference type or the type void.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment GB 39The note in 12.4 [class.dtor] paragraph 4 says,
An explictly defaulted definition has no implicit exception-specification.
There are similar notes in 12.8 [class.copy] paragraphs 15 and 29.
However, 8.4.2 [dcl.fct.def.default] paragraph 2 bullet 4 says that a special member function that is explicitly defaulted on its first declaration
is implicitly considered to have the same exception-specification as if it had been implicitly declared (15.4 [except.spec])
The notes are incorrect.
Proposed resolution (August, 2010):
Change 12.4 [class.dtor] paragraph 4 as follows:
[Note: an implicitly- declared destructor has an exception-specification (15.4). An explictly defaulted definition has no implicit exception-specification. —end note]
Change 12.8 [class.copy] paragraph 15 as follows:
[Note: an implicitly-declared copy/move constructor has an exception-specification (15.4). An explicitly-defaulted definition (8.4.2) has no implicit exception-specification. —end note]
Change 12.8 [class.copy] paragraph 29 as follows:
[Note: An implicitly-declared copy/move assignment operator has an exception- specification (15.4). An explicitly-defaulted definition has no implicit exception-specification. —end note]
[Voted into the WP at the November, 2010 meeting as paper N3204.]
N3092 comment GB 40A user-declared destructor that does not supply an exception specification should be considered as if declared noexcept(true) rather than noexcept(false).
(Duplicate of issue 1123.)
[Voted into the WP at the November, 2010 meeting.]
N3092 comment FI 19It is not clear whether the current specification allows a defaulted copy constructor to call an explicit constructor to copy a base or member subobject, and if so, whether that is desirable or not. See also issues 535 and 1118.
Proposed resolution (August, 2010):
This issue is resolved by the resolution of issue 1051.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 62The new wording describing generated copy constructors (12.8 [class.copy] paragraph 16) does not describe the initialization of members with reference type.
See also issue 992.
Proposed resolution (October, 2010):
Change 12.8 [class.copy] paragraph 12 as follows:
An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (8.4.3 [dcl.fct.def.delete]) if X has:
a variant member with a non-trivial corresponding constructor and X is a union-like class,
a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3 [over.match]), as applied to M's corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor, or
a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3 [over.match]), as applied to B's corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor, or
for the copy constructor, a non-static data member of rvalue reference type, or
for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.
Change 12.8 [class.copy] paragraph 16 as follows:
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its subobjects bases and members. [Note: brace-or-equal-initializers of non-static data members are ignored. See also the example in 12.6.2 [class.base.init]. —end note] The order of copying initialization is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2 [class.base.init]). Let x be either the parameter of the constructor or, for the move constructor, an xvalue referring to the parameter. Each subobject base or non-static data member is copied/moved in the manner appropriate to its type:
if the subobject is of class type, the copy constructor for the class is used;
if the subobject member is an array, each element is copied, in the manner appropriate to the element type direct-initialized with the corresponding subobject of x;
if a member m has rvalue reference type T&&, it is direct-initialized with static_cast<T&&>(x.m);
otherwise, the base or member is direct-initialized with the corresponding base or member of x.
if the subobject is of scalar type, the built-in assignment operator is used.
Virtual base class subobjects shall be copied initialized only once by the implicitly-defined copy/move constructor (see 12.6.2 [class.base.init]).
Delete 12.8 [class.copy] paragraph 17:
The implicitly-defined move constructor for a non-union class X performs a memberwise move of its subobjects. [Note: brace-or-equal-initializers of non-static data members are ignored. See also the example in 12.6.2 [class.base.init]. —end note] The order of moving is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2 [class.base.init]). Given a parameter named x, each base or non-static data member is moved in the manner appropriate to its type:
a named member m of reference or class type T is direct-initialized with the expression static_cast<T&&>(x.m);
a base class B is direct-initialized with the expression static_cast<B&&>(x);
an array is initialized by moving each element in the manner appropriate to the element type;
a scalar type is initialized with the built-in assignment operator.
Virtual base class subobjects shall be moved only once by the implicitly-defined move constructor (see 12.6.2 [class.base.init]).
Change 12.8 [class.copy] paragraph 18 as follows:
The implicitly-defined copy/move constructor for a union X copies the object representation (3.9 [basic.types]) of X.
Change 12.8 [class.copy] paragraph 28 as follows:
A copy/move assignment operator that is defaulted and not defined as deleted is implicitly defined when is assigned a value of its class type or a value of a class type derived from its class type it is used (3.2 [basic.def.odr]) (e.g., when it is selected by overload resolution to assign to an object of its class type) or when it is explicitly defaulted after its first declaration.
Change 12.8 [class.copy] paragraph 30 as follows:
The implicitly-defined copy/move assignment operator for a non-union class X performs memberwise copy/move assignment of its subobjects. The direct base classes of X are assigned first, in the order of their declaration in the base-specifier-list, and then the immediate non-static data members of X are assigned, in the order in which they were declared in the class definition. Let x be either the parameter of the function or, for the move assignment operator, an xvalue referring to the parameter. Each subobject is assigned in the manner appropriate to its type:
if the subobject is of class type, the copy assignment operator for the class is used as if by a call to operator= with the subobject as the object expression and the corresponding subobject of x as a single function argument (as if by explicit qualification; that is, ignoring any possible virtual overriding functions in more derived classes);
if the subobject is an array, each element is assigned, in the manner appropriate to the element type;
if the subobject is of scalar type, the built-in assignment operator is used.
It is unspecified whether subobjects representing virtual base classes are assigned more than once by the implicitly-defined copy assignment operator. [Example:
struct V { }; struct A : virtual V { }; struct B : virtual V { }; struct C : B, A { };It is unspecified whether the virtual base class subobject V is assigned twice by the implicitly-defined copy assignment operator for C. —end example] [Note: This does not apply to move assignment, as a defaulted move assignment operator is deleted if the class has virtual bases. —end note]
Delete 12.8 [class.copy] paragraph 31:
The implicitly-defined move assignment operator for a non-union class X performs memberwise assignment of its subobjects. The direct base classes of X are assigned first, in the order of their declaration in the base-specifier-list, and then the immediate non-static data members of X are assigned, in the order in which they were declared in the class definition. Given a parameter named x, each subobject is assigned in the manner appropriate to its type:
if the subobject is a named member c of class type C, as if by the expression this->c = static_cast<C&&>(x.c);
if the subobject is a direct base class B, as if by the expression this->B::operator=(static_cast<B&&>(x));
if the subobject is an array, each element is moved, in the manner appropriate to the element type;
if the subobject is of scalar type, the built-in assignment operator is used.
This resolution also resolves issues 1020, 1064 and 1066.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 63The new wording in 12.8 [class.copy] specifies the behavior of an implicitly-defined copy constructor for a non-union class (paragraph 16), an implicitly-defined move constructor for a non-union class (paragraph 17), and an implicitly-defined copy constructor for a union (paragraph 18), but not an implicitly-defined move constructor for a union.
Proposed resolution (August, 2010):
This issue is resolved by the resolution of issue 1051.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 64According to 12.8 [class.copy] paragraph 28,
A copy/move assignment operator that is defaulted and not defined as deleted is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type or when it is explicitly defaulted after its first declaration.
This sounds as if any assignment to a class object, regardless of whether it is a copy or a move assignment, defines both the copy and move operators. Presumably an assignment should only define the assignment operator chosen by overload resolution for the operation. (Compare the corresponding wording in paragraph 14 for the copy/move constructors: “...implicitly defined if it is used to initialize an object of its class type...”)
Proposed resolution (August, 2010):
This issue is resolved by the resolution of issue 1051.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment DE 11It is unclear whether copy elision is permitted when returning a parameter of class type. If not, it should still be possible to move, rather than copy, the return value.
Suggested resolution: Amend paragraph 34 to explicitly exclude function parameters from copy elision. Amend paragraph 35 to include function parameters as eligible for move-construction.
Proposed resolution (September, 2010):
Change 12.8 [class.copy] paragraph 34 bullets 1 and 2 as follows:
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value
in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1 [except.throw]) can be omitted by constructing the automatic object directly into the exception object
Change 12.8 [class.copy] paragraph 35 as follows:
When the criteria for elision of a copy operation are met, or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution...
[Voted into the WP at the November, 2010 meeting.]
According to 13 [over] paragraph 1,
Only function declarations can be overloaded; object and type declarations cannot be overloaded.
There are two problems with this statement. First, it does not allow for overloading function templates. (There may be other places in the Standard that refer to “functions” but should include function templates, as well.)
Second, the restriction on “object” declarations should presumably be on “variable” declarations instead, since one can also not overload reference declarations.
Proposed resolution (September, 2010):
Change 13 [over] paragraph 1 as follows:
...Only function and function template declarations can be overloaded; object variable and type declarations cannot be overloaded.
[Voted into the WP at the November, 2010 meeting.]
The resolution of issue 899 needs to be extended to cover move constructors and template constructors as well.
Proposed resolution (August, 2010):
When the type of the initializer expression is a class type “cv S”, the non-explicit conversion functions of S and its base classes are considered. When initializing a temporary to be bound to the first parameter of a copy constructor (12.8 [class.copy]) that takes a reference to possibly cv-qualified T as its first argument, called with a single argument in the context of direct-initialization, explicit conversion functions are also considered. Those that are not hidden within S and yield a type whose cv-unqualified version is the same type as T or is a derived class thereof are candidate functions. Conversion functions that return “reference to X” return lvalues or xvalues, depending on the type of reference, of type X and are therefore considered to yield X for this process of selecting candidate functions.
For example
struct C {
template <class T = int> C(C&, T = 0);
};
struct A {
explicit operator C&() const;
};
int main() {
A a;
C c (a); // should use template constructor
}
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 67To determine whether there is an implicit conversion sequence that converts the argument to the corresponding parameter, 13.3.2 [over.match.viable] paragraph 3 uses 13.3.3.1 [over.best.ics] instead of just saying “there is an ICS if-and-only-if a copy initialization would be well-formed.” Apparently this is intended, but to a casual reader or an implementor reading these rules for the first time for a new implementation, it's not clear why that's desirable. A note should be added to explain the rationale.
Proposed resolution (August, 2010):
Change 13.3.3.1.4 [over.ics.ref] paragraph 3 as follows:
Except for an implicit object parameter, for which see 13.3.1 [over.match.funcs], a standard conversion sequence cannot be formed if it requires binding an lvalue reference to non-const other than a reference to a non-volatile const type to an rvalue or binding an rvalue reference to an lvalue. [Note:...
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 68Overload resolution within the operand of a unary & operator is done by selecting the function “whose type matches the target type required in the context.” The criterion for determining whether the types match, however, is not defined. At least three possibilities suggest themselves:
The types are identical.
The source type can be implicitly converted to the target type.
The expression would be well-formed if the function under consideration were not overloaded.
This question arises for pointer-to-member types, where there is an implicit conversion from a pointer-to-base-member to a pointer-to-derived-member, as well as when the context is an explicit type conversion (which allows, for static_cast, a conversion from pointer-to-derived-member to a pointer-to-base-member and, in the reinterpret_cast interpretation of functional and old-style casts, essentially any conversion).
Notes from the August, 2010 meeting:
CWG observed that the only case in which the types might not match exactly was for pointers to member functions. In this case, the approach should be to ignore the class of which the functions are members and just match (exactly) on the function type.
Proposed resolution (September, 2010):
Change 13.4 [over.over] paragraph 1 as follows:
...The function selected is the one whose type matches is identical to the function type of the target type required in the context. [Note: That is, the class of which the function is a member is ignored when matching a pointer-to-member-function type. —end note] The target can be...
Non-member functions and static member functions match targets of type “pointer-to-function” or “reference- to-function.” Nonstatic member functions match targets of type “pointer-to-member-function;.” the function type of the pointer to member is used to select the member function from the set of overloaded member functions. If a non-static member function is selected, the reference to the overloaded function name is required to have the form of a pointer to member as described in 5.3.1 [expr.unary.op].
[Voted into the WP at the November, 2010 meeting.]
According to 14 [temp] paragraph 2,
In a function template declaration, the last component of the declarator-id shall be a template-name or operator-function-id (i.e., not a template-id).
This is too restrictive; it should also allow conversion-function-ids and literal-operator-ids.
Proposed resolution (September, 2010):
Change 14 [temp] paragraph 2 as follows:
A template-declaration can appear only as a namespace scope or class scope declaration. In a function template declaration, the last component of the declarator-id shall not be a template-id template-name or operator-function-id (i.e., not a template-id). [Note: in That last component may be an identifier, an operator-function-id, a conversion-function-id, or a literal-operator-id. In a class template declaration, if the class name is a simple-template-id, the declaration declares a class template partial specialization (14.5.5 [temp.class.spec]). —end note]
[Voted into the WP at the November, 2010 meeting.]
std::nullptr_t is not currently allowed by 14.1 [temp.param] paragraph 4 to be used as the type of a non-type template parameter. However, this could arise for a template with a non-type template parameter with a dependent type in a template intended for use with pointers, e.g.,
template<typename T, T t> void f();
...
f<std::nullptr_t, nullptr>();
or in a case of delegation.
Proposed resolution (September, 2010):
Change 14.1 [temp.param] paragraph 4 as follows:
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
integral or enumeration type,
pointer to object or pointer to function,
lvalue reference to object or lvalue reference to function,
pointer to member.,
std::nullptr_t.
[Voted into the WP at the November, 2010 meeting.]
The current wording of 14.3.2 [temp.arg.nontype] paragraph 1 does not prevent the use a reference as a non-type template argument. It simply requires
the address of an object or function with external linkage... expressed as & id-expression...
This would presumably (but unintentionally?) allow an example like the following:
struct S { };
template<S*> struct X { };
S s;
S& ref = s;
X<&ref> xr; // well-formed?
The expression &ref is not a constant expression, but the current wording of 14.3.2 [temp.arg.nontype] does not require a constant expression.
Proposed resolution (September, 2010):
Change 14.3.2 [temp.arg.nontype] paragraph 1 bullet 3 as follows:
a constant expression (5.19 [expr.const]) that designates the address of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 69The standard permits the address of a thread_local object as a non-type template argument. The addresses of these objects are not constant, however. Such template arguments should require objects of static storage duration.
Proposed resolution (August, 2010):
This issue is resolved by the resolution of issue 1155.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment DE 12Now that local classes can be used as template arguments, it seems odd that there are “external linkage” restrictions on non-type template parameters. The addresses of objects and functions with internal linkage should be permitted as well.
Proposed resolution (August, 2010):
Change 14.3.2 [temp.arg.nontype] paragraph 1 bullet 3 as follows:
the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates...
This resolution also resolves issue 1154.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 7014.8.2.4 [temp.deduct.partial] paragraph 3 specifies that the deduction used in partial ordering in a non-call context is based on the complete function type of the function templates. The wording in 14.5.6.2 [temp.func.order] paragraph 2 (and echoed in paragraph 4) reflects an earlier specification, however, saying that the deduction uses only “the function parameter types, or in the case of a conversion function the return type.” This is a contradiction. The wording in 14.5.6.2 [temp.func.order] should be changed.
Proposed resolution (September, 2010):
Change 14.5.6.2 [temp.func.order] paragraph 2 as follows:
Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function parameter types, or in the case of a conversion function the return type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.
Change 14.5.6.2 [temp.func.order] paragraph 4 as follows:
Using the transformed function template's function parameter list, or in the case of a conversion function its transformed return type, perform type deduction against the function parameter list (or return type) type of the other function template. The mechanism for performing these deductions is given in 14.8.2.4 [temp.deduct.partial].
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 73The current wording of 7.1.3 [dcl.typedef] paragraph 2 requires that the identifier in an alias-declaration “...shall not appear in the type-id.” With template aliases, however, the name of the alias can be used indirectly:
template<typename T> struct A;
template<typename T> using B=typename A<T>::U;
template<typename T> struct A {
typedef B<T> U;
};
B<short> b;
Here the instantiation of B<short> causes the instantiation of A<short>, and the typedef in A<short> ends up attempting to use B<short>, which is still in the process of being instantiated.
There should be an explicit prohibition of such uses.
Proposed resolution (August, 2010):
Add the following as a new paragraph at the end of 14.5.7 [temp.alias]:
[Note: this wording assumes the change from “template alias” to “alias template” requested by comment FI 11 on FCD N3092.]The type-id in an alias template declaration shall not refer to the alias template being declared. The type produced by an alias template specialization shall not directly or indirectly make use of that specialization. [Example:
template <class T> struct A; template <class T> using B = typename A<T>::U; template <class T> struct A { typedef B<T> U; }; B<short> b; // Error: instantiation of B<short> uses own type via A<short>::U.—end example]
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 74An alias-declaration allows a class or enumeration type to be defined in its type-id (7.1.6 [dcl.type] paragraph 3). However, it's not clear that this is desirable when the alias-declaration is part of a template alias:
template<typename T> using A =
struct { void f(T) { } };
Proposed resolution (August, 2010):
Change 7.1.6 [dcl.type] paragraph 3 as follows:
...A type-specifier-seq shall not define a class or enumeration unless it appears in the type-id of an alias-declaration (7.1.3 [dcl.typedef]) that is not the declaration of a template-declaration.
[Voted into the WP at the November, 2010 meeting.]
N3092 comment GB 44It is not clear from the current wording of 14.6 [temp.res] whether the following is well-formed or not:
template<typename T> struct id { typedef T type; };
template<typename T> void f() { int id<T>::type::*p = 0; }
struct A { };
int main() { f<A>(); }
If typename is required before the use of id<T>::type, it is not permitted by the current syntax.
Proposed resolution (August, 2010):
Change 14.6 [temp.res] paragraph 5 as follows:
A qualified name used as the name in a mem-initializer-id, a base-specifier, or an elaborated-type-specifier is implicitly assumed to name a type, without the use of the typename keyword. In a nested-name-specifier that immediately contains a nested-name-specifier that depends on a template parameter, the identifier or simple-template-id is implicitly assumed to name a type, without the use of the typename keyword. [Note: the typename keyword is not permitted by the syntax of these constructs. —end note]
[Voted into the WP at the November, 2010 meeting.]
N3092 comment GB 43The current rules for determining whether a name refers to the current instantiation, given in 14.6.2.1 [temp.dep.type] paragraph 1, do not cover the case when a template-id matching a primary template or partial specialization appears in the definition of a member of the template.
Proposed resolution (August, 2010):
Change 14.6.2.1 [temp.dep.type] paragraph 1 as follows:
In the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, a A name refers to the current instantiation if it is
in the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, the injected-class-name (9 [class]) of the class template or nested class,
in the definition of a primary class template or a member of a primary class template, the name of the class template followed by the template argument list of the primary template (as described below) enclosed in <>,
in the definition of a nested class of a class template, the name of the nested class referenced as a member of the current instantiation, or
in the definition of a partial specialization or a member of a partial specialization, the name of the class template followed by the template argument list of the partial specialization enclosed in <>. If the nth template parameter is a parameter pack, the nth template argument is a pack expansion (14.5.3 [temp.variadic]) whose pattern is the name of the parameter pack.
[Voted into the WP at the November, 2010 meeting.]
The Standard should, but does not currently, say that typeid is value-dependent if its expression or type is type-dependent.
Proposed resolution (September, 2010):
Change 14.6.2.3 [temp.dep.constexpr] paragraph 2 as follows:
...Expressions of the following form are value-dependent if the unary-expression or expression is type-dependent or the type-id is dependent:
sizeof unary-expression
sizeof ( type-id )
typeid ( expression )
typeid ( type-id )
alignof ( type-id )
noexcept ( expression )
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 77The following example is ambiguous:
template<typename T> int f(T&);
template<typename T> int f(T&&);
int i;
int j = f(i);
Because of the special deduction rule for lvalues passed to rvalue-reference parameters, deduction produces f(int&) for both templates, and they are indistinguishable.
Because f(T&) accepts a strict subset of the things that f(T&&) does, it should be considered more specialized by the partial ordering rules.
Proposed resolution (August, 2010):
Change 14.8.2.4 [temp.deduct.partial] paragraph 9 as follows:
If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):
If the type from the argument template was an lvalue reference and the type from the parameter template was not, the argument type is considered to be more specialized than the other; otherwise,
and if the type from the argument template is more cv-qualified than the type from the parameter template (as described above), the argument that type is considered to be more specialized than the other.; otherwise
If neither type is more cv-qualified than the other then neither type is more specialized than the other.
[Editing note: this change transforms the running text at the end of the paragraph into a bulleted list.]
[Voted into the WP at the November, 2010 meeting.]
N3092 comment US 78According to 15.2 [except.ctor] paragraph 2,
An object that is partially constructed or partially destroyed will have destructors executed for all of its fully constructed base classes and non-variant members, that is, for subobjects for which the principal constructor (12.6.2 [class.base.init]) has completed execution and the destructor has not yet begun execution.
This wording leaves unclear whether the remaining elements of an array will be destroyed if the destructor for one of the elements exits via an exception: an array element is a subobject (1.8 [intro.object] paragraph 2), but it is not a base class or non-variant member.
Proposed resolution (September, 2010):
Change 15.2 [except.ctor] paragraph 2 as follows:
An object that is partially constructed or partially destroyed of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed base classes and non-variant members subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (12.6.2 [class.base.init]) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor...
[Voted into the WP at the November, 2010 meeting.]
N3092 comment GB 47The list of reasons for which std::terminate is called needs to be extended to cover several additional cases in C++0x:
when function std::nested_exception::rethrow_nested is called for an object that stores a null exception pointer.
when execution of a function registered with std::at_quick_exit exits using an exception.
when the destructor or a copy constructor of class std::thread is called for the object that is joinable.
Proposed resolution (August, 2010):
Change 15.5.1 [except.terminate] paragraph 1 as follows:
In the following some situations exception handling must be abandoned for less subtle error handling techniques:. [Note: These situations are:
when the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught (15.1 [except.throw]), calls a function that exits via an uncaught exception, [Footnote: For example, if the object being thrown is of a class with a copy constructor, std::terminate() will be called if that copy constructor exits with an exception during a throw. —end footnote] or
when the exception handling mechanism cannot find a handler for a thrown exception (15.3 [except.handle]), or
when the search for a handler (15.3 [except.handle]) encounters the outermost block of a function with a noexcept-specification that does not allow the exception (15.4 [except.spec]), or
when the destruction of an object during stack unwinding (15.2) terminates by throwing an exception, or
when initialization of a non-local variable with static or thread storage duration (3.6.2 [basic.start.init], 3.6.3 [basic.start.term]) terminates by throwing exits via an exception, or
when destruction of an object with static or thread storage duration exits using via an exception (3.6.3 [basic.start.term]), or
when execution of a function registered with std::atexit or std::at_quick_exit exits using via an exception (18.5 [support.start.term]), or
when a throw-expression with no operand attempts to rethrow an exception and no exception is being handled (15.1 [except.throw]), or
when std::unexpected throws an exception which is not allowed by the previously violated dynamic-exception-specification, and std::bad_exception is not included in that dynamic-exception-specification (15.5.2 [except.unexpected]), or
when the implementation's default unexpected exception handler is called (D.13.1 [unexpected.handler])., or
when the function std::nested_exception::rethrow_nested is called for an object that has captured no exception (18.8.6 [except.nested]), or
when execution of the initial function of a thread exits via an exception (30.3.1.2 [thread.thread.constr]), or
when the destructor or the copy assignment operator is invoked on a std::thread object that refers to a joinable thread (30.3.1.3 [thread.thread.destr], 30.3.1.4 [thread.thread.assign]).
—end note]
Insert the following as a new paragraph following 15.1 [except.throw] paragraph 6:
An exception is considered caught...
If the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught, calls a function that exits via an exception, std::terminate is called (15.5.1 [except.terminate]). [Example:
struct C { C() { } C(const C&) { throw 0; } }; int main() { try { throw C(); // calls std::terminate() } catch(C) { } }—end example]
Change 15.2 [except.ctor] paragraph 3 as follows:
The process of calling destructors for automatic objects constructed on the path from a try block to a throw-expression is called “stack unwinding.” [Note: If a destructor called during stack unwinding exits with an exception, std::terminate is called (15.5.1 [except.terminate]). [Note: So destructors should generally catch exceptions and not let them propagate out of the destructor. —end note]
Change 3.6.2 [basic.start.init] paragraph 6 as follows:
[Note: If the initialization of a non-local variable with static or thread storage duration terminates by throwing exits via an exception, std::terminate is called (see 15.5.1 [except.terminate]). —end note]
Change 3.6.3 [basic.start.term] paragraph 1 as follows:
...[Note: If the destruction of a non-local an object with static or thread storage duration terminates by throwing exits via an exception, std::terminate is called (see 15.5.1 [except.terminate]). —end note]
Change 18.5 [support.start.term] paragraph 8 bullet 1 as follows:
First, objects with thread storage duration...
If control leaves a registered function called by exit because the function does not provide a handler for a thrown exception, terminate() shall be called (15.5.1 [except.terminate]).
[Voted into the WP at the November, 2010 meeting.]
The current wording of 15.5.1 [except.terminate] paragraph 2 makes it sound as if stack unwinding in the case of a noexcept violation is an all-or-nothing proposition. It would be useful to be able to partially unwind the stack, in particular, not to call destructors for the function containing the noexcept-specification.
Proposed resolution (August, 2010):
Change 15.5.1 [except.terminate] paragraph 2 as follows:
...In the situation where the search for a handler (15.3 [except.handle]) encounters the outermost block of a function with a noexcept-specification that does not allow the exception (15.4 [except.spec]), it is implementation-defined whether the stack is unwound, unwound partially, or not unwound at all before std::terminate() is called. In all other situations, the stack shall not be unwound...
[Voted into the WP at the November, 2010 meeting.]
N3092 comment DE 13The recommendations of document N2693 included a feature macro to enable a program to determine whether the implementation enforces strict pointer safety or not, but this macro is not specified in 16.8 [cpp.predefined].
Proposed resolution (August, 2010):
Add the following to the end of 16.8 [cpp.predefined] paragraph 2:
[Voted into WP at August, 21010 meeting.]
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?
Proposed resolution (March, 2010):
Change 3.1 [basic.def] paragraphs 1-2 as follows:
A declaration (Clause 7 [dcl.dcl]) may introduces one or more names into a translation unit or redeclares names introduced by previous declarations. A If so, the declaration specifies the interpretation and attributes of these names. A declaration may also have effects including
a static assertion (Clause 7 [dcl.dcl]),
controlling template instantiation (14.7.2 [temp.explicit],
use of attributes (Clause 7 [dcl.dcl], or
nothing (in the case of an empty-declaration).
A declaration is a definition unless it declares a function without specifying the function's body (8.4 [dcl.fct.def]), it contains the extern specifier (7.1.1 [dcl.stc]) or a linkage-specification25 (7.5 [dcl.link]) and neither an initializer nor a function-body, it declares a static data member in a class definition (9.4 [class.static]), it is a class name declaration (9.1 [class.name]), it is an opaque-enum-declaration (7.2 [dcl.enum]), or it is a typedef declaration (7.1.3 [dcl.typedef]), a using-declaration (7.3.3 [namespace.udecl]), a static_assert-declaration (Clause 7 [dcl.dcl]), an attribute-declaration (Clause 7 [dcl.dcl]), an empty-declaration (Clause 7 [dcl.dcl]), or a using-directive (7.3.4 [namespace.udir]). [Example:...
[Voted into WP at August, 21010 meeting.]
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.
Proposed resolution (March, 2010):
Add a new bullet following 3.2 [basic.def.odr] paragraph 5, second bullet:
...Given such an entity named D defined in more than one translation unit, then
each definition of D shall consist of the same sequence of tokens; and
in each definition of D, corresponding names, looked up according to 3.4 [basic.lookup], shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (13.3 [over.match]) and after matching of partial template specialization (14.8.3 [temp.over]), except that a name can refer to a const object with internal or no linkage if the object has the same literal type in all definitions of D, and the object is initialized with a constant expression (5.19 [expr.const]), and the value (but not the address) of the object is used, and the object has the same value in all definitions of D; and
in each definition of D, corresponding entities shall have the same language linkage; and
...
[Voted into WP at August, 2010 meeting.]
Is this case valid? G++ compiles it.
namespace X {
namespace Y {
struct X {
void f()
{
using namespace X::Y;
namespace Z = X::Y;
}
};
}
}
The relevant citation from the standard is 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.
[Voted into WP at August, 21010 meeting.]
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):
Change 8.3.4 [dcl.array] paragraph 1 as follows:
...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...
Change 8.3.4 [dcl.array] paragraph 3 as follows:
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.
Proposed resolution (March, 2010):
Change 8.3.4 [dcl.array] paragraph 1 as follows:
...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...
Change 8.3.4 [dcl.array] paragraphs 3-4 as follows:
When several “array of” specifications are adjacent, a multidimensional array is created; only the first of the constant expressions that specify the bounds of the arrays can may be omitted only for the first member of the sequence. [Note: this elision is useful for function parameters of array types, and when the array is external and the definition, which allocates storage, is given elsewhere. —end note] In addition to declarations in which an incomplete object type is allowed, an array bound may be omitted in some cases in the declaration of a function parameter (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 preceding declaration of the entity in the same scope in which the bound was specified, an omitted array bound is taken to be the same as in that earlier declaration, and similarly for the definition of a static data member of a class.
[Example:...
...can reasonably appear in an expression. Finally,
extern int x[10]; struct S { static int y[10]; }; int x[]; //OK: bound is 10 int S::y[]; //OK: bound is 10 void f() { extern int x[]; int i = sizeof(x); //error: incomplete object type }—end example]
[Voted into WP at August, 21010 meeting.]
The grammar for condition in 6.4 [stmt.select] paragraph 1 does not allow for the constexpr specifier. This was not intended by the original proposal.
Proposed resolution (March, 2010):
Change the definition of condition in 6.4 [stmt.select] paragraph 1 as follows:
Insert the following text as a new paragraph at the end of 6.4 [stmt.select]:
In the decl-specifier-seq of a condition, each decl-specifier shall be either a type-specifier or constexpr.
[Voted into WP at August, 21010 meeting.]
The intent is that the range-based for statement should be able to be used with a braced-init-list as the range over which to iterate. However, this does not work grammatically: a braced-init-list is not an expression, as required by the syntax in 6.5.4 [stmt.ranged] paragraph 1:
Even if this were resolved, the “equivalent to” code is not correct. It contains the declaration,
This has a similar problem, in that 7.1.6.4 [dcl.spec.auto] paragraph 3 requires that the initializer have one of the forms
which does not allow for a braced-initializer-list. In addition, although not allowed by the grammar, 7.1.6.4 [dcl.spec.auto] paragraph 6 treats the braced-init-list specially, in order for the type deduction to work correctly:
Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list (8.5.4 [dcl.init.list]), with std::initializer_list<U>.
The problem here is that a parenthesized initializer, as in the code expansion of the range-based for statement, is not a braced-init-list.
Proposed resolution (June, 2010):
Change 6.5 [stmt.iter] paragraph 1 as follows:
Iteration statements specify looping.
iteration-statement:
while ( condition ) statement
do statement while ( expression ) ;
for ( for-init-statement conditionopt ; expressionopt ) statement
for ( for-range-declaration : expression for-range-initializer ) statement
for-init-statement:
expression-statement
simple-declaration
for-range-declaration:
type-specifier-seq attribute-specifieropt declarator
for-range-initializer:
expression
braced-init-list
[Note: a for-init-statement ends with a semicolon. —end note]
Change 6.5.4 [stmt.ranged] paragraph 1 as follows:
The For a range-based for statement of the form
for ( for-range-declaration : expression ) statement
let range-init be equivalent to the expression surrounded by parentheses:
( expression )
[Footnote: this ensures that a top-level comma operator cannot be reinterpreted as a delimiter between init-declarators in the declaration of __range. —end footnote] and for a range-based for statement of the form
for ( for-range-declaration : braced-init-list ) statement
let range-init be equivalent to the braced-init-list. In each case, a range-based for statement is equivalent to
{ auto && __range = ( expression ) range-init; for ( auto __begin = begin-expr, ...
Note to editor:
The formatting in the preceding change for range-init follows that of the existing text for begin-expr and end-expr. However, CWG is concerned that this style makes all of these elements look too much like grammar nonterminals and asks that the editor consider some other formatting convention.
[Voted into WP at August, 21010 meeting.]
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.6 [temp.res] paragraph 8 for an implementation to diagnose a template definition from which no valid specialization can be instantiated?
Notes from the July, 2009 meeting:
The consensus of the CWG was that an “ignored” constexpr specifier in this case simply means that the specialization is not constexpr, not that it is not const. The CWG also decided not to address the question of non-dependent expressions that render a function template specialization non-constexpr, leaving it to quality of implementation whether a (warning) diagnostic is issued in such cases.
Proposed resolution (February, 2010):
Change 7.1.5 [dcl.constexpr] paragraph 5 as follows:
If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, the constexpr specifier is ignored that specialization is not a constexpr function or constexpr constructor. [Note: if the function is a member function it will still be const as described below. Implementations are encouraged to issue a warning if a function was rendered not constexpr by a non-dependent construct. —end note]
[Voted into WP at August, 21010 meeting.]
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.
Proposed resolution (February, 2010):
Change 7.1.5 [dcl.constexpr] paragraph 6 as follows:
A constexpr specifier for a non-static member function that is not a constructor declares that member function to be const (9.3.1 [class.mfct.non-static]). [Note: the constexpr specifier has no other effect on the function type. —end note] The keyword const is ignored if it appears in the cv-qualifier-seq of the function declarator of the declaration of such a member function. The class of which that function is a member shall be a literal type (3.9 [basic.types]). [Example:...
[Voted into WP at August, 21010 meeting.]
The rules for constexpr constructors are missing some necessary requirements. In particular, there is no requirement that a brace-or-equal-initializer for a non-static data member be a constant expression, and the requirement for constexpr constructors for initializing non-static data members applies only to members named in a mem-initializer, allowing a non-constexpr default constructor to be invoked.
Proposed resolution (February, 2010):
Change 7.1.5 [dcl.constexpr] paragraph 4 as follows:
The definition of a constexpr constructor shall satisfy the following constraints:
each of its parameter types shall be a literal type or a reference to a literal type
its function-body shall not be a function-try-block
the compound-statement of its function-body shall be empty
every non-static data member and base class sub-object shall be initialized (12.6.2 [class.base.init])
every constructor involved in initializing non-static data members and base class sub-objects invoked by a mem-initializer shall be a constexpr constructor.
every constructor argument and full-expression in a mem-initializer shall be a potential constant expression
every assignment-expression that is an initializer-clause appearing directly or indirectly within a brace-or-equal-initializer for a non-static data member that is not named by a mem-initializer-id shall be a constant expression
every implicit conversion used in converting a constructor argument to the corresponding parameter type and converting a full-expression to the corresponding member type shall be one of those allowed in a constant expression.
[Voted into WP at August, 2010 meeting.]
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:
12.4 [class.dtor] paragraph 6: “Bases and members are destroyed in the reverse order of the completion of their constructor.” If a given base or member is value-initialized without running its constructor, is it destroyed? (For that matter, paragraph 10 refers to “constructed” objects; is an object that is value-initialized without invoking a constructor “constructed?”)
15.2 [except.ctor] paragraph 2: “An object that is partially constructed or partially destroyed will have destructors executed for all of its fully constructed subobjects, that is, for subobjects for which the constructor has completed execution...”
3.8 [basic.life] paragraph 1: The lifetime of an object begins when “the constructor call has completed.” (In the TC1 wording — “if T is a class type with a non-trivial constructor (12.1 [class.ctor]), the constructor call has completed” — the lifetime of some value-initialized objects never began; in the current wording — “the constructor invoked to create the object is non-trivial” — the lifetime begins before any of the members are initialized.)
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.
Proposed resolution (February, 2010):
Change 8.5 [dcl.init] paragraph 7 as follows:
To value-initialize an object of type T means:
...
An object that is value-initialized is deemed to be constructed and thus subject to provisions of this International Standard applying to “constructed” objects, objects “for which the constructor has completed,” etc., even if no constructor is invoked for the object's initialization.
[Voted into WP at August, 21010 meeting.]
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.
Proposed resolution (February, 2010):
Change 8.5 [dcl.init] paragraph 2 as follows:
Automatic, register, thread_local, static, and namespace-scoped external variables can be initialized by Except for objects declared with the constexpr specifier, for which see 7.1.5 [dcl.constexpr], an initializer in the definition of a variable can consist of arbitrary expressions involving literals and previously declared variables and functions, regardless of the variable's storage duration. [Example:...
[Voted into WP at August, 21010 meeting.]
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.
Proposed resolution (February, 2010):
In the grammar in 9.2 [class.mem], add the indicated production to the definition of member-declaration:
[Voted into WP at August, 21010 meeting.]
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.)
Proposed resolution (February, 2010):
Change 9.6 [class.bit] paragraph 3 as follows:
...It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int or, long, or long long bit-field is signed or unsigned...
[Voted into WP at August, 21010 meeting.]
According to 12.1 [class.ctor] paragraph 1, only function-specifiers are permitted in the declaration of a constructor, and constexpr is not a function-specifier. (See also issue 263, in which the resolution of a similar concern regarding the friend specifier did not change 12.1 [class.ctor] paragraph 1 but perhaps should have done so.)
Proposed resolution (February, 2010):
Change 12.1 [class.ctor] paragraph 1 as follows:
Constructors do not have names. A special declarator syntax using an optional sequence of function-specifiers (7.1.2 [dcl.fct.spec]) followed by the constructor's class name followed by a parameter list is used to declare or define the constructor. The syntax uses
an optional decl-specifier-seq in which each decl-specifier is either a function-specifier or constexpr,
the constructor's class name, and
a parameter list
in that order. In such a declaration, optional parentheses around the constructor class name are ignored. [Example:...
[Voted into WP at August, 21010 meeting.]
The changes for delegating constructors overlooked the need to change 12.6.2 [class.base.init] paragraph 3:
The expression-list in a mem-initializer is used to initialize the base class or non-static data member subobject denoted by the mem-initializer-id. The semantics of a mem-initializer are as follows:
if the expression-list of the mem-initializer is omitted, the base class or member subobject is value-initialized (see 8.5 [dcl.init]);
otherwise, the subobject indicated by mem-initializer-id is direct-initialized using expression-list as the initializer (see 8.5 [dcl.init]).
The initialization of each base and member constitutes a full-expression. Any expression in a mem-initializer is evaluated as part of the full-expression that performs the initialization.
This paragraph deals only with subobjects; it needs to be made more general to apply to the complete object as well when the mem-initializer-id designates the constructor's class.
Proposed resolution (June, 2008):
Change 12.6.2 [class.base.init] paragraph 3 as follows:
The expression-list in a mem-initializer is used to initialize the base class or non-static data member subobject denoted by the mem-initializer-id. The semantics of a mem-initializer are A mem-initializer in which the mem-initializer-id names the constructor's class initializes the object by invoking the selected target constructor with the mem-initializer's expression-list. A mem-initializer in which the mem-initializer-id names a base class or non-static data member initializes the designated subobject as follows:
if the expression-list of the mem-initializer is omitted, the base class or member subobject is value-initialized (see 8.5 [dcl.init]);
otherwise, the subobject indicated by mem-initializer-id is direct-initialized using expression-list as the initializer (see 8.5 [dcl.init]).
...
The initialization of each base and member performed by each mem-initializer constitutes a full-expression. Any expression...
Notes from the September, 2008 meeting:
This text was significantly modified by N2756 (nonstatic data member initializers) and needs to be reworked in light of those changes.
Proposed resolution (February, 2010):
Change 12.6.2 [class.base.init] paragraph 7 as follows:
The expression-list or braced-init-list in a mem-initializer is used to initialize the base class or non-static data member subobject denoted by the mem-initializer-id designated subobject (or, in the case of a delegating constructor, the complete class object) according to the initialization rules of 8.5 [dcl.init] for direct-initialization.
[Example: ...
—end example] The initialization of each base and member performed by each mem-initializer constitutes a full-expression...
Change 12.6.2 [class.base.init] paragraph 8 as follows:
If In a non-delegating constructor, if a given non-static data member or base class is not named by a mem-initializer-id...
Change 12.6.2 [class.base.init] paragraph 10 as follows:
Initialization In a non-delegating constructor, initialization proceeds in the following order:
Change 12.6.2 [class.base.init] paragraph 12 as follows (this is an unrelated change correcting an error noticed while preparing the resolution of this issue):
Names in the expression-list or braced-init-list of a mem-initializer are evaluated in the scope of the constructor...
Change the next-to-last bullet of the note in 8.5.4 [dcl.init.list] paragraph 1 as follows:
as a base-or-member initializer in a mem-initializer (12.6.2 [class.base.init])
[Voted into WP at August, 21010 meeting.]
14.1 [temp.param] paragraph 11 currently says,
If a template-parameter of a class template is a template parameter pack, it shall be the last template-parameter. [Note: These are not requirements for function templates because template arguments might be deduced (14.8.2 [temp.deduct])...
This restriction was only meant to apply to primary class templates, not partial specializations.
Suggested resolution:
If a template-parameter of a primary class template is a template parameter pack, it shall be the last template-parameter. [Note: These are not requirements for function templates or class template partial specializations because template arguments might be deduced (14.8.2 [temp.deduct])...
Proposed resolution (February, 2010):
Change 14.1 [temp.param] paragraph 11 as follows:
If a template-parameter of a class template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. If a template-parameter of a primary class template is a template parameter pack, it shall be the last template-parameter. [Note: These are not requirements for function templates or class template partial specializations because template arguments might can be deduced (14.8.2 [temp.deduct]). [Example:...
[Voted into WP at August, 2010 meeting.]
The following is the wording from 14.2 [temp.names] paragraphs 4 and 5 that discusses the use of the "template" keyword following . or -> and in qualified names.
class X {
public:
template<std::size_t> X* alloc();
template<std::size_t> static X* adjust();
};
template<class T> void f(T* p) {
T* p1 = p->alloc<200>();
// ill-formed: < means less than
T* p2 = p->template alloc<200>();
// OK: < starts template argument list
T::adjust<100>();
// ill-formed: < means less than
T::template adjust<100>();
// OK: < starts explicit qualification
}
—end example]
If a name prefixed by the keyword template is not the name of a member template, the program is ill-formed. [Note: the keyword template may not be applied to non-template members of class templates. ]
The whole point of this feature is to say that the "template" keyword is needed to indicate that a "<" begins a template parameter list in certain contexts. The constraints in paragraph 5 leave open to debate certain cases.First, I think it should be made more clear that the template name must be followed by a template argument list when the "template" keyword is used in these contexts. If we don't make this clear, we would have to add several semantic clarifications instead. For example, if you say "p->template f()", and "f" is an overload set containing both templates and nontemplates: a) is this valid? b) are the nontemplates in the overload set ignored? If the user is forced to write "p->template f<>()" it is clear that this is valid, and it is equally clear that nontemplates in the overload set are ignored. As this feature was added purely to provide syntactic guidance, I think it is important that it otherwise have no semantic implications.
I propose that paragraph 5 be modified to:
(See also issue 30 and document J16/00-0008 = WG21 N1231.)
Notes from 04/00 meeting:
The discussion of this issue revived interest in issues 11 and 109.
Notes from the October 2003 meeting:
We reviewed John Spicer's paper N1528 and agreed with his recommendations therein.
Proposed resolution (February, 2010):
Change 14.2 [temp.names] paragraph 5 as follows:
If a A name prefixed by the keyword template is not the name of a template, shall be a template-id or the name shall refer to a class template the program is ill-formed. [Note: the keyword template may not be applied to non-template members of class templates. —end note] [Note: as is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the nested-name-specifier or the expression on the left of the -> or . is not dependent on a template-parameter, or the use does not appear in the scope of a template. —end note] [Example:
template <class T> struct A { void f(int); template <class U> void f(U); }; template <class T> void f(T t) { A<T> a; a.template f<>(t); // OK: calls template a.template f(t); // error: not a template-id } template <class T> struct B {template <class T2> struct C {}; }; // OK: T::template C names a class template: template <class T, template <class X> class TT = T::template C> struct D {}; D<B<int>> db;—end example]
[Voted into WP at August, 2010 meeting.]
Consider this example:
class Foo {
public:
template< typename T > T *get();
};
template< typename U >
U *testFoo( Foo &foo ) {
return foo.get< U >(); //#1
}
I am under the impression that this should compile without requiring the insertion of the template keyword before get in the expression at //#1. This notion is supported by this note excerpted from 14.2 [temp.names]/5:
[Note: just as is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the expression on the left of the -> or ., or the nested-name-specifier is not dependent on a template parameter.]
But 14.2 [temp.names]/4 contains this text:
When the name of a member template specialization appears after . or -> in a postfix-expression, or after nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.
The only way that I can read this to support my assumption above is if I assume that the phrase postfix-expression is used twice above with different meaning. That is I read the first use as referring to the full expression while the second use refers to the subexpression preceding the operator. Is this the correct determination of intent? I find this text confusing. Would it be an improvement if the second occurrence of "postfix-expression" should be replaced by "the subexpression preceding the operator". Of course that begs the question "where is subexpression actually defined in the standard?"
John Spicer: I agree that the code should work, and that we should tweak the wording.
Proposed resolution (March, 2010):
Change 14.2 [temp.names] paragraph 4 as follows:
When the name of a member template specialization appears after . or -> in a postfix-expression, or after a nested-name-specifier in a qualified-id, and the object or pointer expression of the postfix-expression or the nested-name-specifier in the qualified-id explicitly depends on a template-parameter template parameter (14.6.2 [temp.dep]) but does not refer to a member of the current instantiation (14.6.2.1 [temp.dep.type]), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template. [Example:...
[Voted into WP at August, 21010 meeting.]
According to 14.3.2 [temp.arg.nontype] paragraph 1, bullet 3, one of the acceptable forms of a non-type, non-template template argument is:
the address of an object or function... expressed as & id-expression where the & is optional if the name refers to a function or array, or if the corresponding template-parameter is a reference
It is not clear from this whether a template argument like (&i) satisfies the requirement or not.
Notes from the March, 2009 meeting:
The consensus of the CWG was that the parentheses should be allowed.
Proposed resolution (February, 2010):
Change 14.3.2 [temp.arg.nontype] paragraph 1 bullet 3 as follows:
the address of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that where the & is optional may be omitted if the name refers to a function or array, or and shall be omitted if the corresponding template-parameter is a reference; or
[Drafting note: The change requiring the omission of the & in the reference case fixes an existing problem that is not related to this issue.]
[Voted into WP at August, 21010 meeting.]
14.5.4 [temp.friend] paragraph 1 bullet 3 says:
if the name of the friend is a qualified-id and a matching specialization of a function template is found in the specified class or namespace, the friend declaration refers to that function template specialization, otherwise,
I'm not sure this says what it's supposed to say. For example:
namespace N {
template<class T> int f(T);
}
class A {
friend int N::f(int);
int m;
A();
};
namespace N {
template< class T > int f(T) {
A a; // ok for T=int?
return a.m; // ok for T=int?
}
}
int m = N::f(42); // ok?
char c = N::f('a'); // Clearly ill-formed.
The key is that the wording talks about a “matching specialization,” which to me means that N::f<int> is befriended only if that specialization existed in N before the friend declaration. So it's ill-formed as written, but if we move the call to N::f<int> up to a point before the definition of A, it's well-formed.
That seems surprising, especially given that the first bullet does not require a pre-existing specialization. So I suggest replacing bullet 3 with something like:
if the name of the friend is a qualified-id and a matching function template is found in the specified class or namespace, the friend declaration refers to the deduced specialization of that function template, otherwise,
Proposed resolution (June, 2010):
Change 14.5.4 [temp.friend] paragraph 1 bullet 3 as follows:
...For a friend function declaration that is not a template declaration:
...
if the name of the friend is a qualified-id and a matching specialization of a function template is found in the specified class or namespace, the friend declaration refers to that function template specialization the deduced specialization of that function template (14.8.2.6 [temp.deduct.decl]), otherwise,
...
(This resolution depends on that of issue 873; in particular, the cross-reference to 14.8.2.6 [temp.deduct.decl] refers to a new section added by the resolution of that issue.)
[Voted into WP at August, 2010 meeting.]
The Standard does not specify how member and nonmember function templates are to be ordered. This question arises with an example like the following:
struct A {
template<class T> void operator<<(T&);
};
template<class T> struct B { };
template<class T> void operator<<(A&, B<T>&);
int main() {
A a;
B<A> b;
a << b;
}
The two candidates for “a << b” are:
How should we treat the implicit this parameter of #1 and the explicit first parameter of #2?
Option 0: Make them unordered.
Option 1: If either function is a non-static member function, ignore any this parameter and ignore the first parameter of any non-member function. This option will select #2, as “B<T>&” is more specialized than “T&”.
Option 2: Treat the this parameter as if it were of reference to object type, and then perform comparison to the first parameter of the other function. The other function's first parameter will either be another this parameter, or it will be a by-value or by-reference object parameter. In the example above, this option will also select #2.
The difference between option 1 and option 2 can be seen in the following example:
struct A { };
template<class T> struct B {
template<typename R> int operator*(R&); // #1
};
template <typename T> int operator*(T&, A&); // #2
int main() {
A a;
B<A> b;
b * a;
}
Should this select #1, select #2, or be ambiguous? Option 1 will select #2, because “A&” is more specialized than “T&”. Option 2 will make this example ambiguous, because “B<A>&” is more specialized than “T&”.
If one were considering two non-member templates,
template <typename T> int operator*(T&, A&); // #2
template <typename T, typename R> int operator*(B<A>&, R&); // #3
the current rules would make these unordered. Option 2 thus seems more consistent with this existing behavior.
Notes from the April, 2006 meeting:
The group favored option 2.
Proposed resolution (February, 2010):
Change 14.5.6.2 [temp.func.order] paragraph 3 as follows:
...and substitute it for each occurrence of that parameter in the function type of the template. If only one of the function templates is a non-static member, that function template is considered to have a new first parameter inserted in its function parameter list. The new parameter is of type “reference to cv A,” where cv are the cv-qualifiers of the function template (if any) and A is the class of which the function template is a member. [Note: This allows a non-static member to be ordered with respect to a nonmember function and for the results to be equivalent to the ordering of two equivalent nonmembers. —end note] [Example:
struct A { }; template<class T> struct B { template<typename R> int operator*(R&); // #1 }; template<typename T, typename R> int operator*(T&, R&); // #2 // The declaration of B::operator* is transformed into the equivalent of // template<typename R> int operator*(B<A>&, R&); // #1a int main() { A a; B<A> b; b * a; // calls #1a }—end example]
[Voted into WP at August, 2010 meeting.]
Is this program valid?
template <typename T> int g(int);
class h{};
template <typename T> int l(){h j; return g<T>(j);}
template <typename T> int g(const h&);
class j{};
int jj(){return l<j>();}
The key issue is when "g" is looked up, i.e., whether both overloaded template "g" functions are available at the call site or only the first. Clearly, the entire postfix-expression "g<T>(j)" is dependent, but when is the set of available template functions determined?
For consistency with the rules about when the set of available overloads is determined when calling a function given by an unqualified-id, I would think that we should postpone determining the set of template functions if (and only if) any of the explicit template arguments are dependent.
John Spicer: I agree that there should be a core issue for this. The definition of "dependent name" (14.6.2 [temp.dep] paragraph 1) should probably be modified to cover this case. It currently only handles cases where the function name is a simple identifier.
Notes from the March 2004 meeting:
A related issue is a call with a qualified name and dependent arguments, e.g., x::y(depa, depb).
Proposed resolution (June, 2010):
Change 14.6.2 [temp.dep] paragraph 1 as follows:
...In an expression of the form:
postfix-expression ( expression-listopt )
where the postfix-expression is an unqualified-id id-expression, the unqualified-id id-expression denotes a dependent name if and only if any of the expressions in the expression-list is a type-dependent expression (14.6.2.2 [temp.dep.expr]) or if the unqualified-id of the id-expression is a template-id in which any of the template arguments depends on a template parameter. If an operand of an operator is a type-dependent expression, the operator also denotes a dependent name. Such names are unbound and are looked up at the point of the template instantiation (14.6.4.1 [temp.point]) in both the context of the template definition and the context of the point of instantiation.
Change 14.6.4.2 [temp.dep.candidate] paragraph 1 as follows:
For a function call that depends on a template parameter, if the function name is an unqualified-id or if the function is called using operator notation, the candidate functions are found using the usual lookup rules (3.4.1 [basic.lookup.unqual], 3.4.2 [basic.lookup.argdep], 3.4.3 [basic.lookup.qual]) except that:
For the part of the lookup using unqualified name lookup (3.4.1 [basic.lookup.unqual]) or qualified name lookup (3.4.3 [basic.lookup.qual]), only function declarations from the template definition context are found.
For the part of the lookup using associated namespaces (3.4.2 [basic.lookup.argdep]), only function declarations found in either the template definition context or the template instantiation context are found.
If the function name is an unqualified-id and the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior.
[Voted into WP at August, 2010 meeting.]
The list of cases in 14.6.1 [temp.local] about when a template parameter is hidden seems to be incomplete.
Consider
// example-1
struct S {
int C;
template<class> void f();
};
template<class C>
void S::f()
{
C c; // #1
}
Someone asked whether line #1 is well-formed and I responded "no" based on my understanding of the rules in 14.6.1. After a second looking, I've realized that the above case is currently missing from the list.
The list in 14.6.1 covers cases like
// example-2
template<class T>
struct S {
int C;
void f();
};
template<class C>
void S<C>::f()
{
C c; // ERROR: 'C' is 'S::C' not the template parameter
}
or
// example-3
struct A { int C; }
template<class C>
struct S : A {
C c; // ERROR: 'C' is 'A::C', not the template parameter
};
But the case of a 'member template' is missing. I believe it should
follow the same rule as above. The reason is this.
In the case listed in 14.6.1 (having to do with members of classes), the "algorithm" seems to be this:
I believe that any rule, coherent with 14.6.1/5 and 14.6.1/7, for covering the cases of member templates (example-1) will be described by the above "algorithm".
Am I missing something?
[1] of course, the standard text does not formally speak of "template parameter scope", but we all know that the template parameters "live" somewhere. I'm using that terminology to designate the declarative region of the template parameters.
Mike Miller: I have a somewhat different perspective on this question. I think your example-1 is fundamentally different from your example-2 and example-3. Looking, for instance, at your example-2, I see four nested scopes:
namespace scope
template scope (where the parameter is)
class S scope
S::f() block scope
Naturally, S::C hides the template parameter C. The same is true of your example-3, with three scopes:
namespace scope
template scope
class S scope (includes 10.2 base class lookup)
Again, it's clear that the C inherited from A hides the template parameter in the containing scope.
The scopes I see in your example-1, however, are different:
namespace scope
struct S scope
template scope (where the parameter is)
S::f() block scope
Here it seems clear to me that the template parameter hides the class member.
It might help to look at the case where the function template is defined inline in the class:
struct S {
int C;
template<class C> int f() {
C c; // #1
}
};
It would be pretty strange, I think, if the #1 C were the member and not the template parameter. It would also be odd if the name lookup were different between an inline definition and an out-of-line definition.
See also issue 459.
Notes from the March 2004 meeting:
Basically, the standard is okay. We think Gaby's desired cases like #1 should be ill-formed.
There is a wording problem in 14.6.1 [temp.local] paragraph 7. It says:
In the definition of a member of a class template that appears outside of the class template definition, the name of a member of this template hides the name of a template-parameter.
It should say "hides the name of a template-parameter of the class template (but not a template-parameter of the member, if the member is itself a template)" or words to that effect.
Proposed resolution (February, 2010):
Change 14.6.1 [temp.local] paragraph 8 as follows:
In the definition of a member of a class template that appears outside of the class template definition, the name of a member of this the class template hides the name of a template-parameter of any enclosing class templates (but not a template-parameter of the member, if the member is a class or function template). [Example:
template<class T> struct A { struct B { /* ... */ }; typedef void C; void f(); template<class U> void g(U); }; template<class B> void A<B>::f() { B b; // A's B, not the template parameter } template<class B> template<class C> void A<B>::g(C) { B b; // A's B, not the template parameter C c; // the template parameter C, not A's C }
[Voted into WP at August, 2010 meeting.]
The Standard is currently silent on the dependency status of enumerations and enumerators that are members of class templates. There are three questions that must be answered in this regard:
Are enumeration members of class templates dependent types?
It seems clear that nested enumerations must be dependent. For example:
void f(int);
template<typename T> struct S {
enum E { e0 };
void g() {
f(e0);
}
};
void f(S<int>::E);
void x() {
S<int> si;
si->g(); // Should call f(S<int>::E)
}
Is sizeof applied to a nested enumeration a value-dependent expression (14.6.2.3 [temp.dep.constexpr])?
There are three distinct cases that might have different answers to this question:
template<typename T> struct S {
enum E { e0 };
};
Here, the size of E is, in principle, known at the time the template is defined.
template<short I> struct S {
enum E { e0 = I };
};
In this case, the minimum size required for E cannot be determined until instantiation, but it is clear that the underlying type need be no larger than short.
template<typename T> struct S {
enum E { e0 = T::e0; };
}
Here, nothing can be known about the size of E at the time the template is defined.
14.6.2.3 [temp.dep.constexpr] paragraph 2 says that a sizeof expression is value-dependent if the type of the operand is type-dependent. Unless enumerations are given special treatment, all three of these examples will have value-dependent sizes. This could be surprising for the first case, at least, if not the second as well.
Are nested enumerators value-dependent expressions?
Again the question of dependent initializers comes into play. As an example, consider:
template<short I> struct S {
enum E { e0, e1 = I, e2 };
};
There seem to be three possible approaches as to whether the enumerators of E are value-dependent:
The enumerators of a nested enumeration are all value-dependent, regardless of whether they have a value-dependent initializer or not. This is the current position of 14.6.2.3 [temp.dep.constexpr] paragraph 2, which says that an identifier is value-dependent if it is a name declared with a dependent type.
The enumerators of a nested enumeration are all value-dependent if any of the enumeration's enumerators has a value-dependent initializer. In this approach, e0 would be value-dependent, even though it is clear that it has the value 0.
An enumerator of a nested enumeration is value-dependent only if it has a value-dependent initializer (explict or implicit). This approach would make e1 and e2 value-dependent, but not e0.
An example that bears on the third approach is the following:
template<typename T> struct S {
enum E { N = UINT_MAX, O = T::O };
int a[N + 2];
};
With the normal treatment of enumerations, the type of a might be either int[UINT_MAX+2] or int[1], depending on whether the value of T::O was such that the underlying type of E is unsigned int or long.
One possibility for addressing this problem under the third
approach would be to treat a given enumerator as having the type of
its initializer in such cases, rather than the enumeration type. This
would be similar to the way enumerators are treated within the
enumerator list, before the enumeration declaration is complete
(
Notes from the April, 2005 meeting:
The CWG agreed on the following positions:
Nested enumerations are dependent types.
The result of the sizeof operator applied to a nested enumeration is value-dependent unless there are no dependent initializers in its definition; the first case above is not dependent, while the second and third are dependent.
The approach described in 3.C above is correct. This is similar to the treatment of static const integral data members, which are dependent only if their initializer is dependent.
Notes from the October, 2005 meeting:
There was no consensus among the CWG regarding question #3 (which enumerators should be considered value-dependent). The argument in favor of 3.C is principally that the values of enumerators with non-dependent initializers are known at definition time, so there is no need to treat them as dependent.
One objection to 3.C is that, according to the consensus of the CWG, the enumeration type is dependent and thus even the known values of the enumeration would have a dependent type, which could affect the results when such enumerations are used in expressions. A possible response to this concern would be to treat non-dependent initializers as having the type of the initializer rather than the enumeration type, similar to the treatment of enumerators within the enumerator-list (7.2 [dcl.enum] paragraph 5). However, this approach would be inconsistent with the treatment of other enumeration types. It would also interfere with overload resolution (e.g., the call in the example under question #1 above would resolve to f(int) with this approach rather than f(S<int>::E)).
Those in favor of option 3.A also suggested that it would be simpler and require less drafting: if all the enumerators have the (dependent) type of the enumeration, 14.6.2.3 [temp.dep.constexpr] paragraph 2 already says that a name with a dependent type is value-dependent, so nothing further would need to be said. Option 3.C would require additional caveats to exempt some enumerators.
The proponents of 3.A also pointed out that there are many other cases where a known value with a dependent type is treated as dependent:
static const T t = 0;
... A<t> ...
or
template <int I> void f() {
g(I-I);
}
With regard to current practice, g++ and MSVC++ implement 3.A, while EDG implements 3.C.
Notes from the July, 2009 meeting:
The consensus of the CWG was that all the types and values are dependent.
Proposed resolution (June, 2010):
Change 14.6.2.1 [temp.dep.type] paragraph 6 as follows:
A type is dependent if it is
...
a nested class or enumeration that is a member of the current instantiation,
...
[Voted into WP at August, 2010 meeting.]
Issue 470 specified the explicit instantiation of members of explicitly-instantiated class templates. In restricting the affected members to those “whose definition is visible at the point of instantiation,” however, this resolution introduced an incompatibility between explicitly instantiating a member function or static data member and explicitly instantiating the class template of which it is a member (14.7.2 [temp.explicit] paragraph 3 requires only that the class template definition, not that of the member function or static data member, be visible at the point of the explicit instantiation). It would be better to treat the member instantiations the same, regardless of whether they are directly or indirectly explicitly instantiated.
Notes from the April, 2006 meeting:
In forwarding document J16/06-0057 = WG21 N1987 to be approved by the full Committee, the CWG reaffirmed its position that explicitly instantiating a class template only explicitly instantiates those of its members that have been defined before the point of the explicit instantiation. The effect of the position advocated above would be to require all non-exported member functions to be defined in the translation unit in which the class template is explicitly instantiated (cf paragraph 4), and we did not want to require that. We did agree that the “visible” terminology should be replaced by wording along the lines of “has been defined.”
Proposed resolution (February, 2010):
Change 14.7.2 [temp.explicit] paragraph 8 as follows:
An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is only an explicit instantiation definition of members whose definition is visible that have been defined at the point of instantiation.
[Voted into WP at August, 2010 meeting.]
Given
template <class T> static T f(T t) { ... }
template <> int f(int t) { ... }
what is the linkage of f(int)?
Section 14 [temp] paragraph 4 says,
Entities generated from a template with internal linkage are distinct from all entities generated in other translation units.
But is the explicit specialization “generated from” the primary template? Does it inherit the local linkage? If so, where do I find a reference saying so explicitly?
James Widman: Data points: EDG 3.8 inherits, GCC 4.0 does not.
Mike Miller: There's a pretty strong presumption that the linkage of an explicit specialization cannot be different from that of its primary template, given that storage class specifiers cannot appear in an explicit specialization (7.1.1 [dcl.stc] paragraph 1).
Notes from the April, 2007 meeting:
The CWG agreed that the linkage of an explicit specialization must be that of the template. Gabriel dos Reis will investigate the reason for the different behavior of g++.
Proposed resolution (March, 2010):
Change 14 [temp] paragraph 4 as follows:
...Entities generated from Specializations (explicit or implicit) of a template with that has internal linkage are distinct from all entities generated specializations in other translation units...
[Voted into WP at August, 21010 meeting.]
It does not appear that the following example is well-formed, although most compilers accept it:
template <typename T> T foo();
template <> int foo();
The reason is that 14.7.3 [temp.expl.spec] paragraph 11 only allows trailing template-arguments to be omitted if they “can be deduced from the function argument type,” and there are no function arguments in this example.
14.7.3 [temp.expl.spec] should probably say “function type” instead of “function argument type.” Also, a subsection should probably be added to 14.8.2 [temp.deduct] to cover “Deducing template arguments from declarative contexts” or some such. It would be essentially the same as 14.8.2.2 [temp.deduct.funcaddr] except that the function type from the declaration would be used as the type of P.
Proposed resolution (March, 2008):
Insert the following as a new subsection after 14.8.2.5 [temp.deduct.type]:
14.9.2.6 Deducing template arguments in a declaration that names a specialization of a function template [temp.deduct.funcdecl] Template arguments can be deduced from the function type specified when declaring a specialization of a function template. [Note: this can occur in the context of an explicit specialization, an explicit instantiation, or a friend declaration. —end note] The function template's function type and the declared type are used as the types of P and A, and the deduction is done as described in 14.8.2.5 [temp.deduct.type].
Change 14.7.3 [temp.expl.spec] paragraph 11 as follows:
A trailing template-argument can be left unspecified in the template-id naming an explicit function template specialization provided it can be deduced from the function argument type (14.9.2.6 [temp.deduct.funcdecl])...
Notes from the September, 2008 meeting:
The proposed resolution is probably more than is needed. Instead of a complete new section, the material could become a paragraph in 14.5.6 [temp.fct].
Proposed resolution (February, 2010):
Add the following paragraph at the end of 14.5.6 [temp.fct]:
In a declaration that names a specialization of a function template, template arguments can be deduced from the function type. [Note: this can occur in the context of an explicit specialization, an explicit instantiation, or a friend declaration. —end note] The function template's function type and the declared type are used as the types of P and A and the deduction is done as described in 14.8.2.5 [temp.deduct.type].
Proposed resolution (March, 2010):
This issue is resolved by the resolution of issue 873.
[Voted into WP at August, 21010 meeting.]
According to 14.7.3 [temp.expl.spec] paragraph 1, only non-deleted function templates may be explicitly specialized. There doesn't appear to be a compelling need for this restriction, however, and it could be useful to forbid use of implicitly-instantiated specializations while still allowing use of explicitly-specialized versions.
Proposed resolution (February, 2010):
Change 14.7.3 [temp.expl.spec] paragraph 1 as follows:
An explicit specialization of any of the following:
non-deleted function template
class template
non-deleted member function of a class template
static data member of a class template
member class of a class template
member class template of a class or class template
non-deleted member function template of a class or class template
can be declared...
[Voted into WP at August, 2010 meeting.]
The last two sentences of 14.8.2 [temp.deduct] paragraph 5 read:
When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in non-deduced contexts are replaced with the corresponding deduced or default argument values. If the substitution results in an invalid type, as described above, type deduction fails.
Shouldn't the substitution occur for all uses of the parameters, so that any of them could result in deduction failure?
Proposed resolution (October, 2006):
Change 14.8.2 [temp.deduct] paragraph 5 as follows:
...When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in non-deduced contexts the function type are replaced with the corresponding deduced or default argument values. If the substitution results in an invalid type, as described above, type deduction fails.
Notes from the September, 2008 meeting:
This issue was returned to "drafting" status in order to coordinate the wording with the concepts proposal.
Proposed resolution (March, 2010):
Change 14.8.2 [temp.deduct] paragraph 5 as follows:
When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in non-deduced contexts the template parameter list of the template and the function type are replaced with the corresponding deduced or default argument values. If the substitution results in an invalid type, as described above, type deduction fails.
[Voted into WP at August, 21010 meeting.]
The current rules in 14.8.2 [temp.deduct] say that type deduction fails as a result of attempting to use a type that is not a class type in a qualified name. However, it is now possible to use enumeration names as nested-name-specifiers, so this rule needs to be updated accordingly.
Proposed resolution (February, 2010):
Change the third bullet of the note in 14.8.2 [temp.deduct] paragraph 8 as follows:
[Note: Type deduction may fail for the following reasons:
...
Attempting to use a type that is not a class or enumeration type in a qualified name. [Example:...
...
[Voted into WP at August, 21010 meeting.]
14.8.2.1 [temp.deduct.call] paragraph 3 gives the deduction of rvalue references special treatment in the context of a function call:
If P is of the form T&&, where T is a template parameter, and the argument is an lvalue, the type A& is used in place of A for type deduction.
A similar provision is needed, but is not present, in declarative contexts. For example:
template<typename T> void f(T&&);
template<> void f(int&) { } // #1
template<> void f(int&&) { } // #2
void g(int i) {
f(i); // calls f<int&>(int&), i.e., #1
f(0); // calls f<int>(int&&), i.e., #2
}
There need to be rules that deduce the template arguments for the specializations in the same way that the arguments are deduced in the calls.
Proposed resolution (February, 2010):
Change 14.8.2.5 [temp.deduct.type] paragraph 10 as follows:
Similarly, if P has a form that contains (T), then each parameter type Pi of the respective parameter-type-list of P is compared with the corresponding parameter type Ai of the corresponding parameter-type-list of A. If P and A are function types that originated from deduction when taking the address of a function template (14.8.2.2 [temp.deduct.funcaddr]) or when deducing template arguments from a function declaration ([temp.deduct.decl]) and Pi and Ai are parameters of the top-level parameter-type-list of P and A, respectively, Pi is adjusted if it is an rvalue reference to a cv-unqualified template parameter and Ai is an lvalue reference, in which case the type of Pi is changed to be the template parameter type (i.e., T&& is changed to simply T). [Note: As a result, when Pi is T&& and Ai is X&, the adjusted Pi will be T, causing T to be deduced as X&. —end note] [Example:
template<typename T> void f(T&&); template<> void f(int&) { } // #1 template<> void f(int&&) { } // #2 void g(int i) { f(i); // calls f<int&>(int&), i.e., #1 f(0); // calls f<int>(int&&), i.e., #2 }—end example]
If the parameter-declaration corresponding to Pi is a function parameter pack...
Add a new section under 14.8.2 [temp.deduct], either before or after 14.8.2.5 [temp.deduct.type], as follows:
14.8.2.x Deducing template arguments from a function declaration [temp.deduct.decl]
In a declaration whose declarator-id refers to a specialization of a function template, template argument deduction is performed to identify the specialization to which the declaration refers. Specifically, this is done for explicit instantiations (14.7.2 [temp.explicit]), explicit specializations (14.7.3 [temp.expl.spec]), and certain friend declarations (14.5.4 [temp.friend]). This is also done to determine whether a function template specialization matches a placement operator new (3.7.4.2 [basic.stc.dynamic.deallocation], 5.3.4 [expr.new]). In all these cases, P is the type of the function template being considered as a potential match and A is the function type from the declaration. The deduction is done as described in 14.8.2.5 [temp.deduct.type].
If, for the set of function templates so considered, there is either no match or more than one match after partial ordering has been considered (14.5.6.2 [temp.func.order]), deduction fails and the declaration is ill-formed.
(Note that the resolution of issue 674 depends on this resolution.)
[Voted into WP at August, 2010 meeting.]
See also issue 37.
Given this piece of code and S having a user-defined ctor, at precisely which point must std::uncaught_exception() return true and where false?
try { S s0; throw s0; } catch (S s2) { }
My understanding of the semantics of the code is as follows:
Is my understanding correct?
15.1 [except.throw] paragraph 3 talks about “the exception object” when describing the semantics of the throw-expression:
a throw-expression initializes a temporary object, called the exception object...
However, 15.5.1 [except.terminate] paragraph 1 talks about “the expression to be thrown” when enumerating the conditions under which terminate() is called:
when the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught (15.1 [except.throw]), calls a user function that exits via an uncaught exception...
And, 15.5.3 [except.uncaught] paragraph 1 refers to “the object to be thrown” in the description of uncaught_exception():
The function std::uncaught_exception() returns true after completing evaluation of the object to be thrown...
Are all these objects one and the same? I believe the answer is important in case the construction of the temporary exception object throws another exception.
Suppose they are the same. Then uncaught_exception() invoked from the copy ctor for s1 (from the example [above]) must return false and a new exception (e.g., bad_alloc) may be thrown and caught by a matching handler (i.e., without calling terminate()).
But if they are not the same, then uncaught_exception() invoked from the copy ctor for s1 must return true and throwing another exception would end up calling terminate(). This would, IMO, have pretty severe consequences on writing exception safe exception classes.
As in the first case, different compilers behave differently, with most compilers not calling terminate() when the ctor for the temporary exception object throws. Unfortunately, the two compilers that I trust the most do call terminate().
FWIW, my feeling is that it should be possible for the copy ctor invoked to initialize the temporary exception object to safely exit by throwing another exception, and that the new exception should be allowed to be caught without calling terminate.
Mike Miller: The way I see this, a throw-expression has an assignment-expression as an operand. This expression is “the expression to be thrown.” Evaluation of this expression yields an object; this object is “the object to be thrown.” This object is then copied to the exception object.
Martin Sebor: Here's a survey of the return value from uncaught_exception() in the various stages of exception handling, implemented by current compilers:
| expr | temp | unwind | handlr | 2nd ex | |
|---|---|---|---|---|---|
| HP aCC 6 | 0 | 0 | 1 | 0 | OK |
| Compaq C++ 6.5 | 0 | 0 | 1 | 1 | ABRT |
| EDG eccp 3.4 | 0 | 1 | 1 | 1 | ABRT |
| g++ 3.4.2 | 0 | 0 | 1 | 0 | OK |
| Intel C++ 7.0 | 0 | 0 | 1 | 0 | OK |
| MIPSpro 7.4.1 | 0 | 0 | 1 | 1 | ABRT |
| MSVC 7.0 | 0 | 0 | 1 | 0 | OK |
| SunPro 5.5 | 1 | 1 | 1 | 0 | OK |
| VisualAge 6.0 | 0 | 1 | 1 | 1 | OK |
In the table above:
| expr | is the evaluation of the assignment-expression in the throw-expression |
| temp | is the invocation of the copy ctor for the unnamed temporary exception object created by the runtime. |
| unwind | is stack unwinding. |
| handlr | is the invocation of the copy ctor in the exception-declaration in the catch handler. |
| 2nd ex | describes the behavior of the implementation when the invocation of the copy ctor for the unnamed temporary exception object [temp] throws another exception. |
Proposed resolution (October, 2004):
Change 15.1 [except.throw] paragraph 3 as follows:
A throw-expression initializes a temporary object, called the exception object, the by copying the thrown object (i.e., the result of evaluating its assignment-expression operand) to it. The type of which the exception object is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T,” respectively. [Note: the temporary object created for by a throw-expression that whose operand is a string literal is never of type char* or wchar_t*; that is, the special conversions for string literals from the types “array of const char” and “array of const wchar_t” to the types “pointer to char” and “pointer to wchar_t,” respectively (4.2 [conv.array]), are never applied to the operand of a throw-expression. —end note] The temporary is an lvalue and is used to initialize the variable named in the matching handler (15.3 [except.handle]). The type of the operand of a throw-expression shall not be an incomplete type, or a pointer to an incomplete type other than (possibly cv-qualified) void. [...]
Change the note in 15.3 [except.handle] paragraph 3 as follows:
[Note: a throw-expression operand that which is an integral constant expression of integer type that evaluates to zero does not match a handler of pointer type; that is, the null pointer constant conversions (4.10 [conv.ptr], 4.11 [conv.mem]) do not apply. —end note]
Change 15.5.1 [except.terminate] paragraph 1 bullet 1 as follows:
when the exception handling mechanism, after completing evaluation of the expression to be thrown operand of throw but before the exception is caught (15.1 [except.throw]), calls a user function that exits via an uncaught exception,
Change 15.5.3 [except.uncaught] paragraph 1 as follows:
The function std::uncaught_exception() returns true after completing evaluation of the object to be thrown operand of throw until completing the initialization of the exception-declaration in the matching handler (18.8.4 [uncaught]).
Change 18.8.4 [uncaught] paragraph 1 by adding the indicated words:
Returns: true after completing evaluation of the operand of a throw-expression until either completing initialization of the exception-declaration in the matching handler or entering unexpected() due to the throw; or after entering terminate() for any reason other than an explicit call to terminate(). [Note: This includes stack unwinding (15.2 [except.ctor]). —end note]
Notes from the April, 2005 meeting:
The CWG discussed this resolution both within the group and with other interested parties. Among the points that were made:
Martin Sebor pointed to a posting in which he argues that writing copy constructors is more difficult if an exception during the copy to the exception object will result in a call to std::terminate().
In response to a question about why the copy to the exception object is different from the copy from the exception object to the object in the exception-declaration, it was observed that the writer of the handler can avoid the second copy (by using a reference declaration), but the first copy is unavoidable.
John Spicer observed that not exiting via exception should be a design constraint for copy constructors in exception objects, regardless of whether std::terminate() is called or not.
Adopting the position that uncaught_exception() returns false during the copy to the exception object would reduce the differences between the case where that copy is elided and the case where it is performed.
Jason Merrill observed that making uncaught_exception() return false during the copy to the exception object would simplify the code generated by g++; as it currently stands, the compiler must generate code to catch exceptions during that copy so std::terminate() can be called.
Bjarne Stroustrup worried that allowing the copy constructor to throw an exception during the copy to the exception object could result in a serious and specific exception being silently transformed into a more trivial and generic one (although the CWG later noted that this risk already exists if something in the expression being thrown throws an exception before the expression completes).
The CWG felt that more input from a wider audience was necessary before a decision could be made on the appropriate resolution.
Notes from the April, 2006 meeting:
The CWG agreed with the position that std::uncaught_exception() should return false during the copy to the exception object and that std::terminate() should not be called if that constructor exits with an exception. The issue was returned to “drafting” status for rewording to reflect this position.
Additional notes (September, 2007):
Although this issue deals primarily with when std::uncaught_exception() begins to return true, the specification of when it begins to return false is also problematic. There are two parallel sections that define the meaning of std::uncaught_exception() and each has a different problem. 15.5.3 [except.uncaught] reads,
The function std::uncaught_exception() returns true after completing evaluation of the object to be thrown until completing the initialization of the exception-declaration in the matching handler (18.8.4 [uncaught]).
The problem here is that whether an exception is considered caught (the underlying condition tested by the function) is here presented in terms of having initialized the exception-declaration, while in other places it is specified by having an active handler for the exception, e.g., 15.1 [except.throw] paragraph 6:
An exception is considered caught when a handler for that exception becomes active (15.3 [except.handle]).
This distinction is important because of 15.3 [except.handle] paragraph 3:
A handler is considered active when initialization is complete for the formal parameter (if any) of the catch clause. [Note: the stack will have been unwound at that point. —end note] Also, an implicit handler is considered active when std::terminate() or std::unexpected() is entered due to a throw.
Note that there is no exception-declaration to be initialized for the std::terminate() and std::unexpected() cases; nevertheless, according to 18.8.4 [uncaught], std::uncaught_exception() is supposed to return false when one of those two functions is entered.
The specification in 18.8.4 [uncaught] is not well phrased, however, and is open to misinterpretation. It reads,
Returns: true after completing evaluation of a throw-expression until either completing initialization of the exception-declaration in the matching handler or entering unexpected() due to the throw; or after entering terminate() for any reason other than an explicit call to terminate().
The problem here is lack of parallelism: does “after entering terminate” refer to the condition for returning true or false? This would be better phrased along the lines of
Returns: true after completing evaluation of a throw-expression until a handler for the exception becomes active (15.3 [except.handle]).
Proposed resolution (March, 2010):
Change 15.5.1 [except.terminate] paragraph 1 bullet 1 as follows:
In the following situations exception handling must be abandoned for less subtle error handling techniques:
when the exception handling mechanism, after completing evaluation of the expression to be thrown the initialization of the exception object but before the exception is caught activation of a handler for the exception (15.1 [except.throw]), calls a function that exits via an uncaught exception, [Footnote: For example, if the object being thrown is of a class with a copy constructor, std::terminate() will be called if that copy constructor exits with an exception during a throw the initialization of the formal parameter of a catch clause. —end footnote]
...
Change 15.5.3 [except.uncaught] paragraph 1 as follows:
The function std::uncaught_exception() returns true after completing evaluation of the object to be thrown the initialization of the exception object (15.1 [except.throw]) until completing the initialization of the exception-declaration in the matching handler activation of a handler for the exception (15.3 [except.handle], 18.8.4 [uncaught])...
Change 18.8.4 [uncaught] paragraph 1 as follows:
Returns: true after completing evaluation of a throw-expression initializing an exception object 15.1 [except.throw] until either completing initialization of the exception-declaration in the matching handler or entering unexpected() due to the throw; or after entering terminate() for any reason other than an explicit call to terminate() a handler for the exception (including unexpected() or terminate()) is activated (15.3 [except.handle]). [Note: This includes stack unwinding (15.2 [except.ctor]). —end note]
[Voted into the WP at the June, 2008 meeting.]
The C99 and C++ Standards disagree about the validity of two Cyrillic characters for use in identifiers. C++ (_N2691_.E [extendid]) says that 040d is valid in an identifier but that 040e is not; C99 (Annex D) says exactly the opposite. In fact, both characters should be accepted in identifiers; see the Unicode chart.
Proposed resolution (February, 2008):
The reference in paragraph 2 should be changed to ISO/IEC TR 10176:2003 and the table should be changed to conform to the one in that document (beginning on page 34).
[Voted into WP at April, 2007 meeting.]
Section 1.3 [intro.defs], definition of "signature" omits the function name as part of the signature. Since the name participates in overload resolution, shouldn't it be included in the definition? I didn't find a definition of signature in the ARM, but I might have missed it.
Fergus Henderson: I think so. In particular, 17.6.3.3.2 [global.names] reserves certain "function signatures" for use by the implementation, which would be wrong unless the signature includes the name.
-2- Each global function signature declared with external linkage in a header is reserved to the implementation to designate that function signature with external linkage.
-5- Each function signature from the Standard C library declared with external linkage is reserved to the implementation for use as a function signature with both extern "C" and extern "C++" linkage, or as a name of namespace scope in the global namespace.
Other uses of the term "function signature" in the description of the standard library also seem to assume that it includes the name.
James Widman:
Names don't participate in overload resolution; name lookup is separate from overload resolution. However, the word “signature” is not used in clause 13 [over]. It is used in linkage and declaration matching (e.g., 14.5.6.1 [temp.over.link]). This suggests that the name and scope of the function should be part of its signature.
Proposed resolution (October, 2006):
Replace 1.3 [intro.defs] “signature” with the following:
the name and the parameter-type-list (8.3.5 [dcl.fct]) of a function, as well as the class or namespace of which it is a member. If a function or function template is a class member its signature additionally includes the cv-qualifiers (if any) on the function or function template itself. The signature of a function template additionally includes its return type and its template parameter list. The signature of a function template specialization includes the signature of the template of which it is a specialization and its template arguments (whether explicitly specified or deduced). [Note: Signatures are used as a basis for name-mangling and linking. —end note]
Delete paragraph 3 and replace the first sentence of 14.5.6.1 [temp.over.link] as follows:
The signature of a function template specialization consists of the signature of the function template and of the actual template arguments (whether explicitly specified or deduced).
The signature of a function template consists of its function signature, its return type and its template parameter list is defined in 1.3 [intro.defs]. The names of the template parameters are significant...
(See also issue 537.)
[Voted into WP at April, 2007 meeting.]
The standard defines “signature” in two places: 1.3 [intro.defs] and 14.5.6.1 [temp.over.link] paragraphs 3-4. The former seems to be meant as a formal definition (I think it's the only place covering the nontemplate case), yet it lacks some bits mentioned in the latter (specifically, the notion of a “signature of a function template,” which is part of every signature of the associated function template specializations).
Also, I think the 1.3 [intro.defs] words “the information about a function that participates in overload resolution” isn't quite right either. Perhaps, “the information about a function that distinguishes it in a set of overloaded functions?”
Eric Gufford:
In 1.3 [intro.defs] the definition states that “Function signatures do not include return type, because that does not participate in overload resolution,” while 14.5.6.1 [temp.over.link] paragraph 4 states “The signature of a function template consists of its function signature, its return type and its template parameter list.” This seems inconsistent and potentially confusing. It also seems to imply that two identical function templates with different return types are distinct signatures, which is in direct violation of 13.3 [over.match]. 14.5.6.1 [temp.over.link] paragraph 4 should be amended to include verbiage relating to overload resolution.
Either return types are included in function signatures, or they're not, across the board. IMHO, they should be included as they are an integral part of the function declaration/definition irrespective of overloads. Then verbiage should be added about overload resolution to distinguish between signatures and overload rules. This would help clarify things, as it is commonly understood that overload resolution is based on function signature.
In short, the term “function signature” should be made consistent, and removed from its (implicit, explicit or otherwise) linkage to overload resolution as it is commonly understood.
James Widman:
The problem is that (a) if you say the return type is part of the signature of a non-template function, then you have overloading but not overload resolution on return types (i.e., what we have now with function templates). I don't think anyone wants to make the language uglier in that way. And (b) if you say that the return type is not part of the signature of a function template, you will break code. Given those alternatives, it's probably best to maintain the status quo (which the implementors appear to have rendered faithfully).
Proposed resolution (September, 2006):
This issue is resolved by the resolution of issue 357.
[Voted into WP at April, 2006 meeting.]
The standard uses “most derived object” in some places (for example, 1.3 [intro.defs] “dynamic type,” 5.3.5 [expr.delete]) to refer to objects of both class and non-class type. However, 1.8 [intro.object] only formally defines it for objects of class type.
Possible fix: Change the wording in 1.8 [intro.object] paragraph 4 from
an object of a most derived class type is called a most derived object
to
an object of a most derived class type, or of non-class type, is called a most derived object
Proposed resolution (October, 2005):
Add the indicated words to 1.8 [intro.object] paragraph 4:
If a complete object, a data member (9.2 [class.mem]), or an array element is of class type, its type is considered the most derived class, to distinguish it from the class type of any base class subobject; an object of a most derived class type, or of a non-class type, is called a most derived object.
[Voted into the WP at the September, 2008 meeting.]
In 1.9 [intro.execution] paragraph 16, the following expression is still listed as an example of undefined behavior:
i = ++i + 1;
However, it appears that the new sequencing rules make this expression well-defined:
The assignment side-effect is required to be sequenced after the value computations of both its LHS and RHS (5.17 [expr.ass] paragraph 1).
The LHS (i) is an lvalue, so its value computation involves computing the address of i.
In order to value-compute the RHS (++i + 1), it is necessary to first value-compute the lvalue expression ++i and then do an lvalue-to-rvalue conversion on the result. This guarantees that the incrementation side-effect is sequenced before the computation of the addition operation, which in turn is sequenced before the assignment side effect. In other words, it yields a well-defined order and final value for this expression.
It should be noted that a similar expression
i = i++ + 1;
is still not well-defined, since the incrementation side-effect remains unsequenced with respect to the assignment side-effect.
It's unclear whether making the expression in the example well-defined was intentional or just a coincidental byproduct of the new sequencing rules. In either case either the example should be fixed, or the rules should be changed.
Clark Nelson: In my opinion, the poster's argument is perfectly correct. The rules adopted reflect the CWG's desired outcome for issue 222. At the Portland meeting, I presented (and still sympathize with) Tom Plum's case that these rules go a little too far in nailing down required behavior; this is a consequence of that.
One way or another, a change needs to be made, and I think we should seriously consider weakening the resolution of issue 222 to keep this example as having undefined behavior. This could be done fairly simply by having the sequencing requirements for an assignment expression depend on whether it appears in an lvalue context.
James Widman: How's this for a possible re-wording?
In all cases, the side effect of the assignment expression is sequenced after the value computations of the right and left operands. Furthermore, if the assignment expression appears in a context where an lvalue is required, the side effect of the assignment expression is sequenced before its value computation.
Notes from the February, 2008 meeting:
There was no real support in the CWG for weakening the resolution of issue 222 and returning the example to having undefined behavior. No one knew of an implementation that doesn't already do the (newly) right thing for such an example, so there was little motivation to go out of our way to increase the domain of undefined behavior. So the proposed resolution is to change the example to one that definitely does have undependable behavior in existing practice, and undefined behavior under the new rules.
Also, the new formulation of the sequencing rules approved in Oxford contained the wording that by and large resolved issue 222, so with the resolution of this issue, we can also close issue 222.
Proposed resolution (March, 2008):
Change the example in 1.9 [intro.execution] paragraph 16 as follows:
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = ++i i++ + 1; // the behavior is undefined
i = i + 1; // the value of i is incremented
[Voted into the WP at the September, 2008 meeting.]
Is the behavior undefined in the following example?
void f() {
int n = 0;
n = --n;
}
1.9 [intro.execution] paragraph 16 says,
If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.
It's not clear to me whether the two side-effects in n=--n are “different.” As far as I can tell, it seems that both side-effects involve the assignment of -1 to n, which in a sense makes them non-“different.” But I don't know if that's the intent. Would it be better to say “another” instead of “a different?”
On a related note, can we include this example to illustrate?
void f( int, int );
void g( int a ) { f( a = -1, a = -1 ); } // Undefined?
Proposed resolution (March, 2008):
Change 1.9 [intro.execution] paragraph 16 as follows:
...If a side effect on a scalar object is unsequenced relative to either a different another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. [Example:
void f(int, int); void g(int i, int* v) { i = v[i++]; // the behavior is undefined i = 7, i++, i++; // i becomes 9 i = ++i + 1; // the behavior is undefined i = i + 1; // the value of i is incremented f(i = -1, i = -1); // the behavior is undefined }—end example] When calling...
[Voted into WP at March 2004 meeting.]
Should this program do what its author obviously expects? As far as I can tell, the standard says that the point of instantiation for Fib<n-1>::Value is the same as the point of instantiation as the enclosing specialization, i.e., Fib<n>::Value. What in the standard actually says that these things get initialized in the right order?
template<int n>
struct Fib { static int Value; };
template <>
int Fib<0>::Value = 0;
template <>
int Fib<1>::Value = 1;
template<int n>
int Fib<n>::Value = Fib<n-1>::Value + Fib<n-2>::Value;
int f ()
{
return Fib<40>::Value;
}
John Spicer: My opinion is that the standard does not specify the behavior of this program. I thought there was a core issue related to this, but I could not find it. The issue that I recall proposed tightening up the static initialization rules to make more cases well defined.
Your comment about point of instantiation is correct, but I don't think that really matters. What matters is the order of execution of the initialization code at execution time. Instantiations don't really live in "translation units" according to the standard. They live in "instantiation units", and the handling of instantiation units in initialization is unspecified (which should probably be another core issue). See 2.2 [lex.phases] paragraph 8.
Notes from October 2002 meeting:
We discussed this and agreed that we really do mean the the order is unspecified. John Spicer will propose wording on handling of instantiation units in initialization.
Proposed resolution (April 2003):
TC1 contains the following text in 3.6.2 [basic.start.init] paragraph 1:
Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.
This was revised by issue 270 to read:
Dynamic initialization of an object is either ordered or unordered. Explicit specializations and definitions of class template static data members have ordered initialization. Other class template static data member instances have unordered initialization. Other objects defined in namespace scope have ordered initialization. Objects defined within a single translation unit and with ordered initialization shall be initialized in the order of their definitions in the translation unit. The order of initialization is unspecified for objects with unordered initialization and for objects defined in different translation units.
This addresses this issue but while reviewing this issue some additional changes were suggested for the above wording:
Dynamic initialization of an object is either ordered or unordered. Definitions of explicitly specialized Explicit specializations and definitions of class template static data members have ordered initialization. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) instances have unordered initialization. Other objects defined in namespace scope have ordered initialization. Objects defined within a single translation unit and with ordered initialization shall be initialized in the order of their definitions in the translation unit. The order of initialization is unspecified for objects with unordered initialization and for objects defined in different translation units.
[Moved to DR at October 2007 meeting.]
C99 and C++ differ in their approach to universal character names (UCNs).
Issue 248 already covers the differences in UCNs allowed for identifiers, but a more fundamental issue is that of UCNs that correspond to codes reserved by ISO 10676 for surrogate pair forms.
Specifically, C99 does not allow UCNs whose short names are in the range 0xD800 to 0xDFFF. I think C++ should have the same constraint. If someone really wants to place such a code in a character or string literal, they should use a hexadecimal escape sequence instead, for example:
wchar_t w1 = L'\xD900'; // Okay.
wchar_t w2 = L'\uD900'; // Error, not a valid character.
(Compare 6.4.3 paragraph 2 in ISO/IEC 9899/1999 with 2.3 [lex.charset] paragraph 2 in the C++ standard.)
Proposed resolution (October, 2007):
This issue is resolved by the adoption of paper J16/07-0030 = WG21 N2170.
[Voted into WP at the October, 2006 meeting.]
The current wording of 2.14.3 [lex.ccon] paragraph 3 states,
If the character following a backslash is not one of those specified, the behavior is undefined.
Paper J16/04-0167=WG21 N1727 suggests that such character escapes be ill-formed. In discussions at the Lillehammer meeting, however, the CWG felt that the newly-approved category of conditionally-supported behavior would be more appropriate.
Proposed resolution (April, 2006):
Change the next-to-last sentence of 2.14.3 [lex.ccon] paragraph 3 from:
If the character following a backslash is not one of those specified, the behavior is undefined.
to:
Escape sequences in which the character following the backslash is not listed in Table 6 are conditionally-supported, with implementation-defined semantics.
[Voted into the WP at the June, 2008 meeting.]
3 [basic] paragraph 8, while not incorrect, does not allow for linkage of operators and conversion functions. It says:
An identifier used in more than one translation unit can potentially refer to the same entity in these translation units depending on the linkage (3.5 [basic.link]) of the identifier specified in each translation unit.
Proposed Resolution (November, 2006):
This issue is resolved by the proposed resolution of issue 485.
[Voted into the WP at the June, 2008 meeting.]
Clause 3 [basic] paragraph 4 says:
A name is a use of an identifier (2.11 [lex.name]) that denotes an entity or label (6.6.4 [stmt.goto], 6.1 [stmt.label]).
Just three paragraphs later, it says
Two names are the same if
- they are identifiers composed of the same character sequence; or
- they are the names of overloaded operator functions formed with the same operator; or
- they are the names of user-defined conversion functions formed with the same type.
The last two bullets contradict the definition of name in paragraph 4 because they are not identifiers.
This definition affects other parts of the Standard, as well. For example, in 3.4.2 [basic.lookup.argdep] paragraph 1,
When an unqualified name is used as the postfix-expression in a function call (5.2.2 [expr.call]), other namespaces not considered during the usual unqualified lookup (3.4.1 [basic.lookup.unqual]) may be searched, and in those namespaces, namespace-scope friend function declarations (11.4 [class.friend]) not otherwise visible may be found.
With the current definition of name, argument-dependent lookup apparently does not apply to function-notation calls to overloaded operators.
Another related question is whether a template-id is a name or not and thus would trigger an argument-dependent lookup. Personally, I have always viewed a template-id as a name, just like operator+.
Proposed Resolution (November, 2006):
Change clause 3 [basic] paragraphs 3-8 as follows:
An entity is a value, object, subobject, base class subobject, array element, variable, reference, function, instance of a function, enumerator, type, class member, template, template specialization, namespace, or parameter pack.
A name is a use of an identifier identifier (2.11 [lex.name]), operator-function-id (13.5 [over.oper]), conversion-function-id (12.3.2 [class.conv.fct]), or template-id (14.2 [temp.names]) that denotes an entity or label (6.6.4 [stmt.goto], 6.1 [stmt.label]). A variable is introduced by the declaration of an object. The variable’s name denotes the object.
Every name that denotes an entity is introduced by a declaration. Every name that denotes a label is introduced either by a goto statement (6.6.4 [stmt.goto]) or a labeled-statement (6.1 [stmt.label]).
A variable is introduced by the declaration of an object. The variable's name denotes the object.
Some names denote types, classes, enumerations, or templates. In general, it is necessary to determine whether or not a name denotes one of these entities before parsing the program that contains it. The process that determines this is called name lookup (3.4 [basic.lookup]).
Two names are the same if
they are identifiers identifiers composed of the same character sequence; or
they are the names of overloaded operator functions operator-function-ids formed with the same operator; or
they are the names of user-defined conversion functions conversion-function-ids formed with the same type., or
they are template-ids that refer to the same class or function (14.4 [temp.type]).
An identifier A name used in more than one translation unit can potentially refer to the same entity in these translation units depending on the linkage (3.5 [basic.link]) of the identifier name specified in each translation unit.
Change 3.3.7 [basic.scope.class] paragraph 1 item 5 as follows:
The potential scope of a declaration that extends to or past the end of a class definition also extends to the regions defined by its member definitions, even if the members are defined lexically outside the class (this includes static data member definitions, nested class definitions, member function definitions (including the member function body and any portion of the declarator part of such definitions which follows the identifier declarator-id, including a parameter-declaration-clause and any default arguments (8.3.6 [dcl.fct.default]).
[Drafting note: This last change is not really mandated by the issue, but it's another case of “identifier” confusion.]
(This proposed resolution also resolves issue 309.)
[Moved to DR at October 2002 meeting.]
3.2 [basic.def.odr] paragraph 2 says that a deallocation function is "used" by a new-expression or delete-expression appearing in a potentially-evaluated expression. 3.2 [basic.def.odr] paragraph 3 requires only that "used" functions be defined.
This wording runs afoul of the typical implementation technique for polymorphic delete-expressions in which the deallocation function is invoked from the virtual destructor of the most-derived class. The problem is that the destructor must be defined, because it's virtual, and if it contains an implicit reference to the deallocation function, the deallocation function must also be defined, even if there are no relevant new-expressions or delete-expressions in the program.
For example:
struct B { virtual ~B() { } };
struct D: B {
void operator delete(void*);
~D() { }
};
Is it required that D::operator delete(void*) be defined, even if no B or D objects are ever created or deleted?
Suggested resolution: Add the words "or if it is found by the lookup at the point of definition of a virtual destructor (12.4 [class.dtor])" to the specification in 3.2 [basic.def.odr] paragraph 2.
Notes from 04/01 meeting:
The consensus was in favor of requiring that any declared non-placement operator delete member function be defined if the destructor for the class is defined (whether virtual or not), and similarly for a non-placement operator new if a constructor is defined.
Proposed resolution (10/01):
In 3.2 [basic.def.odr] paragraph 2, add the indicated text:
An allocation or deallocation function for a class is used by a new expression appearing in a potentially-evaluated expression as specified in 5.3.4 [expr.new] and 12.5 [class.free]. A deallocation function for a class is used by a delete expression appearing in a potentially-evaluated expression as specified in 5.3.5 [expr.delete] and 12.5 [class.free]. A non-placement allocation or deallocation function for a class is used by the definition of a constructor of that class. A non-placement deallocation function for a class is used by the definition of the destructor of that class, or by being selected by the lookup at the point of definition of a virtual destructor (12.4 [class.dtor]). [Footnote: An implementation is not required to call allocation and deallocation functions from constructors or destructors; however, this is a permissible implementation technique.]
[Moved to DR at October 2002 meeting.]
3.2 [basic.def.odr] paragraph 4 has a note listing the contexts that require a class type to be complete. It does not list use as a base class as being one of those contexts.
Proposed resolution (10/01):
In 3.2 [basic.def.odr] paragraph 4 add a new bullet at the end of the note as the next-to-last bullet:
[Voted into WP at March 2004 meeting.]
Consider the following translation unit:
template<class T> struct S {
void f(union U*); // (1)
};
template<class T> void S<T>::f(union U*) {} // (2)
U *p; // (3)
Does (1) introduce U as a visible name in the surrounding namespace scope?
If not, then (2) could presumably be an error since the "union U" in that definition does not find the same type as the declaration (1).
If yes, then (3) is OK too. However, we have gone through much trouble to allow template implementations that do not pre-parse the template definitions, but requiring (1) to be visible would change that.
A slightly different case is the following:
template<typename> void f() { union U *p; }
U *q; // Should this be valid?
Notes from October 2003 meeting:
There was consensus that example 1 should be allowed. (Compilers already parse declarations in templates; even MSVC++ 6.0 accepts this case.) The vote was 7-2.
Example 2, on the other hand, is wrong; the union name goes into a block scope anyway.
Proposed resolution:
In 3.3.2 [basic.scope.pdecl] change the second bullet of paragraph 5 as follows:
for an elaborated-type-specifier of the formclass-key identifierif the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest non-class, non-function-prototype scope that contains the declaration. [Note: These rules also apply within templates.] [Note: ...]
[Voted into WP at March 2004 meeting.]
Consider the following example (inspired by a question from comp.lang.c++.moderated):
template<typename> struct B {};
template<typename T> struct D: B<D> {};
Most (all?) compilers reject this code because D is handled as a template name rather than as the injected class name.
9 [class]/2 says that the injected class name is "inserted into the scope of the class."
3.3.7 [basic.scope.class]/1 seems to be the text intended to describe what "scope of a class" means, but it assumes that every name in that scope was introduced using a "declarator". For an implicit declaration such as the injected-class name it is not clear what that means.
So my questions:
John Spicer: I do not believe the injected class name should be available in the base specifier. I think the semantics of injected class names should be as if a magic declaration were inserted after the opening "{" of the class definition. The injected class name is a member of the class and members don't exist at the point where the base specifiers are scanned.
John Spicer: I believe the 3.3.7 [basic.scope.class] wording should be updated to reflect the fact that not all names come from declarators.
Notes from October 2003 meeting:
We agree with John Spicer's suggested answers above.
Proposed Resolution (October 2003):
The answer to question 1 above is No and no change is required.
For question 1, change 3.3.7 [basic.scope.class] paragraph 1 rule 1 to:
1) The potential scope of a name declared in a class consists not only of the declarative region following the name's point of declaration declarator, but also of all function bodies, default arguments, and constructor ctor-initializers in that class (including such things in nested classes). The point of declaration of an injected-class-name (clause 9 [class]) is immediately following the opening brace of the class definition.
(Note that this change overlaps a change in issue 417.)
Also change 3.3.2 [basic.scope.pdecl] by adding a new paragraph 8 for the injected-class-name case:
The point of declaration for an injected-class-name (clause 9 [class]) is immediately following the opening brace of the class definition.
Alternatively this paragraph could be added after paragraph 5 and before the two note paragraphs (i.e. it would become paragraph 5a).
[Moved to DR at 10/01 meeting.]
The example in 3.4.1 [basic.lookup.unqual] paragraph 3 is incorrect:
typedef int f;
struct A {
friend void f(A &);
operator int();
void g(A a) {
f(a);
}
};
Regardless of the resolution of other issues concerning the lookup
of names in friend declarations, this example is ill-formed
(the function and the typedef cannot exist in the same scope).
One possible repair of the example would be to make f a class with a constructor taking either A or int as its parameter.
(See also issues 95, 136, 138, 143, 165, and 166.)
Proposed resolution (04/01):
Change the example in 3.4.1 [basic.lookup.unqual] paragraph 3 to read:
typedef int f;
namespace N {
struct A {
friend int f(A &);
operator int();
void g(A a) {
int i = f(a);
// f is the typedef, not the friend function:
// equivalent to int(a)
}
};
}
Delete the sentence immediately following the example:
The expression f(a) is a cast-expression equivalent to int(a).
[Voted into WP at the October, 2006 meeting.]
Is the following code well-formed?
namespace N {
int i;
extern int j;
}
int N::j = i;
The question here is whether the lookup for i in the initializer of N::j finds the declaration in namespace N or not. Implementations differ on this question.
If N::j were a static data member of a class, the answer would be clear: both 3.4.1 [basic.lookup.unqual] paragraph 12 and 8.5 [dcl.init] paragraph 11 say that the initializer “is in the scope of the member's class.” There is no such provision for namespace members defined outside the namespace, however.
The reasoning given in 3.4.1 [basic.lookup.unqual] may be instructive:
A name used in the definition of a static data member of class X (9.4.2 [class.static.data]) (after the qualified-id of the static member) is looked up as if the name was used in a member function of X.
It is certainly the case that a name used in a function that is a member of a namespace is looked up in that namespace (3.4.1 [basic.lookup.unqual] paragraph 6), regardless of whether the definition is inside or outside that namespace. Initializers for namespace members should probably be looked up the same way.
Proposed resolution (April, 2006):
Add a new paragraph following 3.4.1 [basic.lookup.unqual] paragraph 12:
If a variable member of a namespace is defined outside of the scope of its namespace then any name used in the definition of the variable member (after the declarator-id) is looked up as if the definition of the variable member occurred in its namespace. [Example:
namespace N { int i = 4; extern int j; } int i = 2; int N::j = i; // N::j == 4—end example]
[Moved to DR at 4/02 meeting.]
Paragraphs 1 and 2 of 3.4.2 [basic.lookup.argdep] say, in part,
When an unqualified name is used as the postfix-expression in a function call (5.2.2 [expr.call] )... namespace-scope friend function declarations (11.4 [class.friend] ) not otherwise visible may be found... the set of declarations found by the lookup of the function name [includes] the set of declarations found in the... classes associated with the argument types.The most straightforward reading of this wording is that if a function of namespace scope (as opposed to a class member function) is declared as a friend in a class, and that class is an associated class in a function call, the friend function will be part of the overload set, even if it is not visible to normal lookup.
Consider the following example:
namespace A {
class S;
};
namespace B {
void f(A::S);
};
namespace A {
class S {
int i;
friend void B::f(S);
};
}
void g() {
A::S s;
f(s); // should find B::f(A::S)
}
This example would seem to satisfy the criteria from
3.4.2 [basic.lookup.argdep]
:
A::S is an associated class of the argument, and
A::S has a
friend declaration of the namespace-scope function
B::f(A::S),
so Koenig lookup should include B::f(A::S) as part of the
overload set in the call.
Another interpretation is that, instead of finding the friend declarations in associated classes, one only looks for namespace-scope functions, visible or invisible, in the namespaces of which the the associated classes are members; the only use of the friend declarations in the associated classes is to validate whether an invisible function declaration came from an associated class or not and thus whether it should be included in the overload set or not. By this interpretation, the call f(s) in the example will fail, because B::f(A::S) is not a member of namespace A and thus is not found by the lookup.
Notes from 10/99 meeting: The second interpretation is correct. The wording should be revised to make clear that Koenig lookup works by finding "invisible" declarations in namespace scope and not by finding friend declarations in associated classes.
Proposed resolution (04/01): The "associated classes" are handled adequately under this interpretation by 3.4.2 [basic.lookup.argdep] paragraph 3, which describes the lookup in the associated namespaces as including the friend declarations from the associated classes. Other mentions of the associated classes should be removed or qualified to avoid the impression that there is a lookup in those classes:
In 3.4.2 [basic.lookup.argdep], change
When an unqualified name is used as the postfix-expression in a function call (5.2.2 [expr.call]), other namespaces not considered during the usual unqualified lookup (3.4.1 [basic.lookup.unqual]) may be searched, and namespace-scope friend function declarations (11.4 [class.friend]) not otherwise visible may be found.
to
When an unqualified name is used as the postfix-expression in a function call (5.2.2 [expr.call]), other namespaces not considered during the usual unqualified lookup (3.4.1 [basic.lookup.unqual]) may be searched, and in those namespaces, namespace-scope friend function declarations (11.4 [class.friend]) not otherwise visible may be found.
In 3.4.2 [basic.lookup.argdep] paragraph 2, delete the words and classes in the following two sentences:
If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated namespaces and classes are not considered. Otherwise the set of declarations found by the lookup of the function name is the union of the set of declarations found using ordinary unqualified lookup and the set of declarations found in the namespaces and classes associated with the argument types.
(See also issues 95, 136, 138, 139, 165, 166, and 218.)
[Voted into WP at April, 2007 meeting.]
The original intent of the Committee when Koenig lookup was added to the language was apparently something like the following:
This approach is not reflected in the current wording of the Standard. Instead, the following appears to be the status quo:
John Spicer: Argument-dependent lookup was created to solve the problem of looking up function names within templates where you don't know which namespace to use because it may depend on the template argument types (and was then expanded to permit use in nontemplates). The original intent only concerned functions. The safest and simplest change is to simply clarify the existing wording to that effect.
Bill Gibbons: I see no reason why non-function declarations should not be found. It would take a special rule to exclude "function objects", as well as pointers to functions, from consideration. There is no such rule in the standard and I see no need for one.
There is also a problem with the wording in 3.4.2 [basic.lookup.argdep] paragraph 2:
If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated namespaces and classes are not considered.
This implies that if the ordinary lookup of the name finds the declaration of a data member which is a pointer to function or function object, argument-dependent lookup is still done.
My guess is that this is a mistake based on the incorrect assumption that finding any member other than a member function would be an error. I would just change "class member function" to "class member" in the quoted sentence.
Mike Miller: In light of the issue of "short-circuiting" Koenig lookup when normal lookup finds a non-function, perhaps it should be written as "...finds the declaration of a class member, an object, or a reference, the associated namespaces..."?
Andy Koenig: I think I have to weigh in on the side of extending argument-dependent lookup to include function objects and pointers to functions. I am particularly concerned about [function objects], because I think that programmers should be able to replace functions by function objects without changing the behavior of their programs in fundamental ways.
Bjarne Stroustrup: I don't think we could seriously argue from first principles that [argument-dependent lookup should find only function declarations]. In general, C++ name lookup is designed to be independent of type: First we find the name(s), then, we consider its(their) meaning. 3.4 [basic.lookup] states "The name lookup rules apply uniformly to all names ..." That is an important principle.
Thus, I consider text that speaks of "function call" instead of plain "call" or "application of ()" in the context of koenig lookup an accident of history. I find it hard to understand how 5.2.2 [expr.call] doesn't either disallow all occurrences of x(y) where x is a class object (that's clearly not intended) or requires koenig lookup for x independently of its type (by reference from 3.4 [basic.lookup]). I suspect that a clarification of 5.2.2 [expr.call] to mention function objects is in order. If the left-hand operand of () is a name, it should be looked up using koenig lookup.
John Spicer: This approach causes otherwise well-formed programs to be ill-formed, and it does so by making names visible that might be completely unknown to the author of the program. Using-directives already do this, but argument-dependent lookup is different. You only get names from using-directives if you actually use using-directives. You get names from argument-dependent lookup whether you want them or not.
This basically breaks an important reason for having namespaces. You are not supposed to need any knowledge of the names used by a namespace.
But this example breaks if argument-dependent lookup finds non-functions and if the translation unit includes the <list> header somewhere.
namespace my_ns {
struct A {};
void list(std::ostream&, A&);
void f() {
my_ns::A a;
list(cout, a);
}
}
This really makes namespaces of questionable value if you still need to avoid using the same name as an entity in another namespace to avoid problems like this.
Erwin Unruh: Before we really decide on this topic, we should have more analysis on the impact on programs. I would also like to see a paper on the possibility to overload functions with function surrogates (no, I won't write one). Since such an extension is bound to wait until the next official update, we should not preclude any outcome of the discussion.
I would like to have a change right now, which leaves open several outcomes later. I would like to say that:
Koenig lookup will find non-functions as well. If it finds a variable, the program is ill-formed. If the primary lookup finds a variable, Koenig lookup is done. If the result contains both functions and variables, the program is ill-formed. [Note: A future standard will assign semantics to such a program.]
I myself are not comfortable with this as a long-time result, but it prepares the ground for any of the following long term solutions:
The note is there to prevent compiler vendors to put their own extensions in here.
(See also issues 113 and 143.)
Notes from 04/00 meeting:
Although many agreed that there were valid concerns motivating a desire for Koenig lookup to find non-function declarations, there was also concern that supporting this capability would be more dangerous than helpful in the absence of overload resolution for mixed function and non-function declarations.
A straw poll of the group revealed 8 in favor of Koenig lookup finding functions and function templates only, while 3 supported the broader result.
Notes from the 10/01 meeting:
There was unanimous agreement on one less controversial point: if the normal lookup of the identifier finds a non-function, argument-dependent lookup should not be done.
On the larger issue, the primary point of consensus is that making this change is an extension, and therefore it should wait until the point at which we are considering extensions (which could be very soon). There was also consensus on the fact that the standard as it stands is not clear: some introductory text suggests that argument-dependent lookup finds only functions, but the more detailed text that describes the lookup does not have any such restriction.
It was also noted that some existing implementations (e.g., g++) do find some non-functions in some cases.
The issue at this point is whether we should (1) make a small change to make the standard clear (presumably in the direction of not finding the non-functions in the lookup), and revisit the issue later as an extension, or (2) leave the standard alone for now and make any changes only as part of considering the extension. A straw vote favored option (1) by a strong majority.
Additional Notes (September, 2006):
Recent discussion of this issue has emphasized the following points:
The concept of finding function pointers and function objects as part of argument-dependent lookup is not currently under active discussion in the Evolution Working Group.
The major area of concern with argument-dependent lookup is finding functions in unintended namespaces. There are current proposals to deal with this concern either by changing the definition of “associated namespace” so that fewer namespaces are considered or to provide a mechanism for enabling or disabling ADL altogether. Although this concern is conceptually distinct from the question of whether ADL finds function pointers and function objects, it is related in the sense that the current rules are perceived as finding too many functions (because of searching too many namespaces), and allowing function pointers and function objects would also increase the number of entities found by ADL.
Any expansion of ADL to include function pointers and function objects must necessarily update the overloading rules to specify how they interact with functions and function templates in the overload set. Current implementation experience (g++) is not helpful in making this decision because, although it performs a uniform lookup and finds non-function entities, it diagnoses an error in overload resolution if non-function entities are in the overload set.
There is a possible problem if types are found by ADL: it is not clear that overloading between callable entities (functions, function templates, function pointers, and function objects) and types (where the postfix syntax means a cast or construction of a temporary) is reasonable or useful.
James Widman:
There is a larger debate here about whether ADL should find object names; the proposed wording below is only intended to answer the request for wording to clarify the status quo (option 1 above) and not to suggest the outcome of the larger debate.
Proposed Resolution (October, 2006):
Replace the normative text in 3.4.2 [basic.lookup.argdep] paragraph 3 with the following (leaving the text of the note and example unchanged):
Let X be the lookup set produced by unqualified lookup (3.4.1 [basic.lookup.unqual]) and let Y be the lookup set produced by argument dependent lookup (defined as follows). If X contains
- a declaration of a class member, or
- a block-scope function declaration that is not a using-declaration, or
- a declaration that is neither a function nor a function template
then Y is empty. Otherwise Y is the set of declarations found in the namespaces associated with the argument types as described below. The set of declarations found by the lookup of the name is the union of X and Y.
Change 3.4.1 [basic.lookup.unqual] paragraph 4 as indicated:
When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier (3.4.3.2 [namespace.qual]) except that:
- Any using-directives in the associated namespace are ignored.
- Any namespace-scope friend functions or friend function templates declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup (11.4 [class.friend]).
- All names except those of (possibly overloaded) functions and function templates are ignored.
[Voted into WP at March 2004 meeting.]
Spun off from issue 384.
3.4.2 [basic.lookup.argdep] says:
If T is a template-id, its associated namespaces and classes are the namespace in which the template is defined; for member templates, the member template's class; the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces in which any template template arguments are defined; and the classes in which any member templates used as template template arguments are defined. [Note: non-type template arguments do not contribute to the set of associated namespaces. ]There is a problem with the term "is a template-id". template-id is a syntactic construct and you can't really talk about a type being a template-id. Presumably, this is intended to mean "If T is the type of a class template specialization ...".
Proposed Resolution (October 2003):
In 3.4.2 [basic.lookup.argdep], paragraph 2, bullet 8, replace
If T is a template-id ...with
If T is a class template specialization ...
[Voted into WP at the October, 2006 meeting.]
One might assume from 14.7.1 [temp.inst] paragraph 1 that argument-dependent lookup would require instantiation of any class template specializations used in argument types:
Unless a class template specialization has been explicitly instantiated (14.7.2 [temp.explicit]) or explicitly specialized (14.7.3 [temp.expl.spec]), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program.
A complete class type is required to determine the associated classes and namespaces for the argument type (to determine the class's bases) and to determine the friend functions declared by the class, so the completeness of the class type certainly “affects the semantics of the program.”
This conclusion is reinforced by the second bullet of 3.4.2 [basic.lookup.argdep] paragraph 2:
If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces in which its associated classes are defined.
A class template specialization is a class type, so the second bullet would appear to apply, requiring the specialization to be instantiated in order to determine its base classes.
However, bullet 8 of that paragraph deals explicitly with class template specializations:
If T is a class template specialization its associated namespaces and classes are the namespace in which the template is defined; for member templates, the member template’s class; the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces in which any template template arguments are defined; and the classes in which any member templates used as template template arguments are defined.
Note that the class template specialization itself is not listed as an associated class, unlike other class types, and there is no mention of base classes. If bullet 8 were intended as a supplement to the treatment of class types in bullet 2, one would expect phrasing along the lines of, “In addition to the associated namespaces and classes for all class types...” or some such; instead, bullet 8 reads like a self-contained and complete specification.
If argument-dependent lookup does not cause implicit instantiation, however, examples like the following fail:
template <typename T> class C {
friend void f(C<T>*) { }
};
void g(C<int>* p) {
f(p); // found by ADL??
}
Implementations differ in whether this example works or not.
Proposed resolution (April, 2006):
Change bullet 2 of 3.4.2 [basic.lookup.argdep] paragraph 2 as indicated:
If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces in of which its associated classes are defined members. Furthermore, if T is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces of which any template template arguments are members; and the classes of which any member templates used as template template arguments are members. [Note: Non-type template arguments do not contribute to the set of associated namespaces. —end note]
Delete bullet 8 of 3.4.2 [basic.lookup.argdep] paragraph 2:
If T is a class template specialization its associated namespaces and classes are the namespace in which the template is defined; for member templates, the member template’s class; the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces in which any template template arguments are defined; and the classes in which any member templates used as template template arguments are defined. [Note: non-type template arguments do not contribute to the set of associated namespaces. —end note]
[Voted into WP at April 2003 meeting.]
Can a typedef T to a cv-qualified class type be used in a qualified name T::x?
struct A { static int i; };
typedef const A CA;
int main () {
CA::i = 0; // Okay?
}
Suggested answer: Yes. All the compilers I tried accept the test case.
Proposed resolution (10/01):
In 3.4.3.1 [class.qual] paragraph 1 add the indicated text:
If the nested-name-specifier of a qualified-id nominates a class, the name specified after the nested-name-specifier is looked up in the scope of the class (10.2 [class.member.lookup]), except for the cases listed below. The name shall represent one or more members of that class or of one of its base classes (clause 10 [class.derived]). If the class-or-namespace-name of the nested-name-specifier names a cv-qualified class type, it nominates the underlying class (the cv-qualifiers are ignored).
Notes from 4/02 meeting:
There is a problem in that class-or-namespace-name does not include typedef names for cv-qualified class types. See 7.1.3 [dcl.typedef] paragraph 4:
Argument and text removed from proposed resolution (October 2002):
7.1.3 [dcl.typedef] paragraph 5:
Here's a good question: in this example, should X be used as a name-for-linkage-purposes (FLP name)?
typedef class { } const X;
Because a type-qualifier is parsed as a decl-specifier, it isn't possible to declare cv-qualified and cv-unqualified typedefs for a type in a single declaration. Also, of course, there's no way to declare a typedef for the cv-unqualified version of a type for which only a cv-qualified version has a name. So, in the above example, if X isn't used as the FLP name, then there can be no FLP name. Also note that a FLP name usually represents a parameter type, where top-level cv-qualifiers are usually irrelevant anyway.
Data points: for the above example, Microsoft uses X as the FLP name; GNU and EDG do not.
My recommendation: for consistency with the direction we're going on this issue, for simplicity of description (e.g., "the first class-name declared by the declaration"), and for (very slightly) increased utility, I think Microsoft has this right.
If the typedef declaration defines an unnamed class type (or enum type), the first typedef-name declared by the declaration to be have that class type (or enum type) or a cv-qualified version thereof is used to denote the class type (or enum type) for linkage purposes only (3.5 [basic.link]). [Example: ...
Proposed resolution (October 2002):
3.4.4 [basic.lookup.elab] paragraphs 2 and 3:
This sentence is deleted twice:
... If this name lookup finds a typedef-name, the elaborated-type-specifier is ill-formed. ...
Note that the above changes are included in N1376 as part of the resolution of issue 245.
5.1.1 [expr.prim.general] paragraph 7:
This is only a note, and it is at least incomplete (and quite possibly inaccurate), despite (or because of) its complexity. I propose to delete it.
... [Note: a typedef-name that names a class is a class-name (9.1 [class.name]). Except as the identifier in the declarator for a constructor or destructor definition outside of a class member-specification (12.1 [class.ctor], 12.4 [class.dtor]), a typedef-name that names a class may be used in a qualified-id to refer to a constructor or destructor. ]
7.1.3 [dcl.typedef] paragraph 4:
My first choice would have been to make this the primary statement about the equivalence of typedef-name and class-name, since the equivalence comes about as a result of a typedef declaration. Unfortunately, references to class-name point to 9.1 [class.name], so it would seem that the primary statement should be there instead. To avoid the possiblity of conflicts in the future, I propose to make this a note.
[Note: A typedef-name that names a class type, or a cv-qualified version thereof, is also a class-name (9.1 [class.name]). If a typedef-name is used following the class-key in an elaborated-type-specifier (7.1.6.3 [dcl.type.elab]), or in the class-head of a class declaration (9 [class]), or is used as the identifier in the declarator for a constructor or destructor declaration (12.1 [class.ctor], 12.4 [class.dtor]), to identify the subject of an elaborated-type-specifier (7.1.6.3 [dcl.type.elab]), class declaration (clause 9 [class]), constructor declaration (12.1 [class.ctor]), or destructor declaration (12.4 [class.dtor]), the program is ill-formed. ] [Example: ...
7.1.6.3 [dcl.type.elab] paragraph 2:
This is the only remaining (normative) statement that a typedef-name can't be used in an elaborated-type-specifier. The reference to template type-parameter is deleted by the resolution of issue 283.
... If the identifier resolves to a typedef-name or a template type-parameter, the elaborated-type-specifier is ill-formed. [Note: ...
8 [dcl.decl] grammar rule declarator-id:
When I looked carefully into the statement of the rule prohibiting a typedef-name in a constructor declaration, it appeared to me that this grammar rule (inadvertently?) allows something that's always forbidden semantically.
declarator-id:
id-expression
::opt nested-name-specifieropt type-name class-name
9.1 [class.name] paragraph 5:
Unlike the prohibitions against appearing in an elaborated-type-specifier or constructor or destructor declarator, each of which was expressed more than once, the prohibition against a typedef-name appearing in a class-head was previously stated only in 7.1.3 [dcl.typedef]. It seems to me that that prohibition belongs here instead. Also, it seems to me important to clarify that a typedef-name that is a class-name is still a typedef-name. Otherwise, the various prohibitions can be argued around easily, if perversely ("But that isn't a typedef-name, it's a class-name; it says so right there in 9.1 [class.name].")
A typedef-name (7.1.3 [dcl.typedef]) that names a class type or a cv-qualified version thereof is also a class-name, but shall not be used in an elaborated-type-specifier; see also 7.1.3 [dcl.typedef]. as the identifier in a class-head.
12.1 [class.ctor] paragraph 3:
The new nonterminal references are needed to really nail down what we're talking about here. Otherwise, I'm just eliminating redundancy. (A typedef-name that doesn't name a class type is no more valid here than one that does.)
A typedef-name that names a class is a class-name (7.1.3 [dcl.typedef]); however, a A typedef-name that names a class shall not be used as the identifier class-name in the declarator declarator-id for a constructor declaration.
12.4 [class.dtor] paragraph 1:
The same comments apply here as to 12.1 [class.ctor].
... A typedef-name that names a class is a class-name (7.1.3); however, a A typedef-name that names a class shall not be used as the identifier class-name following the ~ in the declarator for a destructor declaration.
[Voted into WP at April 2003 meeting.]
A use of an injected-class-name in an elaborated-type-specifier should not name the constructor of the class, but rather the class itself, because in that context we know that we're looking for a type. See issue 147.
Proposed Resolution (revised October 2002):
This clarifies the changes made in the TC for issue 147.
In 3.4.3.1 [class.qual] paragraph 1a replace:
If the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected class name of C (clause 9 [class]), the name is instead considered to name the constructor of class C.
with
In a lookup in which the constructor is an acceptable lookup result, if the nested-name-specifier nominates a class C and the name specified after the nested-name-specifier, when looked up in C, is the injected class name of C (clause 9 [class]), the name is instead considered to name the constructor of class C. [Note: For example, the constructor is not an acceptable lookup result in an elaborated type specifier so the constructor would not be used in place of the injected class name.]
Note that issue 263 updates a part of the same paragraph.
Append to the example:
struct A::A a2; // object of type A
[Voted into WP at March 2004 meeting.]
Consider this code:
struct A { int i; struct i {}; };
struct B { int i; struct i {}; };
struct D : public A, public B { using A::i; void f (); };
void D::f () { struct i x; }
I can't find anything in the standard that says definitively what this means. 7.3.3 [namespace.udecl] says that a using-declaration shall name "a member of a base class" -- but here we have two members, the data member A::i and the class A::i.
Personally, I'd find it more attractive if this code did not work. I'd like "using A::i" to mean "lookup A::i in the usual way and bind B::i to that", which would mean that while "i = 3" would be valid in D::f, "struct i x" would not be. However, if there were no A::i data member, then "A::i" would find the struct and the code in D::f would be valid.
John Spicer: I agree with you, but unfortunately the standard committee did not.
I remembered that this was discussed by the committee and that a resolution was adopted that was different than what I hoped for, but I had a hard time finding definitive wording in the standard.
I went back though my records and found the paper that proposed a resolution and the associated committee motion that adopted the proposed resolution The paper is N0905, and "option 1" from that paper was adopted at the Stockholm meeting in July of 1996. The resolution is that "using A::i" brings in everything named i from A.
3.4.3.2 [namespace.qual] paragraph 2 was modified to implement this resolution, but interestingly that only covers the namespace case and not the class case. I think the class case was overlooked when the wording was drafted. A core issue should be opened to make sure the class case is handled properly.
Notes from April 2003 meeting:
This is related to issue 11. 7.3.3 [namespace.udecl] paragraph 10 has an example for namespaces.
Proposed resolution (October 2003):
Add a bullet to the end of 3.4.3.1 [class.qual] paragraph 1:
Change the beginning of 7.3.3 [namespace.udecl] paragraph 4 from
A using-declaration used as a member-declaration shall refer to a member of a base class of the class being defined, shall refer to a member of an anonymous union that is a member of a base class of the class being defined, or shall refer to an enumerator for an enumeration type that is a member of a base class of the class being defined.
to
In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined. Such a using-declaration introduces the set of declarations found by member name lookup (10.2 [class.member.lookup], 3.4.3.1 [class.qual]).
[Voted into WP at April 2003 meeting.]
I have some concerns with the description of name lookup for elaborated type specifiers in 3.4.4 [basic.lookup.elab]:
Paragraph 2 has some parodoxical statements concerning looking up names that are simple identifers:
If the elaborated-type-specifier refers to an enum-name and this lookup does not find a previously declared enum-name, the elaborated-type-specifier is ill-formed. If the elaborated-type-specifier refers to an [sic] class-name and this lookup does not find a previously declared class-name... the elaborated-type-specifier is a declaration that introduces the class-name as described in 3.3.2 [basic.scope.pdecl]."
It is not clear how an elaborated-type-specifier can refer to an enum-name or class-name given that the lookup does not find such a name and that class-name and enum-name are not part of the syntax of an elaborated-type-specifier.
The second sentence quoted above seems to suggest that the name found will not be used if it is not a class name. typedef-name names are ill-formed due to the sentence preceding the quote. If lookup finds, for instance, an enum-name then a new declaration will be created. This differs from C, and from the enum case, and can have surprising effects:
struct S {
enum E {
one = 1
};
class E* p; // declares a global class E?
};
Was this really the intent? If this is the case then some more work is needed on 3.4.4 [basic.lookup.elab]. Note that the section does not make finding a type template formal ill-formed, as is done in 7.1.6.3 [dcl.type.elab]. I don't see anything that makes a type template formal name a class-name. So the example in 7.1.6.3 [dcl.type.elab] of friend class T; where T is a template type formal would no longer be ill-formed with this interpretation because it would declare a new class T.
(See also issue 254.)
Notes from the 4/02 meeting:
This will be consolidated with the changes for issue 254. See also issue 298.
Proposed resolution (October 2002):
As given in N1376=02-0034. Note that the inserts and strikeouts in that document do not display correctly in all browsers; <del> --> <strike> and <ins> --> <b>, and the similar changes for the closing delimiters, seem to do the trick.
[Voted into WP at April 2003 meeting.]
The text in 3.4.4 [basic.lookup.elab] paragraph 2 twice refers to the possibility that an elaborated-type-specifier might have the form
class-key identifier ;
However, the grammar for elaborated-type-specifier does not include a semicolon.
In both 3.4.4 [basic.lookup.elab] and 7.1.6.3 [dcl.type.elab], the text asserts that an elaborated-type-specifier that refers to a typedef-name is ill-formed. However, it is permissible for the form of elaborated-type-specifier that begins with typename to refer to a typedef-name.
This problem is the result of adding the typename form to the elaborated-type-name grammar without changing the verbiage correspondingly. It could be fixed either by updating the verbiage or by moving the typename syntax into its own production and referring to both nonterminals when needed.
(See also issue 180. If this issue is resolved in favor of a separate nonterminal in the grammar for the typename forms, the wording in that issue's resolution must be changed accordingly.)
Notes from 04/01 meeting:
The consensus was in favor of moving the typename forms out of the elaborated-type-specifier grammar.
Notes from the 4/02 meeting:
This will be consolidated with the changes for issue 245.
Proposed resolution (October 2002):
As given in N1376=02-0034.
[Voted into the WP at the June, 2008 meeting.]
3.4.5 [basic.lookup.classref] paragraph 1 says,
In a class member access expression (5.2.5 [expr.ref] ), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2 [temp.names] ) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class or function template.
There do not seem to be any circumstances in which use of a non-member template function would be well-formed as the id-expression of a class member access expression.
Proposed Resolution (November, 2006):
Change 3.4.5 [basic.lookup.classref] paragraph 1 as follows:
In a class member access expression (5.2.5 [expr.ref]), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2 [temp.names]) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class or function template...
[Voted into WP at the October, 2006 meeting.]
I believe this program is invalid:
struct A {
};
struct C {
struct A {};
void f ();
};
void C::f () {
::A *a;
a->~A ();
}
The problem is that 3.4.5 [basic.lookup.classref] says that you have to look
up A in both the context of the pointed-to-type (i.e.,
::A), and
in the context of the postfix-expression (i.e., the body of C::f), and
that if the name is found in both places it must name the same type in
both places.
The EDG front end does not issue an error about this program, though.
Am I reading the standardese incorrectly?
John Spicer: I think you are reading it correctly. I think I've been hoping that this would get changed. Unlike other dual lookup contexts, this is one in which the compiler already knows the right answer (the type must match that of the left hand of the -> operator). So I think that if either of the types found matches the one required, it should be sufficient. You can't say a->~::A(), which means you are forced to say a->::A::~A(), which disables the virtual mechanism. So you would have to do something like create a local typedef for the desired type.
See also issues 244, 399, and 466.
Proposed resolution (April, 2006):
Remove the indicated text from 3.4.5 [basic.lookup.classref] paragraph 2:
If the id-expression in a class member access (5.2.5 [expr.ref]) is an unqualified-id, and the type of the object expression is of a class type C (or of pointer to a class type C), the unqualified-id is looked up in the scope of class C...
Change 3.4.5 [basic.lookup.classref] paragraph 3 as indicated:
If the unqualified-id is ~type-name,
the type-name is looked up in the context of the entire
postfix-expression. and If the
type T of the object expression is of a class
type C (or of pointer to a class type C),
the type-name is also looked up in the context of the
entire postfix-expression and in the scope of
class C. The type-name shall refer to
a class-name. If type-name is found in both contexts,
the name shall refer to the same class type. If the type of the object
expression is of scalar type, the type-name is looked up in the
scope of the complete postfix-expression. At least one
of the lookups shall find a name that refers to (possibly
cv-qualified)
T. [Example:
struct A { };
struct B {
struct A { };
void f(::A* a);
};
void B::f(::A* a) {
a->~A(); // OK, lookup in *a finds the injected-class-name
}
—end example]
[Note: this change also resolves issue 414.]
[Voted into WP at October 2004 meeting.]
The example in 3.4.5 [basic.lookup.classref] paragraph 4 is wrong (see 11.2 [class.access.base] paragraph 5; the cast to the naming class can't be done) and needs to be corrected. This was noted when the final version of the algorithm for issue 39 was checked against it.
Proposed Resolution (October 2003):
Remove the entire note at the end of 3.4.5 [basic.lookup.classref] paragraph 4, including the entire example.
[Voted into WP at the October, 2006 meeting.]
By 3.4.5 [basic.lookup.classref] paragraph 3, the following is ill-formed because the two lookups of the destructor name (in the scope of the class of the object and in the surrounding context) find different Xs:
struct X {};
int main() {
X x;
struct X {};
x.~X(); // Error?
}
This is silly, because the compiler knows what the type has to be, and one of the things found matches that. The lookup should require only that one of the lookups finds the required class type.
Proposed resolution (April, 2005):
This issue is resolved by the resolution of issue 305.
[Moved to DR at 10/01 meeting.]
3.5 [basic.link] paragraph 4 says (among other things):A name having namespace scope has external linkage if it is the name ofThat prohibits for example:
- [...]
- a named enumeration (7.2 [dcl.enum]), or an unnamed enumeration defined in a typedef declaration in which the enumeration has the typedef name for linkage purposes (7.1.3 [dcl.typedef])
typedef enum { e1 } *PE;
void f(PE) {} // Cannot declare a function (with linkage) using a
// type with no linkage.
However, the same prohibition was not made for class scope types. Indeed, 3.5 [basic.link] paragraph 5 says:
In addition, a member function, static data member, class or enumeration of class scope has external linkage if the name of the class has external linkage.
That allows for:
struct S {
typedef enum { e1 } *MPE;
void mf(MPE) {}
};
My guess is that this is an unintentional consequence of 3.5 [basic.link] paragraph 5, but I would like confirmation on that.
Proposed resolution:
Change text in 3.5 [basic.link] paragraph 5 from:
In addition, a member function, static data member, class or enumeration of class scope has external linkage if the name of the class has external linkage.to:
In addition, a member function, a static data member, a named class or enumeration of class scope, or an unnamed class or enumeration defined in a class-scope typedef declaration such that the class or enumeration has the typedef name for linkage purposes (7.1.3 [dcl.typedef]), has external linkage if the name of the class has external linkage.
[Voted into WP at October 2004 meeting.]
According to 3.5 [basic.link] paragraph 8, "A name with no linkage ... shall not be used to declare an entity with linkage." This would appear to rule out code such as:
typedef struct {
int i;
} *PT;
extern "C" void f(PT);
[likewise]
static enum { a } e;
which seems rather harmless to me.
See issue 132, which dealt with a closely related issue.
Andrei Iltchenko submitted the same issue via comp.std.c++ on 17 Dec 2001:
Paragraph 8 of Section 3.5 [basic.link] contains the following sentences: "A name with no linkage shall not be used to declare an entity with linkage. If a declaration uses a typedef name, it is the linkage of the type name to which the typedef refers that is considered."
The problem with this wording is that it doesn't cover cases where the type to which a typedef-name refers has no name. As a result it's not clear whether, for example, the following program is well-formed:
#include <vector>
int main()
{
enum { sz = 6u };
typedef int (* aptr_type)[sz];
typedef struct data {
int i, j;
} * elem_type;
std::vector<aptr_type> vec1;
std::vector<elem_type> vec2;
}
Suggested resolution:
My feeling is that the rules for whether or not a typedef-name used in a declaration shall be treated as having or not having linkage ought to be modelled after those for dependent types, which are explained in 14.6.2.1 [temp.dep.type].
Add the following text at the end of Paragraph 8 of Section 3.5 [basic.link] and replace the following example:
In case of the type referred to by a typedef declaration not having a name,
the newly declared typedef-name has linkage if and only if its referred type
comprises no names of no linkage excluding local names that are eligible for
appearance in an integral constant-expression (5.19 [expr.const]).
[Note: if the referred
type contains a typedef-name that does not denote an unnamed class, the
linkage of that name is established by the recursive application of this
rule for the purposes of using typedef names in declarations.] [Example:
void f()
{
struct A { int x; }; // no linkage
extern A a; // ill-formed
typedef A Bl
extern B b; // ill-formed
enum { sz = 6u };
typedef int (* C)[sz]; // C has linkage because sz can
// appear in a constant expression
}
--end example.]
Additional issue (13 Jan 2002, from Andrei Iltchenko):
Paragraph 2 of Section 14.3.1 [temp.arg.type] is inaccurate and unnecessarily prohibits a few important cases; it says "A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template-parameter." The inaccuracy stems from the fact that it is not a type but its name that can have a linkage.
For example based on the current wording of 14.3.1 [temp.arg.type], the following example is ill-formed.
#include <vector>
struct data {
int i, j;
};
int main()
{
enum { sz = 6u };
std::vector<int(*)[sz]> vec1; // The types 'int(*)[sz]' and 'data*'
std::vector<data*> vec2; // have no names and are thus illegal
// as template type arguments.
}
Suggested resolution:
Replace the whole second paragraph of Section 14.3.1 [temp.arg.type] with the following wording:
A type whose name does not have a linkage or a type compounded from any such
type shall not be used as a template-argument for a template-parameter. In
case of a type T used as a template type argument not having a name,
T
constitutes a valid template type argument if and only if the name of an
invented typedef declaration referring to T would have linkage;
see 3.5.
[Example:
template <class T> class X { /* ... */ };
void f()
{
struct S { /* ... */ };
enum { sz = 6u };
X<S> x3; // error: a type name with no linkage
// used as template-argument
X<S*> x4; // error: pointer to a type name with
// no linkage used as template-argument
X<int(*)[sz]> x5; // OK: since the name of typedef int
// (*pname)[sz] would have linkage
}
--end example] [Note: a template type argument may be an incomplete type
(3.9 [basic.types]).]
Proposed resolution:
This is resolved by the changes for issue 389. The present issue was moved back to Review status in February 2004 because 389 was moved back to Review.
[Voted into WP at October 2004 meeting.]
3.5 [basic.link] paragraph 8 says (among other things):
A name with no linkage (notably, the name of a class or enumeration declared in a local scope (3.3.3 [basic.scope.local])) shall not be used to declare an entity with linkage. If a declaration uses a typedef name, it is the linkage of the type name to which the typedef refers that is considered.
I would expect this to catch situations such as the following:
// File 1:
typedef struct {} *UP;
void f(UP) {}
// File 2:
typedef struct {} *UP; // Or: typedef struct {} U, *UP;
void f(UP);
The problem here is that most implementations must generate the same mangled name for "f" in two translation units. The quote from the standard above isn't quite clear, unfortunately: There is no type name to which the typedef refers.
A related situation is the following:
enum { no, yes } answer;
The variable "answer" is declared as having external linkage, but it is
declared
with an unnamed type. Section 3.5 [basic.link]
talks about the linkage of names, however,
and does therefore not prohibit this. There is no implementation issue
for most
compilers because they do not ordinarily mangle variable names, but I
believe
the intent was to allow that implementation technique.
Finally, these problems are much less relevant when declaring names with internal linkage. For example, I would expect there to be few problems with:
typedef struct {} *UP;
static void g(UP);
I recently tried to interpret 3.5 [basic.link] paragraph 8 with the assumption that types with no names have no linkage. Surprisingly, this resulted in many diagnostics on variable declarations (mostly like "answer" above).
I'm pretty sure the standard needs clarifying words in this matter, but which way should it go?
See also issue 319.
Notes from April 2003 meeting:
There was agreement that this check is not needed for variables and functions with extern "C" linkage, and a change there is desirable to allow use of legacy C headers. The check is also not needed for entities with internal linkage, but there was no strong sentiment for changing that case.
We also considered relaxing this requirement for extern "C++" variables but decided that we did not want to change that case.
We noted that if extern "C" functions are allowed an additional check is needed when such functions are used as arguments in calls of function templates. Deduction will put the type of the extern "C" function into the type of the template instance, i.e., there would be a need to mangle the name of an unnamed type. To plug that hole we need an additional requirement on the template created in such a case.
Proposed resolution (April 2003, revised slightly October 2003 and March 2004):
In 3.5 [basic.link] paragraph 8, change
A name with no linkage (notably, the name of a class or enumeration declared in a local scope (3.3.3 [basic.scope.local])) shall not be used to declare an entity with linkage. If a declaration uses a typedef name, it is the linkage of the type name to which the typedef refers that is considered.
to
A type is said to have linkage if and only ifA type without linkage shall not be used as the type of a variable or function with linkage, unless the variable or function has extern "C" linkage (7.5 [dcl.link]). [Note: in other words, a type without linkage contains a class or enumeration that cannot be named outside of its translation unit. An entity with external linkage declared using such a type could not correspond to any other entity in another translation unit of the program and is thus not permitted. Also note that classes with linkage may contain members whose types do not have linkage, and that typedef names are ignored in the determination of whether a type has linkage.]
- it is a class or enumeration type that is named (or has a name for linkage purposes (7.1.3 [dcl.typedef])) and the name has linkage; or
- it is a specialization of a class template (14 [temp]) [Footnote: a class template always has external linkage, and the requirements of 14.3.1 [temp.arg.type] and 14.3.2 [temp.arg.nontype] ensure that the template arguments will also have appropriate linkage]; or
- it is a fundamental type (3.9.1 [basic.fundamental]); or
- it is a compound type (3.9.2 [basic.compound]) other than a class or enumeration, compounded exclusively from types that have linkage; or
- it is a cv-qualified (3.9.3 [basic.type.qualifier]) version of a type that has linkage.
Change 14.3.1 [temp.arg.type] paragraph 2 from (note: this is the wording as updated by issue 62)
The following types shall not be used as a template-argument for a template type-parameter:
- a type whose name has no linkage
- an unnamed class or enumeration type that has no name for linkage purposes (7.1.3 [dcl.typedef])
- a cv-qualified version of one of the types in this list
- a type created by application of declarator operators to one of the types in this list
- a function type that uses one of the types in this list
to
A type without linkage (3.5 [basic.link]) shall not be used as a template-argument for a template type-parameter.
Once this issue is ready, issue 319 should be moved back to ready as well.
[Voted into WP at October 2005 meeting.]
Consider the following bit of code:
namespace N {
struct S {
void f();
};
}
using namespace N;
void S::f() {
extern void g(); // ::g or N::g?
}
In 3.5 [basic.link] paragraph 7 the Standard says (among other things),
When a block scope declaration of an entity with linkage is not found to refer to some other declaration, then that entity is a member of the innermost enclosing namespace.
The question then is whether N is an “enclosing namespace” for the local declaration of g()?
Proposed resolution (October 2004):
Add the following text as a new paragraph at the end of 7.3.1 [namespace.def]:
The enclosing namespaces of a declaration are those namespaces in which the declaration lexically appears, except for a redeclaration of a namespace member outside its original namespace (e.g., a definition as specified in 7.3.1.2 [namespace.memdef]). Such a redeclaration has the same enclosing namespaces as the original declaration. [Example:namespace Q { namespace V { void f(); // enclosing namespaces are the global namespace, Q, and Q::V class C { void m(); }; } void V::f() { // enclosing namespaces are the global namespace, Q, and Q::V extern void h(); // ... so this declares Q::V::h } void V::C::m() { // enclosing namespaces are the global namespace, Q, and Q::V } }—end example]
[Moved to DR at 4/02 meeting.]
The Standard does not appear to address how the rules for order of initialization apply to static data members of class templates.
Suggested resolution: Add the following verbiage to either 3.6.2 [basic.start.init] or 9.4.2 [class.static.data]:
Initialization of static data members of class templates shall be performed during the initialization of static data members for the first translation unit to have static initialization performed for which the template member has been instantiated. This requirement shall apply to both the static and dynamic phases of initialization.
Notes from 04/01 meeting:
Enforcing an order of initialization on static data members of class templates will result in substantial overhead on access to such variables. The problem is that the initialization be required as the result of instantiation in a function used in the initialization of a variable in another translation unit. In current systems, the order of initialization of static data data members of class templates is not predictable. The proposed resolution is to state that the order of initialization is undefined.
Proposed resolution (04/01, updated slightly 10/01):
Replace the following sentence in 3.6.2 [basic.start.init] paragraph 1:
Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.
with
Dynamic initialization of an object is either ordered or unordered. Explicit specializations and definitions of class template static data members have ordered initialization. Other class template static data member instances have unordered initialization. Other objects defined in namespace scope have ordered initialization. Objects defined within a single translation unit and with ordered initialization shall be initialized in the order of their definitions in the translation unit. The order of initialization is unspecified for objects with unordered initialization and for objects defined in different translation units.
Note that this wording is further updated by issue 362.
Note (07/01):
Brian McNamara argues against the proposed resolution. The following excerpt captures the central point of a long message on comp.std.c++:
I have a class for representing linked lists which looks something liketemplate <class T> class List { ... static List<T>* sentinel; ... }; template <class T> List<T>* List<T>::sentinel( new List<T> ); // static member definitionThe sentinel list node is used to represent "nil" (the null pointer cannot be used with my implementation, for reasons which are immaterial to this discussion). All of the List's non-static member functions and constructors depend upon the value of the sentinel. Under the proposed resolution for issue #270, Lists cannot be safely instantiated before main() begins, as the sentinel's initialization is "unordered".
(Some readers may propose that I should use the "singleton pattern" in the List class. This is undesirable, for reasons I shall describe at the end of this post at the location marked "[*]". For the moment, indulge me by assuming that "singleton" is not an adequate solution.)
Though this is a particular example from my own experience, I believe it is representative of a general class of examples. It is common to use static data members of a class to represent the "distinguished values" which are important to instances of that class. It is imperative that these values be initialized before any instances of the class are created, as the instances depend on the values.
In a comp.std.c++ posting on 28 Jul 2001, Brian McNamara proposes the following alternative resolution:
Replace the following sentence in 3.6.2 [basic.start.init] paragraph 1:
Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.with
Objects with static storage duration defined in namespace scope shall be initialized in the order described below.and then after paragraph 1, add this text:
Dynamic initialization is either ordered or quasi-ordered. Explicit specializations of class template static data members have ordered initialization. Other class template static data member instances have quasi-ordered initialization. All other objects defined in namespace scope have ordered initialization. The order of initialization is specified as follows:along with a non-normative note along the lines of
- Objects that are defined within a single translation unit and that have ordered initialization shall be initialized in the order of their definitions in the translation unit.
- Objects that are defined only within a single translation unit and that have quasi-ordered initialization shall also be initialized in the order of their definitions in the translation unit -- that is, as though these objects had ordered initialization.
- Objects that are defined within multiple translation units (which, therefore, must have quasi-ordered initialization) shall be initialized as follows: in exactly one translation unit (which one is unspecified), the object shall be treated as though it has ordered initialization; in the other translation units which define the object, the object will be initialized before all other objects that have ordered initialization in those translation units.
- For any two objects, "X" and "Y", with static storage duration and defined in namespace scope, if the previous bullets do not imply a relationship for the initialization ordering between "X" and "Y", then the relative initialization order of these objects is unspecified.
[ Note: The intention is that translation units can each be compiled separately with no knowledge of what objects may be re-defined in other translation units. Each translation unit can contain a method which initializes all objects (both quasi-ordered and ordered) as though they were ordered. When these translation units are linked together to create an executable program, all of these objects can be initialized by simply calling the initialization methods (one from each translation unit) in any order. Quasi-ordered objects require some kind of guard to ensure that they are not initialized more than once (the first attempt to initialize such an object should succeed; any subsequent attempts should simply be ignored). ]
Erwin Unruh replies: There is a point which is not mentioned with this posting. It is the cost for implementing the scheme. It requires that each static template variable is instantiated in ALL translation units where it is used. There has to be a flag for each of these variables and this flag has to be checked in each TU where the instantiation took place.
I would reject this idea and stand with the proposed resolution of issue 270.
There just is no portable way to ensure the "right" ordering of construction.
Notes from 10/01 meeting:
The Core Working Group reaffirmed its previous decision.
[Voted into WP at April 2005 meeting.]
I have a couple of questions about 3.6.2 [basic.start.init], "Initialization of non-local objects." I believe I recall some discussion of related topics, but I can't find anything relevant in the issues list.
The first question arose when I discovered that different implementations treat reference initialization differently. Consider, for example, the following (namespace-scope) code:
int i; int& ir = i; int* ip = &i;Both initializers, "i" and "&i", are constant expressions, per 5.19 [expr.const] paragraph 4-5 (a reference constant expression and an address constant expression, respectively). Thus, both initializations are categorized as static initialization, according to 3.6.2 [basic.start.init] paragraph 1:
Zero-initialization and initialization with a constant expression are collectively called static initialization; all other initialization is dynamic initialization.
However, that does not mean that both ir and ip must be initialized at the same time:
Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.
Because "int&" is not a POD type, there is no requirement that it be initialized before dynamic initialization is performed, and implementations differ in this regard. Using a function called during dynamic initialization to print the values of "ip" and "&ir", I found that g++, Sun, HP, and Intel compilers initialize ir before dynamic initialization and the Microsoft compiler does not. All initialize ip before dynamic initialization. I believe this is conforming (albeit inconvenient :-) behavior.
So, my first question is whether it is intentional that a reference of static duration, initialized with a reference constant expression, need not be initialized before dynamic initialization takes place, and if so, why?
The second question is somewhat broader. As 3.6.2 [basic.start.init] is currently worded, it appears that there are no requirements on when ir is initialized. In fact, there is a whole category of objects -- non-POD objects initialized with a constant expression -- for which no ordering is specified. Because they are categorized as part of "static initialization," they are not subject to the requirement that they "shall be initialized in the order in which their definition appears in the translation unit." Because they are not POD types, they are not required to be initialized before dynamic initialization occurs. Am I reading this right?
My preference would be to change 3.6.2 [basic.start.init] paragraph 1 so that 1) references are treated like POD objects with respect to initialization, and 2) "static initialization" applies only to POD objects and references. Here's some sample wording to illustrate:
Suggested resolution:
Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. Initializing a reference, or an object of POD type, of static storage duration with a constant expression (5.19) is called constant initialization. Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place. [Remainder unchanged.]
Proposed Resolution:
Change 3.6.2 [basic.start.init] paragraph 1 as follows:
Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. Initializing a reference, or an object of POD type, of static storage duration with a constant expression (5.19) is called constant initialization. Together, zero-initialization and constant initialization are Zero-initialization and initialization with a constant expression are collectively called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.
[Voted into the WP at the September, 2008 meeting (resolution in paper N2757).]
Given this literal type,
struct X {
constexpr X() { }
};
and this definition,
static X x;
the current specification does not require that x be statically initialized because it is not “initialized with a constant expression” (3.6.1 [basic.start.main] paragraph 1).
Lawrence Crowl:
This guarantee is essential for atomics.
Jens Maurer:
Suggestion:
A reference with static storage duration or an object of literal type with static storage duration can be initialized with a constant expression (5.19 [expr.const]) or with a constexpr constructor; this is called constant initialization.
(Not spelling out “default constructor” makes it easier to handle multiple-parameter constexpr constructors, where there isn't “a” constant expression but several.)
Peter Dimov:
In addition, there is a need to enforce static initialization for non-literal types: std::shared_ptr, std::once_flag, and std::atomic_* all have nontrivial copy constructors, making them non-literal types. However, we need a way to ensure that a constexpr constructor called with constant expressions will guarantee static initialization, regardless of the nontriviality of the copy constructor.
Proposed resolution (April, 2008):
Change 3.6.2 [basic.start.init] paragraph 1 as follows:
...A reference with static storage duration and an object of trivial or literal type with static storage duration can be initialized with a constant expression (5.19 [expr.const]); this If a reference with static storage duration is initialized with a constant expression (5.19 [expr.const]) or if the initialization of an object with static storage duration satisfies the requirements for the object being declared with constexpr (7.1.5 [dcl.constexpr]), that initialization is called constant initialization...
Change 6.7 [stmt.dcl] paragraph 4 as follows:
...A local object of trivial or literal type (3.9 [basic.types]) with static storage duration initialized with constant-expressions is initialized Constant initialization (3.6.2 [basic.start.init]) of a local entity with static storage duration is performed before its block is first entered...
Change 7.1.5 [dcl.constexpr] paragraph 7 as follows:
A constexpr specifier used in an object declaration declares the object as const. Such an object shall be initialized, and every expression that appears in its initializer (8.5 [dcl.init]) shall be a constant expression. Every implicit conversion used in converting the initializer expressions and every constructor call used for the initialization shall be one of those allowed in a constant expression (5.19 [expr.const])...
Replace 8.5.1 [dcl.init.aggr] paragraph 14 as follows:
When an aggregate with static storage duration is initialized with a brace-enclosed initializer-list, if all the member initializer expressions are constant expressions, and the aggregate is a trivial type, the initialization shall be done during the static phase of initialization (3.6.2 [basic.start.init]); otherwise, it is unspecified whether the initialization of members with constant expressions takes place during the static phase or during the dynamic phase of initialization. [Note: The order of initialization for aggregates with static storage duration is specified in 3.6.2 [basic.start.init] and 6.7 [stmt.dcl]. —end note]
(Note: the change to 3.6.2 [basic.start.init] paragraph 1 needs to be reconciled with the conflicting change in issue 684.)
[Voted into the WP at the June, 2008 meeting.]
The C++ standard has inherited the definition of the 'exit' function more or less unchanged from ISO C.
However, when the 'exit' function is called, objects of static extent which have been initialised, will be destructed if their types posses a destructor.
In addition, the C++ standard has inherited the definition of the 'signal' function and its handlers from ISO C, also pretty much unchanged.
The C standard says that the only standard library functions that may be called while a signal handler is executing, are the functions 'abort', 'signal' and 'exit'.
This introduces a bit of a nasty turn, as it is not at all unusual for the destruction of static objects to have fairly complex destruction semantics, often associated with resource release. These quite commonly involve apparently simple actions such as calling 'fclose' for a FILE handle.
Having observed some very strange behaviour in a program recently which in handling a SIGTERM signal, called the 'exit' function as indicated by the C standard.
But unknown to the programmer, a library static object performed some complicated resource deallocation activities, and the program crashed.
The C++ standard says nothing about the interaction between signals, exit and static objects. My observations, was that in effect, because the destructor called a standard library function other than 'abort', 'exit' or 'signal', while transitively in the execution context of the signal handler, it was in fact non-compliant, and the behaviour was undefined anyway.
This is I believe a plausible judgement, but given the prevalence of this common programming technique, it seems to me that we need to say something a lot more positive about this interaction.
Curiously enough, the C standard fails to say anything about the analogous interaction with functions registered with 'atexit' ;-)
Proposed Resolution (10/98):
The current Committee Draft of the next version of the ISO C standard specifies that the only standard library function that may be called while a signal handler is executing is 'abort'. This would solve the above problem.
[This issue should remain open until it has been decided that the next version of the C++ standard will use the next version of the C standard as the basis for the behavior of 'signal'.]
Notes (November, 2006):
C89 is slightly contradictory here: It allows any signal handler to terminate by calling abort, exit, longjmp, but (for asynchronous signals, i.e. not those produced by abort or raise) then makes calling any library function other than signal with the current signal undefined behavior (C89 7.7.1.1). For synchronous signals, C99 forbids calls to raise, but imposes no other restrictions. For asynchronous signals, C99 allows only calls to abort, _Exit, and signal with the current signal (C99 7.14.1.1). The current C++ WP refers to “plain old functions” and “conforming C programs” (18.10 [support.runtime] paragraph 6).
Proposed Resolution (November, 2006):
Change the footnote in 18.10 [support.runtime] paragraph 6 as follows:
In particular, a signal handler using exception handling is very likely to have problems. Also, invoking std::exit may cause destruction of objects, including those of the standard library implementation, which, in general, yields undefined behavior in a signal handler (see 1.9 [intro.execution]).
[Voted into WP at the October, 2006 meeting.]
According to 3.7.4.1 [basic.stc.dynamic.allocation] paragraph 3,
Any other allocation function that fails to allocate storage shall only indicate failure by throwing an exception of class std::bad_alloc (18.6.2.1 [bad.alloc]) or a class derived from std::bad_alloc.
Shouldn't this statement have the usual requirements for an unambiguous and accessible base class?
Proposed resolution (April, 2006):
Change the last sentence of 3.7.4.1 [basic.stc.dynamic.allocation] paragraph 3 as indicated:
Any other allocation function that fails to allocate storage shall only indicate failure only by throwing an exception of class std::bad_alloc (18.6.2.1 [bad.alloc]) or a class derived from std::bad_alloc a type that would match a handler (15.3 [except.handle]) of type std::bad_alloc (18.6.2.1 [bad.alloc]).
[Voted into the WP at the September, 2008 meeting (resolution in paper N2757).]
[Picked up by evolution group at October 2002 meeting.]
The default global operators delete are specified to not throw, but there is no requirement that replacement global, or class-specific, operators delete must not throw. That ought to be required.
In particular:
We already require that all versions of an allocator's deallocate() must not throw, so that part is okay.
Rationale (04/00):
Note (March, 2008):
The Evolution Working Group has accepted the intent of this issue and referred it to CWG for action for C++0x (see paper J16/07-0033 = WG21 N2173).
Proposed resolution (March, 2008):
Change 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 3 as follows:
A deallocation function shall not terminate by throwing an exception. The value of the first argument supplied to a deallocation function...
[Voted into WP at October 2005 meeting.]
Standard is clear on behaviour of default allocation/deallocation functions. However, it is surpisingly vague on requirements to the behaviour of user-defined deallocation function and an interaction between delete-expression and deallocation function. This caused a heated argument on fido7.su.c-cpp newsgroup.
Resume:
It is not clear if user-supplied deallocation function is called from delete-expr when the operand of delete-expr is the null pointer (5.3.5 [expr.delete]). If it is, standard does not specify what user-supplied deallocation function shall do with the null pointer operand (18.6.1 [new.delete]). Instead, Standard uses the term "has no effect", which meaning is too vague in context given (5.3.5 [expr.delete]).
Description:
Consider statements
char* p= 0; //result of failed non-throwing ::new char[] ::delete[] p;Argument passed to delete-expression is valid - it is the result of a call to the non-throwing version of ::new, which has been failed. 5.3.5 [expr.delete] paragraph 1 explicitly prohibit us to pass 0 without having the ::new failure.
Standard does NOT specify whether user-defined deallocation function should be called in this case, or not.
Specifically, standard says in 5.3.5 [expr.delete] paragraph 2:
...if the value of the operand of delete is the null pointer the operation has no effect.Standard doesn't specify term "has no effect". It is not clear from this context, whether the called deallocation function is required to have no effect, or delete-expression shall not call the deallocation function.
Furthermore, in para 4 standard says on default deallocation function:
If the delete-expression calls the implementation deallocation function (3.7.4.2 [basic.stc.dynamic.deallocation]), if the operand of the delete expression is not the null pointer constant, ...Why it is so specific on interaction of default deallocation function and delete-expr?
If "has no effect" is a requirement to the deallocation function, then it should be stated in 3.7.4.2 [basic.stc.dynamic.deallocation], or in 18.6.1.1 [new.delete.single] and 18.6.1.2 [new.delete.array], and it should be stated explicitly.
Furthermore, standard does NOT specify what actions shall be performed by user-supplied deallocation function if NULL is given (18.6.1.1 [new.delete.single] paragraph 12):
Required behaviour: accept a value of ptr that is null or that was returned by an earlier call to the default operator new(std::size_t) or operator new(std::size_t, const std::nothrow_t&).
The same corresponds to ::delete[] case.
Expected solution:
Notes from October 2002 meeting:
We believe that study of 18.6.1.1 [new.delete.single] paragraphs 12 and 13, 18.6.1.2 [new.delete.array] paragraphs 11 and 12, and 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 3 shows that the system-provided operator delete functions must accept a null pointer and ignore it. Those sections also show that a user-written replacement for the system-provided operator delete functions must accept a null pointer. There is no requirement that such functions ignore a null pointer, which is okay -- perhaps the reason for replacing the system-provided functions is to do something special with null pointer values (e.g., log such calls and return).
We believe that the standard should not require an implementation to call a delete function with a null pointer, but it must allow that. For the system-provided delete functions or replacements thereof, the standard already makes it clear that the delete function must accept a null pointer. For class-specific delete functions, we believe the standard should require that such functions accept a null pointer, though it should not mandate what they do with null pointers.
5.3.5 [expr.delete] needs to be updated to say that it is unspecified whether or not the operator delete function is called with a null pointer, and 3.7.4.2 [basic.stc.dynamic.deallocation] needs to be updated to say that any deallocation function must accept a null pointer.
Proposed resolution (October, 2004):
Change 5.3.5 [expr.delete] paragraph 2 as indicated:
If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, and the converted operand is used in place of the original operand for the remainder of this section. In either alternative, if the value of the operand of delete is the null pointer the operation has no effect may be a null pointer value. If it is not a null pointer value, in In the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a sub-object (1.8 [intro.object]) representing a base class of such an object (clause 10 [class.derived])...
Change 5.3.5 [expr.delete] paragraph 4 as follows (note that the old wording reflects the changes proposed by issue 442:
The cast-expression in a delete-expression shall be evaluated exactly once. If the delete-expression calls the implementation deallocation function (3.7.4.2 [basic.stc.dynamic.deallocation]), and if the value of the operand of the delete expression is not a null pointer, the deallocation function will deallocate the storage referenced by the pointer thus rendering the pointer invalid. [Note: the value of a pointer that refers to deallocated storage is indeterminate. —end note]
Change 5.3.5 [expr.delete] paragraphs 6-7 as follows:
The If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted. In the case of an array, the elements will be destroyed in order of decreasing address (that is, in reverse order of the completion of their constructor; see 12.6.2 [class.base.init]).
The If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will call a deallocation function (3.7.4.2 [basic.stc.dynamic.deallocation]). Otherwise, it is unspecified whether the deallocation function will be called. [Note: The deallocation function is called regardless of whether the destructor for the object or some element of the array throws an exception. —end note]
Change 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 3 as indicated:
The value of the first argument supplied to one of the a deallocation functions provided in the standard library may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call to the deallocation function has no effect. Otherwise, the value supplied to operator delete(void*) in the standard library shall be one of the values returned by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, const std::nothrow_t&) in the standard library, and the value supplied to operator delete[](void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[](std::size_t) or operator new[](std::size_t, const std::nothrow_t&) in the standard library.
[Note: this resolution also resolves issue 442.]
[Moved to DR at 4/02 meeting.]
Jack Rouse: 3.8 [basic.life] paragraph 1 includes:
The lifetime of an object is a runtime property of the object. The lifetime of an object of type T begins when:Consider the code:
- storage with the proper alignment and size for type T is obtained, and
- if T is a class type with a non-trivial constructor (12.1 [class.ctor] ), the constructor call has completed.
struct B {
B( int = 0 );
~B();
};
struct S {
B b1;
};
int main()
{
S s = { 1 };
return 0;
}
In the code above, class S does have a non-trivial constructor, the
default constructor generated by the compiler. According the text
above, the lifetime of the auto s would never begin because a
constructor for S is never called. I think the second case in the
text needs to include aggregate initialization.
Mike Miller: I see a couple of ways of fixing the problem. One way would be to change "the constructor call has completed" to "the object's initialization is complete."
Another would be to add following "a class type with a non-trivial constructor" the phrase "that is not initialized with the brace notation (8.5.1 [dcl.init.aggr] )."
The first formulation treats aggregate initialization like a constructor call; even POD-type members of an aggregate could not be accessed before the aggregate initialization completed. The second is less restrictive; the POD-type members of the aggregate would be usable before the initialization, and the members with non-trivial constructors (the only way an aggregate can acquire a non-trivial constructor) would be protected by recursive application of the lifetime rule.
Proposed resolution (04/01):
In 3.8 [basic.life] paragraph 1, change
If T is a class type with a non-trivial constructor (12.1 [class.ctor]), the constructor call has completed.
to
If T is a class type with a non-trivial constructor (12.1 [class.ctor]), the initialization is complete. [Note: the initialization can be performed by a constructor call or, in the case of an aggregate with an implicitly-declared non-trivial default constructor, an aggregate initialization (8.5.1 [dcl.init.aggr]).]
[Voted into WP at April 2003 meeting.]
The wording in 3.8 [basic.life] paragraph 6 allows an lvalue designating an out-of-lifetime object to be used as the operand of a static_cast only if the conversion is ultimately to "char&" or "unsigned char&". This description excludes the possibility of using a cv-qualified version of these types for no apparent reason.
Notes on 04/01 meeting:
The wording should be changed to allow cv-qualified char types.
Proposed resolution (04/01):
In 3.8 [basic.life] paragraph 6 change the third bullet:
[Voted into WP at March 2004 meeting.]
3.8 [basic.life] paragraph 1 second bullet says:
if T is a class type with a non-trivial constructor (12.1), the constructor call has completed.
This is confusing; what was intended is probably something like
if T is a class type and the constructor invoked to create the object is non-trivial (12.1), the constructor call has completed.
Proposed Resolution (October 2003):
As given above.
[Voted into the WP at the September, 2008 meeting.]
In ISO/IEC 14882:2003, the second bullet of 3.8 [basic.life] paragraph 1 reads,
if T is a class type with a non-trivial constructor (12.1 [class.ctor]), the constructor call has completed.
Issue 119 pointed out that aggregate initialization can be used with some classes with a non-trivial implicitly-declared default constructor, and that in such cases there is no call to the object's constructor. The resolution for that issue was to change the previously-cited wording to read,
If T is a class type with a non-trivial constructor (12.1 [class.ctor], the initialization is complete.
Later (but before the WP was revised with the wording from the resolution of issue 119), issue 404 changed the 2003 wording to read,
If T is a class type and the constructor invoked to create the object is non-trivial (12.1 [class.ctor]), the constructor call has completed.
thus reversing the effect of issue 119, whose whole purpose was to cover objects with non-trivial constructors that are not invoked.
Through an editorial error, the post-Redmond draft (N1905) still contained the original 2003 wording that should have been replaced by the resolution of issue 119, in addition to the new wording from the resolution:
if T is a class type and the constructor invoked to create the object is non-trivial (12.1 [class.ctor]), the constructor call has completed. the initialization is complete.
Finally, during the application of the edits for delegating constructors (N1986), this editing error was “fixed” by retaining the original 2003 wording (which was needed for the application of the change specified in N1986), so that the current draft (N2009) reads,
if T is a class type and the constructor invoked to create the object is non-trivial (12.1 [class.ctor]), the principal constructor call 12.6.2 [class.base.init]) has completed.
Because the completion of the call to the principal constructor corresponds to the point at which the object is “fully constructed” (15.2 [except.ctor] paragraph 2), i.e., its initialization is complete, I believe that the exact wording of the issue 119 resolution would be correct and should be restored verbatim.
Proposed resolution (June, 2008):
Change 3.8 [basic.life] paragraph 1 as follows:
The lifetime of an object is a runtime property of the object. An object is said to have non-trivial initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor. [Note: Initialization by a trivial copy constructor is non-trivial initialization. —end note] The lifetime of an object of type T begins when:
storage with the proper alignment and size for type T is obtained, and
if T is a class type and the constructor invoked to create the object is non-trivial (12.1 [class.ctor]), the principal constructor call (12.6.2 [class.base.init]) has completed. [Note: the initialization can be performed by a constructor call or, in the case of an aggregate with an implicitly-declared non-trivial default constructor, an aggregate initialization 8.5.1 [dcl.init.aggr]. —end note] the object has non-trivial initialization, its initialization is complete.
The lifetime of an object of type T ends when...
[Voted into the WP at the June, 2008 meeting.]
The original proposed wording for 3.9 [basic.types] paragraph 11 required a constexpr constructor for a literal class only “if the class has at least one user-declared constructor.” This wording was dropped during the review by CWG out of a desire to ensure that literal types not have any uninitialized members. Thus, a class like
struct pixel {
int x, y;
};
is not a literal type. However, if an object of that type is aggregate-initialized or value-initialized, there can be no uninitialized members; the missing wording should be restored in order to permit use of expressions like pixel().x as constant expressions.
Proposed resolution (February, 2008):
Change 3.9 [basic.types] paragraph 10 as follows:
A type is a literal type if it is:
- a scalar type; or
- a class type (clause 9 [class]) with
- a trivial copy constructor,
- a trivial destructor,
- a trivial default constructor or at least one constexpr constructor other than the copy constructor,
- no virtual base classes, and
- all non-static data members and base classes of literal types; or
- an array of literal type.
[Moved to DR at 4/02 meeting.]
3.10 [basic.lval] paragraph 15 lists the types via which an lvalue can be used to access the stored value of an object; using an lvalue type that is not listed results in undefined behavior. It is permitted to add cv-qualification to the actual type of the object in this access, but only at the top level of the type ("a cv-qualified version of the dynamic type of the object").
However, 4.4 [conv.qual] paragraph 4 permits a "conversion [to] add cv-qualifiers at levels other than the first in multi-level pointers." The combination of these two rules allows creation of pointers that cannot be dereferenced without causing undefined behavior. For instance:
int* jp;
const int * const * p1 = &jp;
*p1; // undefined behavior!
The reason that *p1 results in undefined behavior is that the type of the lvalue is const int * const", which is not "a cv-qualified version of" int*.
Since the conversion is permitted, we must give it defined semantics, hence we need to fix the wording in 3.10 [basic.lval] to include all possible conversions of the type via 4.4 [conv.qual].
Proposed resolution (04/01):
Add a new bullet to 3.10 [basic.lval] paragraph 15, following "a cv-qualified version of the dynamic type of the object:"
[Voted into the WP at the September, 2008 meeting.]
The requirements on an implementation when presented with an alignment-specifier not supported by that implementation in that context are contradictory: 3.11 [basic.align] paragraph 9 says,
If a request for a specific extended alignment in a specific context is not supported by an implementation, the implementation may reject the request as ill-formed. The implementation may also silently ignore the requested alignment.
In contrast, 7.6.2 [dcl.align] paragraph 2, bullet 4 says simply,
- if the constant expression evaluates to an extended alignment and the implementation does not support that alignment in the context of the declaration, the program is ill-formed
with no provision to “silently ignore” the requested alignment. These two passages need to be reconciled.
If the outcome of the reconciliation is to grant implementations the license to accept and ignore extended alignment requests, the specification should be framed in terms of mechanisms that already exist in the Standard, such as undefined behavior and/or conditionally-supported constructs; “ill-formed” is a category that is defined by the Standard, not something that an implementation can decide.
Notes from the February, 2008 meeting:
The consensus was that such requests should be ill-formed and require a diagnostic. However, it was also observed that an implementation need not reject an ill-formed program; the only requirement is that it issue a diagnostic. It would thus be permissible for an implementation to “noisily ignore” (as opposed to “silently ignoring”) an unsupported alignment request.
Proposed resolution (June, 2008):
Change 3.11 [basic.align] paragraph 9 as follows:
If a request for a specific extended alignment in a specific context is not supported by an implementation, the implementation may reject the request as program is ill-formed. The implementation may also silently ignore the requested alignment. [Note: aAdditionally, a request for runtime allocation of dynamic memory storage for which the requested alignment cannot be honored may shall be treated as an allocation failure. —end note]
[Voted into WP at April, 2006 meeting.]
The C standard says in 6.3.2.3, paragraph 4:
Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal.
C++ appears to be incompatible with the first sentence in only two areas:
A *a = 0;
void *v = a;
C++ (4.10 [conv.ptr] paragraph 2) says nothing about the value of v.
void *v = 0;
A *b = (A*)v; // aka static_cast<A*>(v)
C++ (5.2.9 [expr.static.cast] paragraph 10) says nothing about the value of b.
Suggested changes:
Add the following sentence to 4.10 [conv.ptr] paragraph 2:
The null pointer value is converted to the null pointer value of the destination type.
Add the following sentence to 5.2.9 [expr.static.cast] paragraph 10:
The null pointer value (4.10 [conv.ptr]) is converted to the null pointer value of the destination type.
Proposed resolution (October, 2005):
Add the indicated words to 4.10 [conv.ptr] paragraph 2:
An rvalue of type “pointer to cv T,” where T is an object type, can be converted to an rvalue of type “pointer to cv void”. The result of converting a “pointer to cv T” to a “pointer to cv void” points to the start of the storage location where the object of type T resides, as if the object is a most derived object (1.8 [intro.object]) of type T (that is, not a base class subobject). The null pointer value is converted to the null pointer value of the destination type.
Add the indicated words to 5.2.9 [expr.static.cast] paragraph 11:
An rvalue of type “pointer to cv1 void” can be converted to an rvalue of type “pointer to cv2 T,” where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. The null pointer value is converted to the null pointer value of the destination type. A value of type pointer to object converted to “pointer to cv void” and back, possibly with different cv-qualification, shall have its original value...
[Voted into the WP at the June, 2008 meeting as paper N2656.]
In the interest of promoting use of nullptr instead of the integer literal 0 as the null pointer constant, the proposal accepted by the Committee does not provide for converting a zero-valued integral constant to type std::nullptr_t. However, this omission reduces the utility of the feature for use in the library for smart pointers. In particular, the addition of that conversion (along with a converting constructor accepting a std::nullptr_t) would allow smart pointers to be used just like ordinary pointers in expressions like:
if (p == 0) { }
if (0 == p) { }
if (p != 0) { }
if (0 != p) { }
p = 0;
The existing use of the “unspecified bool type” idiom supports this usage, but being able to use std::nullptr_t instead would be simpler and more elegant.
Jason Merrill: I have another reason to support the conversion as well: it seems to me very odd for nullptr_t to be more restrictive than void*. Anything we can do with an arbitrary pointer, we ought to be able to do with nullptr_t as well. Specifically, since there is a standard conversion from literal 0 to void*, and there is a standard conversion from void* to bool, nullptr_t should support the same conversions.
This changes two of the example lines in the proposal as adopted:
if (nullptr) ; // error, no conversion to bool
if (nullptr == 0) ; // error
become
if (nullptr) ; // evaluates to false
if( nullptr == 0 ); // evaluates to true
And later,
char* ch3 = expr ? nullptr : nullptr; // ch3 is the null pointer value
char* ch4 = expr ? 0 : nullptr; // ch4 is the null pointer value
int n3 = expr ? nullptr : nullptr; // error, nullptr_t can’t be converted to int
int n4 = expr ? 0 : nullptr; // error, nullptr_t can’t be converted to int
I would also allow reinterpret_cast from nullptr_t to integral type, with the same semantics as a reinterpret_cast from the null pointer value to integral type.
Basically, I would like nullptr_t to act like a void* which is constrained to always be (void*)0.
[Voted into WP at the October, 2006 meeting.]
When the Standard refers to a virtual base class, it should be understood to include base classes of virtual bases. However, the Standard doesn't actually say this anywhere, so when 4.11 [conv.mem] (for example) forbids casting to a derived class member pointer from a virtual base class member pointer, it could be read as meaning:
struct B {};
struct D : public B {};
struct D2 : virtual public D {};
int B::*p;
int D::*q;
void f() {
static_cast<int D2::*>(p); // permitted
static_cast<int D2::*>(q); // forbidden
}
Proposed resolution (October, 2005):
Change 4.11 [conv.mem] paragraph 2 as indicated:
...If B is an inaccessible (clause 11 [class.access]), ambiguous (10.2 [class.member.lookup]) or virtual (10.1 [class.mi]) base class of D, or a base class of a virtual base class of D, a program that necessitates this conversion is ill-formed...
Change 5.2.9 [expr.static.cast] paragraph 2 as indicated:
...and B is not neither a virtual base class of D nor a base class of a virtual base class of D...
Change 5.2.9 [expr.static.cast] paragraph 9 as indicated:
...and B is not neither a virtual base class of D nor a base class of a virtual base class of D...
[Voted into the WP at the September, 2008 meeting.]
I believe that the committee has neglected to take into account one of the differences between C and C++ when defining sequence points. As an example, consider
(a += b) += c;
where a, b, and c all have type int. I believe that this expression has undefined behavior, even though it is well-formed. It is not well-formed in C, because += returns an rvalue there. The reason for the undefined behavior is that it modifies the value of `a' twice between sequence points.
Expressions such as this one are sometimes genuinely useful. Of course, we could write this particular example as
a += b; a += c;
but what about
void scale(double* p, int n, double x, double y) {
for (int i = 0; i < n; ++i) {
(p[i] *= x) += y;
}
}
All of the potential rewrites involve multiply-evaluating p[i] or unobvious circumlocations like creating references to the array element.
One way to deal with this issue would be to include built-in operators in the rule that puts a sequence point between evaluating a function's arguments and evaluating the function itself. However, that might be overkill: I see no reason to require that in
x[i++] = y;
the contents of `i' must be incremented before the assignment.
A less stringent alternative might be to say that when a built-in operator yields an lvalue, the implementation shall not subsequently change the value of that object as a consequence of that operator.
I find it hard to imagine an implementation that does not do this already. Am I wrong? Is there any implementation out there that does not `do the right thing' already for (a += b) += c?
5.17 [expr.ass] paragraph 1 says,
The result of the assignment operation is the value stored in the left operand after the assignment has taken place; the result is an lvalue.
What is the normative effect of the words "after the assignment has taken place"? I think that phrase ought to mean that in addition to whatever constraints the rules about sequence points might impose on the implementation, assignment operators on built-in types have the additional constraint that they must store the left-hand side's new value before returning a reference to that object as their result.
One could argue that as the C++ standard currently stands, the effect of x = y = 0; is undefined. The reason is that it both fetches and stores the value of y, and does not fetch the value of y in order to compute its new value.
I'm suggesting that the phrase "after the assignment has taken place" should be read as constraining the implementation to set y to 0 before yielding the value of y as the result of the subexpression y = 0.
Francis Glassborow:
My understanding is that for a single variable:
It is the 3) that is often ignored because in practice the compiler hardly ever codes for the read because it already has that value but in complicated evaluations with a shortage of registers, that is not always the case. Without getting too close to the hardware, I think we both know that a read too close to a write can be problematical on some hardware.
So, in x = y = 0;, the implementation must NOT fetch a value from y, instead it has to "know" what that value will be (easy because it has just computed that in order to know what it must, at some time, store in y). From this I deduce that computing the lvalue (to know where to store) and the rvalue to know what is stored are two entirely independent actions that can occur in any order commensurate with the overall requirements that both operands for an operator be evaluated before the operator is.
Erwin Unruh:
C distinguishes between the resulting value of an assignment and putting the value in store. So in C a compiler might implement the statement x=y=0; either as x=0;y=0; or as y=0;x=0; In C the statement (x += 5) += 7; is not allowed because the first += yields an rvalue which is not allowed as left operand to +=. So in C an assignment is not a sequence of write/read because the result is not really "read".
In C++ we decided to make the result of assignment an lvalue. In this case we do not have the option to specify the "value" of the result. That is just the variable itself (or its address in a different view). So in C++, strictly speaking, the statement x=y=0; must be implemented as y=0;x=y; which makes a big difference if y is declared volatile.
Furthermore, I think undefined behaviour should not be the result of a single mentioning of a variable within an expression. So the statement (x +=5) += 7; should NOT have undefined behaviour.
In my view the semantics could be:
Jerry Schwarz:
My recollection is different from Erwin's. I am confident that the intention when we decided to make assignments lvalues was not to change the semantics of evaluation of assignments. The semantics was supposed to remain the same as C's.
Ervin seems to assume that because assignments are lvalues, an assignment's value must be determined by a read of the location. But that was definitely not our intention. As he notes this has a significant impact on the semantics of assignment to a volatile variable. If Erwin's interpretation were correct we would have no way to write a volatile variable without also reading it.
Lawrence Crowl:
For x=y=0, lvalue semantics implies an lvalue to rvalue conversion on the result of y=0, which in turn implies a read. If y is volatile, lvalue semantics implies both a read and a write on y.
The standard apparently doesn't state whether there is a value dependence of the lvalue result on the completion of the assignment. Such a statement in the standard would solve the non-volatile C compatibility issue, and would be consistent with a user-implemented operator=.
Another possible approach is to state that primitive assignment operators have two results, an lvalue and a corresponding "after-store" rvalue. The rvalue result would be used when an rvalue is required, while the lvalue result would be used when an lvalue is required. However, this semantics is unsupportable for user-defined assignment operators, or at least inconsistent with all implementations that I know of. I would not enjoy trying to write such two-faced semantics.
Erwin Unruh:
The intent was for assignments to behave the same as in C. Unfortunately the change of the result to lvalue did not keep that. An "lvalue of type int" has no "int" value! So there is a difference between intent and the standard's wording.
So we have one of several choices:
I think the last one has the least impact on existing programs, but it is an ugly solution.
Andrew Koenig:
Whatever we may have intended, I do not think that there is any clean way of making
volatile int v;
int i;
i = v = 42;
have the same semantics in C++ as it does in C. Like it or not, the
subexpression v = 42 has the type ``reference to volatile int,''
so if this statement has any meaning at all, the meaning must be to store 42
in v and then fetch the value of v to assign it to i.
Indeed, if v is volatile, I cannot imagine a conscientious programmer writing a statement such as this one. Instead, I would expect to see
v = 42;
i = v;
if the intent is to store 42 in v and then fetch the (possibly
changed) value of v, or
v = 42;
i = 42;
if the intent is to store 42 in both v and i.
What I do want is to ensure that expressions such as ``i = v = 42'' have well-defined semantics, as well as expressions such as (i = v) = 42 or, more realistically, (i += v) += 42 .
I wonder if the following resolution is sufficient:
Append to 5.17 [expr.ass] paragraph 1:
There is a sequence point between assigning the new value to the left operand and yielding the result of the assignment expression.
I believe that this proposal achieves my desired effect of not constraining when j is incremented in x[j++] = y, because I don't think there is a constraint on the relative order of incrementing j and executing the assignment. However, I do think it allows expressions such as (i += v) += 42, although with different semantics from C if v is volatile.
Notes on 10/01 meeting:
There was agreement that adding a sequence point is probably the right solution.
Notes from the 4/02 meeting:
The working group reaffirmed the sequence-point solution, but we will look for any counter-examples where efficiency would be harmed.
For drafting, we note that ++x is defined in 5.3.2 [expr.pre.incr] as equivalent to x+=1 and is therefore affected by this change. x++ is not affected. Also, we should update any list of all sequence points.
Notes from October 2004 meeting:
Discussion centered around whether a sequence point “between assigning the new value to the left operand and yielding the result of the expression” would require completion of all side effects of the operand expressions before the value of the assignment expression was used in another expression. The consensus opinion was that it would, that this is the definition of a sequence point. Jason Merrill pointed out that adding a sequence point after the assignment is essentially the same as rewriting
b += a
as
b += a, b
Clark Nelson expressed a desire for something like a “weak” sequence point that would force the assignment to occur but that would leave the side effects of the operands unconstrained. In support of this position, he cited the following expression:
j = (i = j++)
With the proposed addition of a full sequence point after the assignment to i, the net effect is no change to j. However, both g++ and MSVC++ behave differently: if the previous value of j is 5, the value of the expression is 5 but j gets the value 6.
Clark Nelson will investigate alternative approaches and report back to the working group.
Proposed resolution (March, 2008):
See issue 637.
[Voted into WP at March 2004 meeting.]
I have found what looks like a bug in clause 5 [expr], paragraph 4:
Between the previous and next sequence point a scalar object shall
have its stored value modified at most once by the evaluation of an
expression. Furthermore, the prior value shall be accessed only to
determine the value to be stored. The requirements of this
paragraph shall be met for each allowable ordering of the
subexpressions of a full expression; otherwise the behavior is
undefined. Example:
i = v[i++]; // the behavior is unspecified
i = 7, i++, i++; // i becomes 9
i = ++i + 1; // the behavior is unspecified
i = i + 1; // the value of i is incremented
--end example]
So which is it, unspecified or undefined?
Notes from October 2002 meeting:
We should find out what C99 says and do the same thing.
Proposed resolution (April 2003):
Change the example in clause 5 [expr], paragraph 4 from
[Example:i = v[i++]; // the behavior is unspecified i = 7, i++, i++; // i becomes 9 i = ++i + 1; // the behavior is unspecified i = i + 1; // the value of i is incremented--- end example]
to (changing "unspecified" to "undefined" twice)
[Example:i = v[i++]; // the behavior is undefined i = 7, i++, i++; // i becomes 9 i = ++i + 1; // the behavior is undefined i = i + 1; // the value of i is incremented--- end example]
[Voted into WP at October 2005 meeting.]
Clause 5 [expr] par. 5 of the standard says:
If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression is a constant expression (5.19), in which case the program is ill-formed.
Well, we do know that except in some contexts (e.g. controlling expression of a #if, array bounds), a compiler is not required to evaluate constant-expressions in compile time, right?
Now, let us consider, the following simple snippet:
if (a && 1/0)
...
with a, to fix our attention, being *not* a constant expression. The
quote above seems to say that since 1/0 is a constant
(sub-)expression, the program is ill-formed. So, is it the intent that
such ill-formedness is diagnosable at run-time? Or is it the intent
that the above gives undefined behavior (if 1/0 is evaluated) and is
not ill-formed?
I think the intent is actually the latter, so I propose the following rewording of the quoted section:
If an expression is evaluated but its result is not mathematically defined or not in the range of representable values for its type the behavior is undefined, unless such an expression is a constant expression (5.19) that shall be evaluated during program translation, in which case the program is ill-formed.
Rationale (March, 2004):
We feel the standard is clear enough. The quoted sentence does begin "If during the evaluation of an expression, ..." so the rest of the sentence does not apply to an expression that is not evaluated.
Note (September, 2004):
Gennaro Prota feels that the CWG missed the point of his original comment: unless a constant expression appears in a context that requires a constant expression, an implementation is permitted to defer its evaluation to runtime. An evaluation that fails at runtime cannot affect the well-formedness of the program; only expressions that are evaluated at compile time can make a program ill-formed.
The status has been reset to “open” to allow further discussion.
Proposed resolution (October, 2004):
Change paragraph 5 of 5 [expr] as indicated: