Document number: | PL22.16/10-0105 = WG21 N3115 |
Date: | 2010-08-23 |
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 on which the Committee (J16 + WG21) has not yet acted, that is, issues with status "Ready," "Tentatively Ready," "Review," "Drafting," and "Open."
This document is part of a group of related documents that together describe the issues that have been raised regarding the C++ Standard. The other documents in the group are:
Section references in this document reflect the section numbering of document PL22.16/10-0082 = WG21 N3092.
The purpose of these documents is to record the disposition of issues that have come before the Core Language Working Group of the ANSI (INCITS PL22.16) and ISO (WG21) C++ Standard Committee.
Some issues represent potential defects in the ISO/IEC IS 14882:2003 document and corrected defects in the earlier ISO/IEC 14882:1998 document; others refer to text in the working draft for the next revision of the C++ language, informally known as C++0x, and not to any Standard text. Issues are not necessarily formal ISO Defect Reports (DRs). While some issues will eventually be elevated to DR status, others will be disposed of in other ways. (See Issue Status below.)
The most current public version of this document can be found at http://www.open-std.org/jtc1/sc22/wg21. Requests for further information about these documents should include the document number, reference ISO/IEC 14882:2003, and be submitted to the InterNational Committee for Information Technology Standards (INCITS), 1250 Eye Street NW, Suite 200, Washington, DC 20005, USA.
Information regarding how to obtain a copy of the C++ Standard, join the Standard Committee, or submit an issue can be found in the C++ FAQ at http://www.comeaucomputing.com/csc/faq.html. Public discussion of the C++ Standard and related issues occurs on newsgroup comp.std.c++.
Issues progress through various statuses as the Core Language Working Group and, ultimately, the full PL22.16 and WG21 committees deliberate and act. For ease of reference, issues are grouped in these documents by their status. Issues have one of the following statuses:
Open: The issue is new or the working group has not yet formed an opinion on the issue. If a Suggested Resolution is given, it reflects the opinion of the issue's submitter, not necessarily that of the working group or the Committee as a whole.
Drafting: Informal consensus has been reached in the working group and is described in rough terms in a Tentative Resolution, although precise wording for the change is not yet available.
Review: Exact wording of a Proposed Resolution is now available for an issue on which the working group previously reached informal consensus.
Ready: The working group has reached consensus that the issue is a defect in the Standard, the Proposed Resolution is correct, and the issue is ready to forward to the full Committee for ratification as a proposed defect report.
Tentatively Ready: Like "ready" except that the resolution was produced and approved by a subset of the working group membership between meetings. Persons not participating in these betwee-meeting activities are encouraged to review such resolutions carefully and to alert the working group with any problems that may be found.
DR: The full Committee has approved the item as a proposed defect report. The Proposed Resolution in an issue with this status reflects the best judgment of the Committee at this time regarding the action that will be taken to remedy the defect; however, the current wording of the Standard remains in effect until such time as a Technical Corrigendum or a revision of the Standard is issued by ISO.
TC1: A DR issue included in Technical Corrigendum 1. TC1 is a revision of the Standard issued in 2003.
CD1: A DR issue not resolved in TC1 but included in Committee Draft 1. CD1 was advanced for balloting at the September, 2008 WG21 meeting.
CD2: A DR issue not resolved in CD1 but included in the Final Committee Draft advanced for balloting at the March, 2010 WG21 meeting.
WP: A DR issue whose resolution is reflected in the current Working Paper. The Working Paper is a draft for a future version of the Standard.
Dup: The issue is identical to or a subset of another issue, identified in a Rationale statement.
NAD: The working group has reached consensus that the issue is not a defect in the Standard. A Rationale statement describes the working group's reasoning.
Extension: The working group has reached consensus that the issue is not a defect in the Standard but is a request for an extension to the language. The working group expresses no opinion on the merits of an issue with this status; however, the issue will be maintained on the list for possible future consideration as an extension proposal.
Concepts: The issue relates to the “Concepts” proposal that was removed from the working paper at the Frankfurt (July, 2009) meeting and hence is no longer under consideration.
The 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.
“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]
Passing a name qualified by the global scope operator :: as a template argument can inadvertently trigger recognition of the <: digraph, causing a syntax error. This should be handled by a lexical rule similar to the special treament given to >> so that <:: would be recognized as an open angle-bracket followed by the scope-resolution operator.
Proposed resolution (August, 2010):
Insert a bullet into the list in 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...
In general, the parameter type of a literal operator must be the same as the argument passed to it. That is not the case for a user-defined-character-literal, where the argument could inadvertently match a literal operator intended for use with user-defined-integer-literals:
typedef unsigned long long ULL; int operator "" X(ULL); int i = 'c'X; // operator"" X(ULL('c'))
Suggested resolution: Add the following phrase to the description in paragraph 6:
S shall contain a literal operator whose parameter type is the same as the type of ch.
Proposed resolution (August, 2010):
Change 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 formoperator "" X (ch )
One of the critieria for giving a name internal linkage is “a variable that is explicitly declared const and neither explicitly declared extern nor previously declared to have external linkage.” This should presumably apply to variables declared constexpr as well.
Proposed resolution (August, 2010):
Change 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
It is odd that, in the following example, N has no linkage but g has external linkage:
namespace { namespace N // has no linkage { void g(); // has external linkage } }
Proposed resolution (August, 2010):
Change 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.
Is the following well-formed?
int f() { int i = 3; new (&i) float(1.2); return i; }
The wording that is intended to prevent such shenanigans, 3.8 [basic.life] paragraphs 7-9, doesn't quite apply here. In particular, paragraph 7 reads,
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:
the storage for the new object exactly overlays the storage location which the original object occupied, and
the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and...
The problem here is that this wording only applies “after the lifetime of an object has ended and before the storage which the object occupied is reused;” for an object of a scalar type, its lifetime only ends when the storage is reused or released (paragraph 1), so it appears that these restrictions cannot apply to such objects.
Proposed resolution (August, 2010):
This issue is resolved by the resolution of issue 1116.
The 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 ...
The adoption of paper N3067 at the March, 2010 meeting moved the position of the optional attribute-specifier in a function declarator from immediately following the parameter-declaration-clause to after the exception-specification. However, the grammar in 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...
The resolution of issue 983 restored an error, inadvertently removed by the resolution of issue 39, for the formation of a member of an ambiguous base class. For example:
struct B { int i; }; struct I1: B { }; struct I2: B { }; struct D: I1, I2 { }; int B::* pm = &D::i; // Originally and again ambiguous
This error is not necessary, because the result of taking the address of a member of an ambiguous base class is a pointer to a member of that class; an actual ambiguity would occur only if that pointer to a base class member is converted to a pointer to a member of the derived class. (See issue 203, which suggests changing the result of taking the address of a member to reflect the naming class of the member instead of the class of which it is directly a member; if that change were made, the ambiguity error would be needed.)
The resolution of issue 983 should be reverted.
Proposed resolution (August, 2010):
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]...
In 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.
Proposed resolution (August, 2010):
Change 3.6.2 [basic.start.init] paragraph 2 as follows:
...Constant initialization is performed:
...
if an object with static or thread storage duration is initialized such that the initialization satisfies the requirements for the object being declared with constexpr (7.1.5 [dcl.constexpr]). by a constructor call, if the constructor is a constexpr constructor, and if, after recursive function invocation substitution (7.1.5 [dcl.constexpr]) for all contained function and constructor calls, every expression or full-expression used for subobject and reference member initialization is a constant expression [Note: In effect, initialization by constructor is broken down to initializing the scalar subobjects and reference members of the class. —end note]
if an object with static or thread storage duration is not initialized by a constructor call and if every full-expression that appears in its initializer is a constant expression.
Change 5.19 [expr.const] paragraph 2 as follows:
...
an invocation of a function other than a constexpr constructor for a literal class or a constexpr function or a constexpr constructor [Note: overload resolution (13.3 [over.match]) is applied as usual —end note];
...
an invocation of a constexpr function with arguments that, when implicitly converted to the corresponding parameter types and substituted for the corresponding parameters in the potential constant expression of the constexpr function, and the resulting expression implicitly converted to the return type substituted by function invocation substitution (7.1.5 [dcl.constexpr]), do not produce a constant expression; [Example:...
an invocation of a constexpr constructor with arguments that, when substituted by recursive function invocation substitution (7.1.5 [dcl.constexpr]) for all contained function and constructor calls, do not produce all constant expressions for subobject initialization [Example:
struct A {
constexpr A(bool b, int x) : m(b ? 42 : x) { }
int m;
};
int x; // not constant
constexpr int v = A(true, x).m; // OK, constructor call initializes
// m with the value 42 after substitution
constexpr int w = A(false, x).m; // error: initializer for m is
// x, which is non-constant
—end example]
...
an id-expression that refers to a variable or data member of reference type; [Footnote: Use of a reference parameter of a constexpr function does not prevent the body from being a potential constant expression because the parameters are replaced by constant expressions during that determination, and later by arguments to a call function from being used in constant expressions, because function invocation substitution will replace the parameters with the argument expressions. —end footnote]
...
Delete 5.19 [expr.const] paragraph 6:
An expression is a potential constant expression if it is a constant expression when all occurrences of function parameters are replaced as follows:
for non-reference parameters, by arbitrary prvalue constant expressions of the appropriate types;
for lvalue reference parameters, by arbitrary variables of the referred-to types with static storage duration initialized with constant expressions; or
for rvalue reference parameters, by arbitrary prvalue constant expressions of the referred-to types implicitly converted to the types of the parameters.
Change 7.1.5 [dcl.constexpr] paragraph 3 bullet 3 as follows:
its function-body shall be a compound-statement of the form
where expression is a potential constant expression (5.19 [expr.const])
Change 7.1.5 [dcl.constexpr] paragraph 4 as follows:
The definition of a constexpr constructor shall satisfy the following constraints:
...
every constructor argument and full-expression in a mem-initializer shall be a potential constant expression
...
A trivial copy/move constructor is also a constexpr constexpr constructor.
Add the following as a new paragraph after 7.1.5 [dcl.constexpr] paragraph 4:
Function invocation substitution for a call of a constexpr function or of a constexpr constructor means to convert implicitly each argument expression to the corresponding parameter type, to substitute that converted expression for each use of the corresponding parameter in the function-body, and, for constexpr functions, to convert implicitly the resulting expression to the return type of the function. Such substitution does not change the meaning. [Example:
constexpr int f(void *) { return 0; } constexpr int f(...) { return 1; } constexpr int g1() { return f(0); } // calls f(void *) constexpr int g2(int n) { return f(n); } // calls f(...) even for n == 0 constexpr int g3(int n) { return f(n*0); } // calls f(...) namespace N { constexpr int c = 5; constexpr int h() { return c; } } constexpr int c = 0; constexpr int g4() { return N::h(); } // value is 5, c is not looked up again after the substitution
—end example] For a constexpr function, if no function argument values exist such that the function invocation substitution would produce a constant expression (5.19 [expr.const]), the program is ill-formed; no diagnostic required. For a constexpr constructor, if no argument values exist such that after recursive function invocation substitution for all contained function and constructor calls, every expression or full-expression used for subobject and reference member initialization would be a constant expression (including conversions), the program is ill-formed; no diagnostic required. [Example:
constexpr int f(bool b) { return b ? throw 0 : 0; } // OK constexpr int f() { throw 0; } // ill-formed, no diagnostic required struct B { constexpr B(int x) : i(0) { } // x is unused int i; }; int global; struct D : B { constexpr D() : B(global) { } // OK };
—end example]
Change 7.1.5 [dcl.constexpr] paragraph 8 as follows:
A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, the constructor shall be a constexpr constructor and every argument to the constructor shall be a constant expression after recursive function invocation substitution for all contained function and constructor calls, every expression or full-expression used for subobject initialization shall be a constant expression. Otherwise, every full-expression that appears...
This resolution also resolves issue 1127.
It does not appear to be clearly enough stated that the example
constexpr int f() { return 42 + 84; } const int sz = f(); int a[sz];
is equivalent to
const int sz = 42 + 84; int a[sz];
Proposed resolution (August, 2010):
Change 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]
It is not clear how overload resolution is performed inside the body of a constexpr function. In particular, if the function is invoked with parameters such that an expression evaluates to an integral 0, does that make the expression eligible for the null pointer conversion and thus potentially select a different overloaded function than in invocations in which the expression has a non-zero value? (Suggested answer: no.)
Proposed resolution (August, 2010):
This issue is resolved by the resolution of issue 1125.
The 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]).
A constexpr function is not permitted to return via an exception. This should be recognised, and a function declared constexpr without an explicit exception specification should be treated as if declared noexcept(true) rather than the usual noexcept(false). For a function template declared constexpr without an explicit exception specification, it should be considered noexcept(true) if and only if the constexpr keyword is respected on a given instantiation.
See also issue 1125.
Notes from the August, 2010 meeting:
The premise is not correct: an exception is forbidden only when a constexpr function is invoked in a context that requires a constant expression. Used as an ordinary function, it can throw.
Proposed resolution (August, 2010):
Change 5.3.7 [expr.unary.noexcept] paragraph 3 bullet 1 as follows:
The 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]
The current wording disallows use of typedef-names in elaborated-type-specifiers. This prohibition should also apply to template aliases:
struct A { }; template<typename T> using X = A; struct X<int>* p2; // ill-formed
Proposed resolution (August, 2010):
Change 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.]
Although 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]).
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.]
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]).
The 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.
The 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.
Access declarations were deprecated in the 1998 standard and have no benefits over using-declarations. They should be removed in C++0x.
Proposed resolution (August, 2010):
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]
The 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]
It is not clear whether the current specification allows a defaulted copy constructor to call an explicit constructor to copy a base or member subobject, and if so, whether that is desirable or not. See also issues 535 and 1118.
Proposed resolution (August, 2010):
This issue is resolved by the resolution of issue 1051.
The new wording describing generated copy constructors (12.8 [class.copy] paragraph 16) does not describe the initialization of members with reference type.
Proposed resolution (August, 2010):
Change 12.8 [class.copy] paragraph 16 as follows:
The implicitly-defined copy constructor for a non-union class X performs a memberwise copy 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 is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2 [class.base.init]). Each subobject Given a parameter named x, each base or non-static data member is copied 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 an lvalue referring to the corresponding subobject of x;
otherwise, the base or member is direct-initialized with an lvalue referring to 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 only once by the implicitly-defined copy constructor (see 12.6.2 [class.base.init]).
Change 12.8 [class.copy] paragraph 17 as follows:
The implicitly-defined move constructor for a non-union class X performs a memberwise 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 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.
if the member is an array, each element is direct-initialized with an xvalue referring to the corresponding subobject of x;
otherwise, the base or member is direct-initialized with an xvalue referring to the corresponding base or member of x.
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 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) 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.
Change 12.8 [class.copy] paragraph 30 as follows:
...the order in which they were declared in the class definition. Each Given a parameter named x, 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 an lvalue referring to the subobject as the object expression and an lvalue referring to 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);
...
Change 12.8 [class.copy] paragraph 31 as follows:
...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 of class type, as if by a call to operator= with an lvalue referring to the subobject as the object expression and an xvalue referring to 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);
...
This resolution also resolves issues 1064 and 1066.
The 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.
According 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.
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
}
To 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:...
The standard permits the address of a thread_local object as a non-type template argument. The addresses of these objects are not constant, however. Such template arguments should require objects of static storage duration.
Proposed resolution (August, 2010):
This issue is resolved by the resolution of issue 1155.
Now that local classes can be used as template arguments, it seems odd that there are “external linkage” restrictions on non-type template parameters. The addresses of objects and functions with internal linkage should be permitted as well.
Proposed resolution (August, 2010):
Change 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.
The 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]
An 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.
It 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]
The 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.
The following example is ambiguous:
template<typename T> int f(T&); template<typename T> int f(T&&); int i; int j = f(i);
Because of the special deduction rule for lvalues passed to rvalue-reference parameters, deduction produces f(int&) for both templates, and they are indistinguishable.
Because f(T&) accepts a strict subset of the things that f(T&&) does, it should be considered more specialized by the partial ordering rules.
Proposed resolution (August, 2010):
Change 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.]
It is not entirely clear that a function-try-block on a destructor will catch exceptions from a base or member destructor; whether such exceptions might be swallowed with a simple return statement rather than being rethrown; and whether such a clause might be entered multiple times if multiple bases/members throw, or if that is an automatic terminate call.
Proposed resolution (August, 2010):
Change 15 [except] paragraph 4 as follows:
...An exception thrown during the execution of the initializer expressions in the ctor-initializer or during the execution of the compound-statement, or — in the case of a destructor — during the destruction of a subobject, transfers control to a handler in a function-try-block in the same way as an exception thrown during the execution of a try-block transfers control to other handlers. [Example:...
The 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 (18.8.2.2 [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]).
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...
The recommendations of document N2693 included a feature macro to enable a program to determine whether the implementation enforces strict pointer safety or not, but this macro is not specified in 16.8 [cpp.predefined].
Proposed resolution (August, 2010):
Add the following to the end of 16.8 [cpp.predefined] paragraph 2:
In describing static data members initialized inside the class definition, 9.4.2 [class.static.data] paragraph 3 says,
The member shall still be defined in a namespace scope if it is used in the program...
The definition of “used” is in 3.2 [basic.def.odr] paragraph 1:
An object or non-overloaded function whose name appears as a potentially-evaluated expression is used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19 [expr.const]) and the lvalue-to-rvalue conversion (4.1 [conv.lval]) is immediately applied.
Now consider the following example:
struct S { static const int a = 1; static const int b = 2; }; int f(bool x) { return x ? S::a : S::b; }
According to the current wording of the Standard, this example requires that S::a and S::b be defined in a namespace scope. The reason for this is that, according to 5.16 [expr.cond] paragraph 4, the result of this conditional-expression is an lvalue and the lvalue-to-rvalue conversion is applied to that, not directly to the object, so this fails the “immediately applied” requirement. This is surprising and unfortunate, since only the values and not the addresses of the static data members are used. (This problem also applies to the proposed resolution of issue 696.)
Proposed resolution (November, 2009):
Change 3.2 [basic.def.odr] paragraph 1 as follows:
...An object or non-overloaded function whose name appears as a potentially-evaluated expression x is used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19 [expr.const]) and the lvalue-to-rvalue conversion (4.1 [conv.lval]) is immediately applied eventually applied to all lvalue expressions e that could possibly denote that object, where e is a subexpression of the full-expression containing x...
Additional notes (November, 2009):
The proposed wording (like the existing wording) requires that S::a be defined in the following example:
struct S {
static const int a = 1;
};
void g() {
S::a; // no lvalue-to-rvalue conversion
}
Although this particular example is obviously unimportant, there could be similar cases where a use is buried in a nested conditional and the result eventually discarded, perhaps as the result of a macro expansion. An alternative approach that addresses this point might be something along the lines of
There is no lvalue expression e of which x is a subexpression to which a reference is bound or to which the unary & operator is applied that could possibly denote that object.
One objection to this latter approach is that it would require defining S::a if the expression were something like *&S::a, which would not be the case with the wording in the proposed resolution.
The various uses of the term “declarative region” throughout the Standard indicate that the term is intended to refer to the entire block, class, or namespace that contains a given declaration. For example, 3.3 [basic.scope] paragraph 2 says, in part:
[Example: in
int j = 24; int main() { int i = j, j; j = 42; }The declarative region of the first j includes the entire example... The declarative region of the second declaration of j (the j immediately before the semicolon) includes all the text between { and }...
However, the actual definition given for “declarative region” in 3.3 [basic.scope] paragraph 1 does not match this usage:
Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity.
Because (except in class scope) a name cannot be used before it is declared, this definition contradicts the statement in the example and many other uses of the term throughout the Standard. As it stands, this definition is identical to that of the scope of a name.
The term “scope” is also misused. The scope of a declaration is defined in 3.3 [basic.scope] paragraph 1 as the region in which the name being declared is valid. However, there is frequent use of the phrase “the scope of a class,” not referring to the region in which the class's name is valid but to the declarative region of the class body, and similarly for namespaces, functions, exception handlers, etc. There is even a mention of looking up a name “in the scope of the complete postfix-expression” (3.4.5 [basic.lookup.classref] paragraph 3), which is the exact inverse of the scope of a declaration.
This terminology needs a thorough review to make it logically consistent. (Perhaps a discussion of the scope of template parameters could also be added to section 3.3 [basic.scope] at the same time, as all other kinds of scopes are described there.)
Proposed resolution (November, 2006):
Change 3.3 [basic.scope] paragraph 1 as follows:
Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity a statement, block, function declarator, function-definition, class, handler, template-declaration, template-parameter-list of a template template-parameter, or namespace. In general, each particular name is valid may be used as an unqualified name to refer to the entity of its declaration or to the label only within some possibly discontiguous portion of program text called its scope. To determine the scope of a declaration...
Change 3.3 [basic.scope] paragraph 3 as follows:
The names declared by a declaration are introduced into the scope in which the declaration occurs declarative region that directly encloses the declaration, except that declaration-statements, function parameter names in the declarator of a function-definition, exception-declarations (3.3.3 [basic.scope.local]), the presence of a friend specifier (11.4 [class.friend]), certain uses of the elaborated-type-specifier (7.1.6.3 [dcl.type.elab]), and using-directives (7.3.4 [namespace.udir]) alter this general behavior.
Change 3.3.3 [basic.scope.local] paragraphs 1-3 and add a new paragraph 4 before the existing paragraph 4 as follows:
A name declared in a block (6.3 [stmt.block]) is local to that block. Its potential scope begins at its point of declaration (3.3.2 [basic.scope.pdecl]) and ends at the end of its declarative region. The declarative region of a name declared in a declaration-statement is the directly enclosing block (6.3 [stmt.block]). Such a name is local to the block.
The potential scope declarative region of a function parameter name (including one appearing in the declarator of a function-definition or in a lambda-parameter-declaration-clause) or of a function-local predefined variable in a function definition (8.4 [dcl.fct.def]) begins at its point of declaration. If the function has a function-try-block the potential scope of a parameter or of a function-local predefined variable ends at the end of the last associated handler, otherwise it ends at the end of the outermost block of the function definition. A parameter name is the entire function definition or lambda-expression. Such a name is local to the function definition and shall not be redeclared in the any outermost block of the function definition nor in the outermost block of any handler associated with a function-try-block function-body (including handlers of a function-try-block) or lambda-expression.
The name in a catch exception-declaration The declarative region of a name declared in an exception-declaration is its entire handler. Such a name is local to the handler and shall not be redeclared in the outermost block of the handler.
The potential scope of any local name begins at its point of declaration (3.3.2 [basic.scope.pdecl]) and ends at the end of its declarative region.
Change 3.3.5 [basic.funscope] as indicated:
Labels (6.1 [stmt.label]) have function scope and may be used anywhere in the function in which they are declared except in members of local classes (9.8 [class.local]) of that function. Only labels have function scope.
Change 6.7 [stmt.dcl] paragraph 1 as follows:
A declaration statement introduces one or more new identifiers names into a block; it has the form
declaration-statement:
block-declaration
[Note: If an identifier a name introduced by a declaration was previously declared in an outer block, the outer declaration is hidden for the remainder of the block, after which it resumes its force (3.3.10 [basic.scope.hiding]). —end note]
[Drafting notes: This resolution deals almost exclusively with the unclear definition of “declarative region.” I've left the ambiguous use of “scope” alone for now. However sections 3.3.x all have headings reading “xxx scope,” but they don't mean the scope of a declaration but the different kinds of declarative regions and their effects on the scope of declarations contained therein. To me, it looks like most of 3.4 should refer to “declarative region” and not to “scope.”
The change to 6.7 fixes an “identifier” misuse (e.g., extern T operator+(T,T); at block scope introduces a name but not an identifier) and removes normative redundancy.]
The Standard does not completely specify how to look up the type-name(s) in a pseudo-destructor-name (5.2 [expr.post] paragraph 1, 5.2.4 [expr.pseudo]), and what information it does have is incorrect and/or in the wrong place. Consider, for instance, 3.4.5 [basic.lookup.classref] paragraphs 2-3:
If the id-expression in a class member access (5.2.5 [expr.ref]) is an unqualified-id, and the type of the object expression is of a class type C (or of pointer to a class type C), the unqualified-id is looked up in the scope of class C. If the type of the object expression is of pointer to scalar type, the unqualified-id is looked up in the context of the complete postfix-expression.
If the unqualified-id is ~type-name, and the type of the object expression is of a class type C (or of pointer to a class type C), the type-name is looked up in the context of the entire postfix-expression and in the scope of class C. The type-name shall refer to a class-name. If type-name is found in both contexts, the name shall refer to the same class type. If the type of the object expression is of scalar type, the type-name is looked up in the scope of the complete postfix-expression.
There are at least three things wrong with this passage with respect to pseudo-destructors:
A pseudo-destructor call (5.2.4 [expr.pseudo]) is not a “class member access”, so the statements about scalar types in the object expressions are vacuous: the object expression in a class member access is required to be a class type or pointer to class type (5.2.5 [expr.ref] paragraph 2).
On a related note, the lookup for the type-name(s) in a pseudo-destructor name should not be described in a section entitled “Class member access.”
Although the class member access object expressions are carefully allowed to be either a class type or a pointer to a class type, paragraph 2 mentions only a “pointer to scalar type” (disallowing references) and paragraph 3 deals only with a “scalar type,” presumably disallowing pointers (although it could possibly be a very subtle way of referring to both non-class pointers and references to scalar types at once).
The other point at which lookup of pseudo-destructors is mentioned is 3.4.3 [basic.lookup.qual] paragraph 5:
If a pseudo-destructor-name (5.2.4 [expr.pseudo]) contains a nested-name-specifier, the type-names are looked up as types in the scope designated by the nested-name-specifier.
Again, this specification is in the wrong location (a pseudo-destructor-name is not a qualified-id and thus should not be treated in the “Qualified name lookup” section).
Finally, there is no place in the Standard that describes the lookup for pseudo-destructor calls of the form p->T::~T() and r.T::~T(), where p and r are a pointer and reference to scalar, respectively. To the extent that it gives any guidance at all, 3.4.5 [basic.lookup.classref] deals only with the case where the ~ immediately follows the . or ->, and 3.4.3 [basic.lookup.qual] deals only with the case where the pseudo-destructor-name contains a nested-name-specifier that designates a scope in which names can be looked up.
See document J16/06-0008 = WG21 N1938 for further discussion of this and related issues, including 244, 305, 399, and 414.
Proposed resolution (June, 2008):
Add a new paragraph following 5.2 [expr.post] paragraph 2 as follows:
When a postfix-expression is followed by a dot . or arrow -> operator, the interpretation depends on the type T of the expression preceding the operator. If the operator is ., T shall be a scalar type or a complete class type; otherwise, T shall be a pointer to a scalar type or a pointer to a complete class type. When T is a (pointer to) a scalar type, the postfix-expression to which the operator belongs shall be a pseudo-destructor call (5.2.4 [expr.pseudo]); otherwise, it shall be a class member access (5.2.5 [expr.ref]).
Change 5.2.4 [expr.pseudo] paragraph 2 as follows:
The left-hand side of the dot operator shall be of scalar type. The left-hand side of the arrow operator shall be of pointer to scalar type. This scalar type The type of the expression preceding the dot operator, or the type to which the expression preceding the arrow operator points, is the object type...
Change 5.2.5 [expr.ref] paragraph 2 as follows:
For the first option (dot) the type of the first expression (the object expression) shall be “class object” (of a complete type) is a class type. For the second option (arrow) the type of the first expression (the pointer expression) shall be “pointer to class object” (of a complete type) is a pointer to a class type. In these cases, the id-expression shall name a member of the class or of one of its base classes.
Add a new paragraph following 3.4 [basic.lookup] paragraph 2 as follows:
In a pseudo-destructor-name that does not include a nested-name-specifier, the type-names are looked up as types in the context of the complete expression.
Delete the last sentence of 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, the unqualified-id is looked up in the scope of class C. If the type of the object expression is of pointer to scalar type, the unqualified-id is looked up in the context of the complete postfix-expression.
An lvalue referring to an out-of-lifetime non-POD class objects can be used in limited ways, subject to the restrictions in 3.8 [basic.life] paragraph 6:
if the original object will be or was of a non-POD class type, the program has undefined behavior if:
the lvalue is used to access a non-static data member or call a non-static member function of the object, or
the lvalue is implicitly converted (4.10 [conv.ptr]) to a reference to a base class type, or
the lvalue is used as the operand of a static_cast (5.2.9 [expr.static.cast]) except when the conversion is ultimately to cv char& or cv unsigned char& ), or
the lvalue is used as the operand of a dynamic_cast (5.2.7 [expr.dynamic.cast]) or as the operand of typeid.
There are at least a couple of questionable things in this list. First, there is no “implicit conversion to a reference to a base class,” as assumed by the second bullet. Presumably this is intended to say that the lvalue is bound to a reference to a base class, and the cross-reference should be to 8.5.3 [dcl.init.ref], not to 4.10 [conv.ptr] (which deals with pointer conversions). However, even given that adjustment, it is not clear why it is forbidden to bind a reference to a non-virtual base class of an out-of-lifetime object, as that is just an address offset calculation. (Binding to a virtual base, of course, would require access to the value of the object and thus cannot be done outside the object's lifetime.)
The third bullet also appears questionable. It's not clear why static_cast is discussed at all here, as the only permissible static_cast conversions involving reference types and non-POD classes are to references to base or derived classes and to the same type, modulo cv-qualification; if implicit “conversion” to a base class reference is forbidden in the second bullet, why would an explicit conversion be permitted in the third? Was this intended to refer to reinterpret_cast? Also, is there a reason to allow char types but disallow array-of-char types (which are more likely to be useful than a single char)?
Proposed resolution (March, 2008):
Change 3.8 [basic.life] paragraph 5 as follows:
...If the object will be or was of a non-trivial class type, the program has undefined behavior if:
the pointer is used to access a non-static data member or call a non-static member function of the object, or
the pointer is implicitly converted (
4.10 [conv.ptr] ) to a pointer to a virtual base class type, orthe pointer is used as the operand of a static_cast (5.2.9 [expr.static.cast]) (except when the conversion is to void*, or to void* and subsequently to char*, or unsigned char*). pointer to void, or to pointer to void and subsequently to pointer to cv char or pointer to cv unsigned char, or
the pointer is used as the operand of a dynamic_cast (5.2.7 [expr.dynamic.cast])...
Change 3.8 [basic.life] paragraph 6 as follows:
...if the original object will be or was of a non-trivial class type, the program has undefined behavior if:
the lvalue is used to access a non-static data member or call a non-static member function of the object, or
the lvalue is implicitly converted (4.10 [conv.ptr]) bound to a reference to a virtual base class type (8.5.3 [dcl.init.ref]), or
the lvalue is used as the operand of a static_cast (5.2.9 [expr.static.cast]) except when the conversion is ultimately to cv char& or cv unsigned char&, or
the lvalue is used as the operand of a dynamic_cast (5.2.7 [expr.dynamic.cast]) or as the operand of typeid.
[Drafting notes: Paragraph 5 was changed to track the changes to paragraph 6. See also the resolution for issue 658.]
Related to issue 1027, consider:
int f() { union U { double d; } u1, u2; (int&)u1.d = 1; u2 = u1; return (int&)u2.d; }
Does this involve undefined behavior? 3.8 [basic.life] paragraph 4 seems to say that it's OK to clobber u1 with an int object. Then union assignment copies the object representation, possibly creating an int object in u2 and making the return statement well-defined. If this is well-defined, compilers are significantly limited in the assumptions they can make about type aliasing. On the other hand, the variant where U has an array of unsigned char member must be well-defined in order to support std::aligned_storage.
Suggested resolution: Clarify that this case is undefined, but that adding an array of unsigned char to union U would make it well-defined — if a storage location is allocated with a particular type, it should be undefined to create an object in that storage if it would be undefined to access the stored value of the object through the allocated type.
Proposed resolution (August, 2010):
Change 3.8 [basic.life] paragraph 1 as follows:
...The lifetime of an object of type T begins when storage with the proper alignment and size for type T is obtained, and either:
- storage with the proper alignment and size for type T is obtained, and
if the object has non-trivial initialization, its initialization is complete., or
if T is trivially copyable, the object representation of another T object is copied into the storage.
The lifetime of an object of type T ends...
Change 3.8 [basic.life] paragraph 4 as follows:
A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (5.3.5 [expr.delete]) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior. If a program obtains storage for an object of a particular type A (e.g. with a variable definition or new-expression) and later reuses that storage for an object of another type B such that accessing the stored value of the B object through a glvalue of type A would have undefined behavior (3.10 [basic.lval]), the behavior is undefined. [Example:
int i; (double&)i = 1.0; // undefined behavior struct S { unsigned char alignas(double) ar[sizeof (double)]; } s; (double&)s = 1.0; // OK, can access stored double through s because it has an unsigned char subobject
—end example]
Change 3.10 [basic.lval] paragraph 10 as follows:
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined52:
the dynamic type of the object,
a cv-qualified version of the dynamic type of the object,
a type similar (as defined in 4.4 [conv.qual]) to the dynamic type of the object,
a type that is the signed or unsigned type corresponding to the dynamic type of the object,
a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
a char or unsigned char type,
an aggregate or union type that includes one of the aforementioned types among its elements, bases, or non-static data members (including, recursively, an element, base, or non-static data member of a subaggregate, base, or contained union),.
a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
a char or unsigned char type.
This resolution also resolves issue 1027.
4 [conv] paragraph 1 says,
Standard conversions are implicit conversions defined for built-in types.
However, enumeration types (which take part in the integral promotions) and class types (which take part in the lvalue-to-rvalue conversion) are not “built-in” types, so the definition of “standard conversions” is wrong.
Proposed resolution (October, 2006):
Change 4 [conv] paragraph 1 as follows:
Standard conversions are implicit conversions defined for built-in types with built-in meaning...
4.1 [conv.lval] paragraph 1 says,
If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.
I think there are at least three related issues around this specification:
Presumably assigning a valid value to an uninitialized object allows it to participate in the lvalue-to-rvalue conversion without undefined behavior (otherwise the number of programs with defined behavior would be vanishingly small :-). However, the wording here just says "uninitialized" and doesn't mention assignment.
There's no exception made for unsigned char types. The wording in 3.9.1 [basic.fundamental] was carefully crafted to allow use of unsigned char to access uninitialized data so that memcpy and such could be written in C++ without undefined behavior, but this statement undermines that intent.
It's possible to get an uninitialized rvalue without invoking the lvalue-to-rvalue conversion. For instance:
struct A { int i; A() { } // no init of A::i }; int j = A().i; // uninitialized rvalue
There doesn't appear to be anything in the current IS wording that says that this is undefined behavior. My guess is that we thought that in placing the restriction on use of uninitialized objects in the lvalue-to-rvalue conversion we were catching all possible cases, but we missed this one.
In light of the above, I think the discussion of uninitialized objects ought to be removed from 4.1 [conv.lval] paragraph 1. Instead, something like the following ought to be added to 3.9 [basic.types] paragraph 4 (which is where the concept of "value" is introduced):
Any use of an indeterminate value (5.3.4 [expr.new], 8.5 [dcl.init], 12.6.2 [class.base.init]) of any type other than char or unsigned char results in undefined behavior.
John Max Skaller:
A().i had better be an lvalue; the rules are wrong. Accessing a member of a structure requires it be converted to an lvalue, the above calculation is 'as if':
struct A { int i; A *get() { return this; } }; int j = (*A().get()).i;
and you can see the bracketed expression is an lvalue.
A consequence is:
int &j= A().i; // OK, even if the temporary evaporates
j now refers to a 'destroyed' value. Any use of j is an error. But the binding at the time is valid.
Proposed Resolution (November, 2006):
Add the indicated words to 3.9 [basic.types] paragraph 4:
... For trivial types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values. Any use of an indeterminate value (5.3.4 [expr.new], 8.5 [dcl.init], 12.6.2 [class.base.init]) of a type other than unsigned char results in undefined behavior.
Change 4.1 [conv.lval] paragraph 1 as follows:
If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.
Additional note (May, 2008):
The C committee is dealing with a similar issue in their DR336. According to this analysis, they plan to take almost the opposite approach to the one described above by augmenting the description of their version of the lvalue-to-rvalue conversion. The CWG did not consider that access to an unsigned char might still trap if it is allocated in a register and needs to reevaluate the proposed resolution in that light. See also issue 129.
It is not permitted to use reinterpret_cast to convert between pointers to object type and pointers to void.
See also issue 573.
Proposed resolution (August, 2010):
Change 5.2.10 [expr.reinterpret.cast] paragraph 7 as follows:
A pointer to an object An object pointer can be explicitly converted to a pointer to a different object type different object pointer type.70 When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (3.9 [basic.types]) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.
(Note: this resolution depends on that of issue 573.)
Split off from issue 315.
Incidentally, another thing that ought to be cleaned up is the inconsistent use of "indirection" and "dereference". We should pick one.
Proposed resolution (December, 2006):
Change 5.3.1 [expr.unary.op] paragraph 1 as follows:
The unary * operator performs indirection dereferences a pointer value: the expression to which it is applied shall be a pointer...
Change 8.3.4 [dcl.array] paragraph 8 as follows:
The results are added and indirection applied values are added and the result is dereferenced to yield an array (of five integers), which in turn is converted to a pointer to the first of the integers.
Change 8.3.5 [dcl.fct] paragraph 9 as follows:
The binding of *fpi(int) is *(fpi(int)), so the declaration suggests, and the same construction in an expression requires, the calling of a function fpi, and then using indirection through dereferencing the (pointer) result to yield an integer. In the declarator (*pif)(const char*, const char*), the extra parentheses are necessary to indicate that indirection through dereferencing a pointer to a function yields a function, which is then called.
Change the index for * and “dereferencing” no longer to refer to “indirection.”
[Drafting note: 26.6.9 [template.indirect.array] requires no change. Many more places in the current wording use “dereferencing” than “indirection.”]
According to the C++ Standard section 5.3.4 [expr.new] paragraph 21 it is unspecified whether the allocation function is called before evaluating the constructor arguments or after evaluating the constructor arguments but before entering the constructor.
On top of that paragraph 17 of the same section insists that
If any part of the object initialization described above [Footnote: This may include evaluating a new-initializer and/or calling a constructor.] terminates by throwing an exception and a suitable deallocation function is found, the deallocation function is called to free the memory in which the object was being constructed... If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object's memory to be freed...
Now suppose we have:
struct copy_throw { copy_throw(const copy_throw&) { throw std::logic_error("Cannot copy!"); } copy_throw(long, copy_throw) { } copy_throw() { } };
int main() try { copy_throw an_object, /* undefined behaviour */ * a_pointer = ::new copy_throw(0, an_object); return 0; } catch(const std::logic_error&) { }
Here the new-expression '::new copy_throw(0, an_object)' throws an exception when evaluating the constructor's arguments and before the allocation function is called. However, 5.3.4 [expr.new] paragraph 17 prescribes that in such a case the implementation shall call the deallocation function to free the memory in which the object was being constructed, given that a matching deallocation function can be found.
So a call to the Standard library deallocation function '::operator delete(void*)' shall be issued, but what argument is an implementation supposed to supply to the deallocation function? As per 5.3.4 [expr.new] paragraph 17 - the argument is the address of the memory in which the object was being constructed. Given that no memory has yet been allocated for the object, this will qualify as using an invalid pointer value, which is undefined behaviour by virtue of 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 4.
Suggested resolution:
Change the first sentence of 5.3.4 [expr.new] paragraph 17 to read:
If the memory for the object being created has already been successfully allocated and any part of the object initialization described above...
Proposed resolution (March, 2008):
Change 5.3.4 [expr.new] paragraph 18 as follows:
If any part of the object initialization described above [Footnote: ...] terminates by throwing an exception, storage has been obtained for the object, and a suitable deallocation function can be found, the deallocation function is called...
Does an explicit temporary of an integral type qualify as an integral constant expression? For instance,
void* p = int(); // well-formed?
It would appear to be, since int() is an explicit type conversion according to 5.2.3 [expr.type.conv] (at least, it's described in a section entitled "Explicit type conversion") and type conversions to integral types are permitted in integral constant expressions (5.19 [expr.const]). However, this reasoning is somewhat tenuous, and some at least have argued otherwise.
Note (March, 2008):
This issue should be closed as NAD as a result of the rewrite of 5.19 [expr.const] in conjunction with the constexpr proposal.
The constraints on type-specifiers given in 7.1.6 [dcl.type] paragraphs 2 and 3 (at most one type-specifier except as specified, at least one type-specifier, no redundant cv-qualifiers) are couched in terms of decl-specifier-seqs and declarations. However, they should also apply to constructs that are not syntactically declarations and that are defined to use type-specifier-seqs, including 5.3.4 [expr.new], 6.6 [stmt.jump], 8.1 [dcl.name], and 12.3.2 [class.conv.fct].
Proposed resolution (March, 2008):
Change 7.1.6 [dcl.type] paragraph 3 as follows:
At In a complete type-specifier-seq or in a complete decl-specifier-seq of a declaration, at least one type-specifier that is not a cv-qualifier is required in a declaration shall appear unless it the declaration declares a constructor, destructor or conversion function.
(Note: paper N2546, voted into the Working Draft in February, 2008, addresses part of this issue.)
According to 7.2 [dcl.enum] paragraph 10,
An expression of arithmetic or enumeration type can be converted to an enumeration type explicitly. The value is unchanged if it is in the range of enumeration values of the enumeration type; otherwise the resulting enumeration value is unspecified.
(There is similar wording in 5.2.9 [expr.static.cast].) Does the phrase “resulting enumeration value” mean that the result, although unspecified, must lie within the range of enumeration values of the enumeration type? Existing practice seems to allow out-of-range values to be preserved if the underlying type is large enough to represent the value. This freedom is important both for efficiency (to avoid having to mask values while storing and/or fetching) and to prevent optimizers from removing code that tests for out-of-range values.
Proposed resolution (August, 2010):
Change 5.2.9 [expr.static.cast] paragraph 10 as follows:
A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (7.2 [dcl.enum]). Otherwise, the resulting enumeration value is unspecified (and might not be in that range).
Change 7.2 [dcl.enum] paragraph 10 as follows:
An expression of arithmetic or enumeration type can be converted to an enumeration type explicitly. The value is unchanged if it is in the range of enumeration values of the enumeration type; otherwise the resulting enumeration value is unspecified (and might not be in that range).
Change 7.2 [dcl.enum] paragraph 7 as follows:
...If the enumerator-list is empty, the values of the enumeration are as if the enumeration had a single enumerator with value 0. [Footnote: This set of values is used to define promotion and conversion semantics for the enumeration type; it does not exclude an expression of enumeration type from having a value that falls outside this range. —end footnote]
According to 8.3 [dcl.meaning] paragraph 1,
A declarator-id shall not be qualified except for the definition of a member function (9.3 [class.mfct]) or static data member (9.4 [class.static]) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or the definition of a previously declared explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (11.4 [class.friend]). When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers...
This restriction prohibits examples like the following:
void f(); void ::f(); // error: qualified declarator namespace N { void f(); void N::f() { } // error: qualified declarator }
There doesn't seem to be any good reason for disallowing such declarations, and a number of implementations accept them in spite of the Standard's prohibition. Should the Standard be changed to allow them?
Notes from the April, 2006 meeting:
In discussing issue 548, the CWG agreed that the prohibition of qualified declarators inside their namespace should be removed.
Proposed resolution (October, 2006):
Remove the indicated words from 8.3 [dcl.meaning] paragraph 1:
...An unqualified-id occurring in a declarator-id shall be a simple identifier except for the declaration of some special functions (12.3 [class.conv], 12.4 [class.dtor], 13.5 [over.oper]) and for the declaration of template specializations or partial specializations (). A declarator-id shall not be qualified except for the definition of a member function (9.3 [class.mfct]) or static data member (9.4 [class.static]) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or the definition of a previously declared explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (11.4 [class.friend]). When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers, and the member shall not have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id...
[Drafting note: The omission of “outside of its class” here does not give permission for redeclaration of class members; that is still prohibited by 9.2 [class.mem] paragraph 1. The removal of the enumeration of the kinds of declarations in which a qualified-id can appear does allow a typedef declaration to use a qualified-id, which was not permitted before; if that is undesirable, the prohibition can be reinstated here.]
The following example appears to be well-formed, with the partial specialization matching the type of Y::f(), even though it is rejected by many compilers:
template<class T> struct X; template<class R> struct X< R() > { }; template<class F, class T> void test(F T::* pmf) { X<F> x; } struct Y { void f() { } }; int main() { test( &Y::f ); }
However, 8.3.5 [dcl.fct] paragraph 4 says,
A cv-qualifier-seq shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration. The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. In the latter case, the cv-qualifiers are ignored.
This specification makes it impossible to write a partial specialization for a const member function:
template<class R> struct X<R() const> { };
A template argument is not one of the permitted contexts for cv-qualification of a function type. This restriction should be removed.
Notes from the April, 2006 meeting:
During the meeting the CWG was of the opinion that the “R() const” specialization would not match the const member function even if it were allowed and so classified the issue as NAD. Questions have been raised since the meeting, however, suggesting that the template argument in the partial specialization would, in fact, match the type of a const member function (see, for example, the very similar usage via typedefs in 9.3 [class.mfct] paragraph 9). The issue is thus being left open for renewed discussion at the next meeting.
Proposed resolution (June, 2008):
Change 8.3.5 [dcl.fct] paragraph 7 as follows:
A cv-qualifier-seq shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration, or the top-level function type of a type-id that is a template-argument for a type template-parameter. The effect... A ref-qualifier shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration, or the top-level function type of a type-id that is a template-argument for a type template-parameter. The return type...
Can a member of a union be of a class that has a user-declared non-default constructor? The restrictions on union membership in 9.5 [class.union] paragraph 1 only mention default and copy constructors:
An object of a class with a non-trivial default constructor (12.1 [class.ctor]), a non-trivial copy constructor (12.8 [class.copy]), a non-trivial destructor (12.4 [class.dtor]), or a non-trivial copy assignment operator (13.5.3 [over.ass], 12.8 [class.copy]) cannot be a member of a union...
(12.1 [class.ctor] paragraph 11 does say, “a non-trivial constructor,” but it's not clear whether that was intended to refer only to default and copy constructors or to any user-declared constructor. For example, 12.2 [class.temporary] paragraph 3 also speaks of a “non-trivial constructor,” but the cross-references there make it clear that only default and copy constructors are in view.)
Note (March, 2008):
This issue was resolved by the adoption of paper J16/08-0054 = WG21 N2544 (“Unrestricted Unions”) at the Bellevue meeting.
Split off from issue 86.
Should binding a reference to the result of a "," operation whose second operand is a temporary extend the lifetime of the temporary?
const SFileName &C = ( f(), SFileName("abc") );
Notes from the March 2004 meeting:
We think the temporary should be extended.
Proposed resolution (October, 2004):
Change 12.2 [class.temporary] paragraph 2 as indicated:
... In all these cases, the temporaries created during the evaluation of the expression initializing the reference, except the temporary that is the overall result of the expression [Footnote: For example, if the expression is a comma expression (5.18 [expr.comma]) and the value of its second operand is a temporary, the reference is bound to that temporary.] and to which the reference is bound, are destroyed at the end of the full-expression in which they are created and in the reverse order of the completion of their construction...
[Note: this wording partially resolves issue 86. See also issue 446.]
Notes from the April, 2005 meeting:
The CWG suggested a different approach from the 10/2004 resolution, leaving 12.2 [class.temporary] unchanged and adding normative wording to 5.18 [expr.comma] specifying that, if the result of the second operand is a temporary, that temporary is the result of the comma expression as well.
Proposed Resolution (November, 2006):
Add the indicated wording to 5.18 [expr.comma] paragraph 1:
... The type and value of the result are the type and value of the right operand; the result is an lvalue if its right operand is an lvalue, and is a bit-field if its right operand is an lvalue and a bit-field. If the value of the right operand is a temporary (12.2 [class.temporary]), the result is that temporary.
A posting in comp.lang.c++.moderated prompted me to try the following code:
struct S { template<typename T, int N> (&operator T())[N]; };
The goal is to have a (deducible) conversion operator template to a reference-to-array type.
This is accepted by several front ends (g++, EDG), but I now believe that 12.3.2 [class.conv.fct] paragraph 1 actually prohibits this. The issue here is that we do in fact specify (part of) a return type.
OTOH, I think it is legitimate to expect that this is expressible in the language (preferably not using the syntax above ;-). Maybe we should extend the syntax to allow the following alternative?
struct S { template<typename T, int N> operator (T(&)[N])(); };
Eric Niebler: If the syntax is extended to support this, similar constructs should also be considered. For instance, I can't for the life of me figure out how to write a conversion member function template to return a member function pointer. It could be useful if you were defining a null_t type. This is probably due to my own ignorance, but getting the syntax right is tricky.
Eg.
struct null_t { // null object pointer. works. template<typename T> operator T*() const { return 0; } // null member pointer. works. template<typename T,typename U> operator T U::*() const { return 0; } // null member fn ptr. doesn't work (with Comeau online). my error? template<typename T,typename U> operator T (U::*)()() const { return 0; } };
Martin Sebor: Intriguing question. I have no idea how to do it in a single declaration but splitting it up into two steps seems to work:
struct null_t { template <class T, class U> struct ptr_mem_fun_t { typedef T (U::*type)(); }; template <class T, class U> operator typename ptr_mem_fun_t<T, U>::type () const { return 0; } };
Note: In the April 2003 meeting, the core working group noticed that the above doesn't actually work.
Note (June, 2010):
It has been suggested that template aliases effectively address this issue. In particular, an identity alias like
template<typename T> using id = T;
provides the necessary syntactic sugar to be able to specify types with trailing declarator elements as a conversion-type-id. For example, the two cases discussed above could be written as:
struct S { template<typename T, int N> operator id<T[N]>&(); template<typename T, typename U> operator id<T (U::*)()>() const; };
This issue should thus be closed as now NAD.
Jack Rouse: In 12.8 [class.copy] paragraph 8, the standard includes the following about the copying of class subobjects in such a constructor:
Mike Miller: I'm more concerned about 12.8 [class.copy] paragraph 7, which lists the situations in which an implicitly-defined copy constructor can render a program ill-formed. Inaccessible and ambiguous copy constructors are listed, but not a copy constructor with a cv-qualification mismatch. These two paragraphs taken together could be read as requiring the calling of a copy constructor with a non-const reference parameter for a const data member.
Proposed Resolution (November, 2006):
This issue is resolved by the proposed resolution for issue 535.
Footnote 112 (12.8 [class.copy] paragraph 2) says,
Because a template constructor is never a copy constructor, the presence of such a template does not suppress the implicit declaration of a copy constructor. Template constructors participate in overload resolution with other constructors, including copy constructors, and a template constructor may be used to copy an object if it provides a better match than other constructors.
However, many of the stipulations about copy construction are phrased to refer only to “copy constructors.” For example, 12.8 [class.copy] paragraph 14 says,
A program is ill-formed if the copy constructor... for an object is implicitly used and the special member function is not accessible (clause 11 [class.access]).
Does that mean that using an inaccessible template constructor to copy an object is permissible, because it is not a “copy constructor?” Obviously not, but each use of the term “copy constructor” in the Standard should be examined to determine if it applies strictly to copy constructors or to any constructor used for copying. (A similar issue applies to “copy assignment operators,” which have the same relationship to assignment operator function templates.)
Proposed Resolution (February, 2008):
Change 3.2 [basic.def.odr] paragraph 2 as follows:
... [Note: this covers calls to named functions (5.2.2 [expr.call]), operator overloading (clause 13 [over]), user-defined conversions (12.3.2 [class.conv.fct]), allocation function for placement new (5.3.4 [expr.new]), as well as non-default initialization (8.5 [dcl.init]). A copy constructor selected to copy class objects is used even if the call is actually elided by the implementation (12.8 [class.copy]). —end note] ... A copy-assignment function for a class An assignment operator function in a class is used by an implicitly-defined copy-assignment function for another class as specified in 12.8 [class.copy]...
Delete 12.1 [class.ctor] paragraphs 10 and 11:
A copy constructor (12.8 [class.copy]) is used to copy objects of class type.
A union member shall not be of a class type (or array thereof) that has a non-trivial constructor.
Replace the “example” in 12.2 [class.temporary] paragraph 1 with a note as follows:
[Example: even if the copy constructor is not called, all the semantic restrictions, such as accessibility (clause 11 [class.access]), shall be satisfied. —end example] [Note: This includes accessibility (clause 11 [class.access]) for the constructor selected. —end note]
Change 12.8 [class.copy] paragraph 7 as follows:
A non-user-provided copy constructor is implicitly defined if it is used to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type (3.2 [basic.def.odr]). [Footnote: See 8.5 [dcl.init] for more details on direct and copy initialization. —end footnote] [Note: the copy constructor is implicitly defined even if the implementation elided its use (12.2 [class.temporary]) the copy operation (12.8 [class.copy]). —end note] A program is ill-formed if the class for which a copy constructor is implicitly defined or explicitly defaulted has:
a non-static data member of class type (or array thereof) with an inaccessible or ambiguous copy constructor, or
a base class with an inaccessible or ambiguous copy constructor.
Before the non-user-provided copy constructor for a class is implicitly defined...
Change 12.8 [class.copy] paragraph 8 as follows:
...Each subobject is copied in the manner appropriate to its type:
if the subobject is of class type, the copy constructor for the class is used direct-initialization (8.5 [dcl.init]) is performed [Note: If overload resolution fails or the constructor selected by overload resolution is inaccessible (11 [class.access]) in the context of X, the program is ill-formed. —end note];
if the subobject is an array...
[Drafting note: 8.5 [dcl.init] paragraph 15 requires “unambiguous” and 13.3 [over.match] paragraph 3 requires “accessible,” thus no need for normative text here.]
Change 12.8 [class.copy] paragraph 12 as follows:
A non-user-provided copy assignment operator is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type it is used (3.2 [basic.def.odr]). A program is ill-formed if the class for which a copy assignment operator is implicitly defined or explicitly defaulted has: a non-static data member of const or reference type.
a non-static data member of const type, or
a non-static data member of reference type, or
a non-static data member of class type (or array thereof) with an inaccessible copy assignment operator, or
a base class with an inaccessible copy assignment operator.
Change 12.8 [class.copy] paragraph 13 as follows:
... 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 the assignment operator function selected by overload resolution (13.3 [over.match]) for that class is used (as if by explicit qualification; that is, ignoring any possible virtual overriding functions in more derived classes) [Note: If overload resolution fails or the assignment operator function selected by overload resolution is inaccessible (11 [class.access]) in the context of X, the program is ill-formed. —end note];
if the subobject is an array...
Delete 12.8 [class.copy] paragraph 14:
A program is ill-formed if the copy constructor or the copy assignment operator for an object is implicitly used and the special member function is not accessible (clause 11 [class.access]). [Note: Copying one object into another using the copy constructor or the copy assignment operator does not change the layout or size of either object. —end note]
Change 12.8 [class.copy] paragraph 15 as follows:
When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor selected for the copy operation and/or the destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. [Footnote: Because only one object is destroyed instead of two, and one copy constructor is not executed, there is still one object destroyed for each one constructed. —end footnote] This elision...
Change 13.3.3.1.2 [over.ics.user] paragraph 4 as follows:
A conversion of an expression of class type to the same class type is given Exact Match rank, and a conversion of an expression of class type to a base class of that type is given Conversion rank, in spite of the fact that a copy constructor (i.e., a user-defined conversion function) is called for those cases.
Change 15.1 [except.throw] paragraph 3 as follows:
A throw-expression initializes a temporary object, called the exception object, the type of which by copy-initialization (8.5 [dcl.init]). The type of that temporary object is determined...
Change 15.1 [except.throw] paragraph 5 as follows:
When the thrown object is a class object, the copy constructor selected for the copy-initialization and the destructor shall be accessible, even if the copy operation is elided (12.8 [class.copy]).
Change 15.3 [except.handle] paragraphs 16-17 as follows:
When the exception-declaration specifies a class type, a copy constructor copy-initialization (8.5 [dcl.init]) is used to initialize either the object declared in the exception-declaration or, if the exception-declaration does not specify a name, a temporary object of that type. The object shall not have an abstract class type. The object is destroyed when the handler exits, after the destruction of any automatic objects initialized within the handler. The copy constructor selected for the copy-initialization and the destructor shall be accessible in the context of the handler, even if the copy operation is elided (12.8 [class.copy]). If the copy constructor and destructor are implicitly declared (12.8 [class.copy]), such a use in the handler causes these functions to be implicitly defined; otherwise, the program shall provide a definition for these functions.
The copy constructor and destructor associated with the object shall be accessible even if the copy operation is elided (12.8 [class.copy]).
Change the footnote in 15.5.1 [except.terminate] paragraph 1 as follows:
[Footnote: For example, if the object being thrown is of a class with a copy constructor type, std::terminate() will be called if that copy constructor the constructor selected to copy the object exits with an exception during a throw. —end footnote]
(This resolution also resolves issue 111.)
[Drafting note: The following do not require changes: 5.17 [expr.ass] paragraph 4; 9 [class] paragraph 5; 9.5 [class.union] paragraph 1; 12.2 [class.temporary] paragraph 2; 12.8 [class.copy] paragraphs 1-2; 15.4 [except.spec] paragraph 14.]
Notes from February, 2008 meeting:
These changes overlap those that will be made when concepts are added. This issue will be maintained in “review” status until the concepts proposal is adopted and any conflicts will be resolved at that point.
Overload resolution should first look for a viable list constructor, then look for a non-list constructor if no list constructor is viable.
Proposed resolution (August, 2010):
Change 8.5.4 [dcl.init.list] paragraph 3 bullet 5 as follows:
Otherwise, if T is a class type, constructors are considered. If T has an initializer-list constructor, the argument list consists of the initializer list as a single argument; otherwise, the argument list consists of the elements of the initializer list. The applicable constructors are enumerated (13.3.1.7 [over.match.list]) and the best one is chosen through overload resolution (13.3.1.7 [over.match.list], 13.3 [over.match]). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed. [Example:...
Change 13.3.1.7 [over.match.list] as follows:
When objects of non-aggregate class type T are list-initialized (8.5.4 [dcl.init.list]), overload resolution selects the constructor in two phases as follows, where T is the cv-unqualified class type of the object being initialized:
If T has an initializer-list constructor (8.5.4 [dcl.init.list]), Initially, the candidate functions are the initializer-list constructors (8.5.4 [dcl.init.list]) of the class T and the argument list consists of the initializer list as a single argument.
; otherwise, If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.
For direct-list-initialization, the candidate functions are all the constructors of the class T.
For In copy-list-initialization, the candidate functions are all the constructors of T. However, if an explicit constructor is chosen, the initialization is ill-formed. [Note: This differs from other situations (13.3.1.3 [over.match.ctor], 13.3.1.4 [over.match.copy]), where only converting constructors are considered for copy-initialization. This restriction only applies if this initialization is part of the final result of overload resolution. —end note]
The current wording makes some calls involving aggregate initialization ambiguous that should not be. For example, the calls below to f and g should each prefer the second overload:
struct A { int i; }; void f (const A &); void f (A &&); void g (A, double); void g (A, int); int main() { f ( { 1 } ); g ( { 1 }, 1 ); }
Proposed resolution (August, 2010):
Change 13.3.3.2 [over.ics.rank] paragraph 3 bullet 2 as follows:
User-defined conversion sequence U1 is a better conversion sequence than another user-defined conversion sequence U2 if they contain the same user-defined conversion function or constructor or aggregate initialization and if the second standard conversion sequence of U1 is better than the second standard conversion sequence of U2.
The Standard does not fully describe the syntax to be used when a member of an explicitly-specialized member class or member class template is defined in namespace scope. 14.7.3 [temp.expl.spec] paragraph 4 says that the “explicit specialization syntax” (presumably referring to “template<>”) is not used in defining a member of an explicit specialization when a class template is explicitly specialized as a class. However, nothing is said anywhere about how to define a member of a specialization when:
the entity being specialized is a class (member of a template class) rather than a class template.
the result of the specialization is a class template rather than a class (cf 14.7.3 [temp.expl.spec] paragraph 18, which describes this case as a “member template that... remain[s] unspecialized”).
(See paper J16/05-0148 = WG21 N1888 for further details, including a survey of existing implementation practice.)
Notes from the October, 2005 meeting:
The CWG felt that the best approach, balancing consistency with implementation issues and existing practice, would be to require that template<> be used when defining members of all explicit specializations, including those currently covered by 14.7.3 [temp.expl.spec] paragraph 4.
Proposed resolution (February, 2010):
Change 14.7.3 [temp.expl.spec] paragraph 5 as follows:
...The definition of an explicitly specialized class is unrelated to the definition of a generated specialization. That is, its members need not have the same names, types, etc. as the members of a generated specialization. Definitions of members of an explicitly specialized class are defined in the same manner as members of normal classes, and not using the syntax for explicit specialization using the same template<> prefix(es) as the explicitly specialized class. [Example:
template<class T> struct A { void f(T) { /* ... */ } struct B { /* ... */ }; template<class U> struct C { /* ... */ }; }; template<> struct A<int> { void f(int); struct B; template<class U> struct C; }; void h() { A<int> a; a.f(16); // A<int>::f must be defined somewhere } // explicit specialization syntax not used for a member of // explicitly specialized class template specialization // members of explicitly specialized classes are defined using //the same syntax as the explicitly specialized class: template<> void A<int>::f(int) { /* ... */ } template<> struct A<int>::B { /* ... */ }; template<> template<class T> struct A<int>::C { /* ... */ };—end example]
Note (June, 2010):
Because the survey of implementations on which the CWG relied in reaching this resolution is quite old, a new survey of current practice is needed.
I have a question about exception handling with respect to derived to base conversions of pointers caught by reference.
What should the result of this program be?
struct S {}; struct SS : public S {}; int main() { SS ss; int result = 0; try { throw &ss; // throw object has type SS* // (pointer to derived class) } catch (S*& rs) // (reference to pointer to base class) { result = 1; } catch (...) { result = 2; } return result; }
The wording of 15.3 [except.handle] paragraph 3 would seem to say that the catch of S*& does not match and so the catch ... would be taken.
All of the compilers I tried (EDG, g++, Sun, and Microsoft) used the catch of S*& though.
What do we think is the desired behavior for such cases?
My initial reaction is that this is a bug in all of these compilers, but the fact that they all do the same thing gives me pause.
On a related front, if the handler changes the parameter using the reference, what is caught by a subsequent handler?
extern "C" int printf(const char *, ...); struct S {}; struct SS : public S {}; SS ss; int f() { try { throw &ss; } catch (S*& rs) // (reference to pointer to base class) { rs = 0; throw; } catch (...) { } return 0; } int main() { try { f(); } catch (S*& rs) { printf("rs=%p, &ss=%p\n", rs, &ss); } }
EDG, g++, and Sun all catch the original (unmodified) value. Microsoft catches the modified value. In some sense the EDG/g++/Sun behavior makes sense because the later catch could catch the derived class instead of the base class, which would be difficult to do if you let the catch clause update the value to be used by a subsequent catch.
But on this non-pointer case, all of the compilers later catch the modified value:
extern "C" int printf(const char *, ...); int f() { try { throw 1; } catch (int& i) { i = 0; throw; } catch (...) { } return 0; } int main() { try { f(); } catch (int& i) { printf("i=%p\n", i); } }
To summarize:
(See also issue 729.)
Notes from the October, 2009 meeting:
The consensus of the CWG was that it should not be possible to catch a pointer to a derived class using a reference to a base class pointer, and that a handler that takes a reference to non-const pointer should allow the pointer to be modified by the handler.
Proposed resolution (February, 2010):
Change 15.3 [except.handle] paragraph 3 as follows:
A handler is a match for an exception object of type E if
The handler is of type cv T or cv T& and E and T are the same type (ignoring the top-level cv-qualifiers), or
the handler is of type cv T or cv T& and T is an unambiguous public base class of E, or
the handler is of type cv1 T* cv2 or const T& where T is a pointer type and E is a pointer type that can be converted to the type of the handler T by either or both of
a standard pointer conversion (4.10 [conv.ptr]) not involving conversions to pointers to private or protected or ambiguous classes
a qualification conversion
the handler is of type cv T or const T& where T is a pointer or pointer to member type and E is std::nullptr_t.
(This resolution also resolves issue 729.)
Given the following example:
int f() { try { /* ... */ } catch(const int*&) { return 1; } catch(int*&) { return 2; } return 3; }
can f() return 2? That is, does an int* exception object match a const int*& handler?
According to 15.3 [except.handle] paragraph 3, it does not:
A handler is a match for an exception object of type E if
The handler is of type cv T or cv T& and E and T are the same type (ignoring the top-level cv-qualifiers), or
the handler is of type cv T or cv T& and T is an unambiguous public base class of E, or
the handler is of type cv1 T* cv2 and E is a pointer type that can be converted to the type of the handler by either or both of
a standard pointer conversion (4.10 [conv.ptr]) not involving conversions to pointers to private or protected or ambiguous classes
a qualification conversion
the handler is a pointer or pointer to member type and E is std::nullptr_t.
Only the third bullet allows qualification conversions, but only the first bullet applies to a handler of reference-to-pointer type. This is consistent with how other reference bindings work; for example, the following is ill-formed:
int* p; const int*& r = p;
(The consistency is not complete; the reference binding would be permitted if r had type const int* const &, but a handler of that type would still not match an int* exception object.)
However, implementation practice seems to be in the other direction; both EDG and g++ do match an int* with a const int*&, and the Microsoft compiler issues an error for the presumed hidden handler in the code above. Should the Standard be changed to reflect existing practice?
(See also issue 388.)
Notes from the October, 2009 meeting:
The CWG agreed that matching the exception object with a handler should, to the extent possible, mimic ordinary reference binding in cases like this.
Proposed resolution (February, 2010):
This issue is resolved by the resolution of issue 388.
There are core issues surrounding the undefined behavior of dereferencing a null pointer. It appears the intent is that dereferencing is well defined, but using the result of the dereference will yield undefined behavior. This topic is too confused to be the reference example of undefined behavior, or should be stated more precisely if it is to be retained.
(See also issue 232.)
2.5 [lex.pptoken] paragraph 2 specifies that there are 5 categories of tokens in phases 3 to 6. With 2.13 [lex.operators] paragraph 1, it is unclear whether new is an identifier or a preprocessing-op-or-punc; likewise for delete. This is relevant to answer the question whether
#define delete foo
is a well-formed control-line, since that requires an identifier after the define token.
(See also issue 189.)
The nonterminals operator and punctuator in 2.7 [lex.token] are not defined. There is a definition of the nonterminal operator in 13.5 [over.oper] paragraph 1, but it is apparent that the two nonterminals are not the same: the latter includes keywords and multi-token operators and does not include the nonoverloadable operators mentioned in paragraph 3.
There is a definition of preprocessing-op-or-punc in 2.13 [lex.operators] , with the notation that
Each preprocessing-op-or-punc is converted to a single token in translation phase 7 (2.1).However, this list doesn't distinguish between operators and punctuators, it includes digraphs and keywords (can a given token be both a keyword and an operator at the same time?), etc.
Suggested resolution:
Additional note (April, 2005):
The resolution for this problem should also address the fact that sizeof and typeid (and potentially others like decltype that may be added in the future) are described in some places as “operators” but are not listed in 13.5 [over.oper] paragraph 3 among the operators that cannot be overloaded.
(See also issue 369.)
It is not sufficiently clear that std::nullptr_t is a distinct type and neither a pointer type nor a pointer-to-member type. Add a note in 2.14.7 [lex.nullptr] stating that, preferably with cross-references to the normative statements in 3.9 [basic.types].
It 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.
In discussing issue 197, the question arose as to whether the handling of fundamental types in argument-dependent lookup is actually what is desired. This question needs further discussion.
Paragraph 7 of 3.4.5 [basic.lookup.classref] says,
If the id-expression is a conversion-function-id, its conversion-type-id shall denote the same type in both the context in which the entire postfix-expression occurs and in the context of the class of the object expression (or the class pointed to by the pointer expression).Does this mean that the following example is ill-formed?
struct A { operator int(); } a; void foo() { typedef int T; a.operator T(); // 1) error T is not found in the context // of the class of the object expression? }The second bullet in paragraph 1 of 3.4.3.1 [class.qual] says,
a conversion-type-id of an operator-function-id is looked up both in the scope of the class and in the context in which the entire postfix-expression occurs and shall refer to the same type in both contextsHow about:
struct A { typedef int T; operator T(); }; struct B : A { operator T(); } b; void foo() { b.A::operator T(); // 2) error T is not found in the context // of the postfix-expression? }Is this interpretation correct? Or was the intent for this to be an error only if T was found in both scopes and referred to different entities?
If the intent was for these to be errors, how do these rules apply to template arguments?
template <class T1> struct A { operator T1(); } template <class T2> struct B : A<T2> { operator T2(); void foo() { T2 a = A<T2>::operator T2(); // 3) error? when instantiated T2 is not // found in the scope of the class T2 b = ((A<T2>*)this)->operator T2(); // 4) error when instantiated? } }
(Note bullets 2 and 3 in paragraph 1 of 3.4.3.1 [class.qual] refer to postfix-expression. It would be better to use qualified-id in both cases.)
Erwin Unruh: The intent was that you look in both contexts. If you find it only once, that's the symbol. If you find it in both, both symbols must be "the same" in some respect. (If you don't find it, its an error).
Mike Miller: What's not clear to me in these examples is whether what is being looked up is T or int. Clearly the T has to be looked up somehow, but the "name" of a conversion function clearly involves the base (non-typedefed) type, not typedefs that might be used in a definition or reference (cf 3 [basic] paragraph 7 and 12.3 [class.conv] paragraph 5). (This is true even for types that must be written using typedefs because of the limited syntax in conversion-type-ids — e.g., the "name" of the conversion function in the following example
typedef void (*pf)(); struct S { operator pf(); };is S::operator void(*)(), even though you can't write its name directly.)
My guess is that this means that in each scope you look up the type named in the reference and form the canonical operator name; if the name used in the reference isn't found in one or the other scope, the canonical name constructed from the other scope is used. These names must be identical, and the conversion-type-id in the canonical operator name must not denote different types in the two scopes (i.e., the type might not be found in one or the other scope, but if it's found in both, they must be the same type).
I think this is all very vague in the current wording.
3.4.5 [basic.lookup.classref] does not mention template aliases as the possible result of the lookup but should do so.
In an example like
template<typename T> void f(T p)->decltype(p.T::x);
The nested-name-specifier T:: looks like it refers to the template parameter. However, if this is instantiated with a type like
struct T { int x; }; struct S: T { };
the reference will be ambiguous, since it is looked up in both the context of the expression, finding the template parameter, and in the class, finding the base class injected-class-name, and this could be a deduction failure. As a result, the same declaration with a different parameter name
template<typename U> void f(U p)->decltype(p.U::x);
is, in fact, not a redeclaration because the two can be distinguished by SFINAE.
It would be better to add a new lookup rule that says that if a name in a template definition resolves to a template parameter, that name is not subject to further lookup at instantiation time.
According to 3.4.5 [basic.lookup.classref] paragraph 1,
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 template. If the lookup in the class of the object expression finds a template, the name is also looked up in the context of the entire postfix-expression and
if the name is not found, the name found in the class of the object expression is used, otherwise
if the name is found in the context of the entire postfix-expression and does not name a class template, the name found in the class of the object expression is used, otherwise
if the name found is a class template, it shall refer to the same entity as the one found in the class of the object expression, otherwise the program is ill-formed.
This makes the following ill-formed:
#include <set> using std::set; struct X { template <typename T> void set(const T& value); }; void foo() { X x; x.set<double>(3.2); }
That's confusing and unnecessary. The compiler has already done the lookup in X's scope, and the obviously-correct resolution is that one, not the identifier from the postfix-expression's scope. Issue 305 fixed a similar issue for destructor names but missed member functions.
Suggested resolution: Delete the end of paragraph 1, starting with “If the lookup in the class...” and including all three bullets.
An example in 3.5 [basic.link] paragraph 6 creates two file-scope variables with the same name, one with internal linkage and one with external.
static void f(); static int i = 0; //1 void g() { extern void f(); // internal linkage int i; //2: i has no linkage { extern void f(); // internal linkage extern int i; //3: external linkage } }
Is this really what we want? C99 has 6.2.2.7/7, which gives undefined behavior for having an identifier appear with internal and external linkage in the same translation unit. C++ doesn't seem to have an equivalent.
Notes from October 2003 meeting:
We agree that this is an error. We propose to leave the example but change the comment to indicate that line //3 has undefined behavior, and elsewhere add a normative rule giving such a case undefined behavior.
Proposed resolution (October, 2005):
Change 3.5 [basic.link] paragraph 6 as indicated:
...Otherwise, if no matching entity is found, the block scope entity receives external linkage. If, within a translation unit, the same entity is declared with both internal and external linkage, the behavior is undefined.
[Example:
static void f(); static int i = 0; // 1 void g () { extern void f (); // internal linkage int i; // 2: i has no linkage { extern void f (); // internal linkage extern int i; // 3: external linkage } }There are three objects named i in this program. The object with internal linkage introduced by the declaration in global scope (line //1 ), the object with automatic storage duration and no linkage introduced by the declaration on line //2, and the object with static storage duration and external linkage introduced by the declaration on line //3. Without the declaration at line //2, the declaration at line //3 would link with the declaration at line //1. But because the declaration with internal linkage is hidden, //3 is given external linkage, resulting in a linkage conflict. —end example]
Notes frum the April 2006 meeting:
According to 3.5 [basic.link] paragraph 9, the two variables with linkage in the proposed example are not “the same entity” because they do not have the same linkage. Some other formulation will be needed to describe the relationship between those two variables.
Notes from the October 2006 meeting:
The CWG decided that it would be better to make a program with this kind of linkage mismatch ill-formed instead of having undefined behavior.
3.9 [basic.types] paragraph 10 requires that a class have at least one constexpr constructor other than the copy constructor in order to be considered a literal type. However, a constexpr constructor template might be instantiated in such a way that the constexpr specifier is ignored (7.1.5 [dcl.constexpr] paragraph 5). It is therefore not known whether a class with a constexpr constructor template is a literal type or not until the constructor template is specialized, which could mean that an example like
struct IntValue { template<typename T> constexpr IntValue(T t) : val(t) { } constexpr intmax_t get_value() { return val; } private: intmax_t val; };
is ill-formed, because it is an error to declare a member function (like get_value()) of a non-literal class to be constexpr (7.1.5 [dcl.constexpr] paragraph 6).
3.9 [basic.types] paragraph 10 should be revised so that either a constexpr constructor or constexpr constructor template allows a class to be a literal type.
According to 3.9 [basic.types] paragraph 10, one of the requirements for a literal class type is
a trivial default constructor or at least one constexpr constructor other than the copy or move constructor
This rule has unfortunate consequences. For example, in
struct A { int x; }; struct B: A { int y; };
B is a literal type, even though it is impossible to initialize a constant of that type. Conversely, in
struct C { int a, b; constexpr C(int x, int y): a(x), b(y) { } }; struct D { int x; C c; };
D is not a literal type, even though it could be initialized as an aggregate.
It would be an improvement to replace the requirement for a trivial default constructor with a requirement that the class be an aggregate.
According to 3.9.1 [basic.fundamental] paragraph 9,
Any expression can be explicitly converted to type cv void (5.4 [expr.cast]). An expression of type void shall be used only as an expression statement (6.2 [stmt.expr]), as an operand of a comma expression (5.18 [expr.comma]), as a second or third operand of ?: (5.16 [expr.cond]), as the operand of typeid, or as the expression in a return statement (6.6.3 [stmt.return]) for a function with the return type void.
First, this is self-contradictory: if “any expression” can be converted to void, why is such a conversion not listed among the acceptable uses of an expression of type void?
Second, presumably an expression of type void can be used as an operand of decltype, but this use is not listed.
Finally, there are several places in the Standard that speak of expressions having a cv-qualified void type (5.16 [expr.cond] paragraph 2, 6.6.3 [stmt.return] paragraph 3). However, an expression of type void is a non-class prvalue, and there are no cv-qualified non-class prvalues (3.10 [basic.lval] paragraph 4).
In spite of the resolution of issue 112, the exact relationship between cv-qualifiers and array types is not clear. There does not appear to be a definitive normative statement answering the question of whether an array with a const-qualified element type is itself const-qualified; the statement in 3.9.3 [basic.type.qualifier] paragraph 5,
Cv-qualifiers applied to an array type attach to the underlying element type, so the notation “cv T,” where T is an array type, refers to an array whose elements are so-qualified. Such array types can be said to be more (or less) cv-qualified than other types based on the cv-qualification of the underlying element types.
hints at an answer but is hardly decisive. For example, is the following example well-formed?
template <class T> struct is_const { static const bool value = false; }; template <class T> struct is_const<const T> { static const bool value = true; }; template <class T> void f(T &) { char checker[is_const<T>::value]; } int const arr[1] = {}; int main() { f(arr); }
Also, when 3.10 [basic.lval] paragraph 4 says,
Class prvalues can have cv-qualified types; non-class prvalues always have cv-unqualified types.
does this apply to array rvalues, as it appears? That is, given
struct S { const int arr[10]; };
is the array rvalue S().arr an array of int or an array of const int?
(The more general question is, when the Standard refers to non-class types, should it be considered to include array types? Or perhaps only arrays of non-class types?)
The aliasing rules given in 3.10 [basic.lval] paragraph 10 rely on the concept of “dynamic type.” The problem is that the dynamic type of an object often cannot be determined (or even sufficiently constrained) at the point at which an optimizer needs to be able to determine whether aliasing might occur or not. For example, consider the function
void foo(int* p, double* q) { *p = 42; *q = 3.14; }
An optimizer, on the basis of the existing aliasing rules, might decide that an int* and a double* cannot refer to the same object and reorder the assignments. This reordering, however, could result in undefined behavior if the function foo is called as follows:
void goo() { union { int i; double d; } t; t.i = 12; foo(&t.i, &t.d); cout << t.d << endl; };
Here, the reference to t.d after the call to foo will be valid only if the assignments in foo are executed in the order in which they were written; otherwise, the union will contain an int object rather than a double.
One possibility would be to require that if such aliasing occurs, it be done only via member names and not via pointers.
Notes from the July, 2007 meeting:
This is the same issue as C's DR236. The CWG expressed a desire to address the issue the same way C99 does. The issue also occurs in C++ when placement new is used to end the lifetime of one object and start the lifetime of a different object occupying the same storage.
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.
The current wording of the Standard does not recognize the fact that the alignment of a complete object of a given type may be different from its alignment as a subobject. This arises in particular with virtual base classes. For example,
struct B { long double d; }; struct D: virtual B { char c; };
When D is a complete object, it will have a subobject of type B, which must be aligned appropriately for a long double. On the other hand, if D appears as a suboject of another object, the B subobject might be part of a different subobject, reducing the alignment requirement on the D subobject.
The Standard should make clear that it is the complete-object alignment that is being described, in parallel with the distinction between the size of a complete object and a subobject of the same type.
According to 4.1 [conv.lval] paragraph 1, applying the lvalue-to-rvalue conversion to any uninitialized object results in undefined behavior. However, character types are intended to allow any data, including uninitialized objects and padding, to be copied (hence the statements in 3.9.1 [basic.fundamental] paragraph 1 that “For character types, all bits of the object representation participate in the value representation” and in 3.10 [basic.lval] paragraph 15 that char and unsigned char types can alias any object). The lvalue-to-rvalue conversion should be permitted on uninitialized objects of character type without evoking undefined behavior.
The descriptions of explicit (5.2.9 [expr.static.cast] paragraph 9) and implicit (4.11 [conv.mem] paragraph 2) pointer-to-member conversions differ in two significant ways:
(This situation cannot arise in an implicit pointer-to-member conversion where the source value is something like &X::f, since you can only implicitly convert from pointer-to-base-member to pointer-to-derived-member. However, if the source value is the result of an explicit "up-cast," the target type of the conversion might still not contain the member referred to by the source value.)
The first difference seems like an oversight. It is not clear whether the latter difference is intentional or not.
(See also issue 794.)
One 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.)
There are at least a couple of problems in the description of the various id-expressions in 5.1.1 [expr.prim.general]:
Paragraph 4 embodies an incorrect assumption about the syntax of qualified-ids:
The operator :: followed by an identifier, a qualified-id, or an operator-function-id is a primary-expression.
The problem here is that the :: is actually part of the syntax of qualified-id; consequently, “:: followed by... a qualified-id” could be something like “:: ::i,” which is ill-formed. Presumably this should say something like, “A qualified-id with no nested-name-specifier is a primary-expression.”
More importantly, some kinds of id-expressions are not described by 5.1.1 [expr.prim.general]. The structure of this section is that the result, type, and lvalue-ness are specified for each of the cases it covers:
paragraph 4 deals with qualified-ids that have no nested-name-specifier
paragraph 7 deals with bare identifiers and with qualified-ids containing a nested-name-specifier that names a class
paragraph 8 deals with qualified-ids containing a nested-name-specifier that names a namespace
This treatment leaves unspecified all the non-identifier unqualified-ids (operator-function-id, conversion-function-id, and template-id), as well as (perhaps) “:: template-id” (it's not clear whether the “:: followed by a qualified-id” case is supposed to apply to template-ids or not). Note also that the proposed resolution of issue 301 slightly exacerbates this problem by removing the form of operator-function-id that contains a tmeplate-argument-list; as a result, references like “::operator+<X>” are no longer covered in 5.1.1 [expr.prim.general].
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).
5.2.2 [expr.call] paragraph 7 should mention a non-trivial move constructor as well, for consistency with “trivially copyable.”
According 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.
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.
The resolution to issue 195 makes “converting a pointer to a function into a pointer to an object type or vice versa” conditionally-supported behavior. In doing so, however, it overlooked the fact that void is not an “object type” (3.9 [basic.types] paragraph 9). The wording should be amended to allow conversion to and from void* types.
Proposed resolution (June, 2008):
Change 3.9.2 [basic.compound] paragraph 4 as follows:
Objects of cv-qualified (3.9.3 [basic.type.qualifier]) or cv-unqualified type void* (pointer to void) A pointer to cv-qualified or cv-unqualified void can be used to point to objects of unknown type. A void* shall be able to hold any object pointer and is thus considered to be an object pointer type, although it is not a pointer to object type (because void is not an object type). A cv-qualified or cv-unqualified (3.9.3 [basic.type.qualifier]) An object of type cv void* shall have the same representation and alignment requirements as a cv-qualified or cv-unqualified cv char*.
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 object pointer or function pointer type...
Change 5.2.10 [expr.reinterpret.cast] paragraph 7 as follows:
A pointer to an object An object pointer can be explicitly converted to a pointer to an object an object pointer of different type. Except that converting an rvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types or void and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.
Change 5.2.10 [expr.reinterpret.cast] paragraph 8 as follows:
Converting a pointer to a function into a pointer to an object type a function pointer to an object pointer or vice versa is conditionally-supported...
[Drafting note: 14.1 [temp.param] paragraph 4 was not changed and thus continues to allow only pointers to objects, not object pointers, as non-type template parameters.]
Proposed resolution (February, 2010):
Change 3.7.4.3 [basic.stc.dynamic.safety] paragraphs 1-2 as follows:
A traceable pointer object is
an object of pointer-to-object object pointer type (3.9.2 [basic.compound]), or
an object of an integral type that is at least as large as std::intptr_t, or
a sequence of elements in an array of character type, where the size and alignment of the sequence match that of some pointer-to-object object pointer type.
A pointer value is a safely-derived pointer to a dynamic object only if it has pointer-to-object object pointer type and...
Change 3.9.2 [basic.compound] paragraphs 3-4 as follows:
The type of a pointer that can hold the address of an object is called an object pointer type. The type of a pointer that can designate a function is called a function pointer type. A pointer to objects of type T is referred to as a “pointer to T.” Example:...
Objects of cv-qualified (3.9.3 [basic.type.qualifier]) or cv-unqualified type void* (pointer to void), A pointer to cv-qualified or cv-unqualified void can be used to point to objects of unknown type. A void* Such a pointer shall be able to hold any object pointer and is thus considered to be an object pointer and to have an object pointer type (but not a pointer to object type, because void is not an object type). A cv-qualified or cv-unqualified (3.9.3 [basic.type.qualifier]) An object of type cv void* shall have the same representation and alignment requirements as a cv-qualified or cv-unqualified cv char*.
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 object pointer or function pointer type...
Change 5.2.10 [expr.reinterpret.cast] paragraphs 6-8 as follows:
A pointer to a function function pointer can be explicitly converted to a pointer to a function of a different function pointer type...
A pointer to an object An object pointer can be explicitly converted to a pointer to a different object pointer type.69...
Converting a pointer to a function function pointer into a pointer to an object to an object pointer type or vice versa is conditionally-supported...
In the “Index of Implementation Defined Behavior,” change the following item as indicated:
converting pointer to function into pointer to object function pointer to object pointer and vice versa
(See also issue 1120.)
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.
At least a couple of places in the IS state that indirection through a null pointer produces undefined behavior: 1.9 [intro.execution] paragraph 4 gives "dereferencing the null pointer" as an example of undefined behavior, and 8.3.2 [dcl.ref] paragraph 4 (in a note) uses this supposedly undefined behavior as justification for the nonexistence of "null references."
However, 5.3.1 [expr.unary.op] paragraph 1, which describes the unary "*" operator, does not say that the behavior is undefined if the operand is a null pointer, as one might expect. Furthermore, at least one passage gives dereferencing a null pointer well-defined behavior: 5.2.8 [expr.typeid] paragraph 2 says
If the lvalue expression is obtained by applying the unary * operator to a pointer and the pointer is a null pointer value (4.10 [conv.ptr]), the typeid expression throws the bad_typeid exception (18.7.3 [bad.typeid]).
This is inconsistent and should be cleaned up.
Bill Gibbons:
At one point we agreed that dereferencing a null pointer was not undefined; only using the resulting value had undefined behavior.
For example:
char *p = 0; char *q = &*p;
Similarly, dereferencing a pointer to the end of an array should be allowed as long as the value is not used:
char a[10]; char *b = &a[10]; // equivalent to "char *b = &*(a+10);"
Both cases come up often enough in real code that they should be allowed.
Mike Miller:
I can see the value in this, but it doesn't seem to be well reflected in the wording of the Standard. For instance, presumably *p above would have to be an lvalue in order to be the operand of "&", but the definition of "lvalue" in 3.10 [basic.lval] paragraph 2 says that "an lvalue refers to an object." What's the object in *p? If we were to allow this, we would need to augment the definition to include the result of dereferencing null and one-past-the-end-of-array.
Tom Plum:
Just to add one more recollection of the intent: I was very happy when (I thought) we decided that it was only the attempt to actually fetch a value that creates undefined behavior. The words which (I thought) were intended to clarify that are the first three sentences of the lvalue-to-rvalue conversion, 4.1 [conv.lval]:
An lvalue (3.10 [basic.lval]) of a non-function, non-array type T can be converted to an rvalue. If T is an incomplete type, a program that necessitates this conversion is ill-formed. If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.
In other words, it is only the act of "fetching", of lvalue-to-rvalue conversion, that triggers the ill-formed or undefined behavior. Simply forming the lvalue expression, and then for example taking its address, does not trigger either of those errors. I described this approach to WG14 and it may have been incorporated into C 1999.
Mike Miller:
If we admit the possibility of null lvalues, as Tom is suggesting here, that significantly undercuts the rationale for prohibiting "null references" -- what is a reference, after all, but a named lvalue? If it's okay to create a null lvalue, as long as I don't invoke the lvalue-to-rvalue conversion on it, why shouldn't I be able to capture that null lvalue as a reference, with the same restrictions on its use?
I am not arguing in favor of null references. I don't want them in the language. What I am saying is that we need to think carefully about adopting the permissive approach of saying that it's all right to create null lvalues, as long as you don't use them in certain ways. If we do that, it will be very natural for people to question why they can't pass such an lvalue to a function, as long as the function doesn't do anything that is not permitted on a null lvalue.
If we want to allow &*(p=0), maybe we should change the definition of "&" to handle dereferenced null specially, just as typeid has special handling, rather than changing the definition of lvalue to include dereferenced nulls, and similarly for the array_end+1 case. It's not as general, but I think it might cause us fewer problems in the long run.
Notes from the October 2003 meeting:
See also issue 315, which deals with the call of a static member function through a null pointer.
We agreed that the approach in the standard seems okay: p = 0; *p; is not inherently an error. An lvalue-to-rvalue conversion would give it undefined behavior.
Proposed resolution (October, 2004):
(Note: the resolution of issue 453 also resolves part of this issue.)
Add the indicated words to 3.10 [basic.lval] paragraph 2:
An lvalue refers to an object or function or is an empty lvalue (5.3.1 [expr.unary.op]).
Add the indicated words to 5.3.1 [expr.unary.op] paragraph 1:
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points, if any. If the pointer is a null pointer value (4.10 [conv.ptr]) or points one past the last element of an array object (5.7 [expr.add]), the result is an empty lvalue and does not refer to any object or function. An empty lvalue is not modifiable. If the type of the expression is “pointer to T,” the type of the result is “T.” [Note: a pointer to an incomplete type (other than cv void) can be dereferenced. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to an rvalue, see 4.1 [conv.lval].—end note]
Add the indicated words to 4.1 [conv.lval] paragraph 1:
If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, or if the lvalue is an empty lvalue (5.3.1 [expr.unary.op]), a program that necessitates this conversion has undefined behavior.
Change 1.9 [intro.execution] as indicated:
Certain other operations are described in this International Standard as undefined (for example, the effect of dereferencing the null pointer division by zero).
Note (March, 2005):
The 10/2004 resolution interacts with the resolution of issue 73. We added wording to 3.9.2 [basic.compound] paragraph 3 to the effect that a pointer containing the address one past the end of an array is considered to “point to” another object of the same type that might be located there. The 10/2004 resolution now says that it would be undefined behavior to use such a pointer to fetch the value of that object. There is at least the appearance of conflict here; it may be all right, but it at needs to be discussed further.
Notes from the April, 2005 meeting:
The CWG agreed that there is no contradiction between this direction and the resolution of issue 73. However, “not modifiable” is a compile-time concept, while in fact this deals with runtime values and thus should produce undefined behavior instead. Also, there are other contexts in which lvalues can occur, such as the left operand of . or .*, which should also be restricted. Additional drafting is required.
(See also issue 1102.)
The 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.”
It is not clear from 5.3.4 [expr.new] whether a deleted operator delete is referenced by a new-expression in which there is no initialization or in which the initialization cannot throw an exception, rendering the program ill-formed. (The question also arises as to whether such a new-expression constitutes a “use” of the deallocation function in the sense of 3.2 [basic.def.odr].)
Notes from the July, 2009 meeting:
The rationale for defining a deallocation function as deleted would presumably be to prevent such objects from being freed. Treating the new-expression as a use of such a deallocation function would mean that such objects could not be created in the first place. There is already an exemption from freeing an object if “a suitable deallocation function [cannot] be found;” a deleted deallocation function should be treated similarly.
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.
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.
According to 5.5 [expr.mptr.oper] paragraph 6,
The result of a .* expression whose second operand is a pointer to a data member is of the same value category (3.10 [basic.lval]) as its first operand.
This is incorrect if the member has a reference type, in which case the result is an lvalue.
According to 5.19 [expr.const] paragraph 3,
A constant expression is an integral constant expression if it is of integral or enumeration type. [Note: such expressions may be used as array bounds (8.3.4 [dcl.array], 5.3.4 [expr.new]), as case expressions (6.4.2 [stmt.switch]), as bit-field lengths (9.6 [class.bit]), as enumerator initializers (7.2 [dcl.enum]), and as integral or enumeration non-type template arguments (14.3 [temp.arg]). —end note]
Although there is conceptually a conversion from enumeration type to integral type involved in using an enumerator as an array bound or bit-field length, the normative wording for those uses does not explicitly mention it and simply requires an integral constant expression. Consequently, the current wording permits uses like the following:
enum class E { e = 10; }; struct S { int arr[E::e]; int i: E::e; };
This seems surprising.
The grammar for declarations includes the following two nonterminals:
An attribute-specifier followed by a semicolon could thus be parsed as either an attribute-declaration or as a simple-declaration that omits the optional decl-specifier-seq and init-declarator-list, and the current wording does not disambiguate the two. (There doesn't seem to be a compelling need for attribute-declaration as a separate nonterminal, given that simple-declaration can accommodate that form.)
The grammar for an alias-declaration does not have a place for an attribute-specifier, although a typedef declaration does. Since an alias-declaration is essentially a different syntactic form of a typedef declaration (7.1.3 [dcl.typedef] paragraph 2), this could be surprising.
Here's an example:
typedef struct S { ... } S; void fs(S *x) { ... }
The big question is, to what declaration does the reference to identifier S actually refer? Is it the S that's declared as a typedef name, or the S that's declared as a class name (or in C terms, as a struct tag)? (In either case, there's clearly only one type to which it could refer, since a typedef declaration does not introduce a new type. But the debugger apparently cares about more than just the identity of the type.)
Here's a classical, closely related example:
struct stat { ... }; int stat(); ... stat( ... ) ...
Does the identifier stat refer to the class or the function? Obviously, in C, you can't refer to the struct tag without using the struct keyword, because it is in a different name space, so the reference must be to the function. In C++, the reference is also to the function, but for a completely different reason.
Now in C, typedef names and function names are in the same name space, so the natural extrapolation would be that, in the first example, S refers to the typedef declaration, as it would in C. But C++ is not C. For the purposes of this discussion, there are two important differences between C and C++
The first difference is that, in C++, typedef names and class names are not in separate name spaces. On the other hand, according to section 3.3.10 [basic.scope.hiding] (Name hiding), paragraph 2:
A class name (9.1) or enumeration name (7.2) can be hidden by the name of an object, function, or enumerator declared in the same scope. If a class or enumeration name and an object, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the object, function, or enumerator name is visible.
Please consider carefully the phrase I have highlighted, and the fact that a typedef name is not the name of an object, function or enumerator. As a result, this example:
struct stat { ... }; typedef int stat;
Which would be perfectly legal in C, is disallowed in C++, both implicitly (see the above quote) and explicitly (see section 7.1.3 [dcl.typedef] (The typedef specifier), paragraph 3):
In a given scope, a typedef specifier shall not be used to redefine the name of any type declared in that scope to refer to a different type. Similarly, in a given scope, a class or enumeration shall not be declared with the same name as a typedef-name that is declared in that scope and refers to a type other than the class or enumeration itself.
From which we can conclude that in C++ typedef names do not hide class names declared in the same scope. If they did, the above example would be legal.
The second difference is that, in C++, a typedef name that refers to a class is a class-name; see 7.1.3 [dcl.typedef] paragraph 4:
A typedef-name that names a class is a class-name(9.1). If a typedef-name is used following the class-key in an elaborated-type-specifier (7.1.5.3) or in the class-head of a class declaration (9), or is used as the identifier in the declarator for a constructor or destructor declaration (12.1, 12.4), the program is ill-formed.
This implies, for instance, that a typedef-name referring to a class can be used in a nested-name-specifier (i.e. before :: in a qualified name) or following ~ to refer to a destructor. Note that using a typedef-name as a class-name in an elaborated-type-specifier is not allowed. For example:
struct X { }; typedef struct X X2; X x; // legal X2 x2; // legal struct X sx; // legal struct X2 sx2; // illegal
The final relevant piece of the standard is 7.1.3 [dcl.typedef] paragraph 2:
In a given scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers.
This of course is what allows the original example, to which let us now return:
typedef struct S { ... } S; void fs(S *x) { ... }
The question, again is, to which declaration of S does the reference actually refer? In C, it would clearly be to the second, since the first would be accessible only by using the struct keyword. In C++, if typedef names hid class names declared in the same scope, the answer would be the same. But we've already seen that typedef names do not hide class names declared in the same scope.
So to which declaration does the reference to S refer? The answer is that it doesn't matter. The second declaration of S, which appears to be a declaration of a typedef name, is actually a declaration of a class name (7.1.3 [dcl.typedef] paragraph 4), and as such is simply a redeclaration. Consider the following example:
typedef int I, I; extern int x, x; void f(), f();
To which declaration would a reference to I, x or f refer? It doesn't matter, because the second declaration of each is really just a redeclaration of the thing declared in the first declaration. So to save time, effort and complexity, the second declaration of each doesn't add any entry to the compiler's symbol table.
Note (March, 2005):
Matt Austern: Is this legal?
struct A { }; typedef struct A A; struct A* p;
Am I right in reading the standard [to say that this is ill-formed]? On the one hand it's a nice uniform rule. On the other hand, it seems likely to confuse users. Most people are probably used to thinking that 'typedef struct A A' is a null operation, and, if this code really is illegal, it would seem to be a gratuitous C/C++ incompatibility.
Mike Miller: I think you're right. 7.1.3 [dcl.typedef] paragraph 1:
A name declared with the typedef specifier becomes a typedef-name.
7.1.3 [dcl.typedef] paragraph 2:
In a given non-class scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers.
After the typedef declaration in the example, the name X has been “redefined” — it is no longer just a class-name, it has been “redefined” to be a typedef-name (that, by virtue of the fact that it refers to a class type, is also a class-name).
John Spicer: In C, and originally in C++, an elaborated-type-specifier did not consider typedef names, so “struct X* x” would find the class and not the typedef.
When C++ was changed to make typedefs visible to elaborated-type-specifier lookups, I believe this issue was overlooked and inadvertantly made ill-formed.
I suspect we need add text saying that if a given scope contains both a class/enum and a typedef, that an elaborated type specifier lookup finds the class/enum.
Mike Miller: I'm a little uncomfortable with this approach. The model we have for declaring a typedef in the same scope as a class/enum is redefinition, not hiding (like the “struct stat” hack). This approach seems to assume that the typedef hides the class/enum, which can then be found by an elaborated-type-specifier, just as if it were hidden by a variable, function, or enumerator.
Also, this approach reduces but doesn't eliminate the incompatibility with C. For example:
struct S { }; { typedef struct S S; struct S* p; // still ill-formed }
My preference would be for something following the basic principle that declaring a typedef-name T in a scope where T already names the type designated by the typedef should have no effect on whether an elaborated-type-specifier in that or a nested scope is well-formed or not. Another way of saying that is that a typedef-name that designates a same-named class or enumeration in the same or a containing scope is transparent with respect to elaborated-type-specifiers.
John Spicer: This strikes me as being a rather complicated solution. When we made the change to make typedefs visible to elaborated-type-specifiers we did so knowing it would make some C cases ill-formed, so this does not bother me. We've lived with the C incompatibility for many years now, so I don't personally feel a need to undo it. I also don't like the fact that you have to essentially do the old-style elaborated-type-specifier lookup to check the result of the lookup that found the typedef.
I continue to prefer the direction I described earlier where if a given scope contains both a class/enum and a typedef, that an elaborated-type-specifier lookup finds the class/enum.
Notes from the April, 2005 meeting:
The CWG agreed with John Spicer's approach, i.e., permitting a typedef-name to be used in an elaborated-type-specifier only if it is declared in the same scope as the class or enumeration it names.
The body of a constexpr function is required by 7.1.5 [dcl.constexpr] paragraph 3 to be of the form
However, there does not seem to be any good reason for prohibiting the alternate return syntax involving a braced-init-list. The restriction should be removed.
Proposed resolution (March, 2010):
Change 6.6.3 [stmt.return] paragraph 2 as follows:
A return statement without an expression with neither an expression nor a braced-init-list can be used only in functions that do not return a value...
Change 7.1.5 [dcl.constexpr] paragraph 3 bullets 4 and 5 as follows:
its function-body shall be a compound-statement of the form
where expression is a potential constant expression (5.19), or
where every assignment-expression that is an initializer-clause appearing directly or indirectly within the braced-init-list is a potential constant expression
every constructor call and implicit conversion used in converting expression to the function return type initializing the return value (6.6.3 [stmt.return], 8.5 [dcl.init]) shall be one of those allowed in a constant expression (5.19 [expr.const]).
Notes from the March, 2010 meeting:
The new wording added in 5.19 [expr.const] in support of reference parameters for constexpr functions should also be considered to see whether additional changes are needed.
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.
7.3.1.2 [namespace.memdef] paragraph 3 says,
If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace... When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered.It is not clear from this passage how to determine whether an entity is "first declared" in a friend declaration. One question is whether a using-declaration influences this determination. For instance:
void foo(); namespace A{ using ::foo; class X{ friend void foo(); }; }Is the friend declaration a reference to ::foo or a different foo?
Part of the question involves determining the meaning of the word "synonym" in 7.3.3 [namespace.udecl] paragraph 1:
A using-declaration introduces a name into the declarative region in which the using-declaration appears. That name is a synonym for the name of some entity declared elsewhere.Is "using ::foo;" the declaration of a function or not?
More generally, the question is how to describe the lookup of the name in a friend declaration.
John Spicer: When a declaration specifies an unqualified name, that name is declared, not looked up. There is a mechanism in which that declaration is linked to a prior declaration, but that mechanism is not, in my opinion, via normal name lookup. So, the friend always declares a member of the nearest namespace scope regardless of how that name may or may not already be declared there.
Mike Miller: 3.4.1 [basic.lookup.unqual] paragraph 7 says:
A name used in the definition of a class X outside of a member function body or nested class definition shall be declared in one of the following ways:... [Note: when looking for a prior declaration of a class or function introduced by a friend declaration, scopes outside of the innermost enclosing namespace scope are not considered.]The presence of this note certainly implies that this paragraph describes the lookup of names in friend declarations.
John Spicer: It most certainly does not. If that section described the friend lookup it would yield the incorrect results for the friend declarations of f and g below. I don't know why that note is there, but it can't be taken to mean that that is how the friend lookup is done.
void f(){} void g(){} class B { void g(); }; class A : public B { void f(); friend void f(); // ::f not A::f friend void g(); // ::g not B::g };
Mike Miller: If so, the lookups for friend functions and classes behave differently. Consider the example in 3.4.4 [basic.lookup.elab] paragraph 3:
struct Base { struct Data; // OK: declares nested Data friend class Data; // OK: nested Data is a friend };
If the friend declaration is not a reference to ::foo, there is a related but separate question: does the friend declaration introduce a conflicting (albeit "invisible") declaration into namespace A, or is it simply a reference to an as-yet undeclared (and, in this instance, undeclarable) A::foo? Another part of the example in 3.4.4 [basic.lookup.elab] paragraph 3 is related:
struct Data { friend struct Glob; // OK: Refers to (as yet) undeclared Glob // at global scope. };
John Spicer: You can't refer to something that has not yet been declared. The friend is a declaration of Glob, it just happens to declare it in a such a way that its name cannot be used until it is redeclared.
(A somewhat similar question has been raised in connection with issue 36. Consider:
namespace N { struct S { }; } using N::S; struct S; // legal?
According to 9.1 [class.name] paragraph 2,
A declaration consisting solely of class-key identifier ; is either a redeclaration of the name in the current scope or a forward declaration of the identifier as a class name.
Should the elaborated type declaration in this example be considered a redeclaration of N::S or an invalid forward declaration of a different class?)
(See also issues 95, 136, 139, 143, 165, and 166, as well as paper J16/00-0006 = WG21 N1229.)
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.
An attribute-argument-clause should be allowed to consist solely of (), i.e., with no balanced-token-seq. Furthermore, the grammar for balanced-token should make the balanced-token-seq optional. Both of these goals could be accomplished by making the balanced-token optional in the first production of the rule for balanced-token-seq.
According to 7.6.2 [dcl.align] paragraph 5,
The combined effect of all alignment attributes in a declaration shall not specify an alignment that is less strict than the alignment that would otherwise be required for the entity being declared.
“...would otherwise be required” could be read as referring to the alignment set by another declaration of the entity. However, it was intended to prevent specifying an alignment smaller than the natural alignment the entity would have in the absence of an align attribute. The wording should be changed to make that clearer.
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.
8.3.2 [dcl.ref] paragraph 4 says:
A reference shall be initialized to refer to a valid object or function. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the "object" obtained by dereferencing a null pointer, which causes undefined behavior ...]
What is a "valid" object? In particular the expression "valid object" seems to exclude uninitialized objects, but the response to Core Issue 363 clearly says that's not the intent. This is an example (overloading construction on constness of *this) by John Potter, which I think is supposed to be legal C++ though it binds references to objects that are not initialized yet:
struct Fun { int x, y; Fun (int x, Fun const&) : x(x), y(42) { } Fun (int x, Fun&) : x(x), y(0) { } }; int main () { const Fun f1 (13, f1); Fun f2 (13, f2); cout << f1.y << " " << f2.y << "\n"; }
Suggested resolution: Changing the final part of 8.3.2 [dcl.ref] paragraph 4 to:
A reference shall be initialized to refer to an object or function. From its point of declaration on (see 3.3.2 [basic.scope.pdecl]) its name is an lvalue which refers to that object or function. The reference may be initialized to refer to an uninitialized object but, in that case, it is usable in limited ways (3.8 [basic.life], paragraph 6) [Note: On the other hand, a declaration like this:int & ref = *(int*)0;is ill-formed because ref will not refer to any object or function ]
I also think a "No diagnostic is required." would better be added (what about something like int& r = r; ?)
Proposed Resolution (October, 2004):
(Note: the following wording depends on the proposed resolution for issue 232.)
Change 8.3.2 [dcl.ref] paragraph 4 as follows:
A reference shall be initialized to refer to a valid object or function. If an lvalue to which a reference is directly bound designates neither an existing object or function of an appropriate type (8.5.3 [dcl.init.ref]), nor a region of memory of suitable size and alignment to contain an object of the reference's type (1.8 [intro.object], 3.8 [basic.life], 3.9 [basic.types]), the behavior is undefined. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” empty lvalue obtained by dereferencing a null pointer, which causes undefined behavior. As does not designate an object or function. Also, as described in 9.6 [class.bit], a reference cannot be bound directly to a bit-field. ]
The name of a reference shall not be used in its own initializer. Any other use of a reference before it is initialized results in undefined behavior. [Example:
int& f(int&); int& g(); extern int& ir3; int* ip = 0; int& ir1 = *ip; // undefined behavior: null pointer int& ir2 = f(ir3); // undefined behavior: ir3 not yet initialized int& ir3 = g(); int& ir4 = f(ir4); // ill-formed: ir4 used in its own initializer—end example]
Rationale: The proposed wording goes beyond the specific concerns of the issue. It was noted that, while the current wording makes cases like int& r = r; ill-formed (because r in the initializer does not "refer to a valid object"), an inappropriate initialization can only be detected, if at all, at runtime and thus "undefined behavior" is a more appropriate treatment. Nevertheless, it was deemed desirable to continue to require a diagnostic for obvious compile-time cases.
It was also noted that the current Standard does not say anything about using a reference before it is initialized. It seemed reasonable to address both of these concerns in the same wording proposed to resolve this issue.
Notes from the April, 2005 meeting:
The CWG decided that whether to require an implementation to diagnose initialization of a reference to itself should be handled as a separate issue (504) and also suggested referring to “storage” instead of “memory” (because 1.8 [intro.object] defines an object as a “region of storage”).
Proposed Resolution (April, 2005):
(Note: the following wording depends on the proposed resolution for issue 232.)
Change 8.3.2 [dcl.ref] paragraph 4 as follows:
A reference shall be initialized to refer to a valid object or function. If an lvalue to which a reference is directly bound designates neither an existing object or function of an appropriate type (8.5.3 [dcl.init.ref]), nor a region of storage of suitable size and alignment to contain an object of the reference's type (1.8 [intro.object], 3.8 [basic.life], 3.9 [basic.types]), the behavior is undefined. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” empty lvalue obtained by dereferencing a null pointer, which causes undefined behavior. As does not designate an object or function. Also, as described in 9.6 [class.bit], a reference cannot be bound directly to a bit-field. ]
Any use of a reference before it is initialized results in undefined behavior. [Example:
int& f(int&); int& g(); extern int& ir3; int* ip = 0; int& ir1 = *ip; // undefined behavior: null pointer int& ir2 = f(ir3); // undefined behavior: ir3 not yet initialized int& ir3 = g(); int& ir4 = f(ir4); // undefined behavior: ir4 used in its own initializer—end example]
Note (February, 2006):
The word “use” in the last paragraph of the proposed resolution was intended to refer to the description in 3.2 [basic.def.odr] paragraph 2. However, that section does not define what it means for a reference to be “used,” dealing only with objects and functions. Additional drafting is required to extend 3.2 [basic.def.odr] paragraph 2 to apply to references.
Additional note (May, 2008):
The proposed resolution for issue 570 adds wording to define “use” for references.
EDG rejects this code:
template <typename T> struct S {}; void f (S<int (*)[]>);G++ accepts it.
This is another case where the standard isn't very clear:
The language from 8.3.5 [dcl.fct] is:
If the type of a parameter includes a type of the form "pointer to array of unknown bound of T" or "reference to array of unknown bound of T," the program is ill-formed.Since "includes a type" is not a term defined in the standard, we're left to guess what this means. (It would be better if this were a recursive definition, the way a type theoretician would do it:
Notes from April 2003 meeting:
We agreed that the example should be allowed.
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).
8.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.”
It should be allowed to explicitly default a non-public special member function on its first declaration. It is very likely that users will want to default protected/private constructors and copy constructors without having to write such defaulting outside the class.
It should be allowed to explicitly default an explicit special member function on its first declaration. It is very likely that users will want to default explicit copy constructors without having to write such defaulting outside of the class.
It should be allowed to explicitly default a virtual special member function on its first declaration. It is very likely that users will want to default virtual copy assignment operators and destructors without having to write such defaulting outside of the class.
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.
8.5.1 [dcl.init.aggr] paragraph 4 says,
An initializer-list is ill-formed if the number of initializer-clauses exceeds the number of members or elements to initialize.
However, in a new-expression, the number of elements to be initialized is potentially unknown at compile time. How should an overly-long initializer-list in a new-expression be treated?
Notes from the August, 2010 meeting:
The consensus of the CWG was that this case should throw an exception at runtime.
There is an inconsistency in the handling of references vs pointers in user defined conversions and overloading. The reason for that is that the combination of 8.5.3 [dcl.init.ref] and 4.4 [conv.qual] circumvents the standard way of ranking conversion functions, which was probably not the intention of the designers of the standard.
Let's start with some examples, to show what it is about:
struct Z { Z(){} }; struct A { Z x; operator Z *() { return &x; } operator const Z *() { return &x; } }; struct B { Z x; operator Z &() { return x; } operator const Z &() { return x; } }; int main() { A a; Z *a1=a; const Z *a2=a; // not ambiguous B b; Z &b1=b; const Z &b2=b; // ambiguous }
So while both classes A and B are structurally equivalent, there is a difference in operator overloading. I want to start with the discussion of the pointer case (const Z *a2=a;): 13.3.3 [over.match.best] is used to select the best viable function. Rule 4 selects A::operator const Z*() as best viable function using 13.3.3.2 [over.ics.rank] since the implicit conversion sequence const Z* -> const Z* is a better conversion sequence than Z* -> const Z*.
So what is the difference to the reference case? Cv-qualification conversion is only applicable for pointers according to 4.4 [conv.qual]. According to 8.5.3 [dcl.init.ref] paragraphs 4-7 references are initialized by binding using the concept of reference-compatibility. The problem with this is, that in this context of binding, there is no conversion, and therefore there is also no comparing of conversion sequences. More exactly all conversions can be considered identity conversions according to 13.3.3.1.4 [over.ics.ref] paragraph 1, which compare equal and which has the same effect. So binding const Z* to const Z* is as good as binding const Z* to Z* in terms of overloading. Therefore const Z &b2=b; is ambiguous. [13.3.3.1.4 [over.ics.ref] paragraph 5 and 13.3.3.2 [over.ics.rank] paragraph 3 rule 3 (S1 and S2 are reference bindings ...) do not seem to apply to this case]
There are other ambiguities, that result in the special treatment of references: Example:
struct A {int a;}; struct B: public A { B() {}; int b;}; struct X { B x; operator A &() { return x; } operator B &() { return x; } }; main() { X x; A &g=x; // ambiguous }
Since both references of class A and B are reference compatible with references of class A and since from the point of ranking of implicit conversion sequences they are both identity conversions, the initialization is ambiguous.
So why should this be a defect?
So overall I think this was not the intention of the authors of the standard.
So how could this be fixed? For comparing conversion sequences (and only for comparing) reference binding should be treated as if it was a normal assignment/initialization and cv-qualification would have to be defined for references. This would affect 8.5.3 [dcl.init.ref] paragraph 6, 4.4 [conv.qual] and probably 13.3.3.2 [over.ics.rank] paragraph 3.
Another fix could be to add a special case in 13.3.3 [over.match.best] paragraph 1.
In looking at a large handful of core issues related to elaborated-type-specifiers and the naming of classes in general, I discovered an odd fact. It turns out that there is exactly one place in the grammar where nested-name-specifier is not immediately preceded by "::opt": in class-head, which is used only for class definitions. So technically, this example is ill-formed, and should evoke a syntax error:
struct A; struct ::A { };
However, all of EDG, GCC and Microsoft's compiler accept it without a qualm. In fact, I couldn't get any of them to even warn about it.
Suggested resolution:
It would simplify the grammar, and apparently better reflect existing practice, to factor the global-scope operator into the rule for nested-name-specifier.
Proposed resolution (November, 2006):
In 3.4.3 [basic.lookup.qual] paragraph 6, change the grammar snippet as follows:
Delete 5.1.1 [expr.prim.general] paragraph 4 (“The operator :: followed by...”). [Drafting note: It's covered by paragraph 8 (type, lvalue-ness, member-ness, reference to 3.4.3.2 [namespace.qual]) and 3.4.3.2 [namespace.qual] (qualified lookup for namespace members).]
Change the grammar in 5.1.1 [expr.prim.general] paragraph 7 as follows (deleting the :: forms from qualified-id and adding :: as a new production for nested-name-specifier):
Change 5.1.1 [expr.prim.general] paragraph 8 as follows:
A nested-name-specifier that names designates a namespace (7.3 [basic.namespace]), followed by the name of a member of that namespace...
Change 5.1.1 [expr.prim.general] paragraph 10 as follows:
In a qualified-id, if the id-expression unqualified-id is a conversion-function-id...
In 5.2 [expr.post] paragraph 1, change the grammar as follows:
In 5.2.4 [expr.pseudo] paragraph 2, change the grammar snippet as follows:
In 7.1.6.2 [dcl.type.simple] paragraph 1, change the grammar as follows:
In 7.1.6.3 [dcl.type.elab] before paragraph 1, change the grammar as follows:
In 7.1.6.3 [dcl.type.elab] paragraph 1, change the grammar snippet as follows:
In 7.3.2 [namespace.alias] paragraph 1, change the grammar as follows:
In 7.3.3 [namespace.udecl] paragraph 1, change the grammar as follows:
In 7.3.4 [namespace.udir] before paragraph 1, change the grammar as follows:
In 8 [dcl.decl] paragraph 4, change the grammar as follows:
In 8.3.3 [dcl.mptr] paragraph 1, change the grammar snippet as follows:
In 9.2 [class.mem] before paragraph 1, change the grammar as follows:
In 10 [class.derived] paragraph 1, change the grammar as follows:
In 12.6.2 [class.base.init] paragraph 1, change the grammar as follows:
In 14.6 [temp.res] paragraph 3, change the grammar as follows:
[Drafting notes: gcc 4.1.1 rejects the example in the issue description. I still think it's a good idea to make the grammar more uniform, and there ought to be nothing special about the global scope operator. However, there is a slight change in effective grammar with these modification: all places that require a non-optional nested-name-specifier used to required at least one named level of nesting. With these changes, "::" is a valid nested-name-specifier (that denotes the global scope). Any such use needed to protect against non-class (i.e. namespace) scopes in its semantic description anyway, which also covers the "::" case.]
The class
struct A { const int i; };
was a POD in C++98, but is not a POD under the FCD rules because it does not have a trivial default constructor. C++0x POD was intended to be a superset of C++98 POD.
Suggested resolution: Change POD to be standard layout and trivially copyable.
According to 9.2 [class.mem] paragraph 6,
The decl-specifier-seq is omitted in constructor, destructor, and conversion function declarations only.
This is incorrect, as some decl-specifiers (explicit, virtual, inline, constexpr) are permitted in these declarations. Conversely, C.1.5 [diff.dcl], “Banning implicit int,” says,
In C++ a decl-specifier-seq must contain a type-specifier.
This is also incorrect for these declarations.
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 unscoped enumeration; scoped enumerators are not in the scope of the containing class.
The 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.
According to 9.8 [class.local] paragraph 1,
Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators from the enclosing scope.
This would presumably make both of the members of S2 below ill-formed:
void test () { const int local_const = 7; struct S2 { int member:local_const; void f() { int j = local_const; } }; }
Should there be an exception to this rule for constant values? Current implementations seem to accept the reference to local_const in the bit-field declaration but not in the member function definition. Should they be the same or different?
Notes from the September, 2008 meeting:
The CWG agreed that both uses of local_const in the example above should be accepted. The intent of the restriction was to avoid the need to pass a frame pointer into local class member functions, so uses of local const variables as values should be permitted.
Proposed resolution (March, 2009):
Change 9.8 [class.local] paragraph 1 as follows:
Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators shall not use (3.2 [basic.def.odr]) an automatic variable or reference from the enclosing scope. [Example:int x; void f() { static int s ; int x; extern int g(); const int c = 42; struct local { int g() { return x; } // error: x has automatic storage duration int h() { return s; } // OK int k() { return ::x; } // OK int l() { return g(); } // OK int m() { return c; } // OK }; } local* p = 0; // error: local not in scope—end example]
Notes from the July, 2009 meeting:
This proposed resolution relies on the definition of “use” in 3.2 [basic.def.odr]. The CWG was concerned about cases in which it might not be possible to immediately determine whether a reference to a local automatic variable constitutes a “use” or not, such as in overload resolution, conditional expressions, dependent contexts, etc. To address this concern, the CWG expressed support for an approach in which a reference to a local automatic variable in a nested class or lambda body would enter the expression as an rvalue, which would reduce the complexity of the problem.
Proposed resolution (September, 2009):
Change 5.1.1 [expr.prim.general] paragraph 6 as follows and add a new paragraph immediately following:
...The type of the expression is the type of the identifier. The result is the entity denoted by the identifier. The result is an lvalue if the entity is a function, variable, or data member.
Certain contexts (9.8 [class.local], 5.1.2 [expr.prim.lambda]) are called restricted automatic variable contexts because they permit the use of automatic variables declared in enclosing scopes only under certain conditions. In these contexts (only), an identifier denoting a variable with automatic storage duration declared in an enclosing scope that satisfies the requirements for appearing in a constant expression (5.19 [expr.const]) is called an r-variable expression. The result of an r-variable expression is an rvalue whose value is that of the variable. In all other cases, the result of an identifier expression denoting a variable is an lvalue.
Change 5.1.2 [expr.prim.lambda] paragraph 9 as follows:
A The compound-statement of a lambda-expression is a restricted automatic variable context (5.1.1 [expr.prim.general]), and r-variable expressions within its scope are permitted. In addition, a lambda-expression's compound-statement can use (see above) this from an immediately-enclosing member function definition, as well as variables and references with automatic storage duration from an immediately-enclosing function definition or lambda-expression, provided these entities are captured (as described below). Any other use (3.2 [basic.def.odr]) of a variable or reference with automatic storage duration declared outside the lambda-expression is ill-formed. [Example:
void f1(int i) { int const N = 20; [=]{ int const M = 30; int j; [=]{ int x[N][M]; // OK: N and M are not used r-variable expressions x[0][0] = i; // error: i is not declared in the immediately }; // enclosing lambda-expression int y = N+M; // OK: N and M are r-variable expressions &M; // error: taking the address of an rvalue y = i; // error: i is not an r-variable expression and variable i // is declared outside the immediately-enclosing // lambda-expression and thus not captured sizeof(i); // OK: i is not “used” int z = j; // OK: variable j is implicitly captured }; [M]{ int a[M]; // error: variable M explicitly captured, so M //refers to a member of the closure type }; }; }—end example]
Change 5.1.2 [expr.prim.lambda] paragraph 11 as follows:
If a lambda-expression has an associated capture-default and its compound-statement uses (3.2 [basic.def.odr]) this or a variable or reference with automatic storage duration declared in an enclosing function or lambda-expression and the used entity is not explicitly captured, then the used entity is said to be implicitly captured. An entity that is used (3.2 [basic.def.odr]) in the compound-statement of a lambda-expression but not explicitly captured will be implicitly captured if:
the lambda-expression has an associated capture-default, and
the entity is this or a variable or reference with automatic storage duration declared in an enclosing function or lambda-expression, and
the use is not an r-variable expression.
[Note: Implicit uses of this can result in implicit capture. —end note]
Change 5.1.2 [expr.prim.lambda] paragraph 16 as follows:
Every id-expression that is a use (3.2 [basic.def.odr]) of an entity captured by copy If an id-expression denotes an entity that is explicitly captured by copy, or it it is a use (3.2 [basic.def.odr]) of an entity that is implicitly captured by copy, that id-expression is transformed into an access to the corresponding unnamed data member of the closure type. [Note: r-variable expressions designating variables that are explicitly captured by copy are thus not id-expressions after this transformation and consequently are no longer r-variable expressions. If a variable is not explicitly captured, however, the fact that it is used as an r-variable expression does not cause that variable to be implicitly captured, either. As a result, such an id-expression will not be transformed to a member access, and it will therefore be treated as a constant rvalue. —end note] If this is captured, each use of this is transformed into an access to the corresponding unnamed data member of the closure type cast (5.4 [expr.cast]) to the type of this. [Note: The cast ensures that the transformed expression is an rvalue. —end note]
Change 9.8 [class.local] paragraph 1 as follows:
A class can be declared within a function definition; such a class is called a local class. The name of a local class is local to its enclosing scope. The local class is in the scope of the enclosing scope, and has the same access to names outside the function as does the enclosing function. A local class definition is a restricted automatic variable context (5.1.1 [expr.prim.general]). Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators shall not use (3.2 [basic.def.odr]) a variable or reference with automatic storage duration from the enclosing scope except as an r-variable expression. [Example:int x; void f() { static int s ; int x; extern int g(); const int c = 42; struct local { int g() { return x; } // error: x has automatic storage duration is not an r-variable expression int h() { return s; } // OK int k() { return ::x; } // OK int l() { return g(); } // OK int m() { return c; } // OK: c is an r-variable expression int* n() { return &c; } // error: taking the address of an rvalue }; } local* p = 0; // error: local not in scope—end example]
Drafting note: The change to 5.1.2 [expr.prim.lambda] paragraph 16 has the effect of making explicitly-captured variables no longer usable in constant expressions in lambda bodies. This change facilitates the design goal to be able to determine in a context-free manner whether a given id-expression should be transformed to a member access expression or not.
Notes from the October, 2009 meeting:
There was interest in an approach that would allow explicitly-captured constants to appear in constant expressions but also to be “used.” Another suggestion was to have variables captured if they appear in either “use” or “non-use” contexts.
The resolution of issue 372 leaves unclear whether the following are well-formed or not:
class C { typedef int I; // private template <int> struct X; template <int> friend struct Y; } template <C::I> struct C::X { }; // C::I accessible to member? template <C::I> struct Y { }; // C::I accessible to friend?
Presumably the answer to both questions is “yes,” but the new wording does not address template-parameters.
Proposed resolution (June, 2008):
Change 11 [class.access] paragraph 6 as follows:
...For purposes of access control, the base-specifiers of a class, the template-parameters of a template-declaration, and the definitions of class members that appear outside of the class definition are considered to be within the scope of that class...
Notes from the September, 2008 meeting:
The proposed resolution preserves the word “scope” as a holdover from the original specification prior to issue 372, which intended to change access determination from a scope-based model to an entity-based model. The resolution should eliminate all references to scope and simply use the entity-based model.
(See also issue 718.)
Proposed resolution (February, 2010):
Change 11 [class.access] paragraphs 6-7 as follows:
All access controls in Clause 11 [class.access] affect the ability to access a class member name from a declaration of a particular scope entity, including references appearing in those parts of the declaration that precede the name of the entity being declared and implicit references to constructors, conversion functions, and destructors involved in the creation and destruction of a static data member. For purposes of access control, the base-specifiers of a class and the definitions of class members that appear outside of the class definition are considered to be within the scope of that class. In particular, access controls apply as usual to member names accessed as part of a function return type, even though it is not possible to determine the access privileges of that use without first parsing the rest of the function declarator. Similarly, access control for implicit calls to the constructors, the conversion functions, or the destructor called to create and destroy a static data member is performed as if these calls appeared in the scope of the member's class. [Example:
class A { typedef int I; // private member I f(); friend I g(I); static I x; template<int> struct X; template<int> friend struct Y; protected: struct B { }; }; A::I A::f() { return 0; } A::I g(A::I p = A::x); A::I g(A::I p) { return 0; } A::I A::x = 0; template<A::I> struct A::X { }; template<A::I> struct Y { }; struct D: A::B, A { };Here, all the uses of A::I are well-formed because A::f and, A::x, and A::X are members of class A and g is a friend and Y are friends of class A. This implies, for example, that access checking on the first use of A::I must be deferred until it is determined that this use of A::I is as the return type of a member of class A. Similarly, the use of A::B as a base-specifier is well-formed because D is derived from A, so checking of base-specifiers must be deferred until the entire base-specifier-list has been seen. —end example]
The access rules in 11.2 [class.access.base] do not appear to handle references in nested classes and outside of nonstatic member functions correctly. For example,
struct A { typedef int I; // public }; struct B: private A { }; struct C: B { void f() { I i1; // error: access violation } I i2; // OK struct D { I i3; // OK void g() { I i4; // OK } }; };
The reason for this discrepancy is that the naming class in the reference to I is different in these cases. According to 11.2 [class.access.base] paragraph 5,
The access to a member is affected by the class in which the member is named. This naming class is the class in which the member name was looked up and found.
In the case of i1, the reference to I is subject to the transformation described in 9.3.1 [class.mfct.non-static] paragraph 3:
Similarly during name lookup, when an unqualified-id (5.1 [expr.prim]) used in the definition of a member function for class X resolves to a static member, an enumerator or a nested type of class X or of a base class of X, the unqualified-id is transformed into a qualified-id (5.1 [expr.prim]) in which the nested-name-specifier names the class of the member function.
As a result, the reference to I in the declaration of i1 is transformed to C::I, so that the naming class is C, and I is inacessible in C. In the remaining cases, however, the transformation does not apply. Thus, the naming class of I in these references is A, and I is publicly accessible in A.
Presumably either the definition of “naming class” must be changed or the transformation of unqualified-ids must be broadened to include all uses within the scope of a class and not just within nonstatic member functions (and following the declarator-id in the definition of a static member, per 9.4 [class.static] paragraph 4).
Does the restriction in 11.5 [class.protected] apply to upcasts across protected inheritance, too? For instance,
struct B { int i; }; struct I: protected B { }; struct D: I { void f(I* ip) { B* bp = ip; // well-formed? bp->i = 5; // aka "ip->i = 5;" } };
I think the rationale for the 11.5 [class.protected] restriction applies equally well here — you don't know whether ip points to a D object or not, so D::f can't be trusted to treat the protected B subobject consistently with the policies of its actual complete object type.
The current treatment of “accessible base class” in 11.2 [class.access.base] paragraph 4 clearly makes the conversion from I* to B* well-formed. I think that's wrong and needs to be fixed. The rationale for the accessibility of a base class is whether “an invented public member” of the base would be accessible at the point of reference, although we obscured that a bit in the reformulation; it seems to me that the invented member ought to be considered a non-static member for this purpose and thus subject to 11.5 [class.protected].
(See also issues 385 and 471.).Notes from October 2004 meeting:
The CWG tentatively agreed that casting across protective inheritance should be subject to the additional restriction in 11.5 [class.protected].
Proposed resolution (February, 2010):
Change 11.2 [class.access.base] paragraph 4 as follows:
A base class B of N is accessible at R, if
an invented public member of B would be a public member of N, or
R occurs in a member or friend of class N, and an invented public member of B would be a private or protected member of N, or
R occurs in a member or friend of a class P derived from N, and an invented public member of B would be a private or protected member of P, or
there exists a class S such that B is a base class of S accessible at R and S is a base class of N accessible at R.
[Example:
class B { public: int m; }; class S: private B { friend class N; }; class N: private S { void f() { B* p = this; // OK because class S satisfies the fourth condition // above: B is a base class of N accessible in f() because // B is an accessible base class of S and S is an accessible // base class of N. } }; class N2: protected B { }; class P2: public N2 { void f2(N2* n2p) { B* bp = n2p; // error: invented member would be protected and naming // class N2 not the same as or derived from the referencing // class P2 (cf 11.5 [class.protected]) } };—end example]
Mark Mitchell raised a number of issues related to the resolution of issue 244 and of destructor lookup in general.
Issue 244 says:
... in a qualified-id of the form:::opt nested-name-specifieropt class-name :: ~ class-name
the second class-name is looked up in the same scope as the first.
But if the reference is "p->X::~X()", the first class-name is looked up in two places (normal lookup and a lookup in the class of p). Does the new wording mean:
This is a test case that illustrates the issue:
struct A { typedef A C; }; typedef A B; void f(B* bp) { bp->B::~B(); // okay B found by normal lookup bp->C::~C(); // okay C found by class lookup bp->B::~C(); // B found by normal lookup C by class -- okay? bp->C::~B(); // C found by class lookup B by normal -- okay? }
A second issue concerns destructor references when the class involved is a template class.
namespace N { template <typename T> struct S { ~S(); }; } void f(N::S<int>* s) { s->N::S<int>::~S(); }
The issue here is that the grammar uses "~class-name" for destructor names, but in this case S is a template name when looked up in N.
Finally, what about cases like:
template <typename T> void f () { typename T::B x; x.template A<T>::template B<T>::~B(); }
When parsing the template definition, what checks can be done on "~B"?
Sandor Mathe adds :
The standard correction for issue 244 (now in DR status) is still incomplete.
Paragraph 5 of 3.4.3 [basic.lookup.qual] is not applicable for p->T::~T since there is no nested-name-specifier. Section 3.4.5 [basic.lookup.classref] describes the lookup of p->~T but p->T::~T is still not described. There are examples (which are non-normative) that illustrate this sort of lookup but they still leave questions unanswered. The examples imply that the name after ~ should be looked up in the same scope as the name before the :: but it is not stated. The problem is that the name to the left of the :: can be found in two different scopes. Consider the following:
struct S { struct C { ~C() { } }; }; typedef S::C D; int main() { D* p; p->C::~D(); // valid? }
Should the destructor call be valid? If there were a nested name specifier, then D should be looked for in the same scope as C. But here, C is looked for in 2 different ways. First, it is searched for in the type of the left hand side of -> and it is also looked for in the lexical context. It is found in one or if both, they must match. So, C is found in the scope of what p points at. Do you only look for D there? If so, this is invalid. If not, you would then look for D in the context of the expression and find it. They refer to the same underlying destructor so this is valid. The intended resolution of the original defect report of the standard was that the name before the :: did not imply a scope and you did not look for D inside of C. However, it was not made clear whether this was to be resolved by using the same lookup mechanism or by introducing a new form of lookup which is to look in the left hand side if that is where C was found, or in the context of the expression if that is where C was found. Of course, this begs the question of what should happen when it is found in both? Consider the modification to the above case when C is also found in the context of the expression. If you only look where you found C, is this now valid because it is in 1 of the two scopes or is it invalid because C was in both and D is only in 1?
struct S { struct C { ~C() { } }; }; typedef S::C D; typedef S::C C; int main() { D* p; p->C::~D(); // valid? }
I agree that the intention of the committee is that the original test case in this defect is broken. The standard committee clearly thinks that the last name before the last :: does not induce a new scope which is our current interpretation. However, how this is supposed to work is not defined. This needs clarification of the standard.
Martin Sebor adds this example (September 2003), along with errors produced by the EDG front end:
namespace N { struct A { typedef A NA; }; template <class T> struct B { typedef B NB; typedef T BT; }; template <template <class> class T> struct C { typedef C NC; typedef T<A> CA; }; } void foo (N::A *p) { p->~NA (); p->NA::~NA (); } template <class T> void foo (N::B<T> *p) { p->~NB (); p->NB::~NB (); } template <class T> void foo (typename N::B<T>::BT *p) { p->~BT (); p->BT::~BT (); } template <template <class> class T> void foo (N::C<T> *p) { p->~NC (); p->NC::~NC (); } template <template <class> class T> void foo (typename N::C<T>::CA *p) { p->~CA (); p->CA::~CA (); } Edison Design Group C/C++ Front End, version 3.3 (Sep 3 2003 11:54:55) Copyright 1988-2003 Edison Design Group, Inc. "t.cpp", line 16: error: invalid destructor name for type "N::B<T>" p->~NB (); ^ "t.cpp", line 17: error: qualifier of destructor name "N::B<T>::NB" does not match type "N::B<T>" p->NB::~NB (); ^ "t.cpp", line 30: error: invalid destructor name for type "N::C<T>" p->~NC (); ^ "t.cpp", line 31: error: qualifier of destructor name "N::C<T>::NC" does not match type "N::C<T>" p->NC::~NC (); ^ 4 errors detected in the compilation of "t.cpp".
John Spicer: The issue here is that we're unhappy with the destructor names when doing semantic analysis of the template definitions (not during an instantiation).
My personal feeling is that this is reasonable. After all, why would you call p->~NB for a class that you just named as N::B<T> and you could just say p->~B?
Additional note (September, 2004)
The resolution for issue 244 removed the discussion of p->N::~S, where N is a namespace-name. However, the resolution did not make this construct ill-formed; it simply left the semantics undefined. The meaning should either be defined or the construct made ill-formed.
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.)
A defaulted destructor should be implicitly defined as deleted if operator delete is deleted or inaccessible.
12.8 [class.copy] paragraphs 6-7 currently read,
A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-qualified) X and either there are no other parameters or else all other parameters have default arguments.
A member function template is never instantiated to perform the copy of a class object to an object of its class type. [Example:
struct S { template<typename T> S(T); template<typename T> S(T&&); S(); }; S f(); const S g; void h() { S a( f() ); // does not instantiate member template; // uses the implicitly generated move constructor S a(g); // does not instantiate the member template; // uses the implicitly generated copy constructor }
These paragraphs were previously a single paragraph, and the second sentence was intended to mean that
template <class T> A(T):
will never be instantiated to produce A(A). It should not have been split and the example should not have been amended to include move construction.
Lawrence Crowl: I suggest something along the lines of
A member function template is never instantiated to match the signature of an ill-formed constructor.
It is unclear whether copy elision is permitted when returning a parameter of class type. If not, it should still be possible to move, rather than copy, the return value.
Suggested resolution: Amend paragraph 34 to explicitly exclude function parameters from copy elision. Amend paragraph 35 to include function parameters as eligible for move-construction.
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.
Overload resolution within the operand of a unary & operator is done by selecting the function “whose type matches the target type required in the context.” The criterion for determining whether the types match, however, is not defined. At least three possibilities suggest themselves:
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.
According to the Standard (although not implemented this way in most implementations), the following code exhibits non-intuitive behavior:
struct T { operator short() const; operator int() const; }; short s; void f(const T& t) { s = t; // surprisingly calls T::operator int() const }
The reason for this choice is 13.6 [over.built] paragraph 18:
For every triple (L, VQ, R), where L is an arithmetic type, VQ is either volatile or empty, and R is a promoted arithmetic type, there exist candidate operator functions of the form
VQ L& operator=(VQ L&, R);
Because R is a "promoted arithmetic type," the second argument to the built-in assignment operator is int, causing the unexpected choice of conversion function.
Suggested resolution: Provide built-in assignment operators for the unpromoted arithmetic types.
Related to the preceding, but not resolved by the suggested resolution, is the following problem. Given:
struct T { operator int() const; operator double() const; };
I believe the standard requires the following assignment to be ambiguous (even though I expect that would surprise the user):
double x; void f(const T& t) { x = t; }
The problem is that both of these built-in operator=()s exist (13.6 [over.built] paragraph 18):
double& operator=(double&, int); double& operator=(double&, double);
Both are an exact match on the first argument and a user conversion on the second. There is no rule that says one is a better match than the other.
The compilers that I have tried (even in their strictest setting) do not give a peep. I think they are not following the standard. They pick double& operator=(double&, double) and use T::operator double() const.
I hesitate to suggest changes to overload resolution, but a possible resolution might be to introduce a rule that, for built-in operator= only, also considers the conversion sequence from the second to the first type. This would also resolve the earlier question.
It would still leave x += t etc. ambiguous -- which might be the desired behavior and is the current behavior of some compilers.
Notes from the 04/01 meeting:
The difference between initialization and assignment is disturbing. On the other hand, promotion is ubiquitous in the language, and this is the beginning of a very slippery slope (as the second report above demonstrates).
Additional note (August, 2010):
See issue 507 for a similar example involving comparison operators.
Static data members of template classes and of nested classes of template classes are not themselves templates but receive much the same treatment as template. For instance, 14 [temp] paragraph 1 says that templates are only "classes or functions" but implies that "a static data member of a class template or of a class nested within a class template" is defined using the template-declaration syntax.
There are many places in the clause, however, where static data members of one sort or another are overlooked. For instance, 14 [temp] paragraph 6 allows static data members of class templates to be declared with the export keyword. I would expect that static data members of (non-template) classes nested within class templates could also be exported, but they are not mentioned here.
Paragraph 8, however, overlooks static data members altogether and deals only with "templates" in defining the effect of the export keyword; there is no description of the semantics of defining a static data member of a template to be exported.
These are just two instances of a systematic problem. The entire clause needs to be examined to determine which statements about "templates" apply to static data members, and which statements about "static data members of class templates" also apply to static data members of non-template classes nested within class templates.
(The question also applies to member functions of template classes; see issue 217, where the phrase "non-template function" in 8.3.6 [dcl.fct.default] paragraph 4 is apparently intended not to include non-template member functions of template classes. See also issue 108, which would benefit from understanding nested classes of class templates as templates. Also, see issue 249, in which the usage of the phrase "member function template" is questioned.)
Notes from the 4/02 meeting:
Daveed Vandevoorde will propose appropriate terminology.
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.
Consider an example like:
template <typename T, T Value> struct bar { }; template <typename... T, T ...Value> void foo(bar<T, Value>);
The current wording in 14.1 [temp.param] is unclear as to whether this is permitted or not. For comparison, 8.3.5 [dcl.fct] paragraph 13 says,
A declarator-id or abstract-declarator containing an ellipsis shall only be used in a parameter-declaration. Such a parameter-declaration is a parameter pack (14.5.3 [temp.variadic]). When it is part of a parameter-declaration-clause, the parameter pack is a function parameter pack (14.5.3 [temp.variadic]). [Note: Otherwise, the parameter-declaration is part of a template-parameter-list and the parameter pack is a template parameter pack; see 14.1 [temp.param]. —end note] A function parameter pack, if present, shall occur at the end of the parameter-declaration-list. The type T of the declarator-id of the function parameter pack shall contain a template parameter pack; each template parameter pack in T is expanded by the function parameter pack.
The requirement here that the type of a function parameter pack must contain a template parameter pack is not repeated for template non-type parameters in 14.1 [temp.param], nor is the statement that it expands the template parameter pack.
A related issue is that neither function nor template parameter packs are listed in 14.5.3 [temp.variadic] paragraph 4 among the contexts in which a pack expansion can appear.
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.
Since there appear to be no restrictions against it, it would appear that default arguments and template parameter packs can be used with template aliases just as with other templates. If that is the case, then, the current wording in 14.1 [temp.param] paragraph 11 requires adjustment:
If a template-parameter of a class template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. If a template-parameter of a class template is a template parameter pack, it shall be the last template-parameter.
Presumably these restrictions should also apply to template aliases, but as written, they only apply to class templates.
The EDG front-end accepts:
template <typename T> struct A { template <typename U> struct B {}; }; template <typename T> struct C : public A<T>::template B<T> { };
It rejects this code if the base-specifier is spelled A<T>::B<T>.
However, the grammar for a base-specifier does not allow the template keyword.
Suggested resolution:
It seems to me that a consistent approach to the solution that looks like it will be adopted for issue 180 (which deals with the typename keyword in similar contexts) would be to assume that B is a template if it is followed by a "<". After all, an expression cannot appear in this context.Notes from the 4/02 meeting:
We agreed that template must be allowed in this context. The syntax needs to be changed. We also opened the related issue 343.
Additional note (August, 2010):
The same considerations apply to mem-initializer-ids, as noted in issue 1019.
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.
It was intended for empty pack expansions to be useful in contexts like base-specifiers, e.g.,
template<class... T> struct A : T... {}; A<> x; // ok?
However, the current wording provides no description of how that might work. (More generally, the problem arises in any context where the pack expansion follows a token that should only be present when the pack expansion is non-empty: following another argument in a function call, etc.)
In the following example, the template parameter in the partial specialization is non-deducible:
template <class T> struct A { typedef T U; }; template <class T> struct C { }; template <class T> struct C<typename A<T>::U> { };
Several compilers issue errors for this case, but there appears to be nothing in the Standard that would make this ill-formed; it simply seems that the partial specialization will never be matched, so the primary template will be used for all specializations. Should it be ill-formed?
Notes from the April, 2006 meeting:
It was noted that there are similar issues for constructors and conversion operators with non-deducible parameters, and that they should probably be dealt with similarly.
14.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.
Consider the following example:
template <class T> struct Outer { struct Inner { Inner* self(); }; }; template <class T> Outer<T>::Inner* Outer<T>::Inner::self() { return this; }
According to 14.6 [temp.res] paragraph 3 (before the salient wording was inadvertently removed, see issue 559),
A qualified-id that refers to a type and in which the nested-name-specifier depends on a 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]) shall be prefixed by the keyword typename to indicate that the qualified-id denotes a type, forming a typename-specifier.
Because Outer<T>::Inner is a member of the current instantiation, the Standard does not currently require that it be prefixed with typename when it is used in the return type of the definition of the self() member function. However, it is difficult to parse this definition correctly without knowing that the return type is, in fact, a type, which is what the typename keyword is for. Should the Standard be changed to require typename in such contexts?
The injected-class-name of a class template can be used either by itself, in which case it is a type denoting the current instantiation, or followed by a template argument list, in which case it is a template-name. It would be helpful to extend this treatment so that the injected-class-name could be used as an argument for a template template parameter:
template <class T> struct A { }; template <template <class> class TTP> struct B { }; struct C: A<int> { B<A> b; };
(This is accepted by g++.)
James Widman:
It would not be so helpful when used with overloaded function templates, for example:
template <template <class> class TTP> void f(); // #1 template < class T > void f(); // #2 template <class T> struct A { }; struct C: A<int> { void h( ) { f<A>(); // #1? #2? Substitution failure? } };
(See also issue 602.)
The Standard should, but does not currently, say that typeid is value-dependent if its expression or type is type-dependent.
Given
template <const char *N> struct A { static const char *p; }; template <class T> struct B { static const char c[1]; typedef A<B<T>::c> C; }; template <class T> struct D { static const char c[1]; typedef A<c> C; };
14.6.2.4 [temp.dep.temp] says that B<T>::c is dependent because it is used as a non-integral non-type template argument and it is a qualified-id with a nested-name-specifier that names a dependent type.
There doesn't seem to be anything to say that c in the definition of D<T>::C is dependent, which suggests that D<T>::C is the same type for all T, which is clearly false.
Instead of this special rule in 14.6.2.4 [temp.dep.temp], 14.6.2.3 [temp.dep.constexpr] should say that the address of a member of a dependent type is value-dependent, regardless of whether the address is written with a qualified-id.
template <class T> class Foo { public: typedef int Bar; Bar f(); }; template <class T> typename Foo<T>::Bar Foo<T>::f() { return 1;} --------------------In the class template definition, the declaration of the member function is interpreted as:
int Foo<T>::f();In the definition of the member function that appears outside of the class template, the return type is not known until the member function is instantiated. Must the return type of the member function be known when this out-of-line definition is seen (in which case the definition above is ill-formed)? Or is it OK to wait until the member function is instantiated to see if the type of the return type matches the return type in the class template definition (in which case the definition above is well-formed)?
Suggested resolution: (John Spicer)
My opinion (which I think matches several posted on the reflector recently) is that the out-of-class definition must match the declaration in the template. In your example they do match, so it is well formed.
I've added some additional cases that illustrate cases that I think either are allowed or should be allowed, and some cases that I don't think are allowed.
template <class T> class A { typedef int X; }; template <class T> class Foo { public: typedef int Bar; typedef typename A<T>::X X; Bar f(); Bar g1(); int g2(); X h(); X i(); int j(); }; // Declarations that are okay template <class T> typename Foo<T>::Bar Foo<T>::f() { return 1;} template <class T> typename Foo<T>::Bar Foo<T>::g1() { return 1;} template <class T> int Foo<T>::g2() { return 1;} template <class T> typename Foo<T>::X Foo<T>::h() { return 1;} // Declarations that are not okay template <class T> int Foo<T>::i() { return 1;} template <class T> typename Foo<T>::X Foo<T>::j() { return 1;}In general, if you can match the declarations up using only information from the template, then the declaration is valid.
Declarations like Foo::i and Foo::j are invalid because for a given instance of A<T>, A<T>::X may not actually be int if the class is specialized.
This is not a problem for Foo::g1 and Foo::g2 because for any instance of Foo<T> that is generated from the template you know that Bar will always be int. If an instance of Foo is specialized, the template member definitions are not used so it doesn't matter whether a specialization defines Bar as int or not.
The current wording of 14.6.4 [temp.dep.res] seems to assume that dependent names can only appear in the definition of a template:
In resolving dependent names, names from the following sources are considered:
Declarations that are visible at the point of definition of the template.
Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1 [temp.point]) and from the definition context.
However, dependent names can occur in non-defining declarations of the template as well; for instance,
template<typename T> T foo(T, decltype(bar(T())));
bar needs to be looked up, even though there is no definition of foo in the translation unit.
Implementations differ in their treatment of the following code:
template <class T> struct A { typename T::X x; }; template <class T> struct B { typedef T* X; A<B> a; }; int main () { B<int> b; }
Some implementations accept it. At least one rejects it because the instantiation of A<B<int> > requires that B<int> be complete, and it is not at the point at which A<B<int> > is being instantiated.
Erwin Unruh:
In my view the programm is ill-formed. My reasoning:
So each class needs the other to be complete.
The problem can be seen much easier if you replace the typedef with
typedef T (*X) [sizeof(B::a)];
Now you have a true recursion. The compiler cannot easily distinguish between a true recursion and a potential recursion.
John Spicer:
Using a class to form a qualified name does not require the class to be complete, it only requires that the named member already have been declared. In other words, this kind of usage is permitted:
class A { typedef int B; A::B ab; };
In the same way, once B has been declared in A, it is also visible to any template that uses A through a template parameter.
The standard could be more clear in this regard, but there are two notes that make this point. Both 3.4.3.1 [class.qual] and 5.1.1 [expr.prim.general] paragraph 7 contain a note that says "a class member can be referred to using a qualified-id at any point in its potential scope (3.3.7 [basic.scope.class])." A member's potential scope begins at its point of declaration.
In other words, a class has three states: incomplete, being completed, and complete. The standard permits a qualified name to be used once a name has been declared. The quotation of the notes about the potential scope was intended to support that.
So, in the original example, class A does not require the type of T to be complete, only that it have already declared a member X.
Bill Gibbons:
The template and non-template cases are different. In the non-template case the order in which the members become declared is clear. In the template case the members of the instantiation are conceptually all created at the same time. The standard does not say anything about trying to mimic the non-template case during the instantiation of a class template.
Mike Miller:
I think the relevant specification is 14.6.4.1 [temp.point] paragraph 3, dealing with the point of instantiation:
For a class template specialization... if the specialization is implicitly instantiated because it is referenced from within another template specialization, if the context from which the specialization is referenced depends on a template parameter, and if the specialization is not instantiated previous to the instantiation of the enclosing template, the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.
That means that the point of instantiation of A<B<int> > is before that of B<int>, not in the middle of B<int> after the declaration of B::X, and consequently a reference to B<int>::X from A<B<int> > is ill-formed.
To put it another way, I believe John's approach requires that there be an instantiation stack, with the results of partially-instantiated templates on the stack being available to instantiations above them. I don't think the Standard mandates that approach; as far as I can see, simply determining the implicit instantiations that need to be done, rewriting the definitions at their respective points of instantiation with parameters substituted (with appropriate "forward declarations" to allow for non-instantiating references), and compiling the result normally should be an acceptable implementation technique as well. That is, the implicit instantiation of the example (using, e.g., B_int to represent the generated name of the B<int> specialization) could be something like
struct B_int; struct A_B_int { B_int::X x; // error, incomplete type }; struct B_int { typedef int* X; A_B_int a; };
Notes from 10/01 meeting:
This was discussed at length. The consensus was that the template case should be treated the same as the non-template class case it terms of the order in which members get declared/defined and classes get completed.
Proposed resolution:
In 14.6.4.1 [temp.point] paragraph 3 change:
the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.
To:
the point of instantiation is the same as the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the nearest enclosing declaration. [Note: The point of instantiation is still at namespace scope but any declarations preceding the point of instantiation, even if not at namespace scope, are considered to have been seen.]
Add following paragraph 3:
If an implicitly instantiated class template specialization, class member specialization, or specialization of a class template references a class, class template specialization, class member specialization, or specialization of a class template containing a specialization reference that directly or indirectly caused the instantiation, the requirements of completeness and ordering of the class reference are applied in the context of the specialization reference.
and the following example
template <class T> struct A { typename T::X x; }; struct B { typedef int X; A<B> a; }; template <class T> struct C { typedef T* X; A<C> a; }; int main () { C<int> c; }
Notes from the October 2002 meeting:
This needs work. Moved back to drafting status.
The intent is that it is a permissible implementation technique to do template instantiation at the end of a translation unit rather than at an actual point of instantiation. This idea is not reflected in the current rules, however.
Three points have been raised where the wording in 14.7.1 [temp.inst] may not be sufficiently clear.
A class template specialization is implicitly instantiated... if the completeness of the class type affects the semantics of the program...
It is not clear what it means for the "completeness... [to affect] the semantics." Consider the following example:
template<class T> struct A; extern A<int> a; void *foo() { return &a; } template<class T> struct A { #ifdef OPTION void *operator &() { return 0; } #endif };
The question here is whether it is necessary for template class A to declare an operator & for the semantics of the program to be affected. If it does not do so, the meaning of &a will be the same whether the class is complete or not and thus arguably the semantics of the program are not affected.
Presumably what was intended is whether the presence or absence of certain member declarations in the template class might be relevant in determining the meaning of the program. A clearer statement may be desirable.
If the overload resolution process can determine the correct function to call without instantiating a class template definition, it is unspecified whether that instantiation actually takes place.
The intent of this wording, as illustrated in the example in that paragraph, is to allow a "smart" implementation not to instantiate class templates if it can determine that such an instantiation will not affect the result of overload resolution, even though the algorithm described in clause 13 [over] requires that all the viable functions be enumerated, including functions that might be found as members of specializations.
Unfortunately, the looseness of the wording allowing this latitude for implementations makes it unclear what "the overload resolution process" is — is it the algorithm in 13 [over] or something else? — and what "the correct function" is.
If an implicit instantiation of a class template specialization is required and the template is declared but not defined, the program is ill-formed.
Here, it is not clear what conditions "require" an implicit instantiation. From the context, it would appear that the intent is to refer to the conditions in paragraph 4 that cause a specialization to be instantiated.
This interpretation, however, leads to different treatment of template and non-template incomplete classes. For example, by this interpretation,
class A; template <class T> struct TA; extern A a; extern TA<int> ta; void f(A*); void f(TA<int>*); int main() { f(&a); // well-formed; undefined if A // has operator &() member f(&ta); // ill-formed: cannot instantiate }
A different approach would be to understand "required" in paragraph 6 to mean that a complete type is required in the expression. In this interpretation, if an incomplete type is acceptable in the context and the class template definition is not visible, the instantiation is not attempted and the program is well-formed.
The meaning of "required" in paragraph 6 must be clarified.
Notes on 10/01 meeting:
It was felt that item 1 is solved by addition of the word "might" in the resolution for issue 63; item 2 is not much of a problem; and item 3 could be solved by changing "required" to "required to be complete".
Paragraph 17 of 14.7.3 [temp.expl.spec] says,
A member or a member template may be nested within many enclosing class templates. In an explicit specialization for such a member, the member declaration shall be preceded by a template<> for each enclosing class template that is explicitly specialized.
This is curious, because paragraph 3 only allows explicit specialization of members of implicitly-instantiated class specializations, not explicit specializations. Furthermore, paragraph 4 says,
Definitions of members of an explicitly specialized class are defined in the same manner as members of normal classes, and not using the explicit specialization syntax.
Paragraph 18 provides a clue for resolving the apparent contradiction:
In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well. In such explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding the explicit specialization declaration of the member.
It appears from this and the following example that the phrase “explicitly specialized” in paragraphs 17 and 18, when referring to enclosing class templates, does not mean that explicit specializations have been declared for them but that their names in the qualified-id are followed by template argument lists. This terminology is confusing and should be changed.
Proposed resolution (October, 2005):
Change 14.7.3 [temp.expl.spec] paragraph 17 as indicated:
A member or a member template may be nested within many enclosing class templates. In an explicit specialization for such a member, the member declaration shall be preceded by a template<> for each enclosing class template that is explicitly specialized specialization. [Example:...
Change 14.7.3 [temp.expl.spec] paragraph 18 as indicated:
In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well that is, the template-id naming the template may be composed of template parameter names rather than template-arguments. In For each unspecialized template in such an explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding the explicit specialization declaration of the member. The types of the template-parameters in the template-parameter-list shall be the same as those specified in the primary template definition. In such declarations, an unspecialized template-id shall not precede the name of a template specialization in the qualified-id naming the member. [Example:...
Notes from the April, 2006 meeting:
The revised wording describing “unspecialized” templates needs more work to ensure that the parameter names in the template-id are in the correct order; the distinction between template argyments and parameters is also probably not clear enough. It might be better to replace this paragraph completely and avoid the “unspecialized” wording altogether.
Proposed resolution (February, 2010):
Change 14.7.3 [temp.expl.spec] paragraph 17 as follows:
A member or a member template may be nested within many enclosing class templates. In an explicit specialization for such a member, the member declaration shall be preceded by a template<> for each enclosing class template that is explicitly specialized specialization. [Example:...
Change 14.7.3 [temp.expl.spec] paragraph 18 as follows:
In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well. In such explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding the explicit specialization declaration of the member. The types of the template-parameters in the template-parameter-list shall be the same as those specified in the primary template definition. that is, the corresponding template prefix may specify a template-parameter-list instead of template<> and the template-id naming the template be written using those template-parameters as template-arguments. In such a declaration, the number, kinds, and types of the template-parameters shall be the same as those specified in the primary template definition, and the template-parameters shall be named in the template-id in the same order that they appear in the template-parameter-list. An unspecialized template-id shall not precede the name of a template specialization in the qualified-id naming the member. [Example:...
According to 14.8.2 [temp.deduct] paragraph 8,
Access checking is not done as part of the substitution process. Consequently, when deduction succeeds, an access error could still result when the function is instantiated.
This mimics the way access checking is done in overload resolution. However, experience has shown that this exemption of access errors from deduction failure significantly complicates the Standard library, so this rule should be changed.
14.8.2.5 [temp.deduct.type] paragraph 22 describes how we cope with partial ordering between two function templates that differ because one has a function parameter pack while the other has a normal function parameter. However, this paragraph was meant to apply to template parameter packs as well, e.g., to help with partial ordering of class template partial specializations:
template <class T1, class ...Z> class S; // #1 template <class T1, class ...Z> class S<T1, const Z&...> {}; // #2 template <class T1, class T2> class S<T1, const T2&> {};; // #3 S<int, const int&> s; // both #2 and #3 match; #3 is more specialized
(See also issue 818.)
Proposed resolution (March, 2009):
Change 14.8.2.5 [temp.deduct.type] paragraphs 9-10 as follows (and add the example above to paragraph 9):
If P has a form that contains <T> or <i>, then each argument Pi of the respective template argument list of P is compared with the corresponding argument Ai of the corresponding template argument list of A. If the template argument list of P contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context. If Pi is a pack expansion, then the pattern of Pi is compared with each remaining argument in the template argument list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by Pi. During partial ordering (14.8.2.4 [temp.deduct.partial]), if Ai was originally a pack expansion and Pi is not a pack expansion, or if P does not contain a template argument corresponding to Ai, argument deduction fails.
Similarly, if P has a form that contains (T), then each parameter type Pi of the respective parameter-type-list of P is compared with the corresponding parameter type Ai of the corresponding parameter-type-list of A. If the parameter-declaration corresponding to Pi is a function parameter pack, then the type of its declarator-id is compared with each remaining parameter type in the parameter-type-list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. During partial ordering (14.8.2.4 [temp.deduct.partial]), if Ai was originally a function parameter pack and Pi is not a function parameter pack, or if P does not contain a function parameter type corresponding to Ai, argument deduction fails. [Note: A function parameter pack can only occur at the end of a parameter-declaration-list (8.3.5 [dcl.fct]). —end note]
According 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.
It is not clear how to handle compatible dynamic-exception-specifications and noexcept-specifications. For example, given
void f() throw(); void f() noexcept { throw 1; }
should we call terminate() or unexpected()? And for
void g() throw (int); void g() noexcept (false) { throw 1.0; }
should this call unexpected or propagate the exception? Does the order of the declarations (and which is the definition) matter?
Alisdair Meredith:
And what about something like
struct A { ~A() throw() { } }; struct B { ~B() noexcept { } }; struct C: A, B { };
What is the exception specification for C's destructor?
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.
Is the following well-formed?
auto concept HasDestructor<typename T> { T::~T(); } concept_map HasDestructor<int&> { }
According to _N2914_.14.10.2.1 [concept.map.fct] paragraph 4, the destructor requirement in the concept map results in an expression x.~X(), where X is the type int&. According to 5.2.4 [expr.pseudo], this expression is ill-formed because the object type and the type-name must be the same type, but the object type cannot be a reference type (references are dropped from types used in expressions, 5 [expr] paragraph 5).
It is not clear whether this should be addressed by changing 5.2.4 [expr.pseudo] or _N2914_.14.10.2.1 [concept.map.fct].
The C++ Standard uses the phrase “indeterminate value” without defining it. C99 defines it as “either an unspecified value or a trap representation.” Should C++ follow suit?
In addition, 4.1 [conv.lval] paragraph 1 says that applying the lvalue-to-rvalue conversion to an “object [that] is uninitialized” results in undefined behavior; this should be rephrased in terms of an object with an indeterminate value.
The definition of an argument does not seem to cover many assumed use cases, and we believe that is not intentional. There should be answers to questions such as: Are lambda-captures arguments? Are type names in a throw-spec arguments? “Argument” to casts, typeid, alignof, alignas, decltype and sizeof? why in x[arg] arg is not an argument, but the value forwarded to operator[]() is? Does not apply to operators as call-points not bounded by parentheses? Similar for copy initialization and conversion? What are deduced template “arguments?” what are “default arguments?” can attributes have arguments? What about concepts, requires clauses and concept_map instantiations? What about user-defined literals where parens are not used?
According to 1.4 [intro.compliance] paragraph 7,
A freestanding implementation is one in which execution may take place without the benefit of an operating system, and has an implementation-defined set of libraries that includes certain language-support libraries (17.6.1.3 [compliance]).
This definition links two relatively separate topics: the lack of an operating system and the minimal set of libraries. Furthermore, 3.6.1 [basic.start.main] paragraph 1 says:
[Note: in a freestanding environment, start-up and termination is implementation-defined; start-up contains the execution of constructors for objects of namespace scope with static storage duration; termination contains the execution of destructors for objects with static storage duration. —end note]
It would be helpful if the two characteristics (lack of an operating system and restricted set of libraries) were named separately and if these statements were clarified to identify exactly what is implementation-defined.
Notes from the October, 2009 meeting:
The CWG felt that it needed a specific proposal in a paper before attempting to resolve this issue.
There should be a list of incompatibilities between the current and previous Standards, as in ISO/IEC TR 10176 4.1.1 paragraph 9.
(See document N2733 for an initial list of this information.)
Does the Standard require that an uninitialized auto variable have a stable (albeit indeterminate) value? That is, does the Standard require that the following function return true?
bool f() { unsigned char i; // not initialized unsigned char j = i; unsigned char k = i; return j == k; // true iff "i" is stable }3.9.1 [basic.fundamental] paragraph 1 requires that uninitialized unsigned char variables have a valid value, so the initializations of j and k are well-formed and required not to trap. The question here is whether the value of i is allowed to change between those initializations.
Mike Miller: 1.9 [intro.execution] paragraph 10 says,
An instance of each object with automatic storage duration (3.7.3 [basic.stc.auto] ) is associated with each entry into its block. Such an object exists and retains its last-stored value during the execution of the block and while the block is suspended...I think that the most reasonable way to read this is that the only thing that is allowed to change the value of an automatic (non-volatile?) value is a "store" operation in the abstract machine. There are no "store" operations to i between the initializations of j and k, so it must retain its original (indeterminate but valid) value, and the result of the program is well-defined.
The quibble, of course, is whether the wording "last-stored value" should be applied to a "never-stored" value. I think so, but others might differ.
Tom Plum: 7.1.6.1 [dcl.type.cv] paragraph 8 says,
[Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. See 1.9 [intro.execution] for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. ]>From this I would infer that non-volatile means "shall not be changed by means undetectable by an implementation"; that the compiler is entitled to safely cache accesses to non-volatile objects if it can prove that no "detectable" means can modify them; and that therefore i shall maintain the same value during the example above.
Nathan Myers: This also has practical code-generation consequences. If the uninitialized auto variable lives in a register, and its value is really unspecified, then until it is initialized that register can be used as a temporary. Each time it's "looked at" the variable has the value that last washed up in that register. After it's initialized it's "live" and cannot be used as a temporary any more, and your register pressure goes up a notch. Fixing the uninit'd value would make it "live" the first time it is (or might be) looked at, instead.
Mike Ball: I agree with this. I also believe that it was certainly never my intent that an uninitialized variable be stable, and I would have strongly argued against such a provision. Nathan has well stated the case. And I am quite certain that it would be disastrous for optimizers. To ensure it, the frontend would have to generate an initializer, because optimizers track not only the lifetimes of variables, but the lifetimes of values assigned to those variables. This would put C++ at a significant performance disadvantage compared to other languages. Not even Java went this route. Guaranteeing defined behavior for a very special case of a generally undefined operation seems unnecessary.
According to 1.9 [intro.execution] paragraph 14, “sequenced before” is a relation between “evaluations.” However, 3.6.3 [basic.start.term] paragraph 3 says,
If the completion of the initialization of a non-local object with static storage duration is sequenced before a call to std::atexit (see <cstdlib>, 18.5 [support.start.term]), the call to the function passed to std::atexit is sequenced before the call to the destructor for the object. If a call to std::atexit is sequenced before the completion of the initialization of a non-local object with static storage duration, the call to the destructor for the object is sequenced before the call to the function passed to std::atexit. If a call to std::atexit is sequenced before another call to std::atexit, the call to the function passed to the second std::atexit call is sequenced before the call to the function passed to the first std::atexit call.
Except for the calls to std::atexit, these events do not correspond to “evaluation” of expressions that appear in the program. If the “sequenced before” relation is to be applied to them, a more comprehensive definition is needed.
According to 2.2 [lex.phases] paragraph 1, in translation phase 1,
Any source file character not in the basic source character set (2.3 [lex.charset]) is replaced by the universal-character-name that designates that character.
If a character that is not in the basic character set is preceded by a backslash character, for example
"\á"
the result is equivalent to
"\\u00e1"
that is, a backslash character followed by the spelling of the universal-character-name. This is different from the result in C99, which accepts characters from the extended source character set without replacing them with universal-character-names.
2.12 [lex.key] paragraph 2 says,
Furthermore, the alternative representations shown in Table 4 for certain operators and punctuators (2.6 [lex.digraph]) are reserved and shall not be used otherwise:
Also, 2.6 [lex.digraph] paragraph 2 says,
In all respects of the language, each alternative token behaves the same, respectively, as its primary token, except for its spelling.
It is not clear whether the following example is well-formed:
#define STR2(x) #x #define STR(x) STR2(x) int main() { return sizeof STR('\0'bitor 0) - sizeof STR('\0'bitor 0); }
In this example, bitor is not the | operator but the identifier in a user-defined-character-literal. Does this violate the restrictions of 2.12 [lex.key] and 2.6 [lex.digraph]?
“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++.
According to 2.14.3 [lex.ccon] paragraph 1,
A character literal that does not begin with u, U, or L is an ordinary character literal, also referred to as a narrow-character literal. An ordinary character literal that contains a single c-char has type char, with value equal to the numerical value of the encoding of the c-char in the execution character set.
However, the definition of c-char includes as one possibility a universal-character-name. The value of a universal-character-name cannot, in general, be represented as a char, so this specification is impossible to satisfy.
(See also issue 411 for related questions.)
There is no limit placed on the number of c-chars in a multicharacter literal or a wide-character literal containing multiple c-chars, either in 2.14.3 [lex.ccon] paragraphs 1-2 or in Annex B [implimits]. Presumably this means that an implementation must accept arbitrarily long literals.
An alternative approach might be to state that these literals are conditionally supported with implementation-defined semantics, allowing an implementation to impose a documented limit that makes sense for the particular architecture.
2.14.5 [lex.string] paragraph 5 reads
Escape sequences and universal-character-names in string literals have the same meaning as in character literals, except that the single quote ' is representable either by itself or by the escape sequence \', and the double quote " shall be preceded by a \. In a narrow string literal, a universal-character-name may map to more than one char element due to multibyte encoding.
The first sentence refers us to 2.14.3 [lex.ccon], where we read in the first paragraph that "An ordinary character literal that contains a single c-char has type char [...]." Since the grammar shows that a universal-character-name is a c-char, something like '\u1234' must have type char (and thus be a single char element); in paragraph 5, we read that "A universal-character-name is translated to the encoding, in the execution character set, of the character named. If there is no such encoding, the universal-character-name is translated to an implemenation-defined encoding."
This is in obvious contradiction with the second sentence. In addition, I'm not really clear what is supposed to happen in the case where the execution (narrow-)character set is UTF-8. Consider the character \u0153 (the oe in the French word oeuvre). Should '\u0153' be a char, with an "error" value, say '?' (in conformance with the requirement that it be a single char), or an int, with the two char values 0xC5, 0x93, in an implementation defined order (in conformance with the requirement that a character representable in the execution character set be represented). Supposing the former, should "\u0153" be the equivalent of "?" (in conformance with the first sentence), or "\xC5\x93" (in conformance with the second).
Notes from October 2003 meeting:
We decided we should forward this to the C committee and let them resolve it. Sent via e-mail to John Benito on November 14, 2003.
Reply from John Benito:
I talked this over with the C project editor, we believe this was handled by the C committee before publication of the current standard.
WG14 decided there needed to be a more restrictive rule for one-to-one mappings: rather than saying "a single c-char" as C++ does, the C standard says "a single character that maps to a single-byte execution character"; WG14 fully expect some (if not many or even most) UCNs to map to multiple characters.
Because of the fundamental differences between C and C++ character types, I am not sure the C committee is qualified to answer this satisfactorily for WG21. WG14 is willing to review any decision reached for compatibility.
I hope this helps.
(See also issue 912 for a related question.)
User-defined literals should not be part of C++0x unless they have implementation experience.
The list of declarations that are not definitions given in 3.1 [basic.def] paragraph 2 does not mention several plausible candidates: parameter declarations in non-defining function declarations, non-static data members, and template parameters. It might be argued that none of these are declarations (paragraph 1 does not use the syntactic non-terminal declaration but does explicitly refer to clause 7 [dcl.dcl], where that non-terminal is defined). However, the list in paragraph 2 does mention static member declarations, which also are not declarations, so the intent is not clear.
decltype applied to a function call expression requires a complete type (5.2.2 [expr.call] paragraph 3 and 3.2 [basic.def.odr] paragraph 4), even though decltype's result might be used in a way that does not actually require a complete type. This might cause undesired and excessive template instantiations. Immediately applying decltype should not require a complete type, for example, for the return type of a function call.
The current wording of 3.3.2 [basic.scope.pdecl] does not specify the point of declaration for an alias-declaration (although it does do so in paragraph 3 for a template alias: “The point of declaration of a template alias immediately follows the identifier for the alias being declared”). One might assume that an alias-declaration would be the same, but it's not clear that that is the right resolution (for either declaration, but especially for the alias-declaration).
An alias-declaration is intended to be essentially a different syntactic form of a typedef declaration (7.1.3 [dcl.typedef] paragraph 2). Placing the point of declaration at the trailing semicolon instead of following the name of the alias would allow more compatibility with the capabilities of typedefs, for instance:
struct S { }; namespace N { alias S = S; }
The current description of unqualified name lookup in 3.4.1 [basic.lookup.unqual] paragraph 8 does not correctly handle complex cases of nesting. The Standard currently reads,
A name used in the definition of a function that is a member function (9.3) of a class X shall be declared in one of the following ways:In particular, this formulation does not handle the following example:
- before its use in the block in which it is used or in an enclosing block (6.3), or
- shall be a member of class X or be a member of a base class of X (10.2), or
- if X is a nested class of class Y (9.7), shall be a member of Y, or shall be a member of a base class of Y (this lookup applies in turn to Y's enclosing classes, starting with the innermost enclosing class), or
- if X is a local class (9.8) or is a nested class of a local class, before the definition of class X in a block enclosing the definition of class X, or
- if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or nested class within a local class of a function that is a member of N, before the member function definition, in namespace N or in one of N's enclosing namespaces.
struct outer { static int i; struct inner { void f() { struct local { void g() { i = 5; } }; } }; };Here the reference to i is from a member function of a local class of a member function of a nested class. Nothing in the rules allows outer::i to be found, although intuitively it should be found.
A more comprehensive formulation is needed that allows traversal of any combination of blocks, local classes, and nested classes. Similarly, the final bullet needs to be augmented so that a function need not be a (direct) member of a namespace to allow searching that namespace when the reference is from a member function of a class local to that function. That is, the current rules do not allow the following example:
int j; // global namespace struct S { void f() { struct local2 { void g() { j = 5; } }; } };
The description of name lookup in the parameter-declaration-clause of member functions in 3.4.1 [basic.lookup.unqual] paragraphs 7-8 is flawed in at least two regards.
First, both paragraphs 7 and 8 apply to the parameter-declaration-clause of a member function definition and give different rules for the lookup. Paragraph 7 applies to names "used in the definition of a class X outside of a member function body...," which includes the parameter-declaration-clause of a member function definition, while paragraph 8 applies to names following the function's declarator-id (see the proposed resolution of issue 41), including the parameter-declaration-clause.
Second, paragraph 8 appears to apply to the type names used in the parameter-declaration-clause of a member function defined inside the class definition. That is, it appears to allow the following code, which was not the intent of the Committee:
struct S { void f(I i) { } typedef int I; };
There seems to be some confusion in the Standard regarding the relationship between 3.4.1 [basic.lookup.unqual] (Unqualified name lookup) and 3.4.2 [basic.lookup.argdep] (Argument-dependent lookup). For example, 3.4.1 [basic.lookup.unqual] paragraph 3 says,
The lookup for an unqualified name used as the postfix-expression of a function call is described in 3.4.2 [basic.lookup.argdep].
In other words, nothing in 3.4.1 [basic.lookup.unqual] applies to function names; the entire lookup is described in 3.4.2 [basic.lookup.argdep].
3.4.2 [basic.lookup.argdep] does not appear to share this view of its responsibility. The closest it comes is in 3.4.2 [basic.lookup.argdep] paragraph 2a:
...the set of declarations found by the lookup of the function name is the union of the set of declarations found using ordinary unqualified lookup and the set of declarations found in the namespaces and classes associated with the argument types.
Presumably, "ordinary unqualified lookup" is a reference to the processing described in 3.4.1 [basic.lookup.unqual], but, as noted above, 3.4.1 [basic.lookup.unqual] explicitly precludes applying that processing to function names. The details of "ordinary unqualified lookup" of function names are not described anywhere.
The other clauses that reference 3.4.2 [basic.lookup.argdep], clauses 13 [over] and 14 [temp], are split over the question of the relationship between 3.4.1 [basic.lookup.unqual] and 3.4.2 [basic.lookup.argdep]. 13.3.1.1.1 [over.call.func] paragraph 3, for instance, says
The name is looked up in the context of the function call following the normal rules for name lookup in function calls (3.4.2 [basic.lookup.argdep]).
I.e., this reference assumes that 3.4.2 [basic.lookup.argdep] is self-contained. The same is true of 13.3.1.2 [over.match.oper] paragraph 3, second bullet:
The set of non-member candidates is the result of the unqualified lookup of operator@ in the context of the expression according to the usual rules for name lookup in unqualified function calls (3.4.2 [basic.lookup.argdep]), except that all member functions are ignored.
On the other hand, however, 14.6.4.2 [temp.dep.candidate] paragraph 1 explicitly assumes that 3.4.1 [basic.lookup.unqual] and 3.4.2 [basic.lookup.argdep] are both involved in function name lookup and do different things:
For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found using the usual lookup rules (3.4.1 [basic.lookup.unqual], 3.4.2 [basic.lookup.argdep]) except that:
- For the part of the lookup using unqualified name lookup (3.4.1 [basic.lookup.unqual]), only function declarations with external linkage 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 with external linkage found in either the template definition context or the template instantiation context are found.
Suggested resolution:
Change 3.4.1 [basic.lookup.unqual] paragraph 1 from
...name lookup ends as soon as a declaration is found for the name.
to
...name lookup ends with the first scope containing one or more declarations of the name.
Change the first sentence of 3.4.1 [basic.lookup.unqual] paragraph 3 from
The lookup for an unqualified name used as the postfix-expression of a function call is described in 3.4.2 [basic.lookup.argdep].
to
An unqualified name used as the postfix-expression of a function call is looked up as described below. In addition, argument-dependent lookup (3.4.2 [basic.lookup.argdep]) is performed on this name to complete the resulting set of declarations.
The last bullet of the second paragraph of section 3.4.2 [basic.lookup.argdep] says that:
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.
The first problem with this wording is that it is misleading, since one cannot get such a function argument whose type would be a template-id. The bullet should be speaking about template specializations instead.
The second problem is owing to the use of the word "defined" in the phrases "are the namespace in which the template is defined", "in which any template template arguments are defined", and "as template template arguments are defined". The bullet should use the word "declared" instead, since scenarios like the one below are possible:
namespace A { template<class T> struct test { template<class U> struct mem_templ { }; }; // declaration in namespace 'A' template<> template<> struct test<int>::mem_templ<int>; void foo(test<int>::mem_templ<int>&) { } } // definition in the global namespace template<> template<> struct A::test<int>::mem_templ<int> { }; int main() { A::test<int>::mem_templ<int> inst; // According to the current definition of 3.4.2 // foo is not found. foo(inst); }
In addition, the bullet doesn't make it clear whether a T which is a class template specialization must also be treated as a class type, i.e. if the contents of the second bullet of the second paragraph of section 3.4.2 [basic.lookup.argdep].
must apply to it or not. The same stands for a T which is a function template specialization. This detail can make a difference in an example such as the one below:
- 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. [This wording is as updated by core issue 90.]
template<class T> struct slist_iterator { friend bool operator==(const slist_iterator& x, const slist_iterator& y) { return true; } }; template<class T> struct slist { typedef slist_iterator<T> iterator; iterator begin() { return iterator(); } iterator end() { return iterator(); } }; int main() { slist<int> my_list; slist<int>::iterator mi1 = my_list.begin(), mi2 = my_list.end(); // Must the the friend function declaration // bool operator==(const slist_iterator<int>&, const slist_iterator<int>&); // be found through argument dependent lookup? I.e. is the specialization // 'slist<int>' the associated class of the arguments 'mi1' and 'mi2'. If we // apply only the contents of the last bullet of 3.4.2/2, then the type // 'slist_iterator<int>' has no associated classes and the friend declaration // is not found. mi1 == mi2; }
Suggested resolution:
Replace the last bullet of the second paragraph of section 3.4.2 [basic.lookup.argdep]
with
- 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.
- If T is a class template specialization, its associated namespaces and classes are those associated with T when T is regarded as a class type; 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 the primary templates making template template arguments are declared; and the classes in which any primary member templates used as template template arguments are declared.
- If T is a function template specialization, its associated namespaces and classes are those associated with T when T is regarded as a function type; 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 the primary templates making template template arguments are declared; and the classes in which any primary member templates used as template template arguments are declared.
Replace the second bullet of the second paragraph of section 3.4.2 [basic.lookup.argdep]
with
- 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.
- 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 declared [Note: in case of any of the associated classes being a class template specialization, its associated namespace is acually the namespace containing the declaration of the primary class template of the class template specialization].
3.4.2 [basic.lookup.argdep] paragraph 2 excludes dependent parameter types and return types from consideration in determining the associated classes and namespaces of a function template. Presumably this means that an example like
namespace N { template<class T> struct A { }; void f(void (*)()); } template <class T> void g(T, N::A<T>); void g(); int main() { f(g); }
is ill-formed because the second parameter of the function template g does not add namespace N to the list of associated namespaces. This was probably unintentional.
See also issue 1015.
Currently, according to 3.4.2 [basic.lookup.argdep] paragraph 2, explicit template arguments in a function argument do not contribute to the associated namespaces in a function call, although they plausibly should in an example like the following:
namespace N {
struct S { };
void f(void (*)(S));
};
template<typename T> void g(T);
void h() {
f(g<N::S>); // Should find N::f
}
See also issue 997.
Both 3.4.3.1 [class.qual] and 3.4.3.2 [namespace.qual] specify that some lookups are to be performed “in the context of the entire postfix-expression,” ignoring the fact that qualified-ids can appear outside of expressions.
It was suggested in document J16/05-0156 = WG21 N1896 that these uses be changed to “the context in which the qualified-id occurs,” but it isn't clear that this formulation adequately covers all the places a qualified-id can occur.
It is unclear to what extent entities without names match across translation units. For example,
struct S { int :2; enum { a, b, c } x; static class {} *p; };
If this declaration appears in multiple translation units, are all these members "the same" in each declaration?
A similar question can be asked about non-member declarations:
// Translation unit 1: extern enum { d, e, f } y; // Translation unit 2: extern enum { d, e, f } y; // Translation unit 3: enum { d, e, f } y;
Is this valid C++? Is it valid C?
James Kanze: S::p cannot be defined, because to do so requires a type specifier and the type cannot be named. ::y is valid C because C only requires compatible, not identical, types. In C++, it appears that there is a new type in each declaration, so it would not be valid. This differs from S::x because the unnamed type is part of a named type — but I don't know where or if the Standard says that.
John Max Skaller: It's not valid C++, because the type is a synthesised, unique name for the enumeration type which differs across translation units, as if:
extern enum _synth1 { d,e,f} y; .. extern enum _synth2 { d,e,f} y;
had been written.
However, within a class, the ODR implies the types are the same:
class X { enum { d } y; };
in two translation units ensures that the type of member y is the same: the two X's obey the ODR and so denote the same class, and it follows that there's only one member y and one type that it has.
(See also issues 132 and 216.)
The standard says that an unnamed class or enum definition can be given a "name for linkage purposes" through a typedef. E.g.,
typedef enum {} E; extern E *p;
can appear in multiple translation units.
How about the following combination?
// Translation unit 1: struct S; extern S *q; // Translation unit 2: typedef struct {} S; extern S *q;
Is this valid C++?
Also, if the answer is "yes", consider the following slight variant:
// Translation unit 1: struct S {}; // <<-- class has definition extern S *q; // Translation unit 2: typedef struct {} S; extern S *q;
Is this a violation of the ODR because two definitions of type S consist of differing token sequences?
The following declarations are allowed within a translation unit:
struct S; enum { S };
However, 3.5 [basic.link] paragraph 9 seems to say these two declarations cannot appear in two different translation units. That also would mean that the inclusion of a header containing the above in two different translation units is not valid C++.
I suspect this is an oversight and that users should be allowed to have the declarations above appear in different translation units. (It is a fairly common thing to do, I think.)
Mike Miller: I think you meant "enum E { S };" -- enumerators only have external linkage if the enumeration does (3.5 [basic.link] paragraph 4), and 3.5 [basic.link] paragraph 9 only applies to entities with external linkage.
I don't remember why enumerators were given linkage; I don't think it's necessary for mangling non-type template arguments. In any event, I can't think why cross-TU name collisions between enumerators and other entities would cause a problem, so I guess a change here would be okay. I can think of three changes that would have that effect:
Daveed Vandevoorde: I don't think any of these are sufficient in the sense that the problem isn't limited to enumerators. E.g.:
struct X; extern void X();shouldn't create cross-TU collisions either.
Mike Miller: So you're saying that cross-TU collisions should only be prohibited if both names denote entities of the same kind (both functions, both objects, both types, etc.), or if they are both references (regardless of what they refer to, presumably)?
Daveed Vandevoorde: Not exactly. Instead, I'm saying that if two entities (with external linkage) can coexist when they're both declared in the same translation unit (TU), then they should also be allowed to coexist when they're declared in two different translation units.
For example:
int i; void i(); // ErrorThis is an error within a TU, so I don't see a reason to make it valid across TUs.
However, "tag names" (class/struct/union/enum) can sometimes coexist with identically named entities (variables, functions & enumerators, but not namespaces, templates or type names).
The specification of the forms of the definition of main that an impliementation is required to accept is clear in C99 that the parameter names and the exact syntactic form of the types can vary. Although it is reasonable to assume that a C++ implementation would accept a definition like
int main(int foo, char** bar) { /* ... */ }
instead of the canonical
int main(int argc, char* argv[]) { /* ... */ }
it might be a good idea to clarify the intent using wording similar to C99's.
Is a compiler allowed to interleave constructor calls when performing dynamic initialization of nonlocal objects? What I mean by interleaving is: beginning to execute a particular constructor, then going off and doing something else, then going back to the original constructor. I can't find anything explicit about this in clause 3.6.2 [basic.start.init].
I'll present a few different examples, some of which get a bit wild. But a lot of what this comes down to is exactly what the standard means when it talks about the order of initialization. If it says that some object x must be initialized before a particular event takes place, does that mean that x's constructor must be entered before that event, or does it mean that it must be exited before that event? If object x must be initialized before object y, does that mean that x's constructor must exit before y's constructor is entered?
(The answer to that question might just be common sense, but I couldn't find an answer in clause 3.6.2 [basic.start.init]. Actually, when I read 3.6.2 [basic.start.init] carefully, I find there are a lot of things I took for granted that aren't there.)
OK, so a few specific scenerios.
<runtime gunk> <Enter A's constructor> <Enter f> <runtime gunk> <Enter B's constructor> <Enter f> <Leave f> <Leave B's constructor> <Leave f> <Leave A's constructor>The implication of a 'yes' answer for users is that any function called by a constructor, directly or indirectly, must be reentrant.
At this point, you might be thinking we could avoid all of this nonsense by removing compilers' freedom to defer initialization until after the beginning of main(). I'd resist that, for two reasons. First, it would be a huge change to make after the standard has been out. Second, that freedom is necessary if we want to have support for dynamic libraries. I realize we don't yet say anything about dynamic libraries, but I'd hate to make decisions that would make such support even harder.
3.6.3 [basic.start.term] paragraph 2 says,
If a function contains a local object of static storage duration that has been destroyed and the function is called during the destruction of an object with static storage duration, the program has undefined behavior if the flow of control passes through the definition of the previously destroyed local object.
I would like to turn this behavior from undefined to well-defined behavior for the purpose of achieving a graceful shutdown, especially in a multi-threaded world.
Background: Alexandrescu describes the “phoenix singleton” in Modern C++ Design. This is a class used as a function local static, that will reconstruct itself, and reapply itself to the atexit chain, if the program attempts to use it after it is destructed in the atexit chain. It achieves this by setting a “destructed flag” in its own state in its destructor. If the object is later accessed (and a member function is called on it), the member function notes the state of the “destructed flag” and does the reconstruction dance. The phoenix singleton pattern was designed to address issues only in single-threaded code where accesses among static objects can have a non-scoped pattern. When we throw in multi-threading, and the possibility that threads can be running after main returns, the chances of accessing a destroyed static significantly increase.
The very least that I would like to see happen is to standardize what I believe is existing practice: When an object is destroyed in the atexit chain, the memory the object occupied is left in whatever state the destructor put it in. If this can only be reliably done for objects with standard layout, that would be an acceptable compromise. This would allow objects to set “I'm destructed” flags in their state and then do something well-defined if accessed, such as throw an exception.
A possible refinement of this idea is to have the compiler set up a 3-state flag around function-local statics instead of the current 2-state flag:
We have the first two states today. We might choose to add the third state, and if execution passes over a function-local static with “destroyed” state, an exception could be thrown. This would mean that we would not have to guarantee memory stability in destroyed objects of static duration.
This refinement would break phoenix singletons, and is not required for the ~mutex()/~condition() I've described and prototyped. But it might make it easier for Joe Coder to apply this kind of guarantee to his own types.
There are several problems with 3.7 [basic.stc]:
3.7 [basic.stc] paragraph 2 says that "Static and automatic storage durations are associated with objects introduced by declarations (3.1 [basic.def]) and implicitly created by the implementation (12.2 [class.temporary])."
In fact, objects "implicitly created by the implementation" are the temporaries described in (12.2 [class.temporary]), and have neither static nor automatic storage duration, but a totally different duration, described in 12.2 [class.temporary].
3.7 [basic.stc] uses the expression "local object" in several places, without ever defining it. Presumably, what is meant is "an object declared at block scope", but this should be said explicitly.
In a recent discussion in comp.lang.c++.moderated, on poster interpreted "local objects" as including temporaries. This would require them to live until the end of the block in which they are created, which contradicts 12.2 [class.temporary]. If temporaries are covered by this section, and the statement in 3.7 [basic.stc] seems to suggest, and they aren't local objects, then they must have static storage duration, which isn't right either.
I propose adding a fourth storage duration to the list after 3.7 [basic.stc] paragraph 1:
And rewriting the second paragraph of this section as follows:
Temporary storage duration is associated with objects implicitly created by the implementation, and is described in 12.2 [class.temporary]. Static and automatic storage durations are associated with objects defined by declarations; in the following, an object defined by a declaration with block scope is a local object. The dynamic storage duration is associated with objects created by the operator new.
Steve Adamczyk: There may well be an issue here, but one should bear in mind the difference between storage duration and object lifetime. As far as I can see, there is no particular problem with temporaries having automatic or static storage duration, as appropriate. The point of 12.2 [class.temporary] is that they have an unusual object lifetime.
Notes from Ocrober 2002 meeting:
It might be desirable to shorten the storage duration of temporaries to allow reuse of them. The as-if rule allows some reuse, but such reuse requires analysis, including noting whether the addresses of such temporaries have been taken.
The global allocation functions are implicitly declared in every translation unit with exception-specifications (3.7.4 [basic.stc.dynamic] paragraph 2). It is not clear what should happen if a replacement allocation function is declared without an exception-specification. Is that a conflict with the implicitly-declared function (as it would be with explicitly-declared functions, and presumably is if the <new> header is included)? Or does the new declaration replace the implicit one, including the lack of an exception-specification? Or does the implicit declaration prevail? (Regardless of the exception-specification or lack thereof, it is presumably undefined behavior for an allocation function to exit with an exception that cannot be caught by a handler of type std::bad_alloc (3.7.4.1 [basic.stc.dynamic.allocation] paragraph 3).)
3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 4 mentions that the effect of using an invalid pointer value is undefined. However, the standard never says what it means to 'use' a value.
There are a number of possible interpretations, but it appears that each of them leads to undesired conclusions:
int *x = new int(0); delete x; x = 0;into undefined behaviour. As this is a common idiom, this is clearly undesirable.
int *x = new int(0); delete x; x->~int();into undefined behaviour; according to 5.2.4 [expr.pseudo], the variable x is 'evaluated' as part of evaluating the pseudo destructor call. This, in turn, would mean that all containers (23 [containers]) of pointers show undefined behaviour, e.g. 23.3.4.3 [list.modifiers] requires to invoke the destructor as part of the clear() method of the container.
If any other meaning was intended for 'using an expression', that meaning should be stated explicitly.
(See also issue 623.)
When an object is deleted, 3.7.4.2 [basic.stc.dynamic.deallocation] says that the deallocation “[renders] invalid all pointers referring to any part of the deallocated storage.” According to 3.9.2 [basic.compound] paragraph 3, a pointer whose address is one past the end of an array is considered to point to an unrelated object that happens to reside at that address. Does this need to be clarified to specify that the one-past-the-end pointer of an array is not invalidated by deleting the following object? (See also 5.3.5 [expr.delete] paragraph 4, which also mentions that the system deallocation function renders a pointer invalid.)
Consider
extern "C" int printf (const char *,...); struct Base { Base();}; struct Derived: virtual public Base { Derived() {;} }; Derived d; extern Derived& obj = d; int i; Base::Base() { if ((Base *) &obj) i = 4; printf ("i=%d\n", i); } int main() { return 0; }
12.7 [class.cdtor] paragraph 2 makes this valid, but 3.8 [basic.life] paragraph 5 implies that it isn't valid.
Steve Adamczyk: A second issue:
extern "C" int printf(const char *,...); struct A { virtual ~A(); int x; }; struct B : public virtual A { }; struct C : public B { C(int); }; struct D : public C { D(); }; int main() { D t; printf("passed\n");return 0; } A::~A() {} C::C(int) {} D::D() : C(this->x) {}
Core issue 52 almost, but not quite, says that in evaluating "this->x" you do a cast to the virtual base class A, which would be an error according to 12.7 [class.cdtor] paragraph 2 because the base class B constructor hasn't started yet. 5.2.5 [expr.ref] should be clarified to say that the cast does need to get done.
James Kanze submitted the same issue via comp.std.c++ on 11 July 2003:
Richard Smith: Nonsense. You can use "this" perfectly happily in a constructor, just be careful that (a) you're not using any members that are not fully initialised, and (b) if you're calling virtual functions you know exactly what you're doing.
In practice, and I think in intent, you are right. However, the standard makes some pretty stringent restrictions in 3.8 [basic.life]. To start with, it says (in paragraph 1):
The lifetime of an object is a runtime property of the object. The lifetime of an object of type T begins when:(Emphasis added.) Then when we get down to paragraph 5, it says:The lifetime of an object of type T ends when:
- storage with the proper alignment and size for type T is obtained, and
- if T is a class type with a non-trivial constructor, the constructor calls has COMPLETED.
- if T is a class type with a non-trivial destructor, the destructor call STARTS, or
- the storage which the object occupies is reused or released.
Before the lifetime of an object has started but after the storage which the object will occupy has been allocated [which sounds to me like it would include in the constructor, given the text above] or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. [...] If the object will be or was of a non-POD class type, the program has undefined behavior if:
[...]
- the pointer is implicitly converted to a pointer to a base class type, or [...]
I can't find any exceptions for the this pointer.
Note that calling a non-static function in the base class, or even constructing the base class in initializer list, involves an implicit conversion of this to a pointer to the base class. Thus undefined behavior. I'm sure that this wasn't the intent, but it would seem to be what this paragraph is saying.
Sent in by David Abrahams:
Yes, and to add to this tangent, 3.9.1 [basic.fundamental] paragraph 1 states "Plain char, signed char, and unsigned char are three distinct types." Strangely, 3.9 [basic.types] paragraph 2 talks about how "... the underlying bytes making up the object can be copied into an array of char or unsigned char. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value." I guess there's no requirement that this copying work properly with signed chars!
Notes from October 2002 meeting:
We should do whatever C99 does. 6.5p6 of the C99 standard says "array of character type", and "character type" includes signed char (6.2.5p15), and 6.5p7 says "character type". But see also 6.2.6.1p4, which mentions (only) an array of unsigned char.
Proposed resolution (April 2003):
Change 3.8 [basic.life] paragraph 5 bullet 3 from
to
Change 3.8 [basic.life] paragraph 6 bullet 3 from
to
Change the beginning of 3.9 [basic.types] paragraph 2 from
For any object (other than a base-class subobject) of POD type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7 [intro.memory]) making up the object can be copied into an array of char or unsigned char.
to
For any object (other than a base-class subobject) of POD type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7 [intro.memory]) making up the object can be copied into an array of byte-character type.
Add the indicated text to 3.9.1 [basic.fundamental] paragraph 1:
Objects declared as characters (char) shall be large enough to store any member of the implementation's basic character set. If a character from this set is stored in a character object, the integral value of that character object is equal to the value of the single character literal form of that character. It is implementation-defined whether a char object can hold negative values. Characters can be explicitly declared unsigned or signed. Plain char, signed char, and unsigned char are three distinct types, called the byte-character types. A char, a signed char, and an unsigned char occupy the same amount of storage and have the same alignment requirements (3.9 [basic.types]); that is, they have the same object representation. For byte-character types, all bits of the object representation participate in the value representation. For unsigned byte-character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types. In any particular implementation, a plain char object can take on either the same values as a signed char or an unsigned char; which one is implementation-defined.
Change 3.10 [basic.lval] paragraph 15 last bullet from
to
Notes from October 2003 meeting:
It appears that in C99 signed char may have padding bits but no trap representation, whereas in C++ signed char has no padding bits but may have -0. A memcpy in C++ would have to copy the array preserving the actual representation and not just the value.
March 2004: The liaisons to the C committee have been asked to tell us whether this change would introduce any unnecessary incompatibilities with C.
Notes from October 2004 meeting:
The C99 Standard appears to be inconsistent in its requirements. For example, 6.2.6.1 paragraph 4 says:
The value may be copied into an object of type unsigned char [n] (e.g., by memcpy); the resulting set of bytes is called the object representation of the value.
On the other hand, 6.2 paragraph 6 says,
If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one.
Mike Miller will investigate further.
Proposed resolution (February, 2010):
Change 3.8 [basic.life] paragraph 5 bullet 4 as follows:
...The program has undefined behavior if:
...
the pointer is used as the operand of a static_cast (5.2.9 [expr.static.cast]) (except when the conversion is to cv void*, or to cv void* and subsequently to char*, or unsigned char* a pointer to a cv-qualified or cv-unqualified byte-character type (3.9.1 [basic.fundamental])), or
...
Change 3.8 [basic.life] paragraph 6 bullet 4 as follows:
...The program has undefined behavior if:
...
the lvalue is used as the operand of a static_cast (5.2.9 [expr.static.cast]) except when the conversion is ultimately to cv char& or cv unsigned char& a reference to a cv-qualified or cv-unqualified byte-character type (3.9.1 [basic.fundamental]) or an array thereof, or
...
Change 3.9 [basic.types] paragraph 2 as follows:
For any object (other than a base-class subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7 [intro.memory]) making up the object can be copied into an array of char or unsigned char a byte-character type (3.9.1 [basic.fundamental]).39 If the content of the that array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value. [Example:...
Change 3.9.1 [basic.fundamental] paragraph 1 as follows:
...Characters can be explicitly declared unsigned or signed. Plain char, signed char, and unsigned char are three distinct types, called the byte-character types. A char, a signed char, and an unsigned char occupy the same amount of storage and have the same alignment requirements (3.11 [basic.align]); that is, they have the same object representation. For byte-character types, all bits of the object representation participate in the value representation. For unsigned character types unsigned char, all possible bit patterns of the value representation represent numbers...
Change 3.10 [basic.lval] paragraph 15 final bullet as follows:
If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined 52
...
a char or unsigned char byte-character type (3.9.1 [basic.fundamental]).
Change 3.11 [basic.align] paragraph 6 as follows:
The alignment requirement of a complete type can be queried using an alignof expression (5.3.6 [expr.alignof]). Furthermore, the byte-character types (3.9.1 [basic.fundamental]) char, signed char, and unsigned char shall have the weakest alignment requirement. [Note: this enables the byte-character types to be used as the underlying type for an aligned memory area (7.6.2 [dcl.align]). —end note]
Change 5.3.4 [expr.new] paragraph 10 as follows:
...For arrays of char and unsigned char a byte-character type (3.9.1 [basic.fundamental]), the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the strictest fundamental alignment requirement (3.11 [basic.align]) of any object type whose size is no greater than the size of the array being created. [Note: Because allocation functions are assumed to return pointers to storage that is appropriately aligned for objects of any type with fundamental alignment, this constraint on array allocation overhead permits the common idiom of allocating byte-character arrays into which objects of other types will later be placed. —end note]
Notes from the March, 2010 meeting:
The CWG was not convinced that there was a need to change the existing specification at this time. Some were concerned that there might be implementation difficulties with giving signed char the requisite semantics; implementations for which that is true can currently make char equivalent to unsigned char and avoid those problems, but the suggested change would undermine that strategy.
In 3.9 [basic.types] paragraph 10, the standard makes it quite clear that volatile qualified types are PODs:
Arithmetic types (3.9.1 [basic.fundamental]), enumeration types, pointer types, and pointer to member types (3.9.2 [basic.compound]), and cv-qualified versions of these types (3.9.3 [basic.type.qualifier]) are collectively called scalar types. Scalar types, POD-struct types, POD-union types (clause 9 [class]), arrays of such types and cv-qualified versions of these types (3.9.3 [basic.type.qualifier]) are collectively called POD types.
However in 3.9 [basic.types] paragraph 3, the standard makes it clear that PODs can be copied “as if” they were a collection of bytes by memcpy:
For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the value of obj1 is copied into obj2, using the std::memcpy library function, obj2 shall subsequently hold the same value as obj1.
The problem with this is that a volatile qualified type may need to be copied in a specific way (by copying using only atomic operations on multithreaded platforms, for example) in order to avoid the “memory tearing” that may occur with a byte-by-byte copy.
I realise that the standard says very little about volatile qualified types, and nothing at all (yet) about multithreaded platforms, but nonetheless this is a real issue, for the following reason:
The forthcoming TR1 will define a series of traits that provide information about the properties of a type, including whether a type is a POD and/or has trivial construct/copy/assign operations. Libraries can use this information to optimise their code as appropriate, for example an array of type T might be copied with a memcpy rather than an element-by-element copy if T is a POD. This was one of the main motivations behind the type traits chapter of the TR1. However it's not clear how volatile types (or POD's which have a volatile type as a member) should be handled in these cases.
Notes from the April, 2005 meeting:
It is not clear whether the volatile qualifier actually guarantees atomicity in this way. Also, the work on the memory model for multithreading being done by the Evolution Working Group seems at this point likely to specify additional semantics for volatile data, and that work would need to be considered before resolving this issue.
3.9.1 [basic.fundamental] does not impose a requirement on the floating point types that there be an exact representation of the value zero. This omission is significant in 4.12 [conv.bool] paragraph 1, in which any non-zero value converts to the bool value true.
Suggested resolution: require that all floating point types have an exact representation of the value zero.
3.9.1 [basic.fundamental] paragraph 2 says that
There are four signed integer types: "signed char", "short int", "int", and "long int."
This would indicate that const int is not a signed integer type.
There is no normative requirement on the ranges of the integral types, although the footnote in 3.9.1 [basic.fundamental] paragraph 2 indicates the intent (for int, at least) that they match the values given in the <climits> header. Should there be an explicit requirement of some sort?
(See also paper N1693.)
The relationship between the values representable by corresponding signed and unsigned integer types is not completely described, but 3.9 [basic.types] paragraph 4 says,
The value representation of an object is the set of bits that hold the value of type T.
and 3.9.1 [basic.fundamental] paragraph 3 says,
The range of nonnegative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the value representation of each corresponding signed/unsigned type shall be the same.
I.e., the maximum value of each unsigned type must be larger than the maximum value of the corresponding signed type.
C90 doesn't have this restriction, and C99 explicitly says (6.2.6.2, paragraph 2),
For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; there shall be exactly one sign bit. Each bit that is a value bit shall have the same value as the same bit in the object representation of the corresponding unsigned type (if there are M value bits in the signed type and N in the unsigned type, then M <= N).
Unlike C++, the sign bit is not part of the value, and on an architecture that does not have native support of unsigned types, an implementation can emulate unsigned integers by simply ignoring what would be the sign bit in the signed type and be conforming.
The question is whether we intend to make a conforming implementation on such an architecture impossible. More generally, what range of architectures do we intend to support? And to what degree do we want to follow C99 in its evolution since C89?
(See paper J16/08-0141 = WG21 N2631.)
The taxonomy of value categories in 3.10 [basic.lval] classifies temporaries as prvalues. However, some temporaries are explicitly referred to as lvalues (cf 15.1 [except.throw] paragraph 3).
The C and C++ approaches to alignment are incompatible. See document PL22.16 10-0083 = WG21 N3093 for details.
Notes from the August, 2010 meeting:
CWG agreed that the alignment specifier should be a keyword instead of an attributes.
According to 4.1 [conv.lval] paragraph 1, an lvalue-to-rvalue conversion on an uninitialized object produces undefined behavior. Since there is only one “value” of type std::nullptr_t, an lvalue-to-rvalue conversion on a std::nullptr_t glvalue does not need to fetch the value from storage. Is there any need for undefined behavior in this case?
Section 4.4 [conv.qual] covers the case of multi-level pointers, but does not appear to cover the case of pointers to arrays of pointers. The effect is that arrays are treated differently from simple scalar values.
Consider for example the following code: (from the thread "Pointer to array conversion question" begun in comp.lang.c++.moderated)
int main() { double *array2D[2][3]; double * (*array2DPtr1)[3] = array2D; // Legal double * const (*array2DPtr2)[3] = array2DPtr1; // Legal double const * const (*array2DPtr3)[3] = array2DPtr2; // Illegal }and compare this code with:-
int main() { double *array[2]; double * *ppd1 = array; // legal double * const *ppd2 = ppd1; // legal double const * const *ppd3 = ppd2; // certainly legal (4.4/4) }
The problem appears to be that the pointed to types in example 1 are unrelated since nothing in the relevant section of the standard covers it - 4.4 [conv.qual] does not mention conversions of the form "cv array of N pointer to T" into "cv array of N pointer to cv T"
It appears that reinterpret_cast is the only way to perform the conversion.
Suggested resolution:
Artem Livshits proposed a resolution :-
"I suppose if the definition of "similar" pointer types in 4.4 [conv.qual] paragraph 4 was rewritten like this:
it would address the problem.T1 is cv1,0 P0 cv1,1 P1 ... cv1,n-1 Pn-1 cv1,n T
and
T2 is cv1,0 P0 cv1,1 P1 ... cv1,n-1 Pn-1 cv1,n T
where Pi is either a "pointer to" or a "pointer to an array of Ni"; besides P0 may be also a "reference to" or a "reference to an array of N0" (in the case of P0 of T2 being a reference, P0 of T1 may be nothing).
In fact I guess Pi in this notation may be also a "pointer to member", so 4.4 [conv.qual]/{4,5,6,7} would be nicely wrapped in one paragraph."
It is not clear what constraints are placed on a floating point implementation by the wording of the Standard. For instance, is an implementation permitted to generate a "fused multiply-add" instruction if the result would be different from what would be obtained by performing the operations separately? To what extent does the "as-if" rule allow the kinds of optimizations (e.g., loop unrolling) performed by FORTRAN compilers?
(Moved from issue 760.)
Although it was considered and rejected as part of issue 643, more recent developments may argue in favor of allowing the use of this in a late-specified return type. In particular, declaring the return type for a forwarding function in a derived class template that invokes a member function of a dependent base class is difficult without this facility. For example:
template <typename T> struct derived: base<T> { auto invoke() -> decltype(this->base_func()) { return this->base_func(); } };
There does not appear to be any technical difficulty that would require the restriction in 5.1.2 [expr.prim.lambda] paragraph 5 against default arguments in lambda-expressions.
There does not appear to be any technical difficulty that would require the current restriction that the return type of a lambda can be deduced only if the body of the lambda consists of a single return statement. In particular, multiple return statements could be permitted if they all return the same type.
5.2.1 [expr.sub] paragraph 2 deals with one particular aspect of the overloaded operator[], which seems out of place. Either 5.2.1 [expr.sub] should be augmented to discuss the overloaded operator[] in general or the information in paragraph 2 should be moved into 13.5.5 [over.sub].
According to 5.2.3 [expr.type.conv] paragraphs 1 and 3 (stated directly or by reference to another section of the Standard), all the following expressions create temporaries:
T(1) T(1, 2) T{1} T{}
However, paragraph 2 says,
The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete effective object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, which is value-initialized (8.5 [dcl.init]; no initialization is done for the void() case).
This does not say that the result is a temporary, which means that the lifetime of the result is not specified by 12.2 [class.temporary]. Presumably this is just an oversight.
Notes from the October, 2009 meeting:
The specification in 5.2.3 [expr.type.conv] is in error, not because it fails to state that T() is a temporary but because it requires a temporary for the other cases with fewer than two operands. The case where T is a class type is covered by 12.2 [class.temporary] paragraph 1 (“a conversion that creates an rvalue”), and a temporary should not be created when T is not a class type.
Given the following declarations:
struct S { signed long long sll: 3; }; S s = { -1 };
the expressions s.sll-- < 0u and s.sll < 0u have different results. The reason for this is that s.sll-- is an rvalue of type signed long long (5.2.6 [expr.post.incr]), which means that the usual arithmetic conversions (5 [expr] paragraph 10) convert 0u to signed long long and the result is true. s.sll, on the other hand, is a bit-field lvalue, which is promoted (4.5 [conv.prom] paragraph 3) to int; both operands of < have the same rank, so s.sll is converted to unsigned int to match the type of 0u and the result is false. This disparity seems undesirable.
The original proposed resolution for issue 160 included changing extended_type_info (5.2.8 [expr.typeid] paragraph 1, footnote 61) to std::extended_type_info. There was no consensus on whether this name ought to be part of namespace std or in a vendor-specific namespace, so the question was moved into a separate issue.
5.2.8 [expr.typeid] paragraph 4 says,
When typeid is applied to a type-id, the result refers to a std::type_info object representing the type of the type-id. If the type of the type-id is a reference type, the result of the typeid expression refers to a std::type_info object representing the referenced type. If the type of the type-id is a class type or a reference to a class type, the class shall be completely-defined.
I'm wondering whether this is not overly restrictive. I can't think of a reason to require that T be completely-defined in typeid(T) when T is a class type. In fact, several popular compilers enforce that restriction for typeid(T), but not for typeid(T&). Can anyone explain this?
Nathan Sidwell: I think this restriction is so that whenever the compiler has to emit a typeid object of a class type, it knows what the base classes are, and can therefore emit an array of pointers-to-base-class typeids. Such a tree is necessary to implement dynamic_cast and exception catching (in a commonly implemented and obvious manner). If the class could be incomplete, the compiler might have to emit a typeid for incomplete Foo in one object file and a typeid for complete Foo in another object file. The compilation system will then have to make sure that (a) those compare equal and (b) the complete Foo gets priority, if that is applicable.
Unfortunately, there is a problem with exceptions that means there still can be a need to emit typeids for incomplete class. Namely one can throw a pointer-to-pointer-to-incomplete. To implement the matching of pointer-to-derived being caught by pointer-to-base, it is necessary for the typeid of a pointer type to contain a pointer to the typeid of the pointed-to type. In order to do the qualification matching on a multi-level pointer type, one has a chain of pointer typeids that can terminate in the typeid of an incomplete type. You cannot simply NULL-terminate the chain, because one must distinguish between different incomplete types.
Dave Abrahams: So if implementations are still required to be able to do it, for all practical purposes, why aren't we letting the user have the benefits?
Notes from the April, 2006 meeting:
There was some concern expressed that this might be difficult under the IA64 ABI. It was also observed that while it is necessary to handle exceptions involving incomplete types, there is no requirement that the RTTI data structures be used for exception handling.
According to 7.2 [dcl.enum] paragraph 10,
An expression of arithmetic or enumeration type can be converted to an enumeration type explicitly.
However, 5.2.9 [expr.static.cast] paragraph 10 says only,
A value of integral or enumeration type can be explicitly converted to an enumeration type.
This omits floating-point values. Presumably unscoped enumeration types are covered by paragraph 7,
The inverse of any standard conversion sequence (Clause 4 [conv]), other than the lvalue-to-rvalue (4.1 [conv.lval]), array-to- pointer (4.2 [conv.array]), function-to-pointer (4.3 [conv.func]), and boolean (4.12 [conv.bool]) conversions, can be performed explicitly using static_cast.
because 4.9 [conv.fpint] paragraph 2 allows an unscoped enumeration value to be implicitly converted to a floating point type. (Although that also covers the integral types, so it's not clear why they would be mentioned specifically in 5.2.9 [expr.static.cast] paragraph 10.) However, this should presumably say “arithmetic” instead of “integral” to match the statement in 7.2 [dcl.enum] paragraph 10.
During the discussion of issue 799, which specified the result of using reinterpret_cast to convert an operand to its own type, it was observed that it is probably reasonable to allow reinterpret_cast between any two types that have the same size and alignment.
5.3.1 [expr.unary.op] paragraph 2 indicates that the type of an address-of-member expression reflects the class in which the member was declared rather than the class identified in the nested-name-specifier of the qualified-id. This treatment is unintuitive and can lead to strange code and unexpected results. For instance, in
struct B { int i; }; struct D1: B { }; struct D2: B { }; int (D1::* pmD1) = &D2::i; // NOT an errorMore seriously, template argument deduction can give surprising results:
struct A { int i; virtual void f() = 0; }; struct B : A { int j; B() : j(5) {} virtual void f(); }; struct C : B { C() { j = 10; } }; template <class T> int DefaultValue( int (T::*m) ) { return T().*m; } ... DefaultValue( &B::i ) // Error: A is abstract ... DefaultValue( &C::j ) // returns 5, not 10.
Suggested resolution: 5.3.1 [expr.unary.op] should be changed to read,
If the member is a nonstatic member (perhaps by inheritance) of the class nominated by the nested-name-specifier of the qualified-id having type T, the type of the result is "pointer to member of class nested-name-specifier of type T."and the comment in the example should be changed to read,
// has type int B::*
Notes from 04/00 meeting:
The rationale for the current treatment is to permit the widest possible use to be made of a given address-of-member expression. Since a pointer-to-base-member can be implicitly converted to a pointer-to-derived-member, making the type of the expression a pointer-to-base-member allows the result to initialize or be assigned to either a pointer-to-base-member or a pointer-to-derived-member. Accepting this proposal would allow only the latter use.
Additional notes:
Another problematic example has been mentioned:
class Base { public: int func() const; }; class Derived : public Base { }; template<class T> class Templ { public: template<class S> Templ(S (T::*ptmf)() const); }; void foo() { Templ<Derived> x(&Derived::func); // ill-formed }
In this example, even though the conversion of &Derived::func to int (Derived::*)() const is permitted, the initialization of x cannot be done because template argument deduction for the constructor fails.
If the suggested resolution were adopted, the amount of code broken by the change might be reduced by adding an implicit conversion from pointer-to-derived-member to pointer-to-base-member for appropriate address-of-member expressions (not for arbitrary pointers to members, of course).
(See also issues 247 and 1121.)
Requirements for the alignment of pointers returned by new-expressions are given in 5.3.4 [expr.new] paragraph 10:
For arrays of char and unsigned char, the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the most stringent alignment requirement (3.9 [basic.types]) of any object type whose size is no greater than the size of the array being created.
The intent of this wording is that the pointer returned by the new-expression will be suitably aligned for any data type that might be placed into the allocated storage (since the allocation function is constrained to return a pointer to maximally-aligned storage). However, there is an implicit assumption that each alignment requirement is an integral multiple of all smaller alignment requirements. While this is probably a valid assumption for all real architectures, there's no reason that the Standard should require it.
For example, assume that int has an alignment requirement of 3 bytes and double has an alignment requirement of 4 bytes. The current wording only requires that a buffer that is big enough for an int or a double be aligned on a 4-byte boundary (the more stringent requirement), but that would allow the buffer to be allocated on an 8-byte boundary — which might not be an acceptable location for an int.
Suggested resolution: Change "of any object type" to "of every object type."
A similar assumption can be found in 5.2.10 [expr.reinterpret.cast] paragraph 7:
...converting an rvalue of type "pointer to T1" to the type "pointer to T2" (where ... the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value...
Suggested resolution: Change the wording to
...converting an rvalue of type "pointer to T1" to the type "pointer to T2" (where ... the alignment requirements of T1 are an integer multiple of those of T2) and back to its original type yields the original pointer value...
The same change would also be needed in paragraph 9.
Looking up operator new in a new-expression uses a different mechanism from ordinary lookup. According to 5.3.4 [expr.new] paragraph 9,
If the new-expression begins with a unary :: operator, the allocation function's name is looked up in the global scope. Otherwise, if the allocated type is a class type T or array thereof, the allocation function's name is looked up in the scope of T. If this lookup fails to find the name, or if the allocated type is not a class type, the allocation function's name is looked up in the global scope.
Note in particular that the scope in which the new-expression occurs is not considered. For example,
void f() { void* operator new(std::size_t, void*); int* i = new int; // okay? }
In this example, the implicit reference to operator new(std::size_t) finds the global declaration, even though the block-scope declaration of operator new with a different signature would hide it from an ordinary reference.
This seems strange; either the block-scope declaration should be ill-formed or it should be found by the lookup.
Notes from October 2004 meeting:
The CWG agreed that the block-scope declaration should not be found by the lookup in a new-expression. It would, however, be found by ordinary lookup if the allocation function were invoked explicitly.
(See also issue 256.)
An implementation may have an unspecified amount of array allocation overhead (5.3.4 [expr.new] paragraph 10), so that evaluation of a new-expression in which the new-type-id is T[n] involves a request for more than n * sizeof(T) bytes of storage through the relevant operator new[] function.
The placement operator new[] function does not and cannot check whether the requested size is less than or equal to the size of the provided region of memory (18.6.1.3 [new.delete.placement] paragraphs 5-6). A program using placement array new must calculate what the requested size will be in advance.
Therefore any program using placement array new must take into account the implementation's array allocation overhead, which cannot be obtained or calculated by any portable means.
Notes from the April, 2005 meeting:
While the CWG agreed that there is no portable means to accomplish this task in the current language, they felt that a paper is needed to analyze the numerous mechanisms that might address the problem and advance a specific proposal. There is no volunteer to write such a paper at this time.
5.3.4 [expr.new] paragraph 10 says that the result of an array allocation function and the value of the array new-expression from which it was invoked may be different, allowing for space preceding the array to be used for implementation purposes such as saving the number of elements in the array. However, there is no corresponding description of the relationship between the operand of an array delete-expression and the argument passed to its deallocation function.
3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 3 does state that
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 statement might be read as requiring an implementation, when processing an array delete-expression and calling the deallocation function, to perform the inverse of the calculation applied to the result of the allocation function to produce the value of the new-expression. (5.3.5 [expr.delete] paragraph 2 requires that the operand of an array delete-expression "be the pointer value which resulted from a previous array new-expression.") However, it is not completely clear whether the "shall" expresses an implementation requirement or a program requirement (or both). Furthermore, there is no direct statement about user-defined deallocation functions.
Suggested resolution: A note should be added to 5.3.5 [expr.delete] to clarify that any offset added in an array new-expression must be subtracted in the array delete-expression.
The current specification of the alignof operator (5.3.6 [expr.alignof]) allows it to be applied only to types, not to objects. Since the align attribute may be applied to objects, and since existing practice permits querying the alignment of objects, it should be considered whether to allow this in Standard C++ as well.
Destructors should by default be noexcept. Such a rule should be obeyed even for cases where a destructor is defaulted. Then a throwing destructor would need to be declared noexcept(false), and the resulting code breakage is acceptable.
Notes from the August, 2010 meeting:
CWG agreed with the suggested direction.
The meaning of an old-style cast is described in terms of const_cast, static_cast, and reinterpret_cast in 5.4 [expr.cast] paragraph 5. Ignoring const_cast for the moment, it basically says that if the conversion performed by a given old-style cast is one of those performed by static_cast, the conversion is interpreted as if it were a static_cast; otherwise, it's interpreted as if it were a reinterpret_cast, if possible. The following example is given in illustration:
struct A {}; struct I1 : A {}; struct I2 : A {}; struct D : I1, I2 {}; A *foo( D *p ) { return (A*)( p ); // ill-formed static_cast interpretation }
The obvious intent here is that a derived-to-base pointer conversion is one of the conversions that can be performed using static_cast, so (A*)(p) is equivalent to static_cast<A*>(p), which is ill-formed because of the ambiguity.
Unfortunately, the description of static_cast in 5.2.9 [expr.static.cast] does NOT support this interpretation. The problem is in the way 5.2.9 [expr.static.cast] lists the kinds of casts that can be performed using static_cast. Rather than saying something like "All standard conversions can be performed using static_cast," it says
An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration "T t(e);" is well-formed, for some invented temporary variable t.
Given the declarations above, the hypothetical declaration
A* t(p);
is NOT well-formed, because of the ambiguity. Therefore the old-style cast (A*)(p) is NOT one of the conversions that can be performed using static_cast, and (A*)(p) is equivalent to reinterpret_cast<A*>(p), which is well-formed under 5.2.10 [expr.reinterpret.cast] paragraph 7.
Other situations besides ambiguity which might raise similar questions include access violations, casting from virtual base to derived, and casting pointers-to-members when virtual inheritance is involved.
The description of class member access expressions in 5.2.5 [expr.ref] paragraph 2 defines the terms “object expression” and “pointer expression:”
For the first option (dot) the type of the first expression (the object expression) shall be “class object” (of a complete type). For the second option (arrow) the type of the first expression (the pointer expression) shall be “pointer to class object” (of a complete type).
(Note in passing that the phrase “class object” seems very odd when describing a type.) The rest of that section is based on the equivalence of the expression E1->E2 to (*(E1)).E2 and thus is phrased only in terms of “object expression.” This terminology appears to have been misapplied in other parts of the Standard, using the term “object expression” to refer both to class types and pointers to class types. The most egregious of these is 5.5 [expr.mptr.oper] paragraph 4, describing the operands of a pointer-to-member expression:
The first operand is called the object expression. If the dynamic type of the object expression does not contain the member to which the pointer refers, the behavior is undefined.
The dynamic type of the first operand in the ->* case is a pointer type, not a class type, so it cannot “contain the member to which the pointer refers.” Another example is 3.4.5 [basic.lookup.classref], describing the lookup in a class member access expression. The first paragraph uses the term consistently with its use in 5.2.5 [expr.ref], but paragraph 2 reads:
If the id-expression in a class member access (5.2.5 [expr.ref]) is an unqualified-id, and the type of the object expression is of a class type C, the unqualified-id is looked up in the scope of class C. If the type of the object expression is of pointer to scalar type, the unqualified-id is looked up in the context of the complete postfix-expression.
Paragraph 7 gets it right:
...in the context of the class of the object expression (or the class pointed to by the pointer expression).
Another misapplication of the term occurs in 5.2.2 [expr.call] paragraph 1:
...the call is as a member of the object pointed to or referred to by the object expression (5.2.5 [expr.ref], 5.5 [expr.mptr.oper])... its final overrider (10.3 [class.virtual]) in the dynamic type of the object expression is called. [Note: the dynamic type is the type of the object pointed or referred to by the current value of the object expression...
Here again we have the idea that an object expression can “point to” an object.
Another minor complication is that 5 [expr] paragraph 7 has a separate definition for the (hyphenated) term “object-expression:”
An expression designating an object is called an object-expression.
This term is used several times in the Standard, apparently interchangeably with the non-hyphenated version defined in 5.2.5 [expr.ref]; for example, 5.1.1 [expr.prim.general] paragraph 10 bullet 1 mentions
a class member access (5.2.5 [expr.ref]) in which the object-expression refers to the member's class
using the term defined in 5 [expr] paragraph 7 but linking it with 5.2.5 [expr.ref].
These uses of “object expression” and “object-expression” need to be made consistent, especially the reference in 5.5 [expr.mptr.oper] that implies that the dynamic type of a pointer is that of the complete object to which it points.
In C, this is ill-formed (cf C99 6.5.8):
void f(char* s) { if (s < 0) { } }
...but in C++, it's not. Why? Who would ever need to write (s > 0) when they could just as well write (s != 0)?
This has been in the language since the ARM (and possibly earlier); apparently it's because the pointer conversions (4.10 [conv.ptr]) need to be performed on both operands whenever one of the operands is of pointer type. So it looks like the "null-ptr-to-real-pointer-type" conversion is hitching a ride with the other pointer conversions.
One of the bullets in 5.19 [expr.const] paragraph 2 says,
a type conversion from a pointer or pointer-to-member type to a literal type
This appears to prohibit conversion from one pointer type to another; for example,
int x;
constexpr void* p = &x; // ill-formed
This seems excessive and probably unintentional.
It is not clear what happens when a program violates the limits on constexpr function recursion in a context that does not require a constant expression. For example,
constexpr int f(int i) { return f(i); }
const int i = f(1); // error, undefined behavior, or dynamic initialization?
(Presumably the “within its resource limits” caveat of 1.4 [intro.compliance] paragraph 2 would effectively result in undefined behavior in a context that required a constant expression.)
According to 14.3.2 [temp.arg.nontype] paragraph 1, one of the possibilities for a template-argument for a non-type, non-template template-parameter is
an integral constant expression (including a constant expression of literal class type that can be used as an integral constant expression as described in 5.19 [expr.const])
However, the requirement for such a literal class type is (5.19 [expr.const] paragraph 5):
...that class type shall have a single non-explicit conversion function to an integral or enumeration type and that conversion function shall be constexpr.
Note that this normative requirement for a single conversion function is contradicted by the example in that paragraph, which reads in significant part,
struct A {
constexpr A(int i) : val(i) { }
constexpr operator int() { return val; }
constexpr operator long() { return 43; }
private:
int val;
};
template<int> struct X { };
constexpr A a = 42;
X<a> x; // OK: unique conversion to int
C and C++ differ in the treatment of an expression statement, in particular with regard to whether a volatile lvalue is fetched. For example,
volatile int x; void f() { x; // Fetches x in C, not in C++ }
The reason C++ is different in this regard is principally due to the fact that an assignment expression is an lvalue in C++ but not in C. If the lvalue-to-rvalue conversion were applied to expression statements, a statement like
x = 5;
would write to x and then immediately read it.
It is not clear that the current approach to dealing with the difference in assignment expressions is the only or best approach; it might be possible to avoid the unwanted fetch on the result of an assignment statement without giving up the fetch for a variable appearing by itself in an expression statement.
6.4.1 [stmt.if] is silent about whether the else clause of an if statement is executed if the condition is not evaluated. (This could occur via a goto or a longjmp.) C99 covers the goto case with the following provision:
If the first substatement is reached via a label, the second substatement is not executed.
It should probably also be stated that the condition is not evaluated when the “then” clause is entered directly.
7 [dcl.dcl] paragraph 3 reads,
In a simple-declaration, the optional init-declarator-list can be omitted only when... the decl-specifier-seq contains either a class-specifier, an elaborated-type-specifier with a class-key (9.1 [class.name] ), or an enum-specifier. In these cases and whenever a class-specifier or enum-specifier is present in the decl-specifier-seq, the identifiers in those specifiers are among the names being declared by the declaration... In such cases, and except for the declaration of an unnamed bit-field (9.6 [class.bit] ), the decl-specifier-seq shall introduce one or more names into the program, or shall redeclare a name introduced by a previous declaration. [Example:In the absence of any explicit restrictions in 7.1.3 [dcl.typedef] , this paragraph appears to allow declarations like the following:enum { }; // ill-formed typedef class { }; // ill-formed—end example]
typedef struct S { }; // no declarator typedef enum { e1 }; // no declaratorIn fact, the final example in 7 [dcl.dcl] paragraph 3 would seem to indicate that this is intentional: since it is illustrating the requirement that the decl-specifier-seq must introduce a name in declarations in which the init-declarator-list is omitted, presumably the addition of a class name would have made the example well-formed.
On the other hand, there is no good reason to allow such declarations; the only reasonable scenario in which they might occur is a mistake on the programmer's part, and it would be a service to the programmer to require that such errors be diagnosed.
Suppose we've got this class definition:
struct X { void f(); static int n; };
I think I can deduce from the existing standard that the following member definitions are ill-formed:
static void X::f() { } static int X::n;
To come to that conclusion, however, I have to put together several things in different parts of the standard. I would have expected to find an explicit statement of this somewhere; in particular, I would have expected to find it in 7.1.1 [dcl.stc]. I don't see it there, or anywhere.
Gabriel Dos Reis: Or in 3.5 [basic.link] which is about linkage. I would have expected that paragraph to say that that members of class types have external linkage when the enclosing class has an external linkage. Otherwise 3.5 [basic.link] paragraph 8:
Names not covered by these rules have no linkage.
might imply that such members do not have linkage.
Notes from the April, 2005 meeting:
The question about the linkage of class members is already covered by 3.5 [basic.link] paragraph 5.
The phrase “top-level cv-qualifier” is used numerous times in the Standard, but it is not defined. The phrase could be misunderstood to indicate that the const in something like const T& is at the “top level,” because where it appears is the highest level at which it is permitted: T& const is ill-formed.
7.1.6.3 [dcl.type.elab] paragraph 1 seems to impose an ordering constraint on the elements of friend class declarations. However, the general rule is that declaration specifiers can appear in any order. Should
class C friend;be well-formed?
There is disagreement among implementations as to when an enumeration type is complete. For example,
enum E { e = E() };
is rejected by some and accepted by another. The Standard does not appear to resolve this question definitively.
According to 7.3 [basic.namespace] paragraph 1,
The name of a namespace can be used to access entities declared in that namespace; that is, the members of the namespace.
implying that all declarations in a namespace, including definitions of members of nested namespaces, explicit instantiations, and explicit specializations, introduce members of the containing namespace. 7.3.1.2 [namespace.memdef] paragraph 3 clarifies the intent somewhat:
Every name first declared in a namespace is a member of that namespace.
However, current changes to clarify the behavior of deleted functions (which must be deleted on their “first declaration”) state that an explicit specialization of a function template is its first declaration.
According to 7.3.1.2 [namespace.memdef] paragraphs 1 and 2 read,
Members (including explicit specializations of templates (14.7.3 [temp.expl.spec])) of a namespace can be defined within that namespace.
Members of a named namespace can also be defined outside that namespace by explicit qualification (3.4.3.2 [namespace.qual]) of the name being defined, provided that the entity being defined was already declared in the namespace and the definition appears after the point of declaration in a namespace that encloses the declaration's namespace.
It is not clear what these specifications mean for the following pair of examples:
namespace N { struct A; } using N::A; struct A { };
Although this does not satisfy the “by explicit qualification” requirement, it is accepted by major implementations.
struct S; namespace A { using ::S; struct S { }; }
Is this a definition “within that namespace,” or should that wording be interpreted as “directly within” the namespace?
Section 7.3.3 [namespace.udecl] paragraph 8 says:
A using-declaration is a declaration and can therefore be used repeatedly where (and only where) multiple declarations are allowed.It contains the following example:
namespace A { int i; } namespace A1 { using A::i; using A::i; // OK: double declaration } void f() { using A::i; using A::i; // error: double declaration }However, if "using A::i;" is really a declaration, and not a definition, it is far from clear that repeating it should be an error in either context. Consider:
namespace A { int i; void g(); } void f() { using A::g; using A::g; }Surely the definition of f should be analogous to
void f() { void g(); void g(); }which is well-formed because "void g();" is a declaration and not a definition.
Indeed, if the double using-declaration for A::i is prohibited in f, why should it be allowed in namespace A1?
Proposed Resolution (04/99): Change the comment "// error: double declaration" to "// OK: double declaration". (This should be reviewed against existing practice.)
Notes from 04/00 meeting:
The core language working group was unable to come to consensus over what kind of declaration a using-declaration should emulate. In a straw poll, 7 members favored allowing using-declarations wherever a non-definition declaration could appear, while 4 preferred to allow multiple using-declarations only in namespace scope (the rationale being that the permission for multiple using-declarations is primarily to support its use in multiple header files, which are seldom included anywhere other than namespace scope). John Spicer pointed out that friend declarations can appear multiple times in class scope and asked if using-declarations would have the same property under the "like a declaration" resolution.
As a result of the lack of agreement, the issue was returned to "open" status.
See also issues 56, 85, and 138..
Additional notes (January, 2005):
Some related issues have been raised concerning the following example (modified from a C++ validation suite test):
struct A { int i; static int j; }; struct B : A { }; struct C : A { }; struct D : virtual B, virtual C { using B::i; using C::i; using B::j; using C::j; };
Currently, it appears that the using-declarations of i are ill-formed, on the basis of 7.3.3 [namespace.udecl] paragraph 10:
Since a using-declaration is a declaration, the restrictions on declarations of the same name in the same declarative region (3.3 [basic.scope]) also apply to using-declarations.
Because the using-declarations of i refer to different objects, declaring them in the same scope is not permitted under 3.3 [basic.scope]. It might, however, be preferable to treat this case as many other ambiguities are: allow the declaration but make the program ill-formed if a name reference resolves to the ambiguous declarations.
The status of the using-declarations of j, however, is less clear. They both declare the same entity and thus do not violate the rules of 3.3 [basic.scope]. This might (or might not) violate the restrictions of 9.2 [class.mem] paragraph 1:
Except when used to declare friends (11.4 [class.friend]) or to introduce the name of a member of a base class into a derived class (7.3.3 [namespace.udecl], 11.3 [class.access.dcl]), member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class. A member shall not be declared twice in the member-specification, except that a nested class or member class template can be declared and then later defined.
Do the using-declarations of j repeatedly declare the same member? Or is the preceding sentence an indication that a using-declaration is not a declaration of a member?
The following came up recently on comp.lang.c++.moderated (edited for brevity):
namespace N1 { template<typename T> void f( T* x ) { // ... other stuff ... delete x; } } namespace N2 { using N1::f; template<> void f<int>( int* ); // A: ill-formed class Test { ~Test() { } friend void f<>( Test* x ); // B: ill-formed? }; }
I strongly suspect, but don't have standardese to prove, that the friend declaration in line B is ill-formed. Can someone show me the text that allows or disallows line B?
Here's my reasoning: Writing "using" to pull the name into namespace N2 merely allows code in N2 to use the name in a call without qualification (per 7.3.3 [namespace.udecl]). But just as declaring a specialization must be done in the namespace where the template really lives (hence line A is ill-formed), I suspect that declaring a specialization as a friend must likewise be done using the original namespace name, not obliquely through a "using". I see nothing in 7.3.3 [namespace.udecl] that would permit this use. Is there?
Andrey Tarasevich: 14.5.4 [temp.friend] paragraph 2 seems to get pretty close: "A friend declaration that is not a template declaration and in which the name of the friend is an unqualified 'template-id' shall refer to a specialization of a function template declared in the nearest enclosing namespace scope".
Herb Sutter: OK, thanks. Then the question in this is the word "declared" -- in particular, we already know we cannot declare a specialization of a template in any other namespace but the original one.
John Spicer: This seems like a simple question, but it isn't.
First of all, I don't think the standard comments on this usage one way or the other.
A similar example using a namespace qualified name is ill-formed based on 8.3 [dcl.meaning] paragraph 1:
namespace N1 { void f(); } namespace N2 { using N1::f; class A { friend void N2::f(); }; }
Core issue 138 deals with this example:
void foo(); namespace A{ using ::foo; class X{ friend void foo(); }; }
The proposed resolution (not yet approved) for issue 138 is that the friend declares a new foo that conflicts with the using-declaration and results in an error.
Your example is different than this though because the presence of the explicit argument list means that this is not declaring a new f but is instead using a previously declared f.
One reservation I have about allowing the example is the desire to have consistent rules for all of the "declaration like" uses of template functions. Issue 275 (in DR status) addresses the issue of unqualified names in explicit instantiation and explicit specialization declarations. It requires that such declarations refer to templates from the namespace containing the explicit instantiation or explicit specialization. I believe this rule is necessary for those directives but is not really required for friend declarations -- but there is the consistency issue.
Notes from April 2003 meeting:
This is related to issue 138. John Spicer is supposed to update his paper on this topic. This is a new case not covered in that paper. We agreed that the B line should be allowed.
The Standard does not appear to specify what happens for code like the following:
namespace one { template<typename T> void fun(T); } using one::fun; template<typename T> void fun(T);
7.3.3 [namespace.udecl] paragraph 13 does not appear to apply because it deals only with functions, not function templates:
If a function declaration in namespace scope or block scope has the same name and the same parameter types as a function introduced by a using-declaration, and the declarations do not declare the same function, the program is ill-formed.
John Spicer: For function templates I believe the rule should be that if they have the same function type (parameter types and return type) and have identical template parameter lists, the program is ill-formed.
7.3.3 [namespace.udecl] paragraph 20 says,
If a using-declaration uses the keyword typename and specifies a dependent name (14.6.2 [temp.dep]), the name introduced by the using-declaration is treated as a typedef-name (7.1.3 [dcl.typedef]).
This wording does not address use of typename in a using-declaration with a non-dependent name; the primary specification of the typename keyword in 14.6 [temp.res] does not appear to describe this case, either.
The status of an example like the following is unclear in the current Standard:
struct B { void f(); }; template<typename T> struct S: T { using B::f; };
7.3.3 [namespace.udecl] does not deal explicitly with dependent base classes, but does say in paragraph 3,
In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined. If such a using-declaration names a constructor, the nested-name-specier shall name a direct base class of the class being defined; otherwise it introduces the set of declarations found by member name lookup (10.2 [class.member.lookup], 3.4.3.1 [class.qual]).
In the definition of S, B::f is not a dependent name but resolves to an apparently unrelated class. However, because S could be instantiated as S<B>, presumably 14.6 [temp.res] paragraph 8 would apply:
No diagnostic shall be issued for a template definition for which a valid specialization can be generated.
Note also the resolution of issue 515, which permitted a similar use of a dependent base class named with a non-dependent name.
It is not clear whether some of the wording in 7.5 [dcl.link] that applies only to function types and names ought also to apply to object names. In particular, paragraph 3 says,
Every implementation shall provide for linkage to functions written in the C programming language, "C", and linkage to C++ functions, "C++".
Nothing is said about variable names, apparently meaning that implementations need not provide C (or even C++!) linkage for variable names. Also, paragraph 5 says,
Except for functions with C++ linkage, a function declaration without a linkage specification shall not precede the first linkage specification for that function. A function can be declared without a linkage specification after an explicit linkage specification has been seen; the linkage explicitly specified in the earlier declaration is not affected by such a function declaration.
There doesn't seem to be a good reason for these provisions not to apply to variable names, as well.
There are some kinds of declarations that can appear in a derived class and hide names from a base class, but for which the syntax does not permit a [[hiding]] attribute. For example:
struct B1 { int N; int M; }; struct B2 { int M; }; struct [[base_check]] D: B1, B2 { enum { N }; // hides B1::N but cannot take an attribute using B1::M; // hides B2::M but cannot take an attribute };
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.
The intent appears to be that the following example is well-formed, even though D::f(int) hides B2::f():
struct B1 { void f(); }; struct B2 { void f(); }; struct[[base_check]] D: B1, B2 { using B1::f; void f(int); };
However, this is not reflected in the current wording.
The facility for checking hiding and overriding of base class members should not use the attribute syntax but should use keywords instead. Concerns about breaking code by changing current identifiers into keywords can be addressed by using contextual keywords, i.e., by putting the keywords into syntactic locations where identifiers cannot appear and thus continuing to allow their use as ordinary identifiers in other contexts.
Notes from the August, 2010 meeting:
CWG expressed a preference for non-contextual keywords for these features.
Split off from issue 453.
It is in general not possible to determine at compile time whether a reference is used before it is initialized. Nevertheless, there is some sentiment to require a diagnostic in the obvious cases that can be detected at compile time, such as the name of a reference appearing in its own initializer. The resolution of issue 453 originally made such uses ill-formed, but the CWG decided that this question should be a separate issue.
Rationale (October, 2005):
The CWG felt that this error was not likely to arise very often in practice. Implementations can warn about such constructs, and the resolution for issue 453 makes executing such code undefined behavior; that seemed to address the situation adequately.
Note (February, 2006):
Recent discussions have suggested that undefined behavior be reduced. One possibility (broadening the scope of this issue to include object declarations as well as references) was to require a diagnostic if the initializer uses the value, but not just the address, of the object or reference being declared:
int i = i; // Ill-formed, diagnostic required void* p = &p; // Okay
8.3.5 [dcl.fct]/2 restricts the use of void as parameter type, but does not mention CV qualified versions. Since void f(volatile void) isn't a callable function anyway, 8.3.5 [dcl.fct] should also ban cv-qualified versions. (BTW, this follows C)
Suggested resolution:
A possible resolution would be to add (cv-qualified) before void in
The parameter list (void) is equivalent to the empty parameter list. Except for this special case, (cv-qualified) void shall not be a parameter type (though types derived from void, such as void*, can).
The current wording of 8.3.5 [dcl.fct] paragraph 6 encompasses more than it should:
If the type of a parameter includes a type of the form “pointer to array of unknown bound of T” or “reference to array of unknown bound of T,” the program is ill-formed. [Footnote: This excludes parameters of type “ptr-arr-seq T2” where T2 is “pointer to array of unknown bound of T” and where ptr-arr-seq means any sequence of “pointer to” and “array of” derived declarator types. This exclusion applies to the parameters of the function, and if a parameter is a pointer to function or pointer to member function then to its parameters also, etc. —end footnote]
The normative wording (contrary to the intention expressed in the footnote) excludes declarations like
template<class T> struct S {}; void f(S<int (*)[]>);
and
struct S {}; void f(int(*S::*)[]);
but not
struct S {}; void f(int(S::*)[]);
8.3.5 [dcl.fct] paragraph 2 says,
The parameter list (void) is equivalent to the empty parameter list.
This special case is intended for C compatibility, but C99 describes it differently (6.7.5.3 paragraph 10):
The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.
The C99 formulation allows typedefs for void, while C++ (and C90) accept only the keyword itself in this role. Should the C99 approach be adopted?
Notes from the October, 2006 meeting:
The CWG did not take a formal position on this issue; however, there was some concern expressed over the treatment of function templates and member functions of class templates if the C++ rule were changed: for a template parameter T, would a function taking a single parameter of type T become a no-parameter function if it were instantiated with T = void?
According to 8.3.5 [dcl.fct] paragraph 5, top-level cv-qualifiers on parameter types are deleted when determining the function type. It is not clear how or whether this adjustment should be applied to parameters of function templates when the parameter has a dependent type, however. For example:
template<class T> struct A { typedef A arr[3]; }; template<class T> void f(const typename A<T>::arr) { } // #1 template void f<int>(const A<int>::arr); template <class T> struct B { void g(T); }; template <class T> void B<T>::g(const T) { } // #2
If the const in #1 is dropped, f<int> has a parameter type of A* rather than the const A* specified in the explicit instantiation. If the const in #2 is not dropped, we fail to match the definition of B::g to its declaration.
The standard is not precise enough about when the default arguments of member functions are parsed. This leads to confusion over whether certain constructs are legal or not, and the validity of certain compiler implementation algorithms.
8.3.6 [dcl.fct.default] paragraph 5 says "names in the expression are bound, and the semantic constraints are checked, at the point where the default argument expression appears"
However, further on at paragraph 9 in the same section there is an example, where the salient parts are
int b; class X { int mem2 (int i = b); // OK use X::b static int b; };which appears to contradict the former constraint. At the point the default argument expression appears in the definition of X, X::b has not been declared, so one would expect ::b to be bound. This of course appears to violate 3.3.7 [basic.scope.class] paragraph 1(2) "A name N used in a class S shall refer to the same declaration in its context and when reevaluated in the complete scope of S. No diagnostic is required."
Furthermore 3.3.7 [basic.scope.class] paragraph 1(1) gives the scope of names declared in class to "consist not only of the declarative region following the name's declarator, but also of .. default arguments ...". Thus implying that X::b is in scope in the default argument of X::mem2 previously.
That previous paragraph hints at an implementation technique of saving the token stream of a default argument expression and parsing it at the end of the class definition (much like the bodies of functions defined in the class). This is a technique employed by GCC and, from its behaviour, in the EDG front end. The standard leaves two things unspecified. Firstly, is a default argument expression permitted to call a static member function declared later in the class in such a way as to require evaluation of that function's default arguments? I.e. is the following well formed?
class A { static int Foo (int i = Baz ()); static int Baz (int i = Bar ()); static int Bar (int i = 5); };If that is well formed, at what point does the non-sensicalness of
class B { static int Foo (int i = Baz ()); static int Baz (int i = Foo()); };become detected? Is it when B is complete? Is it when B::Foo or B::Baz is called in such a way to require default argument expansion? Or is no diagnostic required?
The other problem is with collecting the tokens that form the default argument expression. Default arguments which contain template-ids with more than one parameter present a difficulty in determining when the default argument finishes. Consider,
template <int A, typename B> struct T { static int i;}; class C { int Foo (int i = T<1, int>::i); };The default argument contains a non-parenthesized comma. Is it required that this comma is seen as part of the default argument expression and not the beginning of another of argument declaration? To accept this as part of the default argument would require name lookup of T (to determine that the '<' was part of a template argument list and not a less-than operator) before C is complete. Furthermore, the more pathological
class D { int Foo (int i = T<1, int>::i); template <int A, typename B> struct T {static int i;}; };would be very hard to accept. Even though T is declared after Foo, T is in scope within Foo's default argument expression.
Suggested resolution:
Append the following text to 8.3.6 [dcl.fct.default] paragraph 8.
The default argument expression of a member function declared in the class definition consists of the sequence of tokens up until the next non-parenthesized, non-bracketed comma or close parenthesis. Furthermore such default argument expressions shall not require evaluation of a default argument of a function declared later in the class.
This would make the above A, B, C and D ill formed and is in line with the existing compiler practice that I am aware of.
Notes from the October, 2005 meeting:
The CWG agreed that the first example (A) is currently well-formed and that it is not unreasonable to expect implementations to handle it by processing default arguments recursively.
Additional notes, May, 2009:
Presumably the following is ill-formed:
int f(int = f());
However, it is not clear what in the Standard makes it so. Perhaps there needs to be a statement to the effect that a default argument only becomes usable after the complete declarator of which it is a part.
Is this program well-formed?
struct S { static int f2(int = f1()); // OK? static int f1(int = 2); }; int main() { return S::f2(); }
A class member function can in general refer to class members that are declared lexically later. But what about referring to default arguments of member functions that haven't yet been declared?
It seems to me that if f2 can refer to f1, it can also refer to the default argument of f1, but at least one compiler disagrees.
Paragraph 9 of 8.5 [dcl.init] says:
If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for an object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.
What if a const POD object has no non-static data members? This wording requires an empty initializer for such cases:
struct Z { // no data members operator int() const { return 0; } }; void f() { const Z z1; // ill-formed: no initializer const Z z2 = { }; // well-formed }
Similar comments apply to a non-POD const object, all of whose non-static data members and base class subobjects have default constructors. Why should the class of such an object be required to have a user-declared default constructor?
(See also issue 78.)
According to 8.5 [dcl.init] paragraph 5,
To zero-initialize an object of type T means:
...
if T is a reference type, no initialization is performed.
However, a reference is not an object, so this makes no sense.
In this example:
struct A {}; struct B: A { B(int); B(B&); B(A); }; void foo(B); void bar() { foo(0); }
we are copy-initializing a B from 0. So by 13.3.1.4 [over.match.copy] we consider all the converting constructors of B, and choose B(int) to create a B. Then, by 8.5 [dcl.init] paragraph 15, we direct-initialize the parameter from that temporary B. By 13.3.1.3 [over.match.ctor] we consider all constructors. The copy constructor cannot be called with a temporary, but B(A) is callable.
As far as I can tell, the Standard says that this example is well-formed, and calls B(A). EDG and G++ have rejected this example with a message about the copy constructor not being callable, but I have been unsuccessful in finding anything in the Standard that says that we only consider the copy constructor in the second step of copy-initialization. I wouldn't mind such a rule, but it doesn't seem to be there. And implementing issue 391 causes G++ to start accepting the example.
This question came up before in a GCC bug report; in the discussion of that bug Nathan Sidwell said that some EDG folks explained to him why the testcase is ill-formed, but unfortunately didn't provide that explanation in the bug report.
I think the resolution of issue 391 makes this example well-formed; it was previously ill-formed because in order to bind the temporary B(0) to the argument of A(const A&) we needed to make another temporary B, and that's what made the example ill-formed. If we want this example to stay ill-formed, we need to change something else.
Steve Adamczyk:
I tracked down my response to Nathan at the time, and it related to my paper N1232 (on the auto_ptr problem). The change that came out of that paper is in 13.3.3.1 [over.best.ics] paragraph 4:
However, when considering the argument of a user-defined conversion function that is a candidate by 13.3.1.3 [over.match.ctor] when invoked for the copying of the temporary in the second step of a class copy-initialization, or by 13.3.1.4 [over.match.copy], 13.3.1.5 [over.match.conv], or 13.3.1.6 [over.match.ref] in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed.
This is intended to prevent use of more than one implicit user- defined conversion in an initialization.
I told Nathan B(A) can't be called because its argument would require yet another user-defined conversion, but I was wrong. I saw the conversion from B to A and immediately thought “user-defined,” but in fact because B is a derived class of A the conversion according to 13.3.3.1 [over.best.ics] paragraph 6 is a derived-to-base Conversion (even though it will be implemented by calling a copy constructor).
So I agree with you: with the analysis above and the change for issue 391 this example is well-formed. We should discuss whether we want to make a change to keep it ill-formed.
8.5 [dcl.init] paragraph 7 only describes how to initialize objects:
To value-initialize an object of type T means:
However, 5.2.3 [expr.type.conv] paragraph 2 calls for value-initializing prvalues, which in the case of scalar types are not objects:
The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates a prvalue of the specified type, which is value-initialized (8.5 [dcl.init]; no initialization is done for the void() case).
The ordering imposed by 8.5.1 [dcl.init.aggr] paragraph 17 applies only to “the full-expressions in an initializer-clause” (i.e., what follows an = in an aggregate initializer); this leaves unspecified the order in which the expressions in an initializer-list (the term used by the braced-init-list form of initializer, with no =) are evaluated.
The current wording of the WP allows aggregate initialization of parameters in function calls. For example, 13.3.3.1.5 [over.ics.list] paragraph 4 reads:
Otherwise, if the parameter has an aggregate type which can be initialized from the initializer list according to the rules for aggregate initialization (8.5.1 [dcl.init.aggr]), the implicit conversion sequence is a user-defined conversion sequence. [Example:
struct A { int m1; double m2; }; void f(A); f( {'a', 'b'} ); // OK: f(A(int,double)) user-defined conversion f( {1.0} ); // error: narrowing—end example]
The rules for aggregate initialization in 8.5.1 [dcl.init.aggr] paragraph 11 allow braces to be elided in the initializer
In a declaration of the form
T x = { a };
It is not clear whether this phrasing should be interpreted as allowing brace elision only in a simple-declaration and thus not in a function argument or whether this restriction is inadvertent and should be removed.
Consider the example,
struct A { char c; }; void f (char d) { A a = { d + 1 }; }
This code is now ill-formed because of the narrowing conversion from the int result type of the addition, not because of any real narrowing. This seems like an embarrassment for C++0x. It would be better not to get an error about any arithmetic involving non-constant operands just because it might overflow with some values.
The current wording of the WP appears not to allow for list-initialization of a reference like the following:
int i; int& ir{i};
First, 8.5 [dcl.init] paragraph 16 bullet 1 reads,
If the initializer is a braced-init-list, the object is list-initialized (8.5.4).
A reference is not an object, so this does not appear to apply; however, the second bullet sends reference initialization off to 8.5.3 [dcl.init.ref], which does not cover braced-init-lists: paragraph 5 of that section deals only with initilizer expressions, and a braced-init-list is not an expression.
Assuming that the use of “object” in the first bullet is just an oversight, 8.5.4 [dcl.init.list] also does not cover the case of a reference to a scalar type whose initalizer is a braced-init-list with a single element. Bullet 7 of paragraph 3 reads,
Otherwise, if the initializer list has a single element, the object is initialized from that element
and would cover this case except that, again, a reference is not an object. As a result, such an initialization would end up in the last bullet and consequently be ill-formed.
Presumably all that is needed is to add “or reference” to the appropriate bullets of 8.5 [dcl.init] paragraph 16 and 8.5.4 [dcl.init.list] paragraph 3.
A POD-struct is not permitted to have a user-declared copy assignment operator (9 [class] paragraph 4). However, a template assignment operator is not considered a copy assignment operator, even though its specializations can be selected by overload resolution for performing copy operations (12.8 [class.copy] paragraph 9 and especially footnote 114). Consequently, X in the following code is a POD, notwithstanding the fact that copy assignment (for a non-const operand) is a member function call rather than a bitwise copy:
struct X {
template<typename T> const X& operator=(T&);
};
void f() {
X x1, x2;
x1 = x2; // calls X::operator=<X>(X&)
}
Is this intentional?
Non-static data member initializers should not be part of C++0x unless they have implementation experience.
Notes from the August, 2010 meeting:
The C++/CLI dialect has a very similar feature that has been implemented.
It's not clear how lookup of a non-dependent qualified name should be handled in a non-static member function of a class template. For example,
struct A { int f(int); static int f(double); }; struct B {}; template<typename T> struct C : T { void g() { A::f(0); } };
The call to A::f inside C::g() appears non-dependent, so one might expect that it would be bound at template definition time to A::f(double). However, the resolution for issue 515 changed 9.3.1 [class.mfct.non-static] paragraph 3 to transform an id-expression to a member access expression using (*this). if lookup resolves the name to a non-static member of any class, making the reference dependent. The result is that if C is instantiated with A, A::f(int) is called; if C is instantiated with B, the call is ill-formed (the call is transformed to (*this).A::f(0), and there is no A subobject in C<B>). Both these results seem unintuitive.
(See also issue 1017.)
The following innocuous-appearing code is currently ill-formed:
struct A { int a; }; struct B { void f() { decltype(A::a) i; // ill-formed } };
The reason is that, according to 9.3.1 [class.mfct.non-static] paragraph 3, the reference to A::a is transformed into (*this).A::a, and there is no A subobject of B. It would seem reasonable to suppress this transformation in unevaluated operands, where a reference to a non-static member is permitted without an object expression.
(See also issue 1005.)
Move semantics for *this should not be part of C++0x unless they have implementation experience.
According to 9.4.2 [class.static.data] paragraph 3,
If a static data member is of const literal type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [Note: In both these cases, the member may appear in constant expressions. —end note]
However, 5.19 [expr.const] paragraph 2 bullet 7 allows only integral and enumeration types in constant expressions for the const case; the other types require constexpr to be considered constant expressions.
There doesn't seem to be a prohibition in 9.5 [class.union] against a declaration like
union { int : 0; } x;Should that be valid? If so, 8.5 [dcl.init] paragraph 5 third bullet, which deals with default-initialization of unions, should say that no initialization is done if there are no data members.
What about:
union { } x; static union { };If the first example is well-formed, should either or both of these cases be well-formed as well?
(See also the resolution for issue 151.)
Notes from 10/00 meeting: The resolution to issue 178, which was accepted as a DR, addresses the first point above (default initialization). The other questions have not yet been decided, however.
Is the signedness of x in the following example implementation-defined?
template <typename T> struct A { T x : 7; }; template struct A<long>;
A similar example could be created with a typedef.
Lawrence Crowl: According to 9.6 [class.bit] paragraph 3,
It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int or long bit-field is signed or unsigned.
This clause is conspicuously silent on typedefs and template parameters.
Clark Nelson: At least in C, the intention is that the presence or absence of this redundant keyword is supposed to be remembered through typedef declarations. I don't remember discussing it in C++, but I would certainly hope that we don't want to do something different. And presumably, we would want template type parameters to work the same way.
So going back to the original example, in an instantiation of A<long>, the signedness of the bit-field is implementation-defined, but in an instantiation of A<signed long>, the bit-field is definitely signed.
Peter Dimov: How can this work? Aren't A<long> and A<signed long> the same type?
(See also issue 739.)9.6 [class.bit] paragraph 3 says,
It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int or long bit-field is signed or unsigned.
The implications of this permission for an implementation that chooses to treat plain bit-fields as unsigned are not clear. Does this mean that the type of such a bit-field is adjusted to the unsigned variant or simply that sign-extension is not performed when the value is fetched? C99 is explicit in specifying the former (6.7.2 paragraph 5: “for bit-fields, it is implementation-defined whether the specifier int designates the same type as signed int or the same type as unsigned int”), while C90 takes the latter approach (6.5.2.1: “Whether the high-order bit position of a (possibly qualified) 'plain' int bit-field is treated as a sign bit is implementation-defined”).
(See also issue 675 and issue 741.)Additional note, May, 2009:
As an example of the implications of this question, consider the following declaration:
struct S { int i: 2; signed int si: 2; unsigned int ui: 2; } s;
Is it implementation-defined which expression, cond?s.i:s.si or cond?s.i:s.ui, is an lvalue (the lvalueness of the result depends on the second and third operands having the same type, per 5.16 [expr.cond] paragraph 4)?
The term "ambiguous base class" doesn't seem to be actually defined anywhere. 10.2 [class.member.lookup] paragraph 7 seems like the place to do it.
According to 10.4 [class.abstract] paragraph 6,
Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3 [class.virtual]) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.
This prohibition is unnecessarily restrictive. It should not apply to cases in which the pure virtual function has been defined.
Currently the "pure" specifier for a virtual member function has two meanings that need not be related:
The prohibition of virtual calls to pure virtual functions arises from the first meaning and unnecessarily penalizes those who only need the second.
For example, consider a scenario such as the following. A class B is defined containing a (non-pure) virtual function f that provides some initialization and is thus called from the base class constructor. As time passes, a number of classes are derived from B and it is noticed that each needs to override f, so it is decided to make B::f pure to enforce this convention while still leaving the original definition of B::f to perform its needed initialization. However, the act of making B::f pure means that every reference to f that might occur during the execution of one of B's constructors must be tracked down and edited to be a qualified reference to B::f. This process is tedious and error-prone: needed edits might be overlooked, and calls that actually should be virtual when the containing function is called other than during construction/destruction might be incorrectly changed.
Suggested resolution: Allow virtual calls to pure virtual functions if the function has been defined.
Referring to a private member of a class, 11 [class.access] paragraph 1 says,
its name can be used only by members and friends of the class in which it is declared.
That wording does not appear to reflect the intent of access control, however. Consider the following:
struct S {
void f(int);
private:
void f(double);
};
void g(S* sp) {
sp->f(2); // Ill-formed?
}
The statement from 11 [class.access] paragraph 1 says that the name f can be used only by members and friends of S. Function g is neither, and it clearly contains a use of the name f. That appears to make it ill-formed, in spite of the fact that overload resolution will select the public member.
A related question is whether the use of the term “name” in the description of the effect of access control means that it does not apply to constructors and destructors, which do not have names.
Mike Miller: The phrase “its name can be used” should be understood as “it can be referred to by name.” Paragraph 4, among other places, makes it clear that access control is applied after overload resolution. The “name” phrasing is there to indicate that access control does not apply where the name is not used (in a call via a pointer, for example).
I have heard a claim that the following code is valid, but I don't see why.
struct A { int foo (); }; struct B: A { private: using A::foo; }; int main () { return B ().foo (); }
It seems to me that the using declaration in B should hide the public foo in A. Then the call to B::foo should fail because B::foo is not accessible in main.
Am I missing something?
Steve Adamczyk: This is similar to the last example in 11.2 [class.access.base]. In prose, the rule is that if you have access to cast to a base class and you have access to the member in the base class, you are given access in the derived class. In this case, A is a public base class of B and foo is public in A, so you can access foo through a B object. The actual permission for this is in the fourth bullet in 11.2 [class.access.base] paragraph 4.
The wording changes for issue 9 make this clearer, but I believe even without them this example could be discerned to be valid.
See my paper J16/96-0034, WG21/N0852 on this topic.
Steve Clamage: But a using-declaration is a declaration (7.3.3 [namespace.udecl]). Compare with
struct B : A { private: int foo(); };
In this case, the call would certainly be invalid, even though your argument about casting B to an A would make it OK. Your argument basically says that an access adjustment to make something less accessible has no effect. That also doesn't sound right.
Steve Adamczyk: I agree that is strange. I do think that's what 11.2 [class.access.base] says, but perhaps that's not what we want it to say.
Consider the following example:
struct B { void f(){} }; class N : protected B { }; struct P: N { friend int main(); }; int main() { N n; B& b = n; // R b.f(); }
This code is rendered well-formed by bullet 3 of 11.2 [class.access.base] paragraph 4, which says that a base class B of N is accessible at R if
R occurs in a member or friend of a class P derived from N, and an invented public member of B would be a private or protected member of P
This provision circumvents the additional restrictions on access to protected members found in 11.5 [class.protected] — main() could not call B::f() directly because the reference is not via an object of the class through which access is obtained. What is the purpose of this rule?
With the change from a scope-based to an entity-based definition of friendship (see issues 372 and 580), it could well make sense to grant friendship to enumerations and variables, for example:
enum E: int; class C { static const int i = 5; // Private friend E; friend int x; }; enum E { e = C::i; }; // OK: E is a friend int x = C::i; // OK: x is a friend
According to the current wording of 11.4 [class.friend] paragraph 3, the friend declaration of E is well-formed but ignored, while the friend declaration of x is ill-formed.
Although it is not possible to specify a constructor's template arguments in a constructor invocation (because the constructor has no name but is invoked by use of the constructor's class's name), it is possible to “name” the constructor in declarative contexts: per 3.4.3.1 [class.qual] paragraph 2,
In a lookup in which the constructor is an acceptable lookup result, if the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (clause 9 [class]), the name is instead considered to name the constructor of class C... Such a constructor name shall be used only in the declarator-id of a declaration that names a constructor.
Should it therefore be possible to specify template-arguments for a templated constructor in an explicit instantiation or specialization? For example,
template <int dim> struct T {}; struct X { template <int dim> X (T<dim> &) {}; }; template X::X<> (T<2> &);
If so, that should be clarified in the text. In particular, 12.1 [class.ctor] paragraph 1 says,
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.
This certainly sounds as if the parameter list must immediately follow the class name, with no allowance for a template argument list.
It would be worthwhile in any event to revise this wording to utilize the “considered to name” approach of 3.4.3.1 [class.qual]; as it stands, this wording sounds as if the following would be acceptable:
struct S {
S();
};
S() { } // qualified-id not required?
Notes from the October, 2006 meeting:
It was observed that explicitly specifying the template arguments in a constructor declaration is never actually necessary because the arguments are, by definition, all deducible and can thus be omitted.
Note that destructors suffer from similar problems as those of constructors dealt with in issue 194 and in 263 (constructors as friends). Also, the wording in 12.4 [class.dtor], paragraph 1 does not permit a destructor to be defined outside of the memberlist.
Change 12.4 [class.dtor], paragraph 1 from
...A special declarator syntax using an optional function-specifier (7.1.2 [dcl.fct.spec]) followed by ~ followed by the destructor's class name followed by an empty parameter list is used to declare the destructor in a class definition. In such a declaration, the ~ followed by the destructor's class name can be enclosed in optional parentheses; such parentheses are ignored....
to
...A special declarator syntax using an optional sequence of function-specifiers (7.1.2 [dcl.fct.spec]), an optional friend keyword, an optional sequence of function-specifiers (7.1.2 [dcl.fct.spec]) followed by an optional :: scope-resolution-operator followed by an optional nested-name-specifier followed by ~ followed by the destructor's class name followed by an empty parameter list is used to declare the destructor. The optional nested-name-specifier shall not be specified in the declaration of a destructor within the member-list of the class of which the destructor is a member. In such a declaration, the optional :: scope-resolution-operator followed by an optional nested-name-specifier followed by ~ followed by the destructor's class name can be enclosed in optional parentheses; such parentheses are ignored....
A user-declared destructor that does not supply an exception specification should be considered as if declared noexcept(true) rather than noexcept(false).
Paragraph 4 of 12.5 [class.free] speaks of looking up a deallocation function. While it is an error if a placement deallocation function alone is found by this lookup, there seems to be an assumption that a placement deallocation function and a usual deallocation function can both be declared in a given class scope without creating an ambiguity. The normal mechanism by which ambiguity is avoided when functions of the same name are declared in the same scope is overload resolution; however, there is no mention of overload resolution in the description of the lookup. In fact, there appears to be nothing in the current wording that handles this case. That is, the following example appears to be ill-formed, according to the current wording:
struct S { void operator delete(void*); void operator delete(void*, int); }; void f(S* p) { delete p; // ill-formed: ambiguous operator delete }
Suggested resolution (Mike Miller, March 2002):
I think you might get the right effect by replacing the last sentence of 12.5 [class.free] paragraph 4 with something like:
After removing all placement deallocation functions, the result of the lookup shall contain an unambiguous and accessible deallocation function.
In an example like,
struct Y {}; template <typename T> struct X : public virtual Y { }; template <typename T> class A : public X<T> { template <typename S> A (S) : S () { } }; template A<int>::A (Y);
Should S be found? (S is a dependent name, so if it resolves to a base class type in the instantiated template, it should satisfy the requirements.) All the compilers I tried allowed this example, but 12.6.2 [class.base.init] paragraph 2 says,
Names in a mem-initializer-id are looked up in the scope of the constructor’s class and, if not found in that scope, are looked up in the scope containing the constructor’s definition.
The name S is not declared in those scopes.
Mike Miller: Here's another example that is accepted by most/all compilers but not by the current wording:
namespace N { struct B { B(int); }; typedef B typedef_B; struct D: B { D(); }; } N::D::D(): typedef_B(0) { }
Except for the fact that the constructor function parameter names are ignored (see paragraph 7), what the compilers seem to be doing is essentially ordinary unqualified name lookup.
Notes from the October, 2009 meeting:
The eventual resolution of this issue should take into account the template parameter scope introduced by the resolution of issue 481.
References to non-static data members inside the body of a non-static member function (which includes the mem-initializers of a constructor definition) are implicitly transformed to member access expressions using (*this) (9.3.1 [class.mfct.non-static] paragraph 3). Although 5.1.1 [expr.prim.general] paragraph 3 permits use of this in a brace-or-equal-initializer for a non-static data member, 12.6.2 [class.base.init] does not give details about the value of this in that context, and there is no parallel to the transformation of member references into class member access expressions. This leaves use of non-static data members in this context underspecified.
[Picked up by evolution group at October 2002 meeting.]
(See also paper J16/99-0005 = WG21 N1182.)At the London meeting, 12.8 [class.copy] paragraph 15 was changed to limit the optimization described to only the following cases:
Can we find an appropriate description for the desired cases?
Rationale (04/99): The absence of this optimization does not constitute a defect in the Standard, although the proposed resolution in the paper should be considered when the Standard is revised.
Note (March, 2008):
The Evolution Working Group has accepted the intent of this issue and referred it to CWG for action (not for C++0x). See paper J16/07-0033 = WG21 N2173.
Notes from the June, 2008 meeting:
The CWG decided to take no action on this issue until an interested party produces a paper with analysis and a proposal.
Paper N2987 suggests that an implicitly-declared copy or move constructor should be explicit if the corresponding constructor of any of its subobjects is explicit. During the discussion at the October, 2009 meeting, the CWG deemed this a separable question from the major emphasis of that paper, and this issue was opened as a placeholder for that discussion.
Consider the following example:
int c; struct A { A() { ++c; } A(const A&) { ++c; } }; struct B { A a; B(const A& a): a(a) { } }; int main() { (B(A())); return c - 1; }
Here we would like to be able to avoid the copy and just construct the A() directly into the A subobject of B. But we can't, because it isn't allowed by 12.8 [class.copy] paragraph 34 bullet 3:
when a temporary class object that has not been bound to a reference (12.2 [class.temporary]) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
The part about not being bound to a reference was added for an unrelated reason by issue 185. If that resolution were recast to require that the temporary object is not accessed after the copy, rather than banning the reference binding, this optimization could be applied.
The similar example using pass by value is also not one of the allowed cases, which could be considered part of issue 6.
It seems odd to have an implicitly declared copy constructor (and the same for the copy assignment operator) if one of the subobjects does not have one. For example,
struct A { A(); A(A&&); }; struct B: A { }; B b; B b2(b); // error when implicitly defining B(B&), should not be declared
If we don't declare it in that case, we need to decide what happens if one base has only a move constructor and another has only a copy constructor.
12.8 [class.copy] paragraph 12 says that a defaulted move constructor is defined as deleted if the class has a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable. This seems more strict than is necessary; the subobject need not be trivially copyable, it should be enough for the selected constructor not to throw. In any case, the wording should be phrased in terms of the function selected by overload resolution rather than properties of the subobject type, and similarly for move assignment.
Moving to always doing overload resolution for determining exception specifications and implicit deletion creates some unfortunate cycles:
template<typename T> struct A { T t; }; template <typename T> struct B { typename T::U u; }; template <typename T> struct C { C(const T&); }; template <typename T> struct D { C<B<T> > v; }; struct E { typedef A<D<E> > U; }; extern A<D<E> > a; A<D<E> > a2(a);
If declaring the copy constructor for A<D<E>> is part of instantiating the class, then we need to do overload resolution on D<E>, and thus C<B<E>>. We consider C(const B<E>&), and therefore look to see if there's a conversion from C<B<E>> to B<E>, which instantiates B<E>, which fails because it has a field of type A<D<E>> which is already being instantiated.
Even if we wait until A<D<E>> is considered complete before finalizing the copy constructor declaration, declaring the copy constructor for B<E> will want to look at the copy constructor for A<D<E>>, so we still have the cycle.
I think that to avoid this cycle we need to short-circuit consideration of C(const T&) somehow. But I don't see how we can do that without breaking
struct F { F(F&); }; struct G; struct G2 { G2(const G&); }; struct G { G(G&&); G(const G2&); }; struct H: F, G { }; extern H h; H h2(h);
Here, since G's move constructor suppresses the implicit copy constructor, the defaulted H copy constructor calls G(const G2&) instead. If the move constructor did not suppress the implicit copy constructor, I believe the implicit copy constructor would always be viable, and therefore a better match than a constructor taking a reference to another type.
So perhaps the answer is to reconsider that suppression and then disqualify any constructor taking (a reference to) a type other than the constructor's class from consideration when looking up a subobject constructor in an implicitly defined constructor. (Or assignment operator, presumably.)
Another possibility would be that when we're looking for a conversion from C<B<E>> to B<E> we could somehow avoid considering, or even declaring, the B<E> copy constructor. But that seems a bit dodgy.
Inheriting constructors should not be part of C++0x unless they have implementation experience.
Consider the following example:
class B1 {}; typedef void (B1::*PB1) (); // memptr to B1 class B2 {}; typedef void (B2::*PB2) (); // memptr to B2 class D1 : public B1, public B2 {}; typedef void (D1::*PD) (); // memptr to D1 struct S { operator PB1(); // can be converted to PD } s; struct T { operator PB2(); // can be converted to PD } t; void foo() { s == t; // Is this an error? }
According to 13.6 [over.built] paragraph 16, there is an operator== for PD (“For every pointer to member type...”), so why wouldn't it be used for this comparison?
Mike Miller: The problem, as I understand it, is that 13.3.1.2 [over.match.oper] paragraph 3, bullet 3, sub-bullet 3 is broader than it was intended to be. It says that candidate built-in operators must “accept operand types to which the given operand or operands can be converted according to 13.3.3.1 [over.best.ics].” 13.3.3.1.2 [over.ics.user] describes user-defined conversions as having a second standard conversion sequence, and there is nothing to restrict that second standard conversion sequence.
My initial thought on addressing this would be to say that user-defined conversion sequences whose second standard conversion sequence contains a pointer conversion or a pointer-to-member conversion are not considered when selecting built-in candidate operator functions. They would still be applicable after the hand-off to Clause 5 (e.g., in bringing the operands to their common type, 5.10 [expr.eq], or composite pointer type, 5.9 [expr.rel]), just not in constructing the list of built-in candidate operator functions.
I started to suggest restricting the second standard conversion sequence to conversions having Promotion or Exact Match rank, but that would exclude the Boolean conversions, which are needed for !, &&, and ||. (It would have also restricted the floating-integral conversions, though, which might be a good idea. They can't be used implicitly, I think, because there would be an ambiguity among all the promoted integral types; however, none of the compilers I tested even tried those conversions because the errors I got were not ambiguities but things like “floating point operands not allowed for %”.)
Bill Gibbons: I recall seeing this problem before, though possibly not in committee discussions. As written this rule makes the set of candidate functions dependent on what classes have been defined, including classes not otherwise required to have been defined in order for "==" to be meaningful. For templates this implies that the set is dependent on what templates have been instantiated, e.g.
template<class T> class U : public T { }; U<B1> u; // changes the set of candidate functions to include // operator==(U<B1>,U<B1>)?
There may be other places where the existence of a class definition, or worse, a template instantiation, changes the semantics of an otherwise valid program (e.g. pointer conversions?) but it seems like something to be avoided.
(See also issue 954.)
According to 13.3.3 [over.match.best] paragraph 4, the following program appears to be ill-formed:
void f(int, int=0); void f(int=0, int); void g() { f(); }
Though I do not expect this is the intent of this paragraph in the standard.
13.3.3 [over.match.best] paragraph 4:
If the best viable function resolves to a function for which multiple declarations were found, and if at least two of these declarations or the declarations they refer to in the case of using-declarations specify a default argument that made the function viable, the program is ill-formed. [Example:namespace A { extern "C" void f(int = 5); } namespace B { extern "C" void f(int = 5); } using A::f; using B::f; void use() { f(3); //OK, default argument was not used for viability f(); //Error: found default argument twice }end example]
It's not clear how overloading and partial ordering handle non-deduced pairs of corresponding arguments. For example:
template<typename T> struct A { typedef char* type; }; template<typename T> char* f1(T, typename A<T>::type); // #1 template<typename T> long* f1(T*, typename A<T>::type*); // #2 long* p1 = f1(p1, 0); // #3
I thought that #3 is ambiguous but different compilers disagree on that. Comeau C/C++ 4.3.3 (EDG 3.0.3) accepted the code, GCC 3.2 and BCC 5.5 selected #1 while VC7.1+ yields ambiguity.
I intuitively thought that the second pair should prevent overloading from triggering partial ordering since both arguments are non-deduced and has different types - (char*, char**). Just like in the following:
template<typename T> char* f2(T, char*); // #3 template<typename T> long* f2(T*, char**); // #4 long* p2 = f2(p2, 0); // #5
In this case all the compilers I checked found #5 to be ambiguous. The standard and DR 214 is not clear about how partial ordering handle such cases.
I think that overloading should not trigger partial ordering (in step 13.3.3 [over.match.best]/1/5) if some candidates have non-deduced pairs with different (specialized) types. In this stage the arguments are already adjusted so no need to mention it (i.e. array to pointer). In case that one of the arguments is non-deuced then partial ordering should only consider the type from the specialization:
template<typename T> struct B { typedef T type; }; template<typename T> char* f3(T, T); // #7 template<typename T> long* f3(T, typename B<T>::type); // #8 char* p3 = f3(p3, p3); // #9
According to my reasoning #9 should yield ambiguity since second pair is (T, long*). The second type (i.e. long*) was taken from the specialization candidate of #8. EDG and GCC accepted the code. VC and BCC found an ambiguity.
John Spicer: There may (or may not) be an issue concerning whether nondeduced contexts are handled properly in the partial ordering rules. In general, I think nondeduced contexts work, but we should walk through some examples to make sure we think they work properly.
Rani's description of the problem suggests that he believes that partial ordering is done on the specialized types. This is not correct. Partial ordering is done on the templates themselves, independent of type information from the specialization.
Notes from October 2004 meeting:
John Spicer will investigate further to see if any action is required.
(See also issue 885.)
The Standard is not clear whether the following example is well-formed or not:
struct S { static void f(int); static void f(double); }; S s; void (*pf)(int) = &s.f;
According to 5.2.5 [expr.ref] paragraph 4 bullet 3, you do function overload resolution to determine whether x.f is a static or non-static member function. 5.3.1 [expr.unary.op] paragraph 6 says that you can only take the address of an overloaded function in a context that determines the overload to be chosen, and the initialization of a function pointer is such a context (13.4 [over.over] paragraph 1). The problem is that 13.4 [over.over] is phrased in terms of “an overloaded function name,” and this is a member access expression, not a name.
There is variability among implementations as to whether this example is accepted; some accept it as written, some only if the & is omitted, and some reject it in both forms.
Consider the following example:
struct NullClass { template<typename T> operator T () { return 0 ; } }; int main() { NullClass n; n==5; // #1 return 0; }
The comparison at #1 is, according to the current Standard, ambiguous. According to 13.6 [over.built] paragraph 12, the candidates for operator==(L, R) include functions “for every pair of promoted arithmetic types,” so L could be either int or long, and the conversion operator template will provide an exact match for either.
Some implementations unambiguously choose the int candidate. Perhaps the overload resolution rules could be tweaked to prefer candidates in which L and R are the same type?
(See also issue 545.)
According to 14 [temp] paragraph 5,
Except that a function template can be overloaded either by (non-template) functions with the same name or by other function templates with the same name (14.8.3 [temp.over] ), a template name declared in namespace scope or in class scope shall be unique in that scope.3.3.10 [basic.scope.hiding] paragraph 2 agrees that only functions, not function templates, can hide a class name declared in the same scope:
A class name (9.1 [class.name] ) or enumeration name (7.2 [dcl.enum] ) can be hidden by the name of an object, function, or enumerator declared in the same scope.However, 3.3 [basic.scope] paragraph 4 treats functions and template functions together in this regard:
Given a set of declarations in a single declarative region, each of which specifies the same unqualified name,
- they shall all refer to the same entity, or all refer to functions and function templates; or
- exactly one declaration shall declare a class name or enumeration name that is not a typedef name and the other declarations shall all refer to the same object or enumerator, or all refer to functions and function templates; in this case the class name or enumeration name is hidden
John Spicer: You should be able to take an existing program and replace an existing function with a function template without breaking unrelated parts of the program. In addition, all of the compilers I tried allow this usage (EDG, Sun, egcs, Watcom, Microsoft, Borland). I would recommend that function templates be handled exactly like functions for purposes of name hiding.
Martin O'Riordan: I don't see any justification for extending the purview of what is decidedly a hack, just for the sake of consistency. In fact, I think we should go further and in the interest of consistency, we should deprecate the hack, scheduling its eventual removal from the C++ language standard.
The hack is there to allow old C programs and especially the 'stat.h' file to compile with minimum effort (also several other Posix and X headers). People changing such older programs have ample opportunity to "do it right". Indeed, if you are adding templates to an existing program, you should probably be placing your templates in a 'namespace', so the issue disappears anyway. The lookup rules should be able to provide the behaviour you need without further hacking.
The removal of the export keyword inadvertently deleted the text (previously found in 14 [temp] paragraph 8 of the 2003 Standard),
A non-exported template must be defined in every translation unit in which it is implicitly instantiated (14.7.1 [temp.inst]), unless the corresponding specialization is explicitly instantiated (14.7.2 [temp.explicit]) in some translation unit; no diagnostic is required.
This requirement must be reinstated.
By analogy with typename, the keyword template used to indicate that a dependent name will be a template name should be optional in contexts where a type is required, e.g., base class lists. We could also consider member and parameter declarations.
This was suggested by issue 314.
The Standard does not normatively define which > and >> tokens are to be taken as closing a template-argument-list; instead, 14.2 [temp.names] paragraph 3 uses the undefined and imprecise term “non-nested:”
When parsing a template-id, the first non-nested > is taken as the end of the template-argument-list rather than a greater-than operator. Similarly, the first non-nested >> is treated as two consecutive but distinct > tokens, the first of which is taken as the end of the template-argument-list and completes the template-id.
The (non-normative) footnote clarifies that
A > that encloses the type-id of a dynamic_cast, static_cast, reinterpret_cast or const_cast, or which encloses the template-arguments of a subsequent template-id, is considered nested for the purpose of this description.
Aside from the questionable wording of this footnote (e.g., in what sense does a single terminating character “enclose” anything, and is a nested template-id “subsequent?”) and the fact that it is non-normative, it does not provide a complete definition of what “nesting” is intended to mean. For example, is the first > in this putative template-id “nested” or not?
X<a ? b > c : d>
None of my compilers accept this, which surprised me a little. Is the base-to-derived member function conversion considered to be a runtime-only thing?
template <class D> struct B { template <class X> void f(X) {} template <class X, void (D::*)(X) = &B<D>::f<X> > struct row {}; }; struct D : B<D> { void g(int); row<int,&D::g> r1; row<char*> r2; };
John Spicer: This is not among the permitted conversions listed in 14.3.
I'm not sure there is a terribly good reason for that. Some of the template argument rules for external entities were made conservatively because of concerns about issues of mangling template argument names.
David Abrahams: I'd really like to see that restriction loosened. It is a serious inconvenience because there appears to be no way to supply a usable default in this case. Zero would be an OK default if I could use the function pointer's equality to zero as a compile-time switch to choose an empty function implementation:
template <bool x> struct tag {}; template <class D> struct B { template <class X> void f(X) {} template <class X, void (D::*pmf)(X) = 0 > struct row { void h() { h(tag<(pmf == 0)>(), pmf); } void h(tag<1>, ...) {} void h(tag<0>, void (D::*q)(X)) { /*something*/} }; }; struct D : B<D> { void g(int); row<int,&D::g> r1; row<char*> r2; };
But there appears to be no way to get that effect either. The result is that you end up doing something like:
template <class X, void (D::*pmf)(X) = 0 > struct row { void h() { if (pmf) /*something*/ } };
which invariably makes compilers warn that you're switching on a constant expression.
[Picked up by evolution group at October 2002 meeting.]
How are default template arguments handled with respect to template template parameters? Two separate questions have been raised:
template <class T, class U = int> class ARG { }; template <class X, template <class Y> class PARM> void f(PARM<X>) { } // specialization permitted? void g() { ARG<int> x; // actually ARG<int, int> f(x); // does ARG (2 parms, 1 with default) // match PARM (1 parm)?Template template parameters are deducible (14.8.2.5 [temp.deduct.type] paragraph 9), but 14.3.3 [temp.arg.template] does not specify how matching is done.
Jack Rouse: I implemented template template parameters assuming template signature matching is analogous to function type matching. This seems like the minimum reasonable implementation. The code in the example would not be accepted by this compiler. However, template default arguments are compile time entities so it seems reasonable to relax the matching rules to allow cases like the one in the example. But I would consider this to be an extension to the language.
Herb Sutter: An open issue in the LWG is that the standard doesn't explicitly permit or forbid implementations' adding additional template-parameters to those specified by the standard, and the LWG may be leaning toward explicitly permitting this. [Under this interpretation,] if the standard is ever modified to allow additional template-parameters, then writing "a template that takes a standard library template as a template template parameter" won't be just ugly because you have to mention the defaulted parameters; it would not be (portably) possible at all except possibly by defining entire families of overloaded templates to account for all the possible numbers of parameters vector<> (or anything else) might actually have. That seems unfortunate.
template <template <class T, class U = int> class PARM> class C { PARM<int> pi; };
Jack Rouse: I decided they could not in the compiler I support. This continues the analogy with function type matching. Also, I did not see a strong need to allow default arguments in this context.
A class template used as a template template argument can have default template arguments from its declarations. How are the two sources of default arguments to be reconciled? The default arguments from the template template formal could override. But it could be cofusing if a template-id using the argument template, ARG<int>, behaves differently from a template-id using the template formal name, FORMAL<int>.
Rationale (10/99): Template template parameters are intended to be handled analogously to function function parameters. Thus the number of parameters in a template template argument must match the number of parameters in a template template parameter, regardless of whether any of those paramaters have default arguments or not. Default arguments are allowed for the parameters of a template template parameter, and those default arguments alone will be considered in a specialization of the template template parameter within a template definition; any default arguments for the parameters of a template template argument are ignored.
Note (Mark Mitchell, February, 2006):
Perhaps it is already obvious to all, but it seems worth noting that this extension would change the meaning of conforming programs:
struct Dense { static const unsigned int dim = 1; }; template <template <typename> class View, typename Block> void operator+(float, View<Block> const&); template <typename Block, unsigned int Dim = Block::dim> struct Lvalue_proxy { operator float() const; }; void test_1d (void) { Lvalue_proxy<Dense> p; float b; b + p; }
If Lvalue_proxy is allowed to bind to View, then the template operator+ will be used to perform addition; otherwise, Lvalue_proxy's implicit conversion to float, followed by the built-in addition on floats will be used.
Note (March, 2008):
The Evolution Working Group has accepted the intent of this issue and referred it to CWG for action (not for C++0x). See paper J16/07-0033 = WG21 N2173.
Notes from the June, 2008 meeting:
The CWG decided to take no action on this issue until an interested party produces a paper with analysis and a proposal.
The Standard does not appear to specify clearly the effect of a partial specialization of a member template of a class template. For example:
template<class T> struct B { template<class U> struct A { // #1 void h() {} }; template<class U> struct A<U*> { // #2 void f() {} }; }; template<> template<class U> struct B<int>::A { // #3 void g() {} }; void q(B<int>::A<char*>& p) { p.f(); // #4 }
The explicit specialization at #3 replaces the primary member template #1 of B<int>; however, it is not clear whether the partial specialization #2 should be considered to apply to the explicitly-specialized member template of A<int> (thus allowing the call to p.f() at #4) or whether the partial specialization will be used only for specializations of B that are implicitly instantiated (meaning that #4 could call p.g() but not p.f()).
Given an example like
template<typename T, typename U> struct Outer { template<typename X, typename Y> struct Inner; template<typename Y> struct Inner<T, Y> {}; template<typename Y> struct Inner<U, Y> {}; }; Outer<int, int> outer; // #1 Outer<int, int>::Inner<int, float> inner; // #2
Is #1 ill-formed because of the identical partial specializations? If not, presumably #2 is ill-formed because of the resulting ambiguity (14.5.5.1 [temp.class.spec.match] paragraph 1).
I get the following error diagnostic [from the EDG front end]:
line 8: error: function template "example<T>::foo<R,A>(A)" has already been declared R foo(const A); ^when compiling this piece of code:
struct example { template<class R, class A> // 1-st member template R foo(A); template<class R, class A> // 2-nd member template const R foo(A&); template<class R, class A> // 3-d member template R foo(const A); }; /*template<> template<> int example<char>::foo(int&);*/ int main() { int (example<char>::* pf)(int&) = &example<char>::foo; }
The implementation complains that
template<class R, class A> // 1-st member template R foo(A); template<class R, class A> // 3-d member template R foo(const A);cannot be overloaded and I don't see any reason for it since it is function template specializations that are treated like ordinary non-template functions, meaning that the transformation of a parameter-declaration-clause into the corresponding parameter-type-list is applied to specializations (when determining its type) and not to function templates.
What makes me think so is the contents of 14.5.6.1 [temp.over.link] and the following sentence from 14.8.2.1 [temp.deduct.call] "If P is a cv-qualified type, the top level cv-qualifiers of P are ignored for type deduction". If the transformation was to be applied to function templates, then there would be no reason for having that sentence in 14.8.2.1 [temp.deduct.call].
14.8.2.2 [temp.deduct.funcaddr], which my example is based upon, says nothing about ignoring the top level cv-qualifiers of the function parameters of the function template whose address is being taken.
As a result, I expect that template argument deduction will fail for the 2-nd and 3-d member templates and the 1-st one will be used for the instantiation of the specialization.
Issue 1:
14.5.6.2 [temp.func.order] paragraph 2 says:
Given two overloaded function templates, whether one is more specialized than another can be determined by transforming each template in turn and using argument deduction (14.8.2 [temp.deduct] ) to compare it to the other.14.8.2 [temp.deduct] now has 4 subsections describing argument deduction in different situations. I think this paragraph should point to a subsection of 14.8.2 [temp.deduct] .
Rationale:
This is not a defect; it is not necessary to pinpoint cross-references to this level of detail.
Issue 2:
14.5.6.2 [temp.func.order] paragraph 4 says:
Using the transformed function parameter list, perform argument deduction against the other function template. The transformed template is at least as specialized as the other if, and only if, the deduction succeeds and the deduced parameter types are an exact match (so the deduction does not rely on implicit conversions).In "the deduced parameter types are an exact match", the terms exact match do not make it clear what happens when a type T is compared to the reference type T&. Is that an exact match?
Issue 3:
14.5.6.2 [temp.func.order] paragraph 5 says:
A template is more specialized than another if, and only if, it is at least as specialized as the other template and that template is not at least as specialized as the first.What happens in this case:
template<class T> void f(T,int); template<class T> void f(T, T); void f(1,1);For the first function template, there is no type deduction for the second parameter. So the rules in this clause seem to imply that the second function template will be chosen.
Rationale:
This is not a defect; the standard unambiguously makes the above example ill-formed due to ambiguity.
This was split off from issue 214 at the April 2003 meeting.
Nathan Sidwell: John Spicer's proposed resolution does not make the following well-formed.
template <typename T> int Foo (T const *) {return 1;} //#1 template <unsigned I> int Foo (char const (&)[I]) {return 2;} //#2 int main () { return Foo ("a") != 2; }
Both #1 and #2 can deduce the "a" argument, #1 deduces T as char and #2 deduces I as 2. However, neither is more specialized because the proposed rules do not have any array to pointer decay.
#1 is only deduceable because of the rules in 14.8.2.1 [temp.deduct.call] paragraph 2 that decay array and function type arguments when the template parameter is not a reference. Given that such behaviour happens in deduction, I believe there should be equivalent behaviour during partial ordering. #2 should be resolved as more specialized as #1. The following alteration to the proposed resolution of DR214 will do that.
Insert before,
the following
For the example above, this change results in deducing 'T const *' against 'char const *' in one direction (which succeeds), and 'char [I]' against 'T const *' in the other (which fails).
John Spicer: I don't consider this a shortcoming of my proposed wording, as I don't think this is part of the current rules. In other words, the resolution of 214 might make it clearer how this case is handled (i.e., clearer that it is not allowed), but I don't believe it represents a change in the language.
I'm not necessarily opposed to such a change, but I think it should be reviewed by the core group as a related change and not a defect in the proposed resolution to 214.
Notes from the October 2003 meeting:
There was some sentiment that it would be desirable to have this case ordered, but we don't think it's worth spending the time to work on it now. If we look at some larger partial ordering changes at some point, we will consider this again.
14.5.6.2 [temp.func.order] paragraph 3 says,
To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs (14.5.3 [temp.variadic]) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.
The characteristics of the synthesized entities and how they are determined is not specified. For example, members of a dependent type referred to in non-deduced contexts are not specified to exist, even though the transformed function type would be invalid in their absence.
Example 1:
template<typename T, typename U> struct A; template<typename T> void foo(A<T, typename T::u> *) { } // #1 // synthetic T1 has member T1::u template <typename T> void foo(A<T, typename T::u::v> *) { } // #2 // synthetic T2 has member T2::u and member T2::u::v // T in #1 deduces to synthetic T2 in partial ordering; // deduced A for the parameter is A<T2, T2::u> * --this is not necessarily compatible // with A<T2, T2::u::v> * and it does not need to be. See Note 1. The effect is that // (in the call below) the compatibility of B::u and B::u::v is respected. // T in #2 cannot be successfully deduced in partial ordering from A<T1, T1::u> *; // invalid type T1::u::v will be formed when T1 is substituted into non-deduced contexts. struct B { struct u { typedef u v; }; }; int main() { foo((A<B, B::u> *)0); // calls #2 }
Note 1: Template argument deduction is an attempt to match a P and a deduced A; however, template argument deduction is not specified to fail if the P and the deduced A are incompatible. This may occur in the presence of non-deduced contexts. Notwithstanding the parenthetical statement in 14.8.2.4 [temp.deduct.partial] paragraph 9, template argument deduction may succeed in determining a template argument for every template parameter while producing a deduced A that is not compatible with the corresponding P.
Example 2:
template <typename T, typename U, typename V> struct A; template <typename T> void foo(A<T, struct T::u, struct T::u::u> *); // #2.1 // synthetic T1 has member non-union class T1::u template <typename T, typename U> void foo(A<T, U , U> *); // #2.2 // synthetic T2 and U2 has no required properties // T in #2.1 cannot be deduced in partial ordering from A<T2, U2, U2> *; // invalid types T2::u and T2::u::u will be formed when T2 is substituted in nondeduced contexts. // T and U in #2.2 deduces to, respectively, T1 and T1::u from A<T1, T1::u, struct T1::u::u> * unless // struct T1::u::u does not refer to the injected-class-name of the class T1::u (if that is possible). struct B { struct u { }; }; int main() { foo((A<B, B::u, struct B::u::u> *)0); // calls #2.1 }
It is, however, unclear to what extent an implementation will have to go to determine these minimal properties.
According to 14.6.2.1 [temp.dep.type] paragraph 1, in a primary class template a name refers to the current instantiation if it is the injected-class-name or the name of the class template followed by the template argument list of the template. Although a template-id referring to a specialization of a template alias is described as “equivalent to” the associated type, a specialization of a template alias is neither of the things that qualifies as naming the current instantiation, so presumably the typename keyword in the following example is required:
template <class T> struct A; template <class T> using B = A<T>; template <class T> struct A { struct C {}; typename B<T>::C bc; // typename needed };
(However, the list in 14.6.2.1 [temp.dep.type] may not be exactly what we want; it doesn't allow use of a typedef denoting the type of the current instantiation, either, but that presumably should be accepted.)
For analogous reasons, it should not be permitted to use a template alias as a nested-name-specifier when defining the members of a class template:
template <class T> struct A { void g(); }; template <class T> using B = A<T>; template <class T> void B<T>::g() {} // error
The standard prohibits a class template from having the same name as one of its template parameters (14.6.1 [temp.local] paragraph 4). This prohibits
template <class X> class X;for the reason that the template name would hide the parameter, and such hiding is in general prohibited.
Presumably, we should also prohibit
template <template <class T> class T> struct A;for the same reason.
Currently, member of nondependent base classes hide references to template parameters in the definition of a derived class template.
Consider the following example:
class B { typedef void *It; // (1) // ... }; class M: B {}; template<typename> X {}; template<typename It> struct S // (2) : M, X<It> { // (3) S(It, It); // (4) // ... };
As the C++ language currently stands, the name "It" in line (3) refers to the template parameter declared in line (2), but the name "It" in line (4) refers to the typedef in the private base class (declared in line (1)).
This situation is both unintuitive and a hindrance to sound software engineering. (See also the Usenet discussion at http://tinyurl.com/32q8d .) Among other things, it implies that the private section of a base class may change the meaning of the derived class, and (unlike other cases where such things happen) there is no way for the writer of the derived class to defend the code against such intrusion (e.g., by using a qualified name).
Changing this can break code that is valid today. However, such code would have to:
It has been suggested to make situations like these ill-formed. That solution is unattractive however because it still leaves the writer of a derived class template without defense against accidental name conflicts with base members. (Although at least the problem would be guaranteed to be caught at compile time.) Instead, since just about everyone's intuition agrees, I would like to see the rules changed to make class template parameters hide members of the same name in a base class.
See also issue 458.
Notes from the March 2004 meeting:
We have some sympathy for a change, but the current rules fall straightforwardly out of the lookup rules, so they're not “wrong.” Making private members invisible also would solve this problem. We'd be willing to look at a paper proposing that.
Additional discussion (April, 2005):
John Spicer: Base class members are more-or-less treated as members of the class, [so] it is only natural that the base [member] would hide the template parameter.
Daveed Vandevoorde: Are base class members really “more or less” members of the class from a lookup perspective? After all, derived class members can hide base class members of the same name. So there is some pretty definite boundary between those two sets of names. IMO, the template parameters should either sit between those two sets, or they should (for lookup purposes) be treated as members of the class they parameterize (I cannot think of a practical difference between those two formulations).
John Spicer: How is [hiding template parameters] different from the fact that namespace members can be hidden by private parts of a base class? The addition of int C to N::A breaks the code in namespace M in this example:
namespace N { class A { private: int C; }; } namespace M { typedef int C; class B : public N::A { void f() { C c; } }; }
Daveed Vandevoorde: C++ has a mechanism in place to handle such situations: qualified names. There is no such mechanism in place for template parameters.
Nathan Myers: What I see as obviously incorrect ... is simply that a name defined right where I can see it, and directly attached to the textual scope of B's class body, is ignored in favor of something found in some other file. I don't care that C1 is defined in A, I have a C1 right here that I have chosen to use. If I want A::C1, I can say so.
I doubt you'll find any regular C++ coder who doesn't find the standard behavior bizarre. If the meaning of any code is changed by fixing this behavior, the overwhelming majority of cases will be mysterious bugs magically fixed.
John Spicer: I have not heard complaints that this is actually a cause of problems in real user code. Where is the evidence that the status quo is actually causing problems?
In this example, the T2 that is found is the one from the base class. I would argue that this is natural because base class members are found as part of the lookup in class B:
struct A { typedef int T2; }; template <class T2> struct B : public A { typedef int T1; T1 t1; T2 t2; };
This rule that base class members hide template parameters was formalized about a dozen years ago because it fell out of the principle that base class members should be found at the same stage of lookup as derived class members, and that to do otherwise would be surprising.
Gabriel Dos Reis: The bottom line is that:
Unless presented with real major programming problems the current rules exhibit, I do not think the simple rule “scopes nest” needs a change that silently mutates program meaning.
Mike Miller: The rationale for the current specification is really very simple:
That's it. Because template parameters are not members, they are hidden by member names (whether inherited or not). I don't find that “bizarre,” or even particularly surprising.
I believe these rules are straightforward and consistent, so I would be opposed to changing them. However, I am not unsympathetic toward Daveed's concern about name hijacking from base classes. How about a rule that would make a program ill-formed if a direct or inherited member hides a template parameter?
Unless this problem is a lot more prevalent than I've heard so far, I would not want to change the lookup rules; making this kind of collision a diagnosable error, however, would prevent hijacking without changing the lookup rules.
Erwin Unruh: I have a different approach that is consistent and changes the interpretation of the questionable code. At present lookup is done in this sequence:
If we change this order to
it is still consistent in that no lookup is placed between the base class and the derived class. However, it introduces another inconsistency: now scopes do not nest the same way as curly braces nest — but base classes are already inconsistent this way.
Nathan Myers: This looks entirely satisfactory. If even this seems like too big a change, it would suffice to say that finding a different name by this search order makes the program ill-formed. Of course, a compiler might issue only a portability warning in that case and use the name found Erwin's way, anyhow.
Gabriel Dos Reis: It is a simple fact, even without templates, that a writer of a derived class cannot protect himself against declaration changes in the base class.
Richard Corden: If a change is to be made, then making it ill-formed is better than just changing the lookup rules.
struct B { typedef int T; virtual void bar (T const & ); }; template <typename T> struct D : public B { virtual void bar (T const & ); }; template class D<float>;
I think changing the semantics of the above code silently would result in very difficult-to-find problems.
Mike Miller: Another case that may need to be considered in deciding on Erwin's suggestion or the “ill-formed” alternative is the treatment of friend declarations described in 3.4.1 [basic.lookup.unqual] paragraph 10:
struct A { typedef int T; void f(T); }; template<typename T> struct B { friend void A::f(T); // Currently T is A::T };
Notes from the October, 2005 meeting:
The CWG decided not to consider a change to the existing rules at this time without a paper exploring the issue in more detail.
Consider the following example:
template<class T> struct A { template<class U> friend struct A; // Which A? };
Presumably the lookup for A in the friend declaration finds the injected-class-name of the template. However, according to 14.6.1 [temp.local] paragraph 1,
The injected-class-name can be used with or without a template-argument-list. When it is used without a template-argument-list, it is equivalent to the injected-class-name followed by the template-parameters of the class template enclosed in <>. When it is used with a template-argument-list, it refers to the specified class template specialization, which could be the current specialization or another specialization.
If that rule applies, then this example is ill-formed (because you can't have a template-argument-list in a class template declaration that is not a partial specialization).
Mike Miller: The injected-class-name has a dual nature, as described in 14.6.1 [temp.local], acting as either a template name or a class name, depending on the context; a template argument list forces the name to be interpreted as a template. It seems reasonable that in this example the injected-class-name has to be understood as referring to the class template; a template header is at least as strong a contextual indicator as a template argument list. However, the current wording doesn't say that.
(See also issue 1004.)
Is the following example well-formed?
template<class T> struct A { typedef int M; struct B { typedef void M; struct C; }; }; template<class T> struct A<T>::B::C : A<T> { M // A<T>::M or A<T>::B::M? p[2]; };
14.6.2 [temp.dep] paragraph 3 says the use of M should refer to A<T>::B::M because the base class A<T> is not searched because it's dependent. But in this case A<T> is also the current instantiation (14.6.2.1 [temp.dep.type]) so it seems like it should be searched.
In 14.6.2.1 [temp.dep.type] paragraph 5 we have:
A name is a member of an unknown specialization if the name is a qualified-id in which the nested-name-specifier names a dependent type that is not the current instantiation.
So given:
template<class T> struct A { struct B { struct C { A<T>::B::C f(); }; }; };
it appears that the name A<T>::B::C should be taken as a member of an unknown specialization, because the WP refers to “the” current instantiation, implying that there can be at most one at any given time. At the declaration of f(), the current instantiation is C, so A<T>::B is not the current instantiation.
Would it be better to refer to “a known instantiation” instead of “the current instantiation?”
Mike Miller:
I agree that there is a problem here, but I don't think the “current instantiation” terminology needs to be replaced. By way of background, paragraph 1 makes it clear that A<T>::B “refers to” the current instantiation:
In the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, a name refers to the current instantiation if it is
the injected-class-name (9 [class]) of the class template or nested class,
in the definition of a primary class template, the name of the class template followed by the template argument list of the primary template (as described below) enclosed in <>,
in the definition of a nested class of a class template, the name of the nested class referenced as a member of the current instantiation...
A<T>::B satisfies bullet 3. Paragraph 4 says,
A name is a member of the current instantiation if it is
An unqualified name that, when looked up, refers to a member of a class template. [Note: this can only occur when looking up a name in a scope enclosed by the definition of a class template. —end note]
A qualified-id in which the nested-name-specifier refers to the current instantiation.
So clearly by paragraphs 1 and 4, A<T>::B::C is a member of the current instantiation. The problem is in the phrasing of paragraph 5, which incorrectly requires that the nested-name-specifier “be” the current instantiation rather than simply “referring to” the current instantiation, which would be the correct complement to paragraph 4. Perhaps paragraph 5 could simply be rephrased as, “...a dependent type and it is not a member of the current instantiation.”
(Paragraph 1 may require a bit more wordsmithing to make it truly recursive across multiple levels of nested classes; as it stands, it's not clear whether the name of a nested class of a nested class of a class template is covered or not.)
14.6.2.1 [temp.dep.type] paragraph 4 treats unqualified-ids and qualified-ids in which the nested-name-specifier refers to the current instantiation as equivalent. However, the lookups associated with these two id-expressions are different in the presence of dependent base classes (14.6.2 [temp.dep] paragraph 3: with an unqualified-id, a dependent base class scope is never examined, while with a qualified-id it is. The current wording does not specify how an example like the following is to be handled:
template<typename T> struct B {}; struct C { typedef int type; }; template<typename T> struct A : B<T>, C { template<typename U> type a(); // #1 template<typename U> typename A<T>::type a(); // #2: different from #1? }; template<typename T> template<typename U> typename A<T>::type A<T>::a() { ... } // defines #1 or #2?
There seem to be two possible strategies for the handling of typename A<T>::type:
It is handled like the unqualified-id case, looking only in non-dependent base classes and not being a dependent type.
Since the current instantiation has dependent base classes, it is handled as a dependent type.
EDG seems to be doing the former, g++ the latter.
According to 14.6.2.1 [temp.dep.type] paragraph 3,
A template argument that is equivalent to a template parameter (i.e., has the same constant value or the same type as the template parameter) can be used in place of that template parameter in a reference to the current instantiation.
This would presumably include something like
template<typename T> struct A { struct B { }; A<decltype(T())>::B b; // no typename };
However, this example is rejected by current implementations. Does this need to be clarified in the existing wording?
Consider the following example:
void f(int*); void f(...); template <int N> void g() { f(N); } int main() { g<0>(); g<1>(); }
The call to f in g is not type-dependent, so the overload resolution must be done at definition time rather than at instantiation time. As a result, both of the calls to g will result in calls to f(...), i.e., N will not be a null pointer constant, even if the value of N is 0.
It would be most consistent to adopt a rule that a value-dependent expression can never be a null pointer constant, even in cases like
template <int N> void g() { int* p = N; }
This would always be ill-formed, even when N is 0.
John Spicer: It's clear that this treatment is required for overload resolution, but it seems too expansive given that there are other cases in which the value of a template parameter can affect the validity of the program, and an implementation is forbidden to issue a diagnostic on a template definition unless there are no possible valid specializations.
Notes from the July, 2009 meeting:
There was a strong consensus among the CWG that only the literal 0 should be considered a null pointer constant, not any arbitrary zero-valued constant expression as is currently specified.
It is not clear why a noexcept-expression is value-dependent if its operand is value-dependent. It would seem that the value of the expression depends only on the type of the operand, not its value.
14.7.2 [temp.explicit] defines an explicit instantiation as
Syntactically, that allows things like:
template int S<int>::i = 5, S<int>::j = 7;
which isn't what anyone actually expects. As far as I can tell, nothing in the standard explicitly forbids this, as written. Syntactically, this also allows:
template namespace N { void f(); }
although perhaps the surrounding context is enough to suggest that this is invalid.
Suggested resolution:
I think we should say:
[Steve Adamczyk: presumably, this should have template at the beginning.]
and then say that:
There are similar problems in 14.7.3 [temp.expl.spec]:
Here, I think we want:
with similar restrictions as above.
[Steve Adamczyk: This also needs to have template <> at the beginning, possibly repeated.]
According to 14.7.2 [temp.explicit] paragraph 10,
An entity that is the subject of an explicit instantiation declaration and that is also used in the translation unit shall be the subject of an explicit instantiation definition somewhere in the program; otherwise the program is ill-formed, no diagnostic required.
The term “used” is too vague and needs to be defined. In particular, “use” of a class template specialization as an incomplete type — to form a pointer, for instance — should not require the presence of an explicit instantiation definition elsewhere in the program.
The note in paragraph 5 of 14.8.1 [temp.arg.explicit] makes clear that explicit template arguments cannot be supplied in invocations of constructors and conversion functions because they are called without using a name. However, there is nothing in the current wording of the Standard that makes declaring a constructor or conversion operator that is unusable because of nondeduced parameters (i.e., that would need to be specified explicitly) ill-formed. It would be a service to the programmer to diagnose this useless construct as early as possible.
Nicolai Josuttis sent me an example like the following:
template <typename RET, typename T1, typename T2> const RET& min (const T1& a, const T2& b) { return (a < b ? a : b); } template const int& min<int>(const int&,const int&); // #1 template const int& min(const int&,const int&); // #2
Among the questions was whether explicit instantiation #2 is valid, where deduction is required to determine the type of RET.
The first thing I realized when researching this is that the standard does not really spell out the rules for deduction in declarative contexts (friend declarations, explicit specializations, and explicit instantiations). For explicit instantiations, 14.7.2 [temp.explicit] paragraph 2 does mention deduction, but it doesn't say which set of deduction rules from 14.8.2 [temp.deduct] should be applied.
Second, Nicolai pointed out that 14.7.2 [temp.explicit] paragraph 6 says
A trailing template-argument can be left unspecified in an explicit instantiation provided it can be deduced from the type of a function parameter (14.8.2 [temp.deduct]).
This prohibits cases like #2, but I believe this was not considered in the wording as there is no reason not to include the return type in the deduction process.
I think there may have been some confusion because the return type is excluded when doing deduction on a function call. But there are contexts where the return type is included in deduction, for example, when taking the address of a function template specialization.
Suggested resolution:
Andrei Iltchenko points out that the standard has no wording that defines how to determine which template is specialized by an explicit specialization of a function template. He suggests "template argument deduction in such cases proceeds in the same way as when taking the address of a function template, which is described in 14.8.2.2 [temp.deduct.funcaddr]."
John Spicer points out that the same problem exists for all similar declarations, i.e., friend declarations and explicit instantiation directives. Finding a corresponding placement operator delete may have a similar problem.
John Spicer: There are two aspects of "determining which template" is referred to by a declaration: determining the function template associated with the named specialization, and determining the values of the template arguments of the specialization.
template <class T> void f(T); #1 template <class T> void f(T*); #2 template <> void f(int*);
In other words, which f is being specialized (#1 or #2)? And then, what are the deduced template arguments?
14.5.6.2 [temp.func.order] does say that partial ordering is done in contexts such as this. Is this sufficient, or do we need to say more about the selection of the function template to be selected?
14.8.2 [temp.deduct] probably needs a new section to cover argument deduction for cases like this.
14.8.2 [temp.deduct] is all about function types, but these rules also apply, e.g., when matching a class template partial specialization. We should add a note stating that we could be doing substitution into the template-id for a class template partial specialization.
Additional note (August 2008):
According to 14.5.5.1 [temp.class.spec.match] paragraph 2, argument deduction is used to determine whether a given partial specialization matches a given argument list. However, there is nothing in 14.5.5.1 [temp.class.spec.match] nor in 14.8.2 [temp.deduct] and its subsections that describes exactly how argument deduction is to be performed in this case. It would seem that more than just a note is required to clarify this processing.
There are certain constructs that are not covered by the existing categories of “type dependent” and “value dependent.” For example, the expression sizeof(sizeof(T())) is neither type-dependent nor value-dependent, but its validity depends on whether T can be value-constructed. We should be able to overload on such characteristics and select via deduction failure, but we need a term like “instantiation-dependent” to describe these cases in the Standard. The phrase “expression involving a template parameter” seems to come pretty close to capturing this idea.
Consider the following program:
template <typename T> int ref (T&) { return 0; } template <typename T> int ref (const T&) { return 1; } template <typename T> int ref (const volatile T&) { return 2; } template <typename T> int ref (volatile T&) { return 4; } template <typename T> int ptr (T*) { return 0; } template <typename T> int ptr (const T*) { return 8; } template <typename T> int ptr (const volatile T*) { return 16; } template <typename T> int ptr (volatile T*) { return 32; } void foo() {} int main() { return ref(foo) + ptr(&foo); }
The Standard appears to specify that the value returned from main is 2. The reason for this result is that references and pointers are handled differently in template argument deduction.
For the reference case, 14.8.2.1 [temp.deduct.call] paragraph 3 says that “If P is a reference type, the type referred to by P is used for type deduction.” Because of issue 295, all four of the types for the ref function parameters are the same, with no cv-qualification; overload resolution does not find a best match among the parameters and thus the most-specialized function is selected.
For the pointer type, argument deduction does not get as far as forming a cv-qualified function type; instead, argument deduction fails in the cv-qualified cases because of the cv-qualification mismatch, and only the cv-unqualified version of ptr survives as a viable function.
I think the choice of ignoring cv-qualifiers in the reference case but not the pointer case is very troublesome. The reason is that when one considers function objects as function parameters, it introduces a semantic difference whether the function parameter is declared a reference or a pointer. In all other contexts, it does not matter: a function name decays to a pointer and the resulting semantics are the same.
The current wording of 15.3 [except.handle] paragraph 16 is:
The object declared in an exception-declaration or, if the exception-declaration does not specify a name, a temporary (12.2 [class.temporary]) is copy-initialized (8.5 [dcl.init]) from the exception object. The object shall not have an abstract class type. The object is destroyed when the handler exits, after the destruction of any automatic objects initialized within the handler.
There are two problems with this. First, it's not clear what it means for the handler's “parameter” to be a temporary. This possibility is briefly mentioned in 12.2 [class.temporary], but the lifetime of such a temporary is not defined there; the discussion of lifetime is restricted to those temporaries that arise during the evaluation of an expression, and this is not such a case.
Second, this wording assumes that there will be an object to be destroyed and thus ignores the possibility that the exception-declaration declares a reference.
According to 15.3 [except.handle] paragraph 16,
The object declared in an exception-declaration or, if the exception-declaration does not specify a name, a temporary (12.2 [class.temporary]) is copy-initialized (8.5 [dcl.init]) from the exception object. The object shall not have an abstract class type. The object is destroyed when the handler exits, after the destruction of any automatic objects initialized within the handler.
This wording leaves unspecified how an exception-declaration that is a reference should be treated. For example, presumably a reference to an abstract class type should be permitted, but that is not specified. The treatment of ellipsis is also not clearly addressed.
It was tentatively agreed at the Santa Cruz meeting that exception specifications should fully participate in the type system. This change would address gaps in the current static checking of exception specifications such as
void (*p)() throw(int); void (**pp)() throw() = &p; // not currently an error
This is such a major change that it deserves to be a separate issue.
See also issues 25, 87, and 133.
A type used in an exception specification must be complete (15.4 [except.spec] paragraph 2). The resolution of issue 437 stated that a class type appearing in an exception specification inside its own member-specification is considered to be complete. Should this also apply to exception specifications in class templates instantiated because of a reference inside the member-specification of a class? For example,
template<class T> struct X { void f() throw(T) {} }; struct S { X<S> xs; };
Destructors that throw can easily cause programs to terminate, with no possible defense. Example: Given
struct XY { X x; Y y; };
Assume that X::~X() is the only destructor in the entire program that can throw. Assume further that Y construction is the only other operation in the whole program that can throw. Then XY cannot be used safely, in any context whatsoever, period — even simply declaring an XY object can crash the program:
XY xy; // construction attempt might terminate program: // 1. construct x -- succeeds // 2. construct y -- fails, throws exception // 3. clean up by destroying x -- fails, throws exception, // but an exception is already active, so call // std::terminate() (oops) // there is no defenseSo it is highly dangerous to have even one destructor that could throw.
Suggested Resolution:
Fix the above problem in one of the following two ways. I prefer the first.
Fergus Henderson: I disagree. Code using XY may well be safe, if X::~X() only throws if std::uncaught_exception() is false.
I think the current exception handling scheme in C++ is certainly flawed, but the flaws are IMHO design flaws, not minor technical defects, and I don't think they can be solved by minor tweaks to the existing design. I think that at this point it is probably better to keep the standard stable, and learn to live with the existing flaws, rather than trying to solve them via TC.
Bjarne Stroustrup: I strongly prefer to have the call to std::terminate() be conforming. I see std::terminate() as a proper way to blow away "the current mess" and get to the next level of error handling. I do not want that escape to be non-conforming — that would imply that programs relying on a error handling based on serious errors being handled by terminating a process (which happens to be a C++ program) in std::terminate() becomes non-conforming. In many systems, there are — and/or should be — error-handling and recovery mechanisms beyond what is offered by a single C++ program.
Andy Koenig: If we were to prohibit writing a destructor that can throw, how would I solve the following problem?
I want to write a class that does buffered output. Among the other properties of that class is that destroying an object of that class writes the last buffer on the output device before freeing memory.
What should my class do if writing that last buffer indicates a hardware output error? My user had the option to flush the last buffer explicitly before destroying the object, but didn't do so, and therefore did not anticipate such a problem. Unfortunately, the problem happened anyway. Should I be required to suppress this error indication anyway? In all cases?
Herb Sutter (June, 2007): IMO, it's fine to suppress it. The user had the option of flushing the buffer and thus being notified of the problem and chose not to use it. If the caller didn't flush, then likely the caller isn't ready for an exception from the destructor, either. You could also put an assert into the destructor that would trigger if flush() had not been called, to force callers to use the interface that would report the error.
In practice, I would rather thrown an exception, even at the risk of crashing the program if we happen to be in the middle of stack unwinding. The reason is that the program would crash only if a hardware error occurred in the middle of cleaning up from some other error that was in the process of being handled. I would rather have such a bizarre coincidence cause a crash, which stands a chance of being diagnosed later, than to be ignored entirely and leave the system in a state where the ignore error could cause other trouble later that is even harder to diagnose.
If I'm not allowed to throw an exception when I detect this problem, what are my options?
Herb Sutter: I understand that some people might feel that "a failed dtor during stack unwinding is preferable in certain cases" (e.g., when recovery can be done beyond the scope of the program), but the problem is "says who?" It is the application program that should be able to decide whether or not such semantics are correct for it, and the problem here is that with the status quo a program cannot defend itself against a std::terminate() — period. The lower-level code makes the decision for everyone. In the original example, the mere existence of an XY object puts at risk every program that uses it, whether std::terminate() makes sense for that program or not, and there is no way for a program to protect itself.
That the "it's okay if the process goes south should a rare combination of things happen" decision should be made by lower-level code (e.g., X dtor) for all apps that use it, and which doesn't even understand the context of any of the hundreds of apps that use it, just cannot be correct.
When a function throws an exception that is not in its exception-specification, std::unexpected() is called. According to 15.5.2 [except.unexpected] paragraph 2,
If [std::unexpected()] throws or rethrows an exception that the exception-specification does not allow then the following happens: If the exception-specification does not include the class std::bad_exception (18.8.2.1 [bad.exception]) then the function std::terminate() is called, otherwise the thrown exception is replaced by an implementation-defined object of the type std::bad_exception, and the search for another handler will continue at the call of the function whose exception-specification was violated.
The “replaced by” wording is imprecise and undefined. For example, does this mean that the destructor is called for the existing exception object, or is it simply abandoned? Is the replacement in situ, so that a pointer to the existing exception object will now point to the std::bad_exception object?
Mike Miller: The call to std::unexpected() is not described as analogous to invoking a handler, but if it were, that would resolve this question; it is clearly specified what happens to the previous exception object when a new exception is thrown from a handler (15.1 [except.throw] paragraph 4).
This approach would also clarify other questions that have been raised regarding the requirements for stack unwinding. For example, 15.5.1 [except.terminate] paragraph 2 says that
In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std::terminate() is called.
This requirement could be viewed as in conflict with the statement in 15.5.2 [except.unexpected] paragraph 1 that
If a function with an exception-specification throws an exception that is not listed in the exception-specification, the function std::unexpected() is called (18.8.2 [exception.unexpected]) immediately after completing the stack unwinding for the former function.
If it is implementation-defined whether stack unwinding occurs before calling std::terminate() and std::unexpected() is called only after doing stack unwinding, does that mean that it is implementation-defined whether std::unexpected() is called if there is ultimately no handler found?
Again, if invoking std::unexpected() were viewed as essentially invoking a handler, the answer to this would be clear, because unwinding occurs before invoking a handler.
According to 16.1 [cpp.cond] paragraph 4,
The resulting tokens comprise the controlling constant expression which is evaluated according to the rules of 5.19 [expr.const] using arithmetic that has at least the ranges specified in 18.3 [support.limits], except that all signed and unsigned integer types act as if they have the same representation as, respectively, intmax_t or uintmax_t (_N3035_.18.4.2 [stdinth]). This includes interpreting character literals, which may involve converting escape sequences into execution character set members.
Ordinary character literals with a single c-char have the type char, which is neither a signed nor an unsigned integer type. Although 4.5 [conv.prom] paragraph 1 is clear that char values promote to int, regardless of whether the implementation treats char as having the values of signed char or unsigned char, 16.1 [cpp.cond] paragraph 4 isn't clear on whether character literals should be treated as signed or unsigned values. In C99, such literals have type int, so the question does not arise. If an implementation in which plain char has the values of unsigned char were to treat character literals as unsigned, an expression like '0'-'1' would thus have different values in C and C++, namely -1 in C and some large unsigned value in C++.
It is not clear from the Standard what the result of the following example should be:
#define NIL(xxx) xxx #define G_0(arg) NIL(G_1)(arg) #define G_1(arg) NIL(arg) G_0(42)
The relevant text from the Standard is found in 16.3.4 [cpp.rescan] paragraph 2:
If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file's preprocessing tokens), it is not replaced. Further, if any nested replacements encounter the name of the macro being replaced, it is not replaced. These nonreplaced macro name preprocessing tokens are no longer available for further replacement even if they are later (re)examined in contexts in which that macro name preprocessing token would otherwise have been replaced.
The sequence of expansion of G0(42) is as follows:
G0(42) NIL(G_1)(42) G_1(42) NIL(42)
The question is whether the use of NIL in the last line of this sequence qualifies for non-replacement under the cited text. If it does, the result will be NIL(42). If it does not, the result will be simply 42.
The original intent of the J11 committee in this text was that the result should be 42, as demonstrated by the original pseudo-code description of the replacement algorithm provided by Dave Prosser, its author. The English description, however, omits some of the subtleties of the pseudo-code and thus arguably gives an incorrect answer for this case.
Suggested resolution (Mike Miller): Replace the cited paragraph with the following:
As long as the scan involves only preprocessing tokens from a given macro's replacement list, or tokens resulting from a replacement of those tokens, an occurrence of the macro's name will not result in further replacement, even if it is later (re)examined in contexts in which that macro name preprocessing token would otherwise have been replaced.
Once the scan reaches the preprocessing token following a macro's replacement list — including as part of the argument list for that or another macro — the macro's name is once again available for replacement. [Example:
#define NIL(xxx) xxx #define G_0(arg) NIL(G_1)(arg) #define G_1(arg) NIL(arg) G_0(42) // result is 42, not NIL(42)The reason that NIL(42) is replaced is that (42) comes from outside the replacement list of NIL(G_1), hence the occurrence of NIL within the replacement list for NIL(G_1) (via the replacement of G_1(42)) is not marked as nonreplaceable. —end example]
(Note: The resolution of this issue must be coordinated with J11/WG14.)
Notes (via Tom Plum) from April, 2004 WG14 Meeting:
Back in the 1980's it was understood by several WG14 people that there were tiny differences between the "non-replacement" verbiage and the attempts to produce pseudo-code. The committee's decision was that no realistic programs "in the wild" would venture into this area, and trying to reduce the uncertainties is not worth the risk of changing conformance status of implementations or programs.
C99 is very clear that a #error directive causes a translation to fail: Clause 4 paragraph 4 says,
The implementation shall not successfully translate a preprocessing translation unit containing a #error preprocessing directive unless it is part of a group skipped by conditional inclusion.
C++, on the other hand, simply says that a #error directive “renders the program ill-formed” (16.5 [cpp.error]), and the only requirement for an ill-formed program is that a diagnostic be issued; the translation may continue and succeed. (Noted in passing: if this difference between C99 and C++ is addressed, it would be helpful for synchronization purposes in other contexts as well to introduce the term “preprocessing translation unit.”)
The specification of how the string-literal in a _Pragma operator is handled does not deal with the new kinds of string literals. 16.9 [cpp.pragma.op] says,
The string literal is destringized by deleting the L prefix, if present, deleting the leading and trailing double-quotes, replacing each escape sequence...
The various other prefixes should either be handled or prohibited.
During the discussion of issues 167 and 174, it became apparent that there was no consensus on the meaning of deprecation. Some thought that deprecating a feature reflected an intent to remove it from the language. Others viewed it more as an encouragement to programmers not to use certain constructs, even though they might be supported in perpetuity.
There is a formal-sounding definition of deprecation in Annex D [depr] paragraph 2:
deprecated is defined as: Normative for the current edition of the Standard, but not guaranteed to be part of the Standard in future revisions.However, this definition would appear to say that any non-deprecated feature is "guaranteed to be part of the Standard in future revisions." It's not clear that that implication was intended, so this definition may need to be amended.
This issue is intended to provide an avenue for discussing and resolving those questions, after which the original issues may be reopened if that is deemed desirable.