Document number: | J16/99-0032 = WG21 N1208 |
Date: | 23 February, 2000 |
Project: | Programming Language C++ |
Reference: | ISO/IEC IS 14882:1998(E) |
Reply to: | William M. Miller |
wmm@fastdial.net |
The purpose of this document is to record the disposition of issues which have come before the Core Language Working Group of the ANSI (J16) and ISO (WG21) C++ Standard Committee.
Issues represent potential defects in the ISO/IEC IS 14882:1998(E) document; they 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.dkuug.dk/jtc1/sc22/wg21. Requests for further information about this document should include the document number above, reference ISO/IEC 14882:1998(E), and be submitted to the Information Technology Information Council (ITI), 1250 Eye Street NW, 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://reality.sgi.com/austern_mti/std-c++/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 J16 and WG21 committees deliberate and act. For ease of reference, issues are grouped in this document by their status. Issues have one of the following statuses:
Open: The issue is new or the working group has not yet formed an opinion on the issue. If a Suggested Resolution is given, it reflects the opinion of the issue's submitter, not necessarily that of the working group or the Committee as a whole.
Drafting: Informal consensus has been reached in the working group and is described in rough terms in a Tentative Resolution, although precise wording for the change is not yet available.
Review: Exact wording of a Proposed Resolution is now available for an issue on which the working group previously reached informal consensus.
Ready: The working group has reached consensus that the issue is a defect in the Standard, the Proposed Resolution is correct, and the issue is ready to forward to the full Committee for ratification as a proposed defect report.
DR: The full J16 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.
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. Under ISO rules, extensions cannot be considered for at least five years from the approval of the Standard, at which time the Standard will be open for review. 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 when extension proposals will be in order.
22.2.1.1.2 lib.locale.ctype.virtuals paragraph 13 states a constraint on the values of the characters representing the decimal digits in the execution character set:
for any digit character c, the expression (do_narrow( c, dfault)-'0') evaluates to the digit value of the character.This requirement is not reflected in the description of the execution character set (2.2 lex.charset paragraph 3).
Proposed resolution (10/99): In 2.2 lex.charset paragraph 3, after the sentence
For each basic execution character set, the values of the members shall be non-negative and distinct from one another.insert the following:
In both the source and execution basic character sets, the value of each character after 0 in the above list of decimal digits shall be one greater than the value of the previous.
The description of Koenig lookup in 3.4.2 basic.lookup.koenig paragraph 1 says,
...other namespaces not considered during the usual unqualified lookup (3.4.1 basic.lookup.unqual ) may be searched.Does this mean that Koenig lookup does not search namespaces that were already searched during the usual unqualified lookup? The answer is academic except for the two-stage lookup during template instantiation. If a given namespace is searched in the context of the template definition, are declarations in that namespace in the instantiation context ignored during the Koenig lookup? For instance,
void f(int); templateIn this example, the call f(t) in the template function will resolve to f(E) if Koenig lookup reexamines already-searched namespaces and to f(int) if not.void g(T t) { f(t); } enum E { e }; void f(E); void h() { g(e); }
Proposed Resolution (10/99): Immediately preceding the example at the end of 3.4.2 basic.lookup.koenig paragraph 2, add the following:
[Note: the namespaces and classes associated with the argument types can include namespaces and classes already considered by the ordinary unqualified lookup.]
The Standard uses confusing terminology when referring to accessibility in connection with ambiguity. For instance:
4.10 conv.ptr paragraph 3:
If B is an inaccessible or ambiguous base ...5.2.7 expr.dynamic.cast paragraph 8:
... has an unambiguous public base ...10.3 class.virtual paragraph 5:
... is an unambiguous direct or indirect base ... and is accessible ...15.3 except.handle paragraph 3:
not involving conversions to pointers to private or protected or ambiguous classes
The phrase "unambiguous public base" is unfortunate as it could mean either "an unambiguous base not considering accessibility, which is public" or "an unambiguous base considering only the publicly accessible bases." I believe the former interpretation correct, as accessibility is applied after visibility (11 class.access paragraph 4) and ambiguity is described in terms of visibility (10.2 class.member.lookup paragraph 2).
Suggested Resolution: Use the phrases "public and unambiguous," "accessible and unambiguous," "non-public or ambiguous," or "inaccessible or ambiguous" as appropriate.
Proposed resolution (10/99):
5.1 expr.prim paragraph 7 says that class-name::class-name names the constructor when both class-name refer to the same class. (Note the different perspective, at least, in 12.1 class.ctor paragraph 1, in which constructors have no names and are recognized by syntactic context rather than by name.)
This formulation does not address the case of classes in which a function template is declared as a constructor, for example:
template <class T> struct A { template <class T2> A(T2); }; template<> template<> A<int>::A<int>(int);Here there is an ambiguity as to whether the second template argument list is for the injected class name or for the constructor.
Suggested resolution: restate the rule as a component of name lookup. Specifically, if when doing a qualified lookup in a given class you look up a name that is the same as the name of the class, the entity found is the constructor and not the injected class name. In all other cases, the name found is the injected class name. For example:
class B { }; class A: public B { A::B ab; // B is the inherited injected B A::A aa; // Error: A::A is the constructor };Without this rule some very nasty backtracking is needed. For example, if the injected class name could be qualified by its own class name, the following code would be well-formed:
template <class T> struct A { template <class T2> A(T2); static A x; }; template<> A<int>::A<int>(A<int>::x);Here the declarator for the definition of the static data member has redundant parentheses, and it's only after seeing the declarator that the parser can know that the second A<int> is the injected class name rather than the constructor.
Proposed resolution (10/99): In 9 class paragraph 2, change
The class-name is also inserted into the scope of the class itself. For purposes of access checking the inserted class name...to
The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name...Also, in 3.4.3.1 class.qual , add the following before paragraph 2:
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 only be used in the declarator-id of a constructor definition that appears outside of the class definition. [Example:struct A { A(); }; struct B: public A { B(); }; A::A() { } B::B() { } B::A ba; // object of type A A::A a; // error, A::A is not a type name—end example]
There may be additional locations in the Standard that should be changed to refer to injected-class-name.
(See also issue 194
.)
According to 7.2 dcl.enum paragraph 9, it is permitted to convert from one enumeration type to another. However, neither 5.2.9 expr.static.cast nor 5.4 expr.cast allows this conversion.
Proposed resolution (10/99): Change the first two sentences of 5.2.9 expr.static.cast paragraph 7 to read
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 ).
According to 5.2.9 expr.static.cast paragraph 10,
An rvalue of type "pointer to cv void" can be explicitly converted to a pointer to object type.No requirements are stated regarding the cv-qualification of the pointer to object type. Contrast this with the formula used in paragraphs 5, 8, and 9, where the treatment of cv-qualification is explicit, requiring that the target type be at least as cv-qualified as the source. There is an apparently general requirement on all forms of static_cast in 5.2.9 expr.static.cast paragraph 1 that it "shall not cast away constness." Assuming that this restriction applies to paragraph 10, since there is no explicit exception to the general rule, that still leaves open the question of whether one can "cast away volatility" in a conversion from volatile void* to a pointer to object type. Should 5.2.9 expr.static.cast paragraph 10 be rewritten to handle cv-qualification in the same way as paragraphs 5, 8, and 9?
Proposed resolution (10/99): Change the first sentence of 5.2.9 expr.static.cast paragraph 10 to
An rvalue of type "pointer to cv1 void" can be converted to an rvalue of type "pointer to cv2 T", where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.
7.3 basic.namespace paragraph 2 says:
A name declared outside all named namespaces, blocks (6.3 stmt.block ) and classes (clause 9 class ) has global namespace scope (3.3.5 basic.scope.namespace ).But 3.3.5 basic.scope.namespace paragraph 3 says:
A name declared outside all named or unnamed namespaces (7.3 basic.namespace ), blocks (6.3 stmt.block ), function declarations (8.3.5 dcl.fct ), function definitions (8.4 dcl.fct.def ) and classes (clause 9 class ) has global namespace scope (also called global scope).7.3 basic.namespace should evidently be changed to match the wording in 3.3.5 basic.scope.namespace — the unnamed namespace is not global scope.
Proposed resolution (10/99): Change 7.3 basic.namespace paragraph 2 to read,
The outermost declarative region of a translation unit is also a namespace, called the global namespace (3.3.5 basic.scope.namespace ).
John Spicer: I believe the standard is not clear with respect to this example:
namespace N { template <class T> void f(T); namespace M { struct A { friend void f<int>(int); // okay - refers to N::f }; } }At issue is whether the friend declaration refers to N::f, or whether it is invalid.
A note in 3.3.1 basic.scope.pdecl paragraph 6 says
friend declarations refer to functions or classes that are members of the nearest enclosing namespace ...I believe it is intended to mean unqualified friend declarations. Certainly friend void A::B() need not refer to a member of the nearest enclosing namespace. Only when the declarator is unqualified (i.e., it is a declaration and not a reference) does this rule need to apply. The presence of an explicit template argument list requires that a previous declaration be visible and renders this a reference and not a declaration that is subject to this rule.
Mike Miller: 7.3.1.2 namespace.memdef paragraph 3 says,
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.On the other hand, the friend declaration would be a syntax error if f weren't declared as a template name; it would seem very strange not to find the declaration that made the friend declaration syntactically correct. However, it also seems strange to treat this case differently from ordinary functions and from templates:
namespace N { template <class T> void f(T); void g(); namespace M { struct A { friend void f<int>(int); // N::f template <class T> friend void f(T); // M::f friend void g(); // M::g }; } }
John Spicer: This section refers to "looking for a prior declaration". This gets back to an earlier discussion we've had about the difference between matching two declarations of the same name and doing name lookup. I would maintain that in f<int> the f is looked up using a normal lookup. In practice, this is really how it has to be done because the declaration could actually be f<int>::x.
Proposed resolution (10/99): In 7.3.1.2 namespace.memdef paragraph 3, change
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.to
When looking for a prior declaration of a class or a function declared as a friend, and when the name of the friend class or function is neither a qualified name nor a template-id, scopes outside the innermost enclosing namespace scope are not considered.Also, change the example in that paragraph as follows:
void h(int); template <class T> void f2(T); namespace A { class X { friend void f(X); // A::f(x) is a friend friend void f2<>(int); // ::f2<>(int) is a friend ...
(See also issues
95
,
136
,
138
,
139
,
143
, and
165
.)
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.)
See also
Core issue 56
and
Core issue 85
.
3.2 basic.def.odr paragraph 4 and 8.3.5 dcl.fct paragraph 6 indicate that the return type and parameter types must be complete in a function definition. However, when 9.2 class.mem paragraph 2 lists the contexts in a class member-specification in which the class is considered complete, the return type and parameter types of a member function defined in the class definition are not included. It thus appears that the following example is ill-formed:
struct S { S f() { return S(); } // error: incomplete return type void g(S) { } // error: incomplete parameter type };Jack Rouse: I suggest supplementing the text in 8.3.5p6 with something like:
The type of a parameter or the return type for a function definition shall not be an incomplete class type unless the function definition is nested in the member-specification for that class (including definitions in nested classes defined within the class).
Proposed resolution (10/99): Replace the last sentence of 8.3.5 dcl.fct paragraph 6 with
The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).
Paragraph 9 of 8.5 dcl.init says:
If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for an object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.It should be made clear that this paragraph does not apply to static objects.
Proposed resolution (10/99): In 8.5 dcl.init paragraph 9, replace
Otherwise, if no initializer is specified for an object..."with
Otherwise, if no initializer is specified for a non-static object...
In 3.6.2 basic.start.init paragraph 1 and 8.5 dcl.init paragraphs 5 and 6, the terms "memory" and "storage" are used in connection with zero-initialization. This is inaccurate; it is the variables that are zero-initialized, not the storage. (An all-zero bit pattern in the storage may, in fact, not correspond to the representation of zero converted to the appropriate type, and it is the latter that is being described.)
Suggested resolution: remove the words "storage" and "memory" in these contexts.
Proposed resolution (10/99):
To zero-initialize an object of type T means:To default-initialize an object of type T means:...
- if T is a scalar type, the object is set to the value of 0 (zero) converted to T;
- if T is a non-union class type, each nonstatic data member and each base-class subobject is zero-initialized;
- if T is a union type, the first named data member is zero-initialized;
- if T is an array type, each element is zero-initialized;
- if T is a reference type, no initialization is performed.
A program that calls...
- otherwise, the object is zero-initialized.
Each object of static storage duration shall be zero-initialized...
Additional notes:
Between the May '96 and September '96 working papers, the text in 9.2 class.mem paragraph 13:
If T is the name of a class, then each of the following shall have a name different from T:was changed by removing the word 'static'. Looking over the meeting minutes from Stockholm, none of the proposals seem to include this change, which breaks C compatibility and is not mentioned in the compatibility annex. Was this change actually voted in by the committee?
- every static data member of class T;
Specifically, this breaks /usr/include/netinet/in.h under Linux, in which "struct ip_opts" shares its name with one of its members.
Proposed resolution (10/99):
In addition, if class T has a user-declared constructor (12.1 class.ctor ), every nonstatic data member of class T shall have a name different from T.
11.2 class.access.base paragraph 4 says:
A base class is said to be accessible if an invented public member of the base class is accessible. If a base class is accessible, one can implicitly convert a pointer to a derived class to a pointer to that base class.Given the above, is the following well-formed?
class D; class B { protected: int b1; friend void foo( D* pd ); }; class D : protected B { }; void foo( D* pd ) { if ( pd->b1 > 0 ); // Is 'b1' accessible? }Can you access the protected member b1 of B in foo? Can you convert a D* to a B* in foo?
1st interpretation:
A public member of B is accessible within foo (since foo is a friend), therefore foo can refer to b1 and convert a D* to a B*.
2nd interpretation:
B is a protected base class of D, and a public member of B is a protected member of D and can only be accessed within members of D and friends of D. Therefore foo cannot refer to b1 and cannot convert a D* to a B*.
Proposed Resolution (10/99):
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. } };—end example]
A base class is said to be accessible if an invented public member of the base class is accessible.
A member m is accessible at the point R when named in class N if
- m as a member of N is public, or
- m as a member of N is private, and R occurs in a member or friend of class N, or
- m as a member of N is protected, and R occurs in a member or friend of class N, or in a member or friend of a class P derived from MTT>N, where m as a member of P is private or protected, or
- there exists a base class B of N that is accessible at R, and m is accessible at R when named in class B. [Example:...
(See J16/99-0042 = WG21 N1218.)
The text in 11.2 class.access.base paragraph 4 does not seem to handle the following cases:
class D; class B { private: int i; friend class D; }; class C : private B { }; class D : private C { void f() { B::i; //1: well-formed? i; //2: well-formed? } };The member i is not a member of D and cannot be accessed in the scope of D. What is the naming class of the member i on line //1 and line //2?
Proposed Resolution (10/99): As described for Core issue 9 . With that change, it is clear that the example is ill-formed.
In the example in paragraph 3 of 11.2 class.access.base , all the references to B in DD::f() should be replaced by ::B. The reason is that the class name B is private in D and thus inaccessible in DD. (The example was probably not updated when class name injection was added.)
Proposed resolution (10/99): Replace the example in 11.2 class.access.base paragraph 3 with:
class B { public: int mi; // nonstatic member static int si; // static member }; class D: private B { }; class DD: public D { void f(); }; void DD::f() { mi = 3; // error: mi is private in D si = 3; // error: si is private in D ::B b; b.mi = 3; // OK (b.mi is different from this->mi) b.si = 3; // OK (b.si is different from this->si) ::B::si = 3; // OK ::B* bp1 = this; // error: B is a private base class ::B* bp2 = (::B*)this; // OK with cast bp2->mi = 3; // OK: access through a pointer to B }
Can a copy-constructor declared as explicit be used to copy class values implicitly? For example,
struct X { X(); explicit X(const X&); }; void f(X); int main() { X x; f(x); }According to 12.3.1 class.conv.ctor paragraphs 2-3,
An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (8.5 dcl.init ) or where casts (5.2.9 expr.static.cast , 5.4 expr.cast ) are explicitly used... A copy-constructor (12.8 class.copy ) is a converting constructor. An implicitly-declared copy constructor is not an explicit constructor; it may be called for implicit type conversions.This passage would appear to indicate that the call in the example is ill-formed, since it uses neither the direct-initialization syntax nor an explicit cast. The last sentences are especially interesting in this regard, indicating that explicit and non-explicit copy constructors are handled differently.
On the other hand, 8.5 dcl.init paragraph 14, bullet 4, sub-bullet 2 says,
If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination... [the] applicable constructors are enumerated (13.3.1.3 over.match.ctor )...The cited passage says that
The candidate functions are all the constructors of the class of the object being initialized.
Proposed resolution (10/99): Change the first two sentences of 13.3.1.3 over.match.ctor paragraph 1 to
When objects of class type are direct-initialized, or copy-initialized from an expression of the same or a derived class type, overload resolution selects the constructor. For direct-initialization, the candidate functions are all the constructors of the class of the object being initialized. For copy-initialization, the candidate functions are all the converting constructors (12.3.1 class.conv.ctor ) of that class.
In 13.3.3.2 over.ics.rank , we have
int f(const int *); int f(int *); int i; int j = f(&i); // Calls f(int *)—end example] or, if not that,
void f(char *); void f(const char *); f("abc");The two conversion sequences differ only in their qualification conversions, and the destination types are similar. The cv-qualification signature of "char *", is a proper subset of the cv-qualification signature of "const char *", so f(char *) is chosen, which is wrong. The rule should be like the one for conversion to bool -- the deprecated conversion should be worse than another exact match that is not the deprecated conversion.
Proposed resolution (10/99): Change 13.3.3.2 over.ics.rank paragraph 3 bullet 1 sub-bullet 3 from
S1 and S2 differ only in their qualification conversion and yield similar types T1 and T2 (4.4 conv.qual ), respectively, and the cv-qualification signature of type T1 is a proper subset of the cv-qualification signature of type T2.to
S1 and S2 differ only in their qualification conversion and yield similar types T1 and T2 (4.4 conv.qual ), respectively, and the cv-qualification signature of type T1 is a proper subset of the cv-qualification signature of type T2, and S1 is not the deprecated string literal array-to-pointer conversion (4.2 conv.array ).
13.3.3.2 over.ics.rank paragraph 3 bullet 1 sub-bullet 2 says,
the rank of S1 is better than the rank of S2 (by the rules defined below)...This wording is confusing. The word "below" refers to paragraph 4 (which may not be clear), and the bulk of paragraph 4 deals with comparing conversion sequences whose "rank" is the same.
Proposed resolution (10/99): In 13.3.3.2 over.ics.rank paragraph 3, change
the rank of S1 is better than the rank of S2 (by the rules defined below)to
the rank of S1 is better than the rank of S2, or S1 and S2 have the same rank and are distinguishable by the rules in the paragraph below
14.1 temp.param paragraph 10 says:
The set of default template-arguments available for use with a template declaration or definition is obtained by merging the default arguments from the definition (if in scope) and all declarations in scope in the same way as default function arguments are (8.3.6 dcl.fct.default )."Can a default argument for a template argument appear in a friend declaration? If so, when is this default argument considered for template instantiations?
For example,
template<class T1, class T2 = int> class A; class B { template<class T1 = int, class T2> friend class A; };Is this well-formed? If it is, should the IS say when the default argument for T1 is considered for instantiations of class A?
Proposed resolution (10/99): Add to the end of 14.1 temp.param paragraph 9,
A default template-argument shall not be specified in a friend template declaration.
(See also issue 136
.)
A template is implicitly instantiated because of a "pointer conversion" on an argument. This was intended to include related-class conversions, but it also inadvertently includes conversions to void*, null pointer conversions, cv-qualification conversions and the identity conversion.
It is not clear whether a reinterpret_cast of a pointer should cause implicit instantiation.
Proposed resolution (10/99): Replace 14.7.1 temp.inst paragraph 4, up to the example, with the following:
A class template specialization is implicitly instantiated if the class type is used in a context that requires a completely-defined object type or if the completeness of the class type affects the semantics of the program. [Note: in particular, if the semantics of an expression depend on the member or base class lists of a class template specialization, the class template specialization is implicitly generated. For instance, deleting a pointer to class type depends on whether or not the class declares a destructor, and conversion between pointer to class types depends on the inheritance relationship between the two classes involved. ]
Some compilers reject the following:
struct A { template <int I> void f(); template <> void f<0>(); };on the basis of 14.7.3 temp.expl.spec paragraph 2:
An explicit specialization shall be declared in the namespace of which the template is a member, or, for member templates, in the namespace of which the enclosing class or enclosing class template is a member. An explicit specialization of a member function, member class or static data member of a class template shall be declared in the namespace of which the class template is a member. ...claiming that the specialization above is not "in the namespace of which the enclosing class ... is a member". Elsewhere, declarations are sometimes required to be "at" or "in" "namespace scope", which is not what it says here. Paragraph 17 says:
A member or a member template may be nested within many enclosing class templates. If the declaration of an explicit specialization for such a member appears in namespace scope, the member declaration shall be preceded by a template<> for each enclosing class template that is explicitly specialized.The qualification "if the declaration ... appears in namespace scope", implies that it might appear elsewhere. The only other place I can think of for a member specialization is in class scope.
Was it the intent of the committee to forbid the construction above? (Note that A itself is not a template.) If so, why?
Suggested resolution (10/99): In-class specializations of member templates are not allowed. In 14.7.3 temp.expl.spec paragraph 17, replace
If the declaration of an explicit specialization for such a member appears in namespace scope...with
In an explicit specialization for such a member...
Paragraph 12 should address partial ordering. It wasn't updated when that change was made and conflicts with 14.5.5.2 temp.func.order paragraph 1.
Proposed resolution (10/99): Remove
14.7.3 temp.expl.spec
paragraph 12 and the
example that follows.
Paragraph 4 lists contexts in which template formals are not deduced. Were template formals in an expression in the array bound of an array type specification intentionally left out of this list? Or was the intent that such formals always be explicitly specified? Otherwise I believe the following should be valid:
template <int I> class IntArr {}; template <int I, int J> void concat( int (&d)[I+J], const IntArr<I>& a, const IntArr<J>& b ) {} int testing() { IntArr<2> a; IntArr<3> b; int d[5]; concat( d, a, b ); }Can anybody shed some light on this?
From John Spicer:
Expressions involving nontype template parameters are nondeduced contexts, even though they are omitted from the list in 14.8.2.4 temp.deduct.type paragraph 4. See 14.8.2.4 temp.deduct.type paragraphs 12-14:
...
Proposed resolution (10/99): In 14.8.2.4 temp.deduct.type paragraph 4, add a third bullet:
D.2 depr.static says that declaring namespace-scope objects as static is deprecated. Declaring namespace-scope functions as static should also be deprecated.
Proposed resolution (10/99): In both 7.3.1.1 namespace.unnamed paragraph 2 and D.2 depr.static paragraph 1, replace
when declaring objects in a namespace scopewith
when declaring entities in a namespace scopeIn addition, there are a number of locations in the Standard where use of or reference to static should be reconsidered. These include:
Paragraphs 1 and 2 of 3.4.2 basic.lookup.koenig say, in part,
When an unqualified name is used as the postfix-expression in a function call (5.2.2 expr.call )... namespace-scope friend function declarations (11.4 class.friend ) not otherwise visible may be found... the set of declarations found by the lookup of the function name [includes] the set of declarations found in the... classes associated with the argument types.The most straightforward reading of this wording is that if a function of namespace scope (as opposed to a class member function) is declared as a friend in a class, and that class is an associated class in a function call, the friend function will be part of the overload set, even if it is not visible to normal lookup.
Consider the following example:
namespace A { class S; }; namespace B { void f(A::S); }; namespace A { class S { int i; friend void B::f(S); }; } void g() { A::S s; f(s); // should find B::f(A::S) }This example would seem to satisfy the criteria from 3.4.2 basic.lookup.koenig : A::S is an associated class of the argument, and A::S has a friend declaration of the namespace-scope function B::f(A::S), so Koenig lookup should include B::f(A::S) as part of the overload set in the call.
Another interpretation is that, instead of finding the friend declarations in associated classes, one only looks for namespace-scope functions, visible or invisible, in the namespaces of which the the associated classes are members; the only use of the friend declarations in the associated classes is to validate whether an invisible function declaration came from an associated class or not and thus whether it should be included in the overload set or not. By this interpretation, the call f(s) in the example will fail, because B::f(A::S) is not a member of namespace A and thus is not found by the lookup.
Proposed Resolution (10/99): The second interpretation is correct. The wording should be revised to make clear that Koenig lookup works by finding "invisible" declarations in namespace scope and not by finding friend declarations in associated classes.
(See also issues
95
,
136
,
138
,
139
,
165
, and
166
.)
In 3.4.4 basic.lookup.elab paragraph 3, there is the example
struct Base { // ... struct Data { /* ... */ }; // Defines nested Data struct Data; // OK: Redeclares nested Data };The final redeclaration is invalid according to 9.2 class.mem paragraph 1 last sentence.
See also
Core issue 36
and
Core issue 56
.
3.5 basic.link paragraph 8 says,
A name with no linkage (notably, the name of a class or enumeration declared in a local scope (3.3.2 basic.scope.local )) shall not be used to declare an entity with linkage.This wording does not, but should, prohibit use of an unnamed local type in the declaration of an entity with linkage. For example,
void f() { extern struct { } x; // currently allowed }
The C++ standard has inherited the definition of the 'exit' function more or less unchanged from ISO C.
However, when the 'exit' function is called, objects of static extent which have been initialised, will be destructed if their types posses a destructor.
In addition, the C++ standard has inherited the definition of the 'signal' function and its handlers from ISO C, also pretty much unchanged.
The C standard says that the only standard library functions that may be called while a signal handler is executing, are the functions 'abort', 'signal' and 'exit'.
This introduces a bit of a nasty turn, as it is not at all unusual for the destruction of static objects to have fairly complex destruction semantics, often associated with resource release. These quite commonly involve apparently simple actions such as calling 'fclose' for a FILE handle.
Having observed some very strange behaviour in a program recently which in handling a SIGTERM signal, called the 'exit' function as indicated by the C standard.
But unknown to the programmer, a library static object performed some complicated resource deallocation activities, and the program crashed.
The C++ standard says nothing about the interaction between signals, exit and static objects. My observations, was that in effect, because the destructor called a standard library function other than 'abort', 'exit' or 'signal', while transitively in the execution context of the signal handler, it was in fact non-compliant, and the behaviour was undefined anyway.
This is I believe a plausible judgement, but given the prevalence of this common programming technique, it seems to me that we need to say something a lot more positive about this interaction.
Curiously enough, the C standard fails to say anything about the analogous interaction with functions registered with 'atexit' ;-)
Proposed Resolution (10/98):
The current Committee Draft of the next version of the ISO C standard specifies that the only standard library function that may be called while a signal handler is executing is 'abort'. This would solve the above problem.
[This issue should remain open until it has been decided that the next
version of the C++ standard will use the next version of the C standard
as the basis for the behavior of 'signal'.]
From J16/98-0026 = WG21 N1169, "Proposed Defect Reports on ISO/IEC
14882, Programming Languages - C++":
A reference is rebindable. This is surprising and unnatural. This can also cause subtle optimizer bugs.Example:
struct T { int& ri; T (int& r) : ri (r) { } }; void bar (T*); void foo () { int i; T x (i); x.ri = 3; // the optimizer understands that this is really i = 3 bar (&x); x.ri = 4; // optimizer assumes that this writes to i, but this is incorrect } int gi; void bar (T* p) { p->~T (); new (p) T (gi); }If we replace T& with T* const in the example then undefined behavior result and the optimizer is correct.Proposal: make T& equivalent to T* const by extending the scope of 3.8 basic.life paragraph 9 to references.
(See also J16/99-0005 = WG21 N1182, "Proposed Resolutions for Core Language Issues 6, 14, 20, 40, and 89")
Proposed Resolution
Add a new bullet to the list of restrictions in 3.8 basic.life paragraph 7, following the second bullet ("the new object is of the same type..."):
Lisa Lippincott: The same argument applies to pointers to const objects of any type, not just class objects that contain const objects. Suggested rewording:
The example below is ambiguous.
struct A{ struct B{}; }; A::B C(); namespace B{ A C(); } struct Test { friend A::B ::C(); };Here, it is not clear whether the friend declaration denotes A B::C() or A::B C(), yet the standard does not resolve this ambiguity.
The ambiguity arises since both the simple-type-specifier (7.1.5.2 dcl.type.simple paragra 1) and an init-declararator (8 dcl.decl paragraph 1) contain an optional :: and an optional nested-name-specifier (5.1 expr.prim paragraph 1). Therefore, two different ways to analyse this code are possible:
simple-type-specifier = A::Bor
init-declarator = ::C()
simple-declaration = friend A::B ::C();
simple-type-specifier = ASince it is a friend declaration, the init-declarator may be qualified, and start with a global scope.
init-declarator = ::B::C()
simple-declaration = friend A ::B::C();
Suggested Resolution: In the definition of nested-name-specifier, add a sentence saying that a :: token immediately following a nested-name-specifier is always considered as part of the nested-name-specifier. Under this interpretation, the example is ill-formed, and should be corrected as either
friend A (::B::C)(); //or friend A::B (::C)();
An alternate suggestion — changing 7.1 dcl.spec to say that
The longest sequence of tokens that could possibly be a type name is taken as the decl-specifier-seq of a declaration.— is undesirable because it would make the example well-formed rather than requiring the user to disambiguate the declaration explicitly.
Christophe de Dinechin: In 5.2.2 expr.call , paragraph 2 reads:
If no declaration of the called function is visible from the scope of the call the program is ill-formed.I think nothing there or in the previous paragraph indicates that this does not apply to calls through pointer or virtual calls.
Mike Miller: "The called function" is unfortunate phraseology; it makes it sound as if it's referring to the function actually called, as opposed to the identifier in the postfix expression. It's wrong with respect to Koenig lookup, too (the declaration need not be visible if it can be found in a class or namespace associated with one or more of the arguments).
In fact, this paragraph should be a note. There's a general
rule that says you have to find an unambiguous declaration of
any name that is used
(3.4 basic.lookup
paragraph 1);
the only reason this paragraph
is here is to contrast with C's implicit declaration of called
functions.
Martin O'Riordan: Having gone through all the relevant references in the IS, it is not conclusive that a call via a pointer to a virtual member function is polymorphic at all, and could legitimately be interpreted as being static.
Consider 5.2.2 expr.call paragraph 1:
The function called in a member function call is normally selected according to the static type of the object expression (clause 10 class.derived ), but if that function is virtual and is not specified using a qualified-id then the function actually called will be the final overrider (10.3 class.virtual ) of the selected function in the dynamic type of the object expression.Here it is quite specific that you get the polymorphic call only if you use the unqualified syntax. But, the address of a member function is "always" taken using the qualified syntax, which by inference would indicate that call with a PMF is static and not polymorphic! Not what was intended.
Yet other references such as 5.5 expr.mptr.oper paragraph 4:
If the dynamic type of the object does not contain the member to which the pointer refers, the behavior is undefined.indicate that the opposite may have been intended, by stating that it is the dynamic type and not the static type that matters. Also, 5.5 expr.mptr.oper paragraph 6:
If the result of .* or ->* is a function, then that result can be used only as the operand for the function call operator (). [Example:which also implies that it is the object pointed to that determines both the validity of the expression (the static type of 'ptr_to_obj' may not have a compatible function) and the implicit (polymorphic) meaning. Note too, that this is stated in the non-normative example text.(ptr_to_obj->*ptr_to_mfct)(10);calls the member function denoted by ptr_to_mfct for the object pointed to by ptr_to_obj. ]
Andy Sawyer: Assuming the resolution is what I've assumed it is for the last umpteen years (i.e. it does the polymorphic thing), then the follow on to that is "Should there also be a way of selecting the non-polymorphic behaviour"?
Mike Miller: It might be argued that the current wording of 5.2.2 expr.call paragraph 1 does give polymorphic behavior to simple calls via pointers to members. (There is no qualified-id in obj.*pmf, and the IS says that if the function is not specified using a qualified-id, the final overrider will be called.) However, it clearly says the wrong thing when the pointer-to-member itself is specified using a qualified-id (obj.*X::pmf).
Bill Gibbons: The phrase qualified-id in 5.2.2 expr.call paragraph 1 refers to the id-expression and not to the "pointer-to-member expression" earlier in the paragraph:
For a member function call, the postfix expression shall be an implicit (9.3.1 class.mfct.nonstatic , 9.4 class.static ) or explicit class member access (5.2.5 expr.ref ) whose id-expression is a function member name, or a pointer-to-member expression (5.5 expr.mptr.oper ) selecting a function member.
Mike Miller: To be clear, here's an example:
struct S { virtual void f(); }; void (S::*pmf)(); void g(S* sp) { sp->f(); // 1: polymorphic sp->S::f(); // 2: non-polymorphic (sp->S::f)(); // 3: non-polymorphic (sp->*pmf)(); // 4: polymorphic (sp->*&S::f)(); // 5: polymorphic }
5.2.5 expr.ref paragraph 4 should make it clear that when a nonstatic member is referenced in a member selection operation, the type of the left operand is implicitly cast to the naming class of the member. This allows for the detection of access and ambiguity errors on that implicit cast.
Proposed Resolution (04/99):
A non-normative note in
11.2 class.access.base
paragraph 4 already
indicates this. Hence, the relevant part of that note should be made
normative.
Section 5.2.9 expr.static.cast paragraph 6 should make it clear that when any of the "inverse of any standard conversion sequence" static_casts are done, the operand undergoes the lvalue-to-rvalue conversions first.
Proposed Resolution (04/99): As suggested.
If a placement allocation function has default arguments for all its parameters except the first, it can be called using non-placement syntax. In such a case, it is not clear whether the deallocation function to be called if the constructor terminates by throwing an expression is determined on the basis of the syntax of the new-expression (i.e., a non-placement deallocation function) or the declaration of the selected (placement) allocation function. 5.3.4 expr.new paragraph 19 indicates that the deallocation function must match the declaration of the allocation function. However, 15.2 except.ctor says that the distinction is based on whether the new-expression contains a new-placement or not.
Proposed resolution (10/99): The statement in
15.2 except.ctor
should be revised to
conform with the one in
5.3.4 expr.new
.
Nathan Myers: In 5.10 expr.eq , we have:
Pointers to objects or functions of the same type (after pointer conversions) can be compared for equality. Two pointers of the same type compare equal if and only if they are both null, both point to the same object or function, or both point one past the end of the same array.What does this say, when we have
int i[1]; int j[1];about the expression (i+1 == j) ? It seems to require padding between i[0] and j[0] so that the comparison will come out false.
Mike Miller: I think this is reading more into the statement in 5.10 expr.eq paragraph 1 than is actually there. What does it mean for a pointer to "point to" an object? I can't find anything that definitively says that i+1 cannot "point to" j[0] (although it's obviously not required to do so). If i+1 is allowed to "point to" j[0], then i+1==j is allowed to be true, and there's no defect. There are places where aliasing is forbidden, but the N+1th element of an array doesn't appear to be one of them.
To put it another way, "points to" is undefined in the Standard. The only definition I can think of that encompasses the possible ways in which a pointer can get its value (e.g., the implementation-defined conversion of an arbitrary integer value to a pointer) is that it means "having the same value representation as would be produced by applying the (builtin) & operator to an lvalue expression designating that object". In other words, if the bits are right, it doesn't matter how you produced the value, as long as you didn't perform any operations that have undefined results. The expression i+1 is not undefined, so if the bits of i+1 are the same as those of &j[0], then i+1 "points to" j[0] and i+i==j is allowed to be true.
Tom MacDonald: C9X contains the following words for the "==" operator:
Two pointers compare equal if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.Matt Austern: I don't think there's anything wrong with saying that the result of
int x[1]; int y[1]; std::cout << (y == x + 1) << std::endl;is implementation defined, or even that it's undefined.
Mike Miller: A similar question could be raised about different objects that (sequentially) share the same storage. Consider the following:
struct B { virtual void f(); }; struct D1: B { }; struct D2: B { }; void g() { B* bp1 = new D1; B* bp2 = new (bp1) D2; bp1 == bp2; // ??? }Section 3.8 basic.life paragraph 5 does not list this kind of comparison among the pointer operations that cause undefined behavior, so presumably the comparison is allowed. However, 5.10 expr.eq paragraph 1 describes pointer comparison in terms of "[pointing] to the same object," which bp1 and bp2 clearly do not do. How should we describe the result of this comparison?
Jason Merrill: When you consider comparing pointers to void, this seems to suggest that no two objects can have the same address, depending on your interpretation of "point to the same object." This would cripple the empty base optimization.
3.9.2 basic.compound refers to 'pointers to void or objects or functions'. In that case, 5.10 expr.eq does not allow you to compare them; it only allows comparing pointers to objects and functions.
Proposed Resolution (04/99): An implementation should not be
forced to provide padding at the end of arrays; however
coming up with the correct words is tricky. In
particular, the C9X wording does not seem satisfactory.
Clark Nelson
pointed out that
1.7 intro.memory
may be a
good place to start with the drafting task.
struct S { static const int c = 5; }; int a[S::c]; // error: S::c not in scopeIs this restriction intentional? If so, what was the rationale for the restriction?
Bjarne Stroustrup: I think that once you have said S::, c is in scope so that
int a[S::c];is ok.
Mike Miller: I'd like to think that's what it meant, but I don't believe that's what it said. According to 3.3 basic.scope paragraph 1, the scope of a name is the region "in which that name may be used as an unqualified name." You can, indeed, use a qualified name to refer to a name that is not in scope, but that only goes to reinforce my point that "S::c" is not in scope at the point where the expression containing it is used. I think the phrase "within its scope" is at best misleading and should be removed. (Unless there's a reason I'm missing for restricting the use of static member constants to their scope.)
As far as I can tell from 5.19 expr.const paragraph 2, "arithmetic constant expressions" (as distinct from "integral constant expressions") are used only in static initializers to distinguish between static and dynamic initialization. They include floating point types and exclude non-type template parameters, as well as the const variables and static data members.
I'm guessing that should be "non-static member," like the similar prohibition in 12.7 class.cdtor regarding out-of-lifetime access to members of non-POD class objects.
Proposed Resolutions (04/99):
Mike Ball: I cannot find anything in the standard that tells me the meaning of a storage-class-specifier on a function template declaration. In particular, there is no indication what effect, if any, it has on the storage class of the instantiations.
There is an explicit prohibition of storage-class-specifiers on explicit specializations.
For example, if we have
template<class T> static int foo(T) { return sizeof(T); }does this generate static functions for all instantiations? By 7.1.1 dcl.stc the storage class applies to the name declared in the declarator, which is the template foo, not an instantiation of foo, which is named with a template-id. There is a statement in clause 14 that template names have linkage, which supports the contention that "static" applies to the template, not to instantiations.
So what does the specifier mean? Lacking a direct statement in the standard, I see the following posibilities, in my preference order.
From John Spicer
The standard does say that a namespace scope template has external linkage unless it is a function template declared "static". It doesn't explicitly say that the linkage of the template is also the linkage of the instantiations, but I believe that is the intent. For example, a storage class is prohibited on an explicit specialization to ensure that a specialization cannot be given a different storage class than the template on which it is based.
Mike: This makes sense, but I couldn't find much support in the document. Sounds like yet another interpretation to add to the list.The standard does not talk about the linkage of instantiations, because only "names" are considered to have linkage, and instances are not really names. So, from an implementation point of view, instances have linkage, but from a language point of view, only the template from which the instances are generated has linkage.John: Agreed.
Mike: Which is why I think it would be cleaner to eliminate storage class specifiers entirely and rely on the unnamed namespace. There is a statement that specializations go into the namespace of the template. No big deal, it's not something it says, so we live with what's there."export" is an additional attribute that is separate from linkage, but that can only be applied to templates with external linkage.John: That would mean prohibiting static function templates. I doubt those are common, but I don't really see much motivation for getting rid of them at this point.
Mike: I can't find that restriction in the standard, though there is one that templates in an unnamed namespace can't be exported. I'm pretty sure that we intended it, though.John: I can't find it either. The "inline" case seems to be addressed, but not static. Surely this is an error as, by definition, a static template can't be used from elsewhere.
I can't find the answer to the following in the standard. Does anybody have a reference?
The syntax for elaborated type specifier is
class foo<int> // foo is a templateOn the other hand, a friend declaration seems to require this production,
An elaborated-type-specifier shall be used in a friend declaration for a class.*And in 14.5.3 temp.friend we find the example[Footnote: The class-key of the elaborated-type-specifier is required. —end footnote]
[Example:Is there some special dispensation somewhere to allow the syntax in this context? Is there something I've missed about elaborated-type-specifier? Is it just another bug in the standard?template<class T> class task; template<class T> task<T>* preempt(task<T>*); template<class T> class task { // ... friend void next_time(); friend void process(task<T>*); friend task<T>* preempt<T>(task<T>*); template<class C> friend int func(C); friend class task<int>; template<class P> friend class frd; // ... };
Proposed Resolution (04/99): This is a bug. Among others, 7.1.5.3 dcl.type.elab should be updated (perhaps using the grammatical construct class-name).
An additional problem was reported via comp.std.c++: the grammar does not allow the following example:
namespace A{ class B{}; }; namespace B{ class A{}; class C{ friend class ::A::B; }; };
(Previously numbered 864.)
7.5 dcl.link paragraph 6 says the following:
extern "C" { static void f(int) {} static void f(float) {} };Can a function with internal linkage "have C linkage" at all (assuming that phrase means "has extern "C" linkage"), for how can a function be extern "C" if it's not extern? The function type can have extern "C" linkage -- but I think that's independent of the linkage of the function name. It should be perfectly reasonable to say, in the example above, that extern "C" applies only to the types of f(int) and f(float), not to the function names, and that the rule in 7.5 dcl.link paragraph 6 doesn't apply.
Suggested resolution: The extern "C" linkage specification applies only to the type of functions with internal linkage, and therefore some of the rules that have to do with name overloading don't apply.
Proposed Resolution:
The intent is to distingush implicit linkage from explicit linkage for both name linkage and language (function type) linkage. (It might be more clear to use the terms name linkage and type linkage to distinguish these concepts. A function can have a name with one kind of linkage and a type with a different kind of linkage. The function itself has no linkage: it has no name, only the declaration has a name. This becomes more obvious when you consider function pointers.)
The tentatively agreed proposal is to apply implicit linkage to names declared in brace-enclosed linkage specifications and to non-top-level names declared in simple linkage specifications; and to apply explicit linkage to top-level names declared in simple linkage specifications.
The language linkage of any function type formed through a function declarator is that of the nearest enclosing linkage-specification. For purposes of determining whether the declaration of a namespace-scope name matches a previous declaration, the language linkage portion of the type of a function declaration (that is, the language linkage of the function itself, not its parameters, return type or exception specification) is ignored.
For a linkage-specification using braces, i.e.
extern string-literal { declaration-seqopt }the linkage of any declaration of a namespace-scope name (including local externs) which is not contained in a nested linkage-specification, is not declared to have no linkage (static), and does not match a previous declaration is given the linkage specified in the string-literal. The language linkage of the type of any function declaration of a namespace-scope name (including local externs) which is not contained in a nested linkage-specification and which is declared with function declarator syntax is the same as that of a matching previous declaration, if any, else is specified by string-literal.
For a linkage-specification without braces, i.e.
extern string-literal declarationthe linkage of the names declared in the top-level declarators of declaration is specified by string-literal; if this conflicts with the linkage of any matching previous declarations, the program is ill-formed. The language linkage of the type of any top-level function declarator is specified by string-literal; if this conflicts with the language linkage of the type of any matching previous function declarations, the program is ill-formed. The effect of the linkage-specification on other (non top-level) names declared in declaration is the same as that of the brace-enclosed form.
[This needs additional drafting work.]
Consider the following:
extern "C" void foo() { extern void bar(); bar(); }Does "bar()" have "C" language linkage?
The ARM is explicit and says
A linkage-specification for a function also applies to functions and objects declared within it.The DIS says
In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names, and variable names introduced by the declaration(s).Is the body of a function definition part of the declaration?
From Mike Miller:
Yes: from 7 dcl.dcl paragraph 1,
From Dag Brück:
Consider the following where extern "C" has been moved to a separate declaration:
extern "C" void foo(); void foo() { extern void bar(); bar(); }I think the ARM wording could possibly be interpreted such that bar() has "C" linkage in my example, but not the DIS wording.
As a side note, I have always wanted to think that placing extern "C" on a function definition or a separate declaration would produce identical programs.
Proposed Resolution:
See the proposed resolution for Core issue 4 , which covers this case.
The ODR should also be checked to see whether it addresses name and
type linkage.
8.3 dcl.meaning paragraph 1 says:
In the qualified declarator-id for a class or namespace member definition that appears outside of the member's class or namespace, the nested-name-specifier shall not name any of the namespaces that enclose the member's definition.This results in the following behavior:
namespace N { namespace M { void f(); void g(); } void M::f(){} // okay void N::M::g(){} // error }I was very surprised when this rule was pointed out to me. The change appears to have been introduced around the time of the first Santa Cruz meeting, but I don't recall discussion of it and could not find a motion related to it.
Regardless of where it came from, I also can't understand why it is there. Certainly it shouldn't matter how you name a given class or namespace.
For example, the standard permits:
namespace N { namespace M { void f(); void g(); } namespace X = M; namespace Y = N::M; void X::f(){} // okay void Y::g(){} // okay }So, it is okay to use an alias for N::M, but not to use N::M directly. Note that it is okay to use N::M in any other context at this point in the program (i.e., the rule is a specific restriction on declarator names, not a general rule on the use of qualified names).
Does anyone recall the intent of this rule or any rationale for its
existence?
8.3.5 dcl.fct paragraph 3 says,
All declarations for a function with a given parameter list shall agree exactly both in the type of the value returned and in the number and type of parameters.It is not clear what this requirement means with respect to a pair of declarations like the following:
int f(const int); int f(int x) { ... }Do they violate this requirement? Is x const in the body of the function declaration?
Tom Plum: I think the FDIS quotation means that the pair of decls are valid. But it doesn't clearly answer whether x is const inside the function definition. As to intent, I know the intent was that if the function definition wants to specify that x is const, the const must appear specifically in the defining decl, not just on some decl elsewhere. But I can't prove that intent from the drafted words.
Mike Miller: I think the intent was something along the following lines:
Two function declarations denote the same entity if the names are the same and the function signatures are the same. (Two function declarations with C language linkage denote the same entity if the names are the same.) All declarations of a given function shall agree exactly both in the type of the value returned and in the number and type of parameters; the presence or absence of the ellipsis is considered part of the signature.(See 3.5 basic.link paragraph 9. That paragraph talks about names in different scopes and says that function references are the same if the "types are identical for purposes of overloading," i.e., the signatures are the same. See also 7.5 dcl.link paragraph 6 regarding C language linkage, where only the name is required to be the same for declarations in different namespaces to denote the same function.)
According to this paragraph, the type of a parameter is determined by considering its decl-specifier-seq and declarator and then applying the array-to-pointer and function-to-pointer adjustments. The cv-qualifier and storage class adjustments are performed for the function type but not for the parameter types.
If my interpretation of the intent of the second sentence of the paragraph is correct, the two declarations in the example violate that restriction -- the parameter types are not the same, even though the function types are. Since there's no dispensation mentioned for "no diagnostic required," an implementation presumably must issue a diagnostic in this case. (I think "no diagnostic required" should be stated if the declarations occur in different translation units -- unless there's a blanket statement to that effect that I have forgotten?)
(I'd also note in passing that, if my interpretation is correct,
void f(int); void f(register int) { }is also an invalid pair of declarations.)
Proposed resolution (10/99): The declarations are
consistent, and the const qualification applies to the
parameter inside the function only if it appears in the parameter
declaration in the function definition.
3.3 basic.scope paragraph 4 says:
Given a set of declarations in a single declarative region, each of which specifies the same unqualified name,8.3.6 dcl.fct.default paragraph 9 says:
- they shall all refer to the same entity, or all refer to functions ...
When a declaration of a function is introduced by way of a using-declaration (7.3.3 namespace.udecl , any default argument information associated with the declaration is imported as well.This is not really clear regarding what happens in the following case:
namespace A { extern "C" void f(int = 5); } namespace B { extern "C" void f(int = 7); } using A::f; using B::f; f(); // ???Proposed Resolution:
Add the following at the end of 13.3.3 over.match.best :
If the best viable function resolves to a function for which multiple declarations were found, and if at least two of these 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]
This wording needs some work to "see through" using-declarations
— that is, the "declarations found" here are the
using-declarations themselves, not the function declarations
with the default arguments.
The description of copy-initialization in 8.5 dcl.init paragraph 14 says:
struct A { A(A&); }; struct B : A { }; struct C { operator B&(); }; C c; const A a = c; // allowed?The temporary created with the conversion function is an lvalue of type B. If the temporary must have the cv-qualifiers of the destination type (i.e. const) then the copy-constructor for A cannot be called to create the object of type A from the lvalue of type const B. If the temporary has the cv-qualifiers of the result type of the conversion function, then the copy-constructor for A can be called to create the object of type A from the lvalue of type const B. This last outcome seems more appropriate.
Proposed Resolution:
As above.
When the Committee considered issue 35 , another context in which value initialization might be relevant was overlooked: mem-initializers. It would seem reasonable that if T() as an expression invokes value initialization, that the same syntactic construct in a mem-initializer-list would do the same, and the usefulness of value initialization in that context is at least as great as the standalone case.
Proposed resolution (10/99): Specify that an empty
initializer, i.e., (), always means value initialization,
whether it occurs in explicit temporary creation, as a
new-initializer, or as a mem-initializer.
The ambiguity text in 10.2 class.member.lookup may not say what we intended. It makes the following example ill-formed:
struct A { int x(int); }; struct B: A { using A::x; float x(float); }; int f(B* b) { b->x(3); // ambiguous }This is a name lookup ambiguity because of 10.2 class.member.lookup paragraph 2:
... Each of these declarations that was introduced by a using-declaration is considered to be from each sub-object of C that is of the type containing the declaration designated by the using-declaration. If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed.This contradicts the text and example in paragraph 12 of 7.3.3 namespace.udecl .
Proposed Resolution:
The above example should be well-formed.
The definition of "friend" in 11.4 class.friend says:
A friend of a class is a function or class that is not a member of the class but is permitted to use the private and protected member names from the class. ...A nested class, i.e. INNER in the example below, is a member of class OUTER. The sentence above states that it cannot be a friend. I think this is a mistake.
class OUTER { class INNER; friend class INNER; class INNER {}; };
12.2 class.temporary paragraph 4 seems self-contradictory:
the temporary that holds the result of the expression shall persist until the object's initialization is complete... the temporary is destroyed after it has been copied, before or when the initialization completes.How can it be destroyed "before the initialization completes" if it is required to "persist until the object's initialization is complete?"
Jack Rouse: 12.2 class.temporary states that temporary objects will normally be destroyed at the end of the full expression in which they are created. This can create some unique code generation requirements when initializing a class array with a default constructor that uses a default argument. Consider the code:
struct T { int i; T( int ); ~T(); }; struct S { S( int = T(0).i ); ~S(); }; S* f( int n ) { return new S[n]; }The full expression allocating the array in f(int) includes the default constructor for S. Therefore according to 1.9 intro.execution paragraph 14, it includes the default argument expression for S(int). So evaluation of the full expression should include evaluating the default argument "n" times and creating "n" temporaries of type T. But the destruction of the temporaries must be delayed until the end of the full expression so this requires allocating space at runtime for "n" distinct temporaries. It is unclear how these temporaries are supposed to be allocated and deallocated. They cannot readily be autos because a variable allocation is required.
I believe that many existing implementations will destroy the temporaries needed by the default constructor after each array element is initialized. But I can't find anything in the standard that allows the temporaries to be destroyed early in this case.
I think the standard should allow the early destruction of temporaries
used in the default initialization of class array elements. I believe
early destruction is the status quo, and I don't think the users of
existing C++ compilers have been adversely impacted by it.
The phrase "template function" is sometimes used to refer to a template (e.g., in 14 temp paragraph 8) and sometimes to refer to a function generated from a template (e.g., 13.4 over.over paragraph 4).
Suggested Resolution:
The phrase should mean "a function generated from a template"
(or might perhaps include explicit specializations).
14 temp paragraph 2 says,
[Note: in a class template declaration, if the declarator-id is a template-id, the declaration declares a class template partial specialization (14.5.4 temp.class.spec ). ]There is no declarator-id in a class template declaration (cf paragraph 3). 14.5.4 temp.class.spec paragraph 1 uses the phrase "in which the class template name is a template-id," and the same terminology ought to be used here.
It appears from the grammar that explicit template arguments cannot be specified for overloaded operator names. Does this mean that template operators can never be friends?
But assuming that I read things wrong, then I should be able to specify a global template 'operator +' by writing:
friend A::B operator + <>(char&);From John Spicer:
You should be able to have explicit template arguments on operator function, but the grammar does seem to prohibit it (unless I'm reading it incorrectly). This is an error in the grammar, they should be permitted.
Tentative Resolution:
As suggested.
Section 14.3.1 temp.arg.type paragraph 2 says
A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template type-parameter.It probably wasn't intended that classes with unnamed members should be included in this list, but they are arguably compounded from unnamed types.
The wording in 14.6 temp.res paragraph 3:
A qualified-name that refers to a type and that depends on a template-parameter (14.6.2 temp.dep ) shall be prefixed by the keyword typename to indicate that the qualified-name denotes a type, forming an elaborated-type-specifier (7.1.5.3 dcl.type.elab ).was intended to say:
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 ) shall ...in much the same vein as 14.6.2.1 temp.dep.type , second bullet, first half.
Mark Mitchell (via John Spicer): Given:
template <class T> struct S { struct I1 { typedef int X; }; struct I2 : public I1 { X x; }; };
Is this legal? The question really boils down to asking whether or not I1 is a dependent type. On the one hand, it doesn't seem to fit any of the qualifications in 14.6.2.1 temp.dep.type . On the other, 14.7.3 temp.expl.spec allows explicit specialization of a member class of a class template, so something like:
template <> struct S<double>::I1 { int X; };
is apparently legal. But, then, `X' no longer refers to a type name. So, it seems like `I1' should be classified as dependent. What am I missing?
Erwin Unruh: I wrote that particular piece of text and I just
missed the problem above. It is intended to be a dependent type. The
reasoning is that I1 is just a shorthand for
S
Suggested Resolution: (Erwin Unruh)
I think the list of what is a dependent type should be extended to cover
"a type declared and used within the same template" modulo of phrasing.
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.
At the top of clause 15, in paragraph 2, it says:
A goto, break, return, or continue statement can be used to transfer control out of a try block or handler, but not into one.What about switch statements?
switch ( f() ) { case 1: try { g(); case 2: h(); } catch (...) { // handler } break; }Daveed Vandevoorde:
Consider:
void f() { try { label: ; } catch(...) { goto label; } }Now the phrase "try block" (without a hyphen) is used in paragraph 1 in a way that causes me to think that it is not intended to include the corresponding handlers. On the other hand, the grammar entity "try-block" (with hyphen) does include the handlers. So is the intent to prohibit the above or not?
Suggested Resolution (John Spicer
):
Our interpretation has been that all transfers into try blocks and handlers
are prohibited.
In 15.4 except.spec paragraph 2:
An exception-specification shall appear only on a function declarator in a function, pointer, reference or pointer to member declaration or definition.Does that mean in the top-level function declarator, or one at any level? Can one, for example, specify an exception specification on a pointer-to-function parameter of a function?
void f(int (*pf)(float) throw(A))Suggested answer: no. The exception specifications are valid only on the top-level function declarators.
However, if exception specifications are made part of a function's type as has been tentatively agreed, they would have to be allowed on any function declaration.
Proposed resolution (10/99): There is already an example of an exception specification for a parameter in the example in 15.4 except.spec paragraph 1. The intent is that an exception specification may appear at any level in a declaration at which a function declarator occurs.
(See also 25
,
92
, and
133
.)
The standard is inconsistent about constness inside exception specifications.
struct X {}; struct Y:X {}; const Y bar() {return Y();} void foo()throw(const X) { throw bar(); }It is unclear whether calling foo will result in a call to unexpected. According to 15.4 except.spec paragraph 7, only two cases are treated specially with regard to inheritance: If "class X" appears in the type-id-list, or if "class X*" appears in the type-id-list. Neither is the case here, so foo only allows exceptions of the same type (const X). As a result, unexpected should be called.
On the other hand, the intent of exception specification appears to allow an implementation of this example as
void foo() try{ throw bar(); }catch(const X){ throw; }catch(...){ std::unexpected(); }According to 15.3 except.handle , this replacement code would catch the exception, so unexpected would not be called.
Suggested resolution: Change 15.4 except.spec paragraph 7 to read
A function is said to allow all exception objects of all types E for which one of the types T in the type-id-list would be a handler, according to 15.3 except.handle .
D.1 depr.post.incr
indicates that use of the postfix ++ with a bool
operand is
deprecated.
Annex D depr
says nothing about prefix ++. However, this
use of prefix ++ is also deprecated, according to
5.3.2 expr.pre.incr
paragraph 1.
Presumably D.1 depr.post.incr
should be expanded to cover prefix ++, or another
section should be added to Annex
D depr
.
The main defect is in the library, where the binder template can easily lead to reference-to-reference situations. See also paper J16/99-0011 = WG21 N1188.
Andrew Koenig illustrates the problem with the following example:
int& f(); template<typename T> void poof(T (*)(), T&); int main() { int n; poof(&f, n); // Second parameter of type int & &? }
Suggested Resolution:
As suggested: a reference-to-reference-to-T should be
equivalent to a reference-to-T. However, such multi-level
references would be allowed only in types constructed via typedef
and template argument substitution, not directly. In addition to
being analogous to the treatment of cv-qualifiers, this restriction
avoids the lexical surprise of && being treated as
a logical-and rather than as a reference-to-reference (per Daveed
Vandevoorde).
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.2 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.5.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.
The nonterminals operator and punctuator in 2.6 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.12 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:
The example in 3.4.1 basic.lookup.unqual paragraph 3 is incorrect:
typedef int f; struct A { friend void f(A &); operator int(); void g(A a) { f(a); } };Regardless of the resolution of other issues concerning the lookup of names in friend declarations, this example is ill-formed (the function and the typedef cannot exist in the same scope).
One possible repair of the example would be to make f a class with a constructor taking either A or int as its parameter.
(See also issues
95
,
136
,
138
,
143
,
165
, and
166
.)
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; };
3.4.5 basic.lookup.classref paragraph 1 says,
In a class member access expression (5.2.5 expr.ref ), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2 temp.names ) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class or function template.There do not seem to be any circumstances in which use of a non-member template function would be well-formed as the id-expression of a class member access expression.
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.
Jack Rouse: 3.8 basic.life paragraph 1 includes:
The lifetime of an object is a runtime property of the object. The lifetime of an object of type T begins when:Consider the code:
- storage with the proper alignment and size for type T is obtained, and
- if T is a class type with a non-trivial constructor (12.1 class.ctor ), the constructor call has completed.
struct B { B( int = 0 ); ~B(); }; struct S { B b1; }; int main() { S s = { 1 }; return 0; }In the code above, class S does have a non-trivial constructor, the default constructor generated by the compiler. According the text above, the lifetime of the auto s would never begin because a constructor for S is never called. I think the second case in the text needs to include aggregate initialization.
Mike Miller: I see a couple of ways of fixing the problem. One way would be to change "the constructor call has completed" to "the object's initialization is complete."
Another would be to add following "a class type with a non-trivial constructor" the phrase "that is not initialized with the brace notation (8.5.1 dcl.init.aggr )."
The first formulation treats aggregate initialization like a
constructor call; even POD-type members of an aggregate could
not be accessed before the aggregate initialization completed.
The second is less restrictive; the POD-type members of the
aggregate would be usable before the initialization, and the
members with non-trivial constructors (the only way an
aggregate can acquire a non-trivial constructor) would be
protected by recursive application of the lifetime rule.
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.10 basic.lval paragraph 15 doesn't take into consideration 4.4 conv.qual . Why this matters is because:
extern "C" int printf(const char *, ... ); main() { int i = 1; int *ip = &i; const int *const *p1; int **p2; p1 = &ip; p2 = &ip; *p2 = (int *)42; printf("%d\n", *p1); }won't get 42 without it. Since the conversion is permitted, we must give it defined semantics, hence we need to fix the wording in 3.10 basic.lval to include all possible conversions of the type via 4.4 conv.qual .
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.)
5.1 expr.prim paragraph 11 reads,
A template-id shall be used as an unqualified-id only as specified in 14.7.2 temp.explicit , 14.7 temp.spec , and 14.5.4 temp.class.spec .What uses of template-ids as unqualified-ids is this supposed to prevent? And is the list of referenced sections correct/complete? For instance, what about 14.8.1 temp.arg.explicit , "Explicit template argument specification?" Does its absence from the list in 5.1 expr.prim paragraph 11 mean that "f<int>()" is ill-formed? This is even more confusing when you recall that unqualified-ids are contained in qualified-ids:
qualified-id: ::opt nested-name-specifier templateopt unqualified-idIs the wording intending to say "used as an unqualified-id that is not part of a qualified-id?" Or something else?
It is currently not permitted to cast directly between a pointer to function type and a pointer to object type. This conversion is not listed in 5.2.9 expr.static.cast and 5.2.10 expr.reinterpret.cast and thus requires a diagnostic to be issued. However, if a sufficiently long integral type exists (as is the case in many implementations), it is permitted to cast between pointer to function types and pointer to object types using that integral type as an intermediary.
In C the cast results in undefined behavior and thus does not require a diagnostic, and Unix C compilers generally do not issue one. This fact is used in the definition of the standard Unix function dlsym, which is declared to return void* but in fact may return either a pointer to a function or a pointer to an object. The fact that C++ compilers are required to issue a diagnostic is viewed as a "competitive disadvantage" for the language.
Suggested resolution: Add wording to 5.2.10 expr.reinterpret.cast allowing conversions between pointer to function and pointer to object types, if the implementation has an integral data type that can be used as an intermediary.
Several points were raised in opposition to this suggestion:
Martin O'Riordan suggested an alternative approach:
The advantage of this approach is that it would permit writing portable, well-defined programs involving such conversions. However, it breaks the current degree of compatibility between old and new casts, and it adds functionality to dynamic_cast which is not obviously related to its current meaning.
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::*
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.3.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[](size_t) or operator new[](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.
5.7 expr.add paragraph 8 explicitly allows subtraction of two pointers to functions:
If two pointers point to the same object or function... and the two pointers are subtracted...However, 5.7 expr.add paragraph 2 requires that two pointers that are subtracted be pointers to an object type; function pointers are not allowed.
Being able to subtract two pointers to functions doesn't seem terribly useful, especially considering that subtracting two pointers to different functions appears to produce undefined behavior rather than simply a non-zero result, according to paragraph 6:
Unless both pointers point to elements of the same array object, or one past the last element of the array object, the behavior is undefined.
Suggested resolution: Remove the words or function
from paragraph 8.
Given
char arr[100]; sizeof(0,arr);What does the sizeof expression return? According to 5.18 expr.comma paragraph 1, the comma operator yields an lvalue if the second argument is an lvalue. Since 4.2 conv.array paragraph 1 says that the array-to-pointer conversion yields an rvalue, it seems that sizeof should see an array type and give the answer 100. If so, the value of the sizeof expression would be different from that of the corresponding expression in C, but there is nothing in Annex C diff to indicate that an incompatible change was intended.
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.
7.1.5.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?
According to 7.2 dcl.enum paragraph 5, the underlying type of an enum is an unspecified integral type, which could potentially be unsigned int. The promotion rules in 4.5 conv.prom paragraph 2 say that such an enumeration value used in an expression will be promoted to unsigned int. This means that a conforming implementation could give the value false for the following code:
enum { zero }; -1 < zero; // might be falseThis is counterintuitive. Perhaps the description of the underlying type of an enumeration should say that an unsigned underlying type can be used only if the values of the enumerators cannot be represented in the corresponding signed type. This approach would be consistent with the treatment of integral promotion of bitfields (4.5 conv.prom paragraph 3).
On a related note, 7.2 dcl.enum paragraph 5 says,
the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int.This specification does not allow for an enumeration like
enum { a = -1, b = UINT_MAX };Since each enumerator can fit in an int or unsigned int, the underlying type is required to be no larger than int, even though there is no such type that can represent all the enumerators.
See also issue 58
.
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.
(See also issues
95
,
136
,
139
,
143
,
165
, and
166
.)
Steve Clamage: I can't find anything in the standard that prohibits a language linkage on an operator function. For example:
extern "C" int operator+(MyInt, MyInt) { ... }
Clearly it is a bad idea, you could have only one operator+ with "C" linkage in the entire program, and you can't call the function from C code.
Mike Miller: Well, you can't name an operator function in C code, but if the arguments are compatible (e.g., not references), you can call it from C code via a pointer. In fact, because the language linkage is part of the function type, you couldn't pass the address of an operator function into C code unless you could declare the function to be extern "C".
Fergus Henderson: In the general case, for linkage to languages other than C, this could well make perfect sense.
Steve Clamage:
But is it disallowed (as opposed to being stupid), and if so, where in the standard does it say so?
Mike Miller: I don't believe there's a restriction. Whether that is because of the (rather feeble) justification of being able to call an operator from C code via a pointer, or whether it was simply overlooked, I don't know.
Fergus Henderson: I don't think it is disallowed. I also don't think there is any need to explicitly disallow it.
Steve Clamage: I don't think the standard is clear enough on this point. I'd like to see a clarification.
I think either of these two clarifications would be appropriate:
extern "C" T operator+(T,T); // ok extern "C" T operator-(T,T); // ok extern "C" U operator-(U); // error, two extern "C" operator-
Mike Miller: I think the point here is that something like
extern "xyzzy" bool operator<(S&,S&)could well make sense, if language xyzzy is sufficiently compatible with C++, and the one-function rule only applies to extern "C", not to other language linkages. Given that it might make sense to have general language linkages for operators, is it worthwhile to make an exception to the general rule by saying that you can have any language linkage on an operator function except "C" linkage? I don't like exceptions to general rules unless they're very well motivated, and I don't see sufficient motivation to make one here.
Certainly this capability isn't very useful. There are lots of things in C++ that aren't very useful but just weren't worth special-casing out of the language. I think this falls into the same category.
Mike Ball: I DON'T want to forbid operator functions within an extern "C". Rather I want to add operator functions to that sentence in paragraph 4 of 7.5 dcl.link which reads
A C language linkage is ignored for the names of class members and the member function type of class member functions.My reason is simple: C linkage makes a total hash of scope. Any "C" functions declared with the same name in any namespace scope are the same function. In other words, namespaces are totally ignored.
This provision was added in toward the end of the standardization process, and was, I thought, primarily to make it possible to put the C library in namespace std. Otherwise, it seems an unwarrented attack on the very concept of scope. We (wisely) didn't force this on static member functions, since it would essentially promote them to the global scope.
Now I think that programmers think of operator functions as essentially part of a class. At least for one very common design pattern they are treated as part of the class interface. This pattern is the reason we invented Koenig lookup for operator functions.
What happens when such a class definition is included, deliberately or not, in an extern "C" declaration? The member operators continue to work, but the non-member operators can suddenly get strange and hard to understand messages. Quite possibly, they get the messages only when combined with other classes in other compilation units. You can argue that the programmer shouldn't put the class header in a linkage delaration in the first place, but I can still find books that recommend putting `extern "C"' around entire header files, so it's going to happen.
I think that including operator functions in the general exclusion from
extern "C" doesn't remove a capability, rather it ensurs a capability
that programmers already think they have.
8.2 dcl.ambig.res paragraph 3 shows an example that includes <cstddef> with no using declarations or directives and refers to size_t without the std:: qualification.
Many references to size_t throughout the document omit the std:: namespace qualification.
This is a typical case. The use of std:: is inconsistent throughout the document.
In addition, the use of exception specifications should be
examined for consistency.
Steve Clamage: Section 8.3.4 dcl.array paragraph 1 reads in part as follows:
Any type of the form "cv-qualifier-seq array of N T" is adjusted to "array of N cv-qualifier-seq T," and similarly for "array of unknown bound of T." [Example:The Note appears to contradict the sentence that precedes it.typedef int A[5], AA[2][3]; typedef const A CA; // type is "array of 5 const int" typedef const AA CAA; // type is "array of 2 array of 3 const int"—end example] [Note: an "array of N cv-qualifier-seq T" has cv-qualified type; such an array has internal linkage unless explicitly declared extern (7.1.5.1 dcl.type.cv ) and must be initialized as specified in 8.5 dcl.init . ]
Mike Miller: I disagree; all it says is that whether the qualification on the element type is direct ("const int x[5]") or indirect ("const A CA"), the array itself is qualified in the same way the elements are.
Steve Clamage: In addition, section 3.9.3 basic.type.qualifier paragraph 2 says:
A compound type (3.9.2 basic.compound ) is not cv-qualified by the cv-qualifiers (if any) of the types from which it is compounded. Any cv-qualifiers applied to an array type affect the array element type, not the array type (8.3.4 dcl.array )."The Note appears to contradict that section as well.
Mike Miller: Yes, but consider the last two sentences of 3.9.3 basic.type.qualifier paragraph 5:
Cv-qualifiers applied to an array type attach to the underlying element type, so the notation "cv T," where T is an array type, refers to an array whose elements are so-qualified. Such array types can be said to be more (or less) cv-qualified than other types based on the cv-qualification of the underlying element types.I think this says essentially the same thing as 8.3.4 dcl.array paragraph 1 and its note: the qualification of an array is (bidirectionally) equivalent to the qualification of its members.
Mike Ball: I find this a very far reach. The text in 8.3.4 dcl.array is essentially that which is in the C standard (and is a change from early versions of C++). I don't see any justification at all for the bidirectional equivalence. It seems to me that the note is left over from the earlier version of the language.
Steve Clamage: Finally, the Note seems to say that the declaration
volatile char greet[6] = "Hello";gives "greet" internal linkage, which makes no sense.
Have I missed something, or should that Note be entirely removed?
Mike Miller: At least the wording in the note should be repaired not to indicate that volatile-qualification gives an array internal linkage. Also, depending on how the discussion goes, either the wording in 3.9.3 basic.type.qualifier paragraph 2 or in paragraph 5 needs to be amended to be consistent regarding whether an array type is considered qualified by the qualification of its element type.
Steve Adamczyk pointed out that
the current state of affairs resulted from the need to handle
reference binding consistently. The wording is intended to define
the question, "Is an array type cv-qualified?" as being equivalent
to the question, "Is the element type of the array cv-qualified?"
8.3.6 dcl.fct.default paragraph 4 says,
For non-template functions, default arguments can be added in later declarations of a function in the same scope. Declarations in different scopes have completely distinct sets of default arguments. That is, declarations in inner scopes do not acquire default arguments from declarations in outer scopes, and vice versa.It is unclear how this wording applies to friend function declarations. For example,
void f(int, int, int=0); // #1 class C { friend void f(int, int=0, int); // #2 }; void f(int=0, int, int); // #3Does the declaration at #2 acquire the default argument from #1, and does the one at #3 acquire the default arguments from #2?
There are several related questions involved with this issue:
Mike Miller: 8.3.6 dcl.fct.default paragraph 4 is speaking about the lexical location of the declaration... The friend declaration occurs in a different declarative region from the declaration at #1, so I would read [this paragraph] as saying that it starts out with a clean slate of default arguments.
Bill Gibbons: Yes. It occurs in a different region, although it declares a name in the same region (i.e. a redeclaration). This is the same as with local externs and is intended to work the same way. We decided that local extern declarations cannot add (beyond the enclosing block) new default arguments, and the same should apply to friend declarations.
John Spicer: The question is whether [this paragraph] does (or should) mean declarations that appear in the same lexical scope or declarations that declare names in the same scope. In my opinion, it really needs to be the latter. It seems somewhat paradoxical to say that a friend declaration declares a function in namespace scope yet the declaration in the class still has its own attributes. To make that work I think you'd have to make friends more like block externs that really do introduce a name into the scope in which the declaration is contained.
Bill Gibbons: In the absence of a declaration visible in class scope to which they could be attached, default arguments on friend declarations do not make sense. [They should be] ill-formed, to prevent surprises.
John Spicer: It is important that the following case work correctly:
class X { friend void f(X x, int i = 1){} }; int main() { X x; f(x); }In other words, a function first declared in a friend declaration must be permitted to have default arguments and those default arguments must be usable when the function is found by argument dependent lookup. The reason that this is important is that it is common practice to define functions in friend declarations in templates, and that definition is the only place where the default arguments can be specified.
John Spicer: We want to avoid instantiation side effects. IMO, the way to do this would be to prohibit a friend declaration from providing default arguments if a declaration of that function is already visible. Once a function has had a default specified in a friend declaration it should not be possible to add defaults in another declaration be it a friend or normal declaration.
Mike Miller: The position that seems most reasonable to me is to allow default arguments in friend declarations to be used in Koenig lookup, but to say that they are completely unrelated to default arguments in declarations in the surrounding scope; and to forbid use of a default argument in a call if more than one declaration in the overload set has such a default, as in the proposed resolution for issue 1 .
It is not clear whether the following declaration is well-formed:
struct S { int i; } s = { { 1 } };According to 8.5.1 dcl.init.aggr paragraph 2, a brace-enclosed initializer is permitted for a subaggregate of an aggregate; however, i is a scalar, not an aggregate. 8.5 dcl.init paragraph 13 says that a standalone declaration like
int i = { 1 };is permitted, but it is not clear whether this says anything about the form of initializers for scalar members of aggregates.
This is (more) clearly permitted by the C89 Standard.
Is the temporary created during copy-initialization of a class object treated as an lvalue or an rvalue? That is, is the following example well-formed or not?
struct B { }; struct A { A(A&); // not const A(const B&); }; B b; A a = b;
According to 8.5 dcl.init paragraph 14, the initialization of a is performed in two steps. First, a temporary of type A is created using A::A(const B&). Second, the resulting temporary is used to direct-initialize a using A::A(A&).
The second step requires binding a reference to non-const to the temporary resulting from the first step. However, 8.5.3 dcl.init.ref paragraph 5 requires that such a reference be bound only to lvalues.
It is not clear from 3.10 basic.lval whether the temporary created in the process of copy-initialization should be treated as an lvalue or an rvalue. If it is an lvalue, the example is well-formed, otherwise it is ill-formed.
(See also issue 84
.)
3.9 basic.types paragraph 10 defines pointer to member types to be scalar types. It also defines scalar types to be one of the POD types.
9 class paragraph 4 defines a POD struct as an aggregate class with no non-static data members of type pointer to member.
It seems contradictory that a type can be POD, yet a class containing that type is non-POD.
Suggested resolution:
Alter 9 class
paragraph 4
to allow pointer to member objects as non-static data members of POD
class.
With class name injection, when a base class name is used in a derived class, the name found is the injected name in the base class, not the name of the class in the scope containing the base class. Consequently, if the base class name is not accessible (e.g., because is is in a private base class), the base class name cannot be used unless a qualified name is used to name the class in the class or namespace of which it is a member.
Without class name injection the following example is valid. With class name injection, A is inaccessible in class C.
class A { }; class B: private A { }; class C: public B { A* p; // error: A inaccessible };
At the least, the standard should be more explicit that this is, in fact, ill-formed.
(See paper J16/99-0010 = WG21 N1187.)
There is some controversy about whether class name injection applies to class templates. If it does apply, what is injected? Is a class name injected or is the thing that is injected actually a template?
Clause 9 class paragraph 2 says,
The class-name is also inserted into the scope of the class itself.In general, clause 9 applies to both classes and class templates, so I would take this to mean that class name imjection does indeed apply to class templates. One problem with this is that clause 9 uses the syntactic term class-name, which I would take to imply that the inserted name is always a class. This is clearly unacceptable for class templates as it makes the template itself unusable from with the template. For example:
template <class T> struct A { A<T*> ptr; // Invalid: A refers to a class };
Clearly the injected name must be usable as both a class and a class template. This kind of magic already exists in the standard. In 14.6.1 temp.local it says,
Within the scope of a class template, when the name of the template is neither qualified nor followed by <, it is equivalent to the name of the template followed by the template-parameters enclosed in <>.
The proposal here is that we clarify that name injection does indeed apply to class templates, and that it is the injected name that has the special property of being usable as both a class and a template name (as described in 14.6.1 temp.local ). This would eliminate the need for special wording regarding the qualification of the name, but would achieve the same result. This would also make this "special" name available to a derived class of a class template — something which is necessary if the benefits of class name injection are to be made uniformly available for class templates, too.
template <class T> struct Base { Base* p; Base<T*>* p2; ::Base* p3; // Error: only injected name usable as class }; template <class T> struct Derived: public Base<T> { Base* p; // Now okay Base<T*>* p2; // Still okay Derived::Base* p3; // Now okayNote that by giving the special attribute of being usable as both a class and a template to the injected name it is now clear where this attribute can and cannot be used.
(See paper J16/99-0010 = WG21 N1187.)
The definition of layout-compatible POD-struct types in 9.2 class.mem paragraph 14 requires that the two types
have the same number of members, and corresponding members (in order) have layout-compatible types (3.9).There does not appear to be any reason for including member functions and static data members in this requirement. It would be more logical to require only that the non-static data members of the two types must match.
The characteristics of layout-compatible types are not well
described in the current wording, either. Apart from their use in
9.2 class.mem
paragraph 16 to define the
term "common initial sequence," there appears to be nothing said about
which operations are possible between objects of layout-compatible types.
For example, 3.9 basic.types
paragraphs
2-3 give certain guarantees regarding use of memcpy on
objects of the same type; it might be reasonable to assume that the
same kinds of guarantees might apply to objects of layout-compatible
types, but that is not said. Similarly,
3.10 basic.lval
paragraph 15 describes
permissible "type punning" but does not mention layout-compatible types.
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
.)
Section 9.6 class.bit paragraph 4 needs to be more specific about the signedness of bit fields of enum type. How much leeway does an implementation have in choosing the signedness of a bit field? In particular, does the phrase "large enough to hold all the values of the enumeration" mean "the implementation decides on the signedness, and then we see whether all the values will fit in the bit field", or does it require the implementation to make the bit field signed or unsigned if that's what it takes to make it "large enough"?
(See also issue 172
.)
9.8 class.local paragraph 1 says,
Declarations in a local class can use only type names, static variables, extern variables and functions, and enumerators from the enclosing scope.The definition of when an object or function is "used" is found in 3.2 basic.def.odr paragraph 2 and essentially says that the operands of sizeof and non-polymorphic typeid operators are not used. (The resolution for issue 48 will add contexts in which integral constant expressions are required to the list of non-uses.)
This definition of "use" would presumably allow code like
void foo() { int i; struct S { int a[sizeof(i)]; }; };which is required for C compatibility.
However, the restrictions on nested classes in 9.7 class.nest paragraph 1 are very similar to those for local classes, and the example there explicitly states that a reference in a sizeof expression is a forbidden use (abbreviated for exposition):
class enclose { public: int x; class inner { void f(int i) { int a = sizeof(x); // error: refers to enclose::x } }; };[As a personal note, I have seen real-world code that was exactly like this; it was hard to persuade the author that the required writearound, sizeof(((enclose*) 0)->x), was an improvement over sizeof(x). —wmm]
Suggested resolution: Add cross-references to
3.2 basic.def.odr
following the word
"use" in both 9.7 class.nest
and
9.8 class.local
, and change the example
in 9.7 class.nest
to indicate that a
reference in a sizeof expression is permitted.
Consider the following example:
class A { class A1{}; static void func(A1, int); static void func(float, int); static const int garbconst = 3; public: template < class T, int i, void (*f)(T, int) > class int_temp {}; template<> class int_temp<A1, 5, func> { void func1() }; friend int_temp<A1, 5, func>::func1(); int_temp<A1, 5, func>* func2(); }; A::int_temp<A::A1, A::garbconst + 2, &A::func>* A::func2() {...}ISSUE 1:
In 11 class.access paragraph 5 we have:
A::int_temp A::A1 A::garbconst (part of an expression) A::func (after overloading is done)I suspect that member templates were not really considered when this was written, and that it might have been written rather differently if they had been. Note that access to the template arguments is only legal because the class has been declared a friend, which is probably not what most programmers would expect.
Rationale:
Not a defect. This behavior is as intended.
ISSUE 2:
Now consider void A::int_temp<A::A1, A::garbconst + 2, &A::func>::func1() {...} By my reading of 11.8 class.access.nest , the references to A::A1, A::garbconst and A::func are now illegal, and there is no way to define this function outside of the class. Is there any need to do anything about either of these Issues?
This issue needs work.
Paragraph 1 says: "The members of a nested class have no special access to members of an enclosing class..."
This prevents a member of a nested class from being defined outside of its class definition. i.e. Should the following be well-formed?
class D { class E { static E* m; }; }; D::E* D::E::m = 1; // ill-formedThis is because the nested class does not have access to the member E in D. 11 class.access paragraph 5 says that access to D::E is checked with member access to class E, but unfortunately that doesn't give access to D::E. 11 class.access paragraph 6 covers the access for D::E::m, but it doesn't affect the D::E access. Are there any implementations that are standard compliant that support this?
Here is another example:
class C { class B { C::B *t; //2 error, C::B is inaccessible }; };This causes trouble for member functions declared outside of the class member list. For example:
class C { class B { B& operator= (const B&); }; }; C::B& C::B::operator= (const B&) { } //3If the return type (i.e. C::B) is access checked in the scope of class B (as implied by 11 class.access paragraph 5) as a qualified name, then the return type is an error just like referring to C::B in the member list of class B above (i.e. //2) is ill-formed.
This issue depends on the outcome of
Core issue 45
.
Example:
#include <iostream.h> class C { // entire body is private struct Parent { Parent() { cout << "C::Parent::Parent()\n"; } }; struct Derived : Parent { Derived() { cout << "C::Derived::Derived()\n"; } }; Derived d; }; int main() { C c; // Prints message from both nested classes return 0; }How legal/illegal is this? Paragraphs that seem to apply here are:
11 class.access paragraph 1:
A member of a class can beand 11.8 class.access.nest paragraph 1:
- private; that is, its name can be used only by members and friends of the class in which it is declared. [...]
The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11 class.access ) shall be obeyed. [...]This makes me think that the ': Parent' part is OK by itself, but that the implicit call of 'Parent::Parent()' by 'Derived::Derived()' is not.
From Mike Miller:
I think it is completely legal, by the reasoning given in the (non-normative) 11.8 class.access.nest paragraph 2. The use of a private nested class as a base of another nested class is explicitly declared to be acceptable there. I think the rationale in the comments in the example ("// OK because of injection of name A in A") presupposes that public members of the base class will be public members in a (publicly-derived) derived class, regardless of the access of the base class, so the constructor invocation should be okay as well.
I can't find anything normative that explicitly says that, though.
Proposed Resolution:
A member class should have access to the members of the enclosing
class in the same manner as if it had been declared a friend of the enclosing
class. See paper J16/99-0009 = WG21 N1186.
According to 12.1 class.ctor paragraph 1, the syntax used in declaring a constructor allows at most one function-specifier. It is thus not permitted to declare a constructor both inline and explicit. This seems overly restrictive.
On a related note, there doesn't seem to be any explicit prohibition against member functions with the same name as the class. (Such a prohibition might reasonably be expected to occur in 9.2 class.mem paragraph 13, but member functions are not listed there.)
One possible interpretation would be that such member functions would violate the restrictions in 3.3.6 basic.scope.class paragraph 1, because the class name would refer to the class at some points in the class scope and to the member function at others. However, this seems a bit tenuous. Is an explicit prohibition needed?
(See also issue 147
.)
In 12.2 class.temporary paragraph 5, should binding a reference to the result of a "?" operation, each of whose branches is a temporary, extend both temporaries?
Here's an example:
const SFileName &C = noDir ? SFileName("abc") : SFileName("bcd");Do the temporaries created by the SFileName conversions survive the end of the full expression?
12.2 class.temporary paragraph 3 simply states the requirement that temporaries created during the evaluation of an expression
are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created.There is nothing said about the relative order in which these temporaries are destroyed.
Paragraph 5, dealing with temporaries bound to references, says
the temporaries created during the evaluation of the expression initializing the reference, except the temporary 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.Is this difference intentional? May temporaries in expressions other than those initializing references be deleted in non-LIFO order?
According to 12.2 class.temporary paragraph 4, an expression appearing as the initializer in an object definition constitutes a context "in which temporaries are destroyed at a different point than the end of the full-expression." It goes on to say that the temporary containing the value of the expression persists until after the initialization is complete (see also issue 117 ). This seems to presume that the end of the full-expression is a point earlier than the completion of the initialization.
However, according to
1.9 intro.execution
paragraphs 12-13, the
full-expression in such cases is, in fact, the entire initialization.
If this is the case, the behavior described for temporaries in an
initializer expression is simply the normal behavior of temporaries in
any expression, and treating it as an exception to the general rule is
both incorrect and confusing.
The Standard is not clear whether automatic objects in a destructor are destroyed before or after the destruction of the class's base and member subobjects. That is, given
struct S { ~S(); }; struct T { S x; ~T() { S y; }; };which will be destroyed first, x or y?
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.
12.8 class.copy paragraph 15 refers only to "temporary class objects." It needs to be made clear that these provisions do not apply to temporaries that have been bound to references. For instance,
struct A { mutable int value; explicit A(int i) : value(i) {} void mutate(int i) const { value = i; } }; int foo() { A const& t = A(1); A n(t); // can this copy be elided? t.mutate(2); return n.value; // can this return 2? }The current wording seems to allow an implementation not to perform the copy in A N(t) because the source object is a temporary (created explicitly by A(1)).
13.3.1.1 over.match.call paragraph 3 says that when a call of the form
(&C::f)()is written, the set of overloaded functions named by C::f must not contain any nonstatic member functions. A footnote gives the rationale: if a member of C::f is a nonstatic member function, &C::f is a pointer to member constant, and therefore the call is invalid.
This is clear, it's implementable, and it doesn't directly contradict anything else in the standard. However, I'm not sure it's consistent with some similar cases.
In 13.4 over.over paragraph 5, second example, it is made amply clear that when &C::f is used as the address of a function, e.g.,
int (*pf)(int) = &C::f;the overload set can contain both static and nonstatic member functions. The function with the matching signature is selected, and if it is nonstatic &C::f is a pointer to member function, and otherwise &C::f is a normal pointer to function.
Similarly, 13.3.1.1.1 over.call.func paragraph 3 makes it clear that
C::f();is a valid call even if the overload set contains both static and nonstatic member functions. Overload resolution is done, and if a nonstatic member function is selected, an implicit this-> is added, if that is possible.
Those paragraphs seem to suggest the general rule that you do overload resolution first and then you interpret the construct you have according to the function selected. The fact that there are static and nonstatic functions in the overload set is irrelevant; it's only necessary that the chosen function be static or nonstatic to match the context.
Given that, I think it would be more consistent if the (&C::f)() case would also do overload resolution first. If a nonstatic member is chosen, the program would be ill-formed.
Suggested resolution: remove the following highlighted text in 13.3.1.1 over.match.call paragraph 3:
The fourth case arises from a postfix-expression of the form &F, where F names a set of overloaded functions. In the context of a function call, the set of functions named by F shall contain only non-member functions and static member functions. And in this context using &F behaves the same as using the name F by itself.Add the following before the parenthesized sentence at the end of the paragraph:
If the function selected by overload resolution according to 13.3.1.1.1 over.call.func is a nonstatic member function, the program is ill-formed.
By the letter of the standard, the conversions required to make auto_ptr work should be accepted.
However, there's good reason to wonder if there isn't a bug in the standard here. Here's the issue: line 16 in the example below comes down to
copy-initialize an auto_ptr<Base> from an auto_ptr<Derived> rvalueTo do that, we first look to see whether we can convert an auto_ptr<Derived> to an auto_ptr<Base>, by enumerating the constructors of auto_ptr<Base> and the conversion functions of auto_ptr<Derived>. There's a single possible way to do the conversion, namely the conversion function
auto_ptr<Derived>::operator auto_ptr<Base>()(generated from the template). (The constructor auto_ptr<Base>(auto_ptr_ref<Base>) doesn't work because it requires a user-defined conversion on the argument.)
So far, so good. Now, we do the copy step:
direct-initialize an auto_ptr<Base> from an auto_ptr<Base> rvalueThis, as we've gone to great lengths to set up, is done by calling the conversion function
auto_ptr<Base>::operator auto_ptr_ref<Base>()(generated from the template), and then the constructor
auto_ptr<Base>(auto_ptr_ref<Base>)(generated from the template).
The problem with this interpretation is that it violates the long-standing common-law rule that only a single user-defined conversion will be called to do an implicit conversion. I find that pretty disturbing. (In fact, the full operation involves two conversion functions and two constructors, but "copy" constructors are generally considered not to be conversions.)
The direct-initialization second step of a copy-initialization was intended to be a simple copy -- you've made a temporary, and now you use a copy constructor to copy it. Because it is defined in terms of direct initialization, however, it can exploit the loophole that auto_ptr is based on.
To switch to personal opinion for a second, I think it's bad enough that auto_ptr has to exploit a really arcane loophole of overload resolution, but in this case it seems like it's exploiting a loophole on a loophole.
struct Base { // 2 static void sink(auto_ptr<Base>); // 3 }; // 4 struct Derived : Base { // 5 static void sink(auto_ptr<Derived>); // 6 }; // 7 auto_ptr<Derived> source() { // 8 auto_ptr<Derived> p(source()); // 9 auto_ptr<Derived> pp(p); // 10 Derived::sink(source()); // 11 p = pp; // 12 p = source(); // 13 auto_ptr<Base> q(source()); // 14 auto_ptr<Base> qp(p); // 15 Base::sink(source()); // 16 q = pp; // 17 q = source(); // 18 return p; // 19 return source(); }
(See also issue 177
.)
Does dropping a cv-qualifier on a reference binding prevent the binding
as far as overload resolution is concerned?
Paragraph 4 says "Other restrictions
on binding a reference to a particular argument do not affect the formation
of a conversion sequence." This was intended to refer to things like access
checking, but some readers have taken that to mean that any aspects of
reference binding not mentioned in this section do not preclude the binding.
template <class T> void f(T); template <class T> void g(T); template <class T> void g(T,T); int main() { (&f<int>); (&g<int>); }The question is whether &f
13.4 over.over paragraph 1 says that a function template name is considered to name a set of overloaded functions. I believe it should be expanded to say that a function template name with an explicit template argument list is also considered to name a set of overloaded functions.
In the general case, you need to have a destination type in order to identify a unique function. While it is possible to permit this, I don't think it is a good idea because such code depends on there only being one template of that name that is visible.
The EDG front end issues an error on this use of "f". egcs 1.1.1 allows it, but the most current snapshot of egcs that I have also issues an error on it.
It has been pointed out that when dealing with nontemplates, the rules for taking the address of a single function differ from the rules for an overload set, but this asymmetry is needed for C compatibility. This need does not exist for the template case.
My feeling is that a general rule is better than a general rule plus an exception. The general rule is that you need a destination type to be sure that the operation will succeed. The exception is when there is only one template in the set and only then when you provide values for all of the template arguments.
It is true that in some cases you can provide a shorthand, but only if you encourage a fragile coding style (that will cause programs to break when additional templates are added).
I think the standard needs to specify one way or the other how this
case should be handled. My recommendation would be that it is
ill-formed.
13.4 over.over paragraph 1 contains a supposedly exhaustive list of contexts in which the name of an overloaded function can be used without an argument list ("...shall not be used without arguments in contexts other than those listed"). However, 14.3.2 temp.arg.nontype paragraph 5, bullet 4 gives another context: as a template nontype argument.
Suggested resolution: Add the missing case to
13.4 over.over
.
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.7 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.
14 temp paragraph 7 allows class templates to be declared exported, including member classes and member class templates (implicitly by virtue of exporting the containing template class). However, paragraph 8 does not exclude exported class templates from the statement that
An exported template need only be declared (and not necessarily defined) in a translation unit in which it is instantiated.This is an incorrect implication; however, it is also not dispelled in 14.7.1 temp.inst paragraph 6:
If an implicit instantiation of a class template specialization is required and the template is declared but not defined, the program is ill-formed.This wording says nothing about the translation unit in which the definition must be provided. Contrast this with 14.7.2 temp.explicit paragraph 3:
A definition of a class template or a class member template shall be in scope at the point of the explicit instantiation of the class template or class member template.
Suggested resolution:
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.
John Spicer: Where can default values for the template parameters of template template parameters be specified and where to they apply?
For normal template parameters, defaults can be specified only in class template declarations and definitions, and they accumulate across multiple declarations in the same way that function default arguments do.
I think that defaults for parameters of template template parameters should be handled differently, though. I see no reason why such a default should extend beyond the template declaration with which it is associated. In other words, such defaults are a property of a specific template declaration and are not part of the interface of the template.
template <class T = float> struct B {}; template <template <class _T = float> class T> struct A { inline void f(); inline void g(); }; template <template <class _T> class T> void A<T>::f() { T<> t; // Okay? (proposed answer - no) } template <template <class _T = char> class T> // Okay? (proposed answer - yes) void A<T>::g() { T<> t; // T<char> or T<float>? (proposed answer - T<char>) } int main() { A<B> ab; ab.f(); }
I don't think this is clear in the standard.
Gabriel Dos Reis: On the other hand I fail to see the reasons why we should introduce yet another special rule to handle that situation differently. I think we should try to keep rules as uniform as possible. For default values, it has been the case that one should look for any declaration specifying default values. Breaking that rules doesn't buy us anything, at least as far as I can see. My feeling is that [allowing different defaults in different declarations] is very confusing.
Mike Miller: I'm with John on this one. Although we don't have the concept of "prototype scope" for template parameter lists, the analogy with function parameters would suggest that the two declarations of T (in the template class definition and the template member function definition) are separate declarations and completely unrelated. While it's true that you accumulate default arguments on top-level declarations in the same scope, it seems to me a far leap to say that we ought also to accumulate default arguments in nested declarations. I would expect those to be treated as being in different scopes and thus not to share default argument information.
When you look up the name T in the definition of
A<T>::f(),
the declaration you find has no default argument for the
parameter of T, so T<> should not be allowed.
At the Dublin meeting (04/99), the Committee proposed to resolve issue 22 by simply changing the wording to make clear that a template parameter cannot be used in its own default argument. This creates a third treatment of this kind of situation, in addition to 3.3.1 basic.scope.pdecl paragraph 1, where declarators are in scope and can be used in their initializers, and paragraph 3, where an enumerator is not in scope until after its complete enumerator-definition. The Dublin resolution is for the template parameter to be in scope in its default argument but not usable. It would be more consistent to treat template parameters like enumerators: simply not in scope until the entire template-parameter declaration is seen.
On a related note, 14.1 temp.param paragraph 14 should be rewritten to connect the prohibition with visibility rules; otherwise, it sounds as if the following example is not permitted:
const int Z = 1; template <int X = Z, int Z> class A {};
The following is the wording from 14.2 temp.names paragraphs 4 and 5 that discusses the use of the "template" keyword following . or -> and in qualified names.
class X { public: template<size_t> X* alloc(); template<size_t> static X* adjust(); }; template<class T> void f(T* p) { T* p1 = p->alloc<200>(); // ill-formed: < means less than T* p2 = p->template alloc<200>(); // OK: < starts template argument list T::adjust<100>(); // ill-formed: < means less than T::template adjust<100>(); // OK: < starts explicit qualification }—end example]
If a name prefixed by the keyword template is not the name of a member template, the program is ill-formed. [Note: the keyword template may not be applied to non-template members of class templates. ] The whole point of this feature is to say that the "template" keyword is needed to indicate that a "<" begins a template parameter list in certain contexts. The constraints in paragraph 5 leave open to debate certain cases.
First, I think it should be made more clear that the template name must be followed by a template argument list when the "template" keyword is used in these contexts. If we don't make this clear, we would have to add several semantic clarifications instead. For example, if you say "p->template f()", and "f" is an overload set containing both templates and nontemplates: a) is this valid? b) are the nontemplates in the overload set ignored? If the user is forced to write "p->template f<>()" it is clear that this is valid, and it is equally clear that nontemplates in the overload set are ignored. As this feature was added purely to provide syntactic guidance, I think it is important that it otherwise have no semantic implications.
I propose that paragraph 5 be modified to:
(See also issue 30
.)
Issue 1:
14.5.5.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.5.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.5.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.
The description of how the partial ordering of template functions is determined in 14.5.5.2 temp.func.order paragraphs 3-5 does not make any provision for nondeduced template parameters. For example, the function call in the following code is ambiguous, even though one template is "obviously" more specialized than the other:
template <class T> T f(int); template <class T, class U> T f(U); void g() { f<int>(1); }The reason is that neither function parameter list allows template parameter T to be deduced; both deductions fail, so neither template is considered more specialized than the other and the function call is ambiguous.
One possibility of addressing this situation would be to
incorporate explicit template arguments from the call in the argument
deduction using the transformed function parameter lists. In this
case, that would result in finding the first template to be more
specialized than the second.
Mike Miller: A question about typename came up in the discussion of issue 68 that is somewhat relevant to the idea of omitting typename in contexts where it is clear that a type is required: consider something like
template <class T> class X { friend class T::nested; };Is typename required here? If so, where would it go? (The grammar doesn't seem to allow it anywhere in an elaborated-type-specifier that has a class-key.)
Bill Gibbons:
The class applies to the last identifier in the qualified name,
since all the previous names must be classes or namespaces. Since the
name is specified to be a class it does not need typename.
[However,] it looks like
14.6 temp.res
paragraph 3 requires
typename and the following paragraphs
do not exempt this case. This is not what we agreed on.
John Spicer: In 14.6 temp.res paragraph 5, the standard says
The keyword typename shall only be used in template declarations and definitions...My understanding of the intent of this restriction is to say that typename is only allowed in contexts in which template dependent names can be found, but the wording leaves open to interpretation whether typename is allowed in an explicit specialization, such as:
template <class T> struct A {}; template <class T> struct B { typedef int X; }; template <> struct A<int> { typename B<int>::X x; };My understanding is that such usage is not permitted. This should be clarified one way or the other.
Mike Miller: I agree with your understanding that you are not allowed to use typename in an explicit specialization. However, I think the standard already says that — an explicit specialization is not a template declaration. According to the grammar in 14 temp paragraph 1, a template-declaration must have a non-empty template-parameter-list.
Nathan Myers:
Is there any actual reason for this restriction? Its only apparent
effect is to make it harder to specialize templates, with no
corresponding benefit.
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.
At what point are semantic constraints applied to uses of non-dependent names in template definitions? According to 14.6.3 temp.nondep , such names are looked up and bound at the point at which they are used, i.e., the point of definition and not the point of instantiation. However, the text does not mention the checking of semantic constraints.
Contrast this omission with the treatment of names in default argument expressions given in 8.3.6 dcl.fct.default paragraph 5, where the treatment of semantic constraints is explicit:
The names in the expression are bound, and the semantic constraints are checked, at the point where the default argument expression appears.The following code is an example of where this distinction matters:
struct S; template <class T> struct Q { S s; // incomplete type if semantic constraints // are applied in the definition context }; struct S { }; // Point of instantiation of Q<int>; S is complete here Q<int> si;There is real-world code that depends on late checking of semantic constraints. The Standard should be explicit about whether this code is broken or not.
The example in 14.6 temp.res paragraph 9 is incorrect, according to 14.6.4.2 temp.dep.candidate . The example reads,
void f(char); template <class T> void g(T t) { f(1); // f(char); f(T(1)); // dependent f(t); // dependent dd++; // not dependent // error: declaration for dd not found } void f(int); double dd; void h() { g(2); // will cause one call of f(char) followed // by two calls of f(int) f('a'); // will cause three calls of f(char) }Since 14.6.4.2 temp.dep.candidate says that only Koenig lookup is done from the instantiation context, and since 3.4.2 basic.lookup.koenig says that fundamental types have no associated namespaces, either the example is incorrect (and f(int) will never be called) or the specification in 14.6.4.2 temp.dep.candidate is incorrect.
If taken literally, the specification in 14.6.4.2 temp.dep.candidate would also cause the following apparently reasonable code to be ill-formed:
struct A { void f(); }; template <class T1, class T2> struct B: T1 { void g(T2 t) { f(t); // dependent; no "f" in scope } }; void h() { B<A, int> b; b.g(1); }Neither of the lookups described in 14.6.4.2 temp.dep.candidate will find a member function of the base class.
John Spicer: Certain access checks are suppressed on explicit instantiations. 14.7.2 temp.explicit paragraph 8 says:
The usual access checking rules do not apply to names used to specify explicit instantiations. [Note: In particular, the template arguments and names used in the function declarator (including parameter types, return types and exception specifications) may be private types or objects which would normally not be accessible and the template may be a member template or member function which would not normally be accessible. ]I was surprised that similar wording does not exist (that I could find) for explicit specializations. I believe that the two cases should be handled equivalently in the example below (i.e., that the specialization should be permitted).
template <class T> struct C { void f(); void g(); }; template <class T> void C<T>::f(){} template <class T> void C<T>::g(){} class A { class B {}; void f(); }; template void C<A::B>::f(); // okay template <> void C<A::B>::g(); // error - A::B inaccessible void A::f() { C<B> cb; cb.f(); }
Mike Miller: According to the note in 14.3 temp.arg paragraph 3,
if the name of a template-argument is accessible at the point where it is used as a template-argument, there is no further access restriction in the resulting instantiation where the corresponding template-parameter name is used.(Is this specified anywhere in the normative text? Should it be?) In the absence of text to the contrary, this blanket permission apparently applies to explicitly-specialized templates as well as to implicitly-generated ones (is that right?). If so, I don't see any reason that an explicit instantiation should be treated differently from an explicit specialization, even though the latter involves new program text and the former is just a placement instruction to the implementation.
14.8.2.4 temp.deduct.type paragraph 18 uses incorrect syntax. Instead of
template <template X<class T> > struct A { }; template <template X<class T> > void f(A<X>) { }it should be
template <template <class T> class X> struct A { }; template <template <class T> class X> void f(A<X>) { }
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
.
The decision to deprecate global static should be reversed.
The wording in 3.2 basic.def.odr paragraph 2 about "potentially evaluated" is incomplete. It does not distinguish between expressions which are used as "integral constant expressions" and those which are not; nor does it distinguish between uses in which an objects address is taken and those in which it is not. (A suitable definition of "address taken" could be written without actually saying "address".)
Currently the definition of "use" has two parts (part (a) and (d) below); but in practice there are two more kinds of "use" as in (b) and (c):
I don't think we discussed (c).
Rationale (04/99): The substantive part of this issue is
covered by Core issue 48
Given the following test case:
enum E { e1, e2, e3 }; void f(int, E e = e1); void f(E, E e = e1); void g() { void f(long, E e = e2); f(1); // calls ::f(int, E) f(e1); // ? }First note that Koenig lookup breaks the concept of hiding functions through local extern declarations as illustrated by the call `f(1)'. Should the WP show this as an example?
Second, it appears the WP is silent as to what happens with the call `f(e1)': do the different default arguments create an ambiguity? is the local choice preferred? or the global?
Tentative Resolution (10/98) In 3.4.2 basic.lookup.koenig paragraph 2, change
If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated namespaces and classes are not considered.to
If the ordinary unqualified lookup of the name finds the declaration of a class member function or the declaration of a function at block scope, the associated namespaces and classes are not considered.
Rationale (04/99): The proposal would also apply to local
using-declarations (per Mike Ball) and was therefore deemed
undesirable. The ambiguity issue is
dealt with in Core issue 1
John Spicer: The standard does say that a namespace scope template has external linkage unless it is a function template declared "static". It doesn't explicitly say that the linkage of the template is also the linkage of the instantiations, but I believe that is the intent. For example, a storage class is prohibited on an explicit specialization to ensure that a specialization cannot be given a different storage class than the template on which it is based.
Mike Ball: This makes sense, but I couldn't find much support in the document. Sounds like yet another interpretation to add to the list.
John Spicer: The standard does not talk about the linkage of instantiations, because only "names" are considered to have linkage, and instances are not really names. So, from an implementation point of view, instances have linkage, but from a language point of view, only the template from which the instances are generated has linkage.
Mike Ball: Which is why I think it would be cleaner to eliminate storage class specifiers entirely and rely on the unnamed namespace. There is a statement that specializations go into the namespace of the template. No big deal, it's not something it says, so we live with what's there.
John Spicer: That would mean prohibiting static function templates. I doubt those are common, but I don't really see much motivation for getting rid of them at this point.
"export" is an additional attribute that is separate from linkage, but that can only be applied to templates with external linkage.
Mike Ball: I can't find that restriction in the standard, though there is one that templates in an unnamed namespace can't be exported. I'm pretty sure that we intended it, though.
John Spicer: I can't find it either. The "inline" case seems to be addressed, but not static. Surely this is an error as, by definition, a static template can't be used from elsewhere.
Rationale: Duplicate of Core issue 69 .
15.4 except.spec paragraph 1 says,
An exception-specification shall appear only on a function declarator in a function, pointer, reference or pointer to member declaration or definition.This wording forbids exception specifications in declarations where they might plausibly occur (e.g., an array of function pointers). This restriction seems arbitrary. It's also unclear whether this wording allows declarations such as
void (*f())() throw(int); // returns a pointer to a function // that might throw "int"
At the same time, other cases are allowed by the wording in paragraph 1 (e.g., a pointer to a pointer to a function), but no checking for such cases is specified in paragraph 3. For example, the following appears to be allowed:
void (*p)() throw(int); void (**pp)() throw() = &p;
Rationale (10/99): Duplicate of issues
87
and
92
.
The example in 18.4.1.3 lib.new.delete.placement reads:
[Example: This can be useful for constructing an object at a known address:This example has potential alignment problems. One way to correct it would be to change the definition of place to read:char place[sizeof(Something)]; Something* p = new (place) Something();—end example]
char* place = new char[sizeof(Something)];
Rationale (10/99): This is an issue for the Library
Working Group.
In 3.2 basic.def.odr paragraph 4 bullet 4, it's presumably the case that a conversion to T* requires that T be complete only if the conversion is from a different type. One could argue that there is no conversion (and therefore the text is accurate as it stands) if a cast does not change the type of the expression, but it's probably better to be more explicit here.
On the other hand, this text is non-normative (it's in a note).
Rationale (04/99):
The relevant normative text makes this clear. Implicit conversion
and static_cast are defined (in
4 conv
and
5.2.9 expr.static.cast
,
respectively) as equivalent to declaration with initialization, which
permits pointers to incomplete types, and dynamic_cast
(5.2.7 expr.dynamic.cast
) explicitly
prohibits pointers to incomplete types.
Consider this code:
struct Base { enum { a, b, c, next }; }; struct Derived : public Base { enum { d = Base::next, e, f, next }; };The idea is that the enumerator "next" in each class is the next available value for enumerators in further derived classes.
If we had written
enum { d = next, e, f, next };I think we would run afoul of 3.3.6 basic.scope.class :
A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.But in the original code, we don't have an unqualified "next" that refers to anything but the current scope. I think the intent was to allow the code, but I don't find the wording clear on on that point.
Is there another section that makes it clear whether the original code is valid? Or am I being obtuse? Or should the quoted section say "An unqualified name N used in a class ..."?
Rationale (04/99): It is sufficiently clear that "name"
includes qualified names and hence the usual lookup rules make this
legal.
When a union is used in argument-dependent lookup, the union's type is not an associated class type. Consequently, code like this will fail to work.
union U { friend void f(U); }; int main() { U u; f(u); // error: no matching f -- U is not an associated class }Is this an error in the description of unions in argument-dependent lookup?
Also, this section is written as if unions were distinct from classes. So adding unions to the "associated classes" requires either rewriting the section so that "associated classes" can include unions, or changing the term to be more inclusive, e.g. "associated classes and unions" or "associated types".
Jason Merrill: Perhaps in both cases, the standard text was intended to only apply to anonymous unions.
Liam Fitzpatrick: One cannot create expressions of an anonymous union type.
Rationale (04/99): Unions are class types, so the example is
well-formed. Although the wording here could be improved, it does not rise
to the level of a defect in the Standard.
An operator expression can, according to 5 expr paragraph 2, require transformation into function call syntax. The reference in that paragraph is to 13.5 over.oper , but it should be to 13.3.1.2 over.match.oper .
Rationale (04/99): The subsections
13.5.1 over.unary
,
13.5.2 over.binary
, etc. of the referenced
section are in fact relevant.
Is it okay to use a static_cast to cast from a private base class to a derived class? That depends on what the words "valid standard conversion" in paragraph 8 mean -- do they mean the conversion exists, or that it would not get an error if it were done? I think the former was intended -- and therefore a static_cast from a private base to a derived class would be allowed.
Rationale (04/99): A static_cast from a private
base to a derived class is not allowed outside a member from the
derived class, because 4.10 conv.ptr
paragraph 3 implies that the conversion is not valid. (Classic style
casts work.)
Section 12.5 class.free paragraph 4 says:
If a delete-expression begins with a unary :: operator, the deallocation function's name is looked up in global scope. Otherwise, if the delete-expression is used to deallocate a class object whose static type has a virtual destructor, the deallocation function is the one found by the lookup in the definition of the dynamic type's virtual destructor (12.4 class.dtor ). Otherwise, if the delete-expression is used to deallocate an object of class T or array thereof, the static and dynamic types of the object shall be identical and the deallocation function's name is looked up in the scope of T. If this lookup fails to find the name, the name is looked up in the global scope. If the result of the lookup is ambiguous or inaccessible, or if the lookup selects a placement deallocation function, the program is ill-formed.I contrast that with 5.3.4 expr.new paragraphs 16 and 17:
If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function (12.5 class.free ), and the constructor (12.1 class.ctor ). If the new-expression creates an array of objects of class type, access and ambiguity control are done for the destructor (12.4 class.dtor ).I think nothing in the latter paragraphs implies that the deallocation function found is the same as that for a corresponding delete-expression. I suspect that may not have been intended and that the lookup should occur "as if for a delete-expression".If any part of the object initialization described above terminates by throwing an exception and a suitable deallocation function can be found, the deallocation function is called to free the memory in which the object was being constructed, after which the exception continues to propagate in the context of the new-expression. If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object's memory to be freed. [Note: This is appropriate when the called allocation function does not allocate memory; otherwise, it is likely to result in a memory leak. ]
Rationale:
Paragraphs 16 through 18 are sufficiently correct and unambiguous as
written.
Clause 5 expr paragraph 4 appears to grant an implementation the right to generate code for a function call like
f(new T1, new T2)in the order
Suggested resolution: either forbid the ordering above or expand the requirement for reclaiming storage to include exceptions thrown in all operations between the allocation and the completion of the constructor.
Rationale (10/99): Even in the "traditional" ordering of the calls
to allocation functions and constructors, memory can still leak. For
instance, if T1 were successfully constructed and then the
construction of T2 were terminated by an exception, the
memory for T1 would be lost. Programmers concerned about
memory leaks will avoid this kind of construct, so it seems
unnecessary to provide special treatment for it to avoid the memory
leaks associated with one particular implementation strategy.
An expression of the form pointer + enum (see paragraph 5) is not given meaning, and ought to be, given that paragraph 2 of this section makes it valid. Presumably, the enum value should be converted to an integral value, and the rest of the processing done on that basis. Perhaps we want to invoke the integral promotions here.
[Should this apply to (pointer - enum) too?]
Rationale (04/99): Paragraph 1 invokes "the usual arithmetic conversions" for operands of enumeration type.
(It was later pointed out that the builtin operator
T* operator+(T*, ptrdiff_t)
(13.6 over.built
paragraph 13) is
selected by overload resolution. Consequently, according to
13.3.1.2 over.match.oper
paragraph 7,
the operand of enumeration type is converted to ptrdiff_t
before being interpreted according to the rules in
5.7 expr.add
.)
Consider:
int* p = false; // Well-formed? int* q = !1; // What about this?>From 3.9.1 basic.fundamental paragraph 6: "As described below, bool values behave as integral types."
From 4.10 conv.ptr paragraph 1: "A null pointer constant is an integral constant expression rvalue of integer type that evaluates to zero."
From 5.19 expr.const paragraph 1: "An integral constant-expression can involve only literals, enumerators, const variables or static members of integral or enumeration types initialized with constant expressions, ..."
In 2.13.1 lex.icon : No mention of true or false as an integer literal.
From 2.13.5 lex.bool : true and false are Boolean literals.
So the definition of q is certainly valid, but the validity of p depends on how the sentence in 5.19 expr.const is parsed. Does it mean
If the latter, then (3.0 < 4.0) is a constant expression, which I don't think we ever wanted. If the former, though, we have the anomalous notion that true and false are not constant expressions.
Now, you may argue that you shouldn't be allowed to convert false to a pointer. But what about this?
static const bool debugging = false; // ... int table[debugging? n+1: n];Whether the definition of table is well-formed hinges on whether false is an integral constant expression.
I think that it should be, and that failure to make it so was just an oversight.
Rationale (04/99): A careful reading of
5.19 expr.const
indicates that all types of
literals can appear in integral constant expressions, but
floating-point literals must immediately be cast to an integral type.
9.5 class.union paragraph 3 implies that anonymous unions in unnamed namespaces need not be declared static (it only places that restriction on anonymous unions "declared in a named namespace or in the global namespace").
However, 7.1.1 dcl.stc paragraph 1 says that "global anonymous unions... shall be declared static." This could be read as prohibiting anonymous unions in unnamed namespaces, which are the preferred alternative to the deprecated use of static.
Rationale (10/99): An anonymous union in an unnamed
namespace is not "a global anonymous union," i.e., it is not a
member of the global namespace.
A change was introduced into the language that made names first declared in friend declarations "invisible" to normal lookups until such time that the identifier was declared using a non-friend declaration. This is described in 7.3.1.2 namespace.memdef paragraph 3 and 11.4 class.friend paragraph 9 (and perhaps other places).
The standard gives examples of how this all works with friend declarations, but there are some cases with nonfriend elaborated type specifiers for which there are no examples, and which might yield surprising results.
The problem is that an elaborated type specifier is sometimes a declaration and sometimes a reference. The meaning of the following code changes depending on whether or not friend class names are injected (visibly) into the enclosing namespace scope.
struct A; struct B; namespace N { class X { friend struct A; friend struct B; }; struct A *p; // N::A with friend injection, ::A without struct B; // always N::B }Is this the desired behavior, or should all elaborated type specifiers (and not just those of the form "class-key identifier;") have the effect of finding previously declared "invisible" names and making them visible?
Mike Miller: That's not how I would categorize the effect of "struct B;". That declaration introduces the name "B" into namespace N in exactly the same fashion as if the friend declaration did not exist. The preceding friend declaration simply stated that, if a class N::B were ever defined, it would have friendly access to the members of N::X. In other words, the lookups in both "struct A*..." and "struct B;" ignore the friend declarations.
(The standard is schizophrenic on the issue of whether such friend declarations introduce names into the enclosing namespace. 3.3 basic.scope paragraph 4 says,
John Spicer: The previous declaration of B is not completely ignored though, because certainly changing "friend struct B;" to "friend union B;" would result in an error when B was later redeclared as a struct, wouldn't it?
Bill Gibbons: Right. I think the intent was to model this after the existing rule for local declarations of functions (which dates back to C), where the declaration is introduced into the enclosing scope but the name is not. Getting this right requires being somewhat more rigorous about things like the ODR because there may be declaration clashes even when there are no name clashes. I suspect that the standard gets this right in most places but I would expect there to be a few that are still wrong, in addition to the one Mike pointed out.
Mike Miller: Regarding would result in an error when B was later redeclared
I don't see any reason why it should. The restriction that the class-key must agree is found in 7.1.5.3 dcl.type.elab and is predicated on having found a matching declaration in a lookup according to 3.4.4 basic.lookup.elab . Since a lookup of a name declared only (up to that point) in a friend declaration does not find that name (regardless of whether you subscribe to the "does-not-introduce" or "introduces-invisibly" school of thought), there can't possibly be a mismatch.
I don't think that the Standard's necessarily broken here. There is no requirement that a class declared in a friend declaration ever be defined. Explicitly putting an incompatible declaration into the namespace where that friend class would have been defined is, to me, just making it impossible to define -- which is no problem, since it didn't have to be defined anyway. The only error would occur if the same-named but unbefriended class attempted to use the nonexisting grant of friendship, which would result in an access violation.
(BTW, I couldn't find anything in the Standard that forbids defining a class with a mismatched class-key, only using one in an elaborated-type-specifier. Is this a hole that needs to be filled?)
John Spicer: This is what 7.1.5.3 dcl.type.elab paragraph 3 says:
class B; union B {};and
union B {}; class B;are both invalid. I think this paragraph is intended to say that. I'm not so sure it actually does say that, though.
Mike Miller: Regarding I think the intent was to model this after the existing rule for local declarations of functions (which dates back to C)
Actually, that's not the C (1989) rule. To quote the Rationale from X3.159-1989:
Regarding Getting this right requires being somewhat more rigorous
Yes, I think if this is to be made illegal, it would have to be done with the ODR; the name-lookup-based current rules clearly (IMHO) don't apply. (Although to be fair, the [non-normative] note in 3.3 basic.scope paragraph 4 sounds as if it expects friend invisible injection to trigger the multiple-declaration provisions of that paragraph; it's just that there's no normative text implementing that expectation.)
Bill Gibbons: Nor does the ODR currently disallow:
translation unit #1 struct A; translation unit #2 union A;since it only refers to class definitions, not declarations.
But the obvious form of the missing rule (all declarations of a class within a program must have compatible struct/class/union keys) would also answer the original question.
The declarations need not be visible. For example:
translation unit #1 int f() { return 0; } translation unit #2: void g() { extern long f(); }is ill-formed even though the second "f" is not a visible declaration.
Rationale (10/99): The main issue (differing behavior of standalone and embedded elaborated-type-specifiers) is as the Committee intended. The remaining questions mentioned in the discussion may be addressed in dealing with related issues.
(See also issues
136
,
138
,
139
,
143
,
165
, and
166
.)
7.3.1.2 namespace.memdef paragraph 2 says,
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...It is not clear whether block-scope extern declarations and friend declarations are sufficient to permit the named entities to be defined outside their namespace. For example,
namespace NS { struct A { friend struct B; }; void foo() { extern void bar(); } } struct NS::B { }; // 1) legal? void NS::bar() { } // 2) legal?
Rationale (10/99): Entities whose names are "invisibly injected" into a namespace as a result of friend declarations are not "declared" in that namespace until an explicit declaration of the entity appears at namespace scope. Consequently, the definitions in the example are ill-formed.
(See also issues
95
,
136
,
138
,
139
,
143
, and
166
.)
7.3.3 namespace.udecl paragraph says,
A using-declaration shall not name a template-id.It is not clear whether this prohibition applies to the entity for which the using-declaration is a synonym or to any name that appears in the using-declaration. For example, is the following code well-formed?
template <typename T> struct base { void bar (); }; struct der : base<int> { using base<int>::bar; // ill-formed ? };
Rationale (10/99):
7.3.3 namespace.udecl
paragraph 1 says,
"A using-declaration introduces a name..." It is the name
that is thus introduced that cannot be a template-id.
Issue 1
7.5 dcl.link paragraph 6 says the following:
extern "C" int f(void); namespace A { extern "C" int f(void); }; using namespace A; int i = f(); // Ok because only one function f() or // ill-formedFor name lookup, both declarations of f are visible and overloading cannot distinguish between them. Has the compiler to check that these functions are really the same function or is the program in error?
Rationale: These are the same function for all purposes.
Issue 2
A similar question may arise with typedefs:
// vendor A typedef unsigned int size_t; // vendor B namespace std { typedef unsigned int size_t; } using namespace std; size_t something(); // error?Is this valid because the typedef size_t refers to the same type in both namespaces?
Rationale (04/99): In 7.3.4 namespace.udir paragraph 4:
If name lookup finds a declaration for a name in two different namespaces, and the declarations do not declare the same entity and do not declare functions, the use of the name is ill-formed.The term entity applied to typedefs refers to the underlying type or class (3 basic , paragraph 3); therefore both declarations of size_t declare the same entity and the above example is well-formed.
7.5 dcl.link paragraph 4 says,
A C language linkage is ignored for the names of class members and the member function types of class member functions.This makes good sense, since C linkage names typically aren't compatible with the naming used for member functions at link time, nor is C language linkage function type necessarily compatible with the calling convention for passing this to a non-static member function.
But C language linkage type (not name) for a static member function is invaluable for a common programming idiom. When calling a C function that takes a pointer to a function, it's common to use a private static member function as a "trampoline" which retrieves an object reference (perhaps by casting) and then calls a non-static private member function. If a static member function can't have a type with C language linkage, then a global or friend function must be used instead. These alternatives expose more of a class's implementation than a static member function; either the friend function itself is visible at namespace scope alongside the class definition or the private member function must be made public so it can be called by a non-friend function.
Suggested Resolution: Change the sentence cited above to:
A C language linkage is ignored for the names of class members and the member function types of non-static class member functions.The example need not be changed because it doesn't involve a static member function.
The following workaround accomplishes the goal of not exposing the class's implementation, but at the cost of significant superstructure and obfuscation:
// foo.h extern "C" typedef int c_func(int); typedef int cpp_func(int); class foo { private: c_func* GetCallback(); static int Callback(int); }; // foo.cpp #include "foo.h" // A local pointer to the static member that will handle the callback. static cpp_func* cpp_callback=0; // The C function that will actually get registered. extern "C" int CFunk(int i) { return cpp_callback(i); } c_func* foo::GetCallback() { cpp_callback = &Callback; // Only needs to be done once. return &CFunk; }
Rationale (10/99): The Standard correctly reflects the
intent of the Committee.
8.3.5 dcl.fct paragraph 2 says:
If the parameter-declaration-clause is empty, the function takes no arguments. The parameter list (void) is equivalent to the empty parameter list.Can a typedef to void be used instead of the type void in the parameter list?
Rationale:
The IS is already clear that this is not allowed.
Paragraph 9 of says that extra default arguments added after a using-declaration but before a call are usable in the call, while 7.3.3 namespace.udecl paragraph 9 says that extra function overloads are not. This seems inconsistent, especially given the similarity of default arguments and overloads.
Rationale (10/99): The Standard accurately reflects the
intent of the Committee.
class Foo { public: Foo() {} ~Foo() {} }; class A : virtual private Foo { public: A() {} ~A() {} }; class Bar : public A { public: Bar() {} ~Bar() {} };~Bar() calls ~Foo(), which is ill-formed due to access violation, right? (Bar's constructor has the same problem since it needs to call Foo's constructor.) There seems to be some disagreement among compilers. Sun, IBM and g++ reject the testcase, EDG and HP accept it. Perhaps this case should be clarified by a note in the draft.
In short, it looks like a class with a virtual private base can't be derived from.
Rationale: This is what was intended.
Footnote 98 says:
As specified previously in clause 11 class.access , private members of a base class remain inaccessible even to derived classes unless friend declarations within the base class declaration are used to grant access explicitly.This footnote does not fit with the algorithm provided in 11.2 class.access.base paragraph 4 because it does not take into account the naming class concept introduced in this paragraph.
(See also paper J16/99-0002 = WG21 N1179.)
Rationale (10/99): The footnote should be read as referring
to immediately-derived classes, and is accurate in that context.
11.5 class.protected paragraph 1 says:
When a friend or a member function of a derived class references a protected nonstatic member of a base class, an access check applies in addition to ...Instead of saying "references a protected nonstatic member of a base class", shouldn't this be rewritten to use the concept of naming class as 11.2 class.access.base paragraph 4 does?
Rationale (04/99): This rule is orthogonal to the specification
in 11.2 class.access.base
paragraph 4.
The working paper is quite explicit about
struct X { X(X, X const& = X()); };being illegal (because of the chicken & egg problem wrt copying.)
Shouldn't it be as explicit about the following?
struct Y { Y(Y const&, Y = Y()); };Rationale: There is no need for additional wording. This example leads to a program which either fails to compile (due to resource limits on recursive inlining) or fails to run (due to unterminated recursion). In either case the implementation may generate an error when the program is compiled.
The following example does not work as one might expect:
namespace N { class C {}; } int operator +(int i, N::C) { return i+1; } #include <numeric> int main() { N::C a[10]; std::accumulate(a, a+10, 0); }According to 3.4.1 basic.lookup.unqual paragraph 6, I would expect that the "+" call inside std::accumulate would find the global operator+. Is this true, or am I missing a rule? Clearly, the operator+ would be found by Koenig lookup if it were in namespace N.
Daveed Vandevoorde: But doesn't unqualified lookup of the operator+ in the definition of std::accumulate proceed in the namespace where the implicit specialization is generated; i.e., in namespace std?
In that case, you may find a non-empty overload set for operator+ in namespace std and the surrounding (global) namespace is no longer considered?
Nathan Myers: Indeed, <string> defines operator+, as do <complex>, <valarray>, and <iterator>. Any of these might hide the global operator.
Herb Sutter: These examples fail for the same reason:
struct Value { int i; }; typedef map<int, Value > CMap; typedef CMap::value_type CPair; ostream & operator<< ( ostream &os, const CPair &cp ) { return os << cp.first << "/" << cp.second.i; } int main() { CMap courseMap; copy( courseMap.begin(), courseMap.end(), ostream_iterator<CPair>( cout, "\n" ) ); } template<class T, class S> ostream& operator<< (ostream& out, pair<T,S> pr) { return out << pr.first << " : " << pr.second << endl; } int main() { map <int, string> pl; copy( pl.begin(), pl.end(), ostream_iterator <places_t::value_type>( cout, "\n" ) ); }This technique (copying from a map to another container or stream) should work. If it really cannot be made to work, that would seem broken to me. The reason is does not work is that copy and pair are in namespace std and the name lookup rules do not permit the global operator<< to be found because the other operator<<'s in namespace std hide the global operator. (Aside: FWIW, I think most programmers don't realize that a typedef like CPair is actually in namespace std, and not the global namespace.)
Bill Gibbons: It looks like part of this problem is that the library is referring to names which it requires the client to declare in the global namespace (the operator names) while also declaring those names in namespace std. This would be considered very poor design for plain function names; but the operator names are special.
There is a related case in the lookup of operator conversion functions. The declaration of a conversion function in a derived class does not hide any conversion functions in a base class unless they convert to the same type. Should the same thing be done for the lookup of operator function names, e.g. should an operator name in the global namespace be visible in namespace std unless there is a matching declaration in std?
Because the operator function names are fixed, it it much more likely that a declaration in an inner namespace will accidentally hide a declaration in an outer namespace, and the two declarations are much less likely to interfere with each other if they are both visible.
The lookup rules for operator names (when used implicitly) are already quite different from those for ordinary function names. It might be worthwhile to add one more special case.
Mike Ball : The original SGI proposal said that non-transitive points of instantiation were also considered. Why, when, and by whom was it added?
Rationale (10/99): This appears to be mainly a program
design issue. Furthermore, any attempt to address it in the core
language would be beyond the scope of what can be done in a
Technical Corrigendum.
Can p->f, where f refers to a set of overloaded functions all of which are static member functions, be used as an expression in an address-of-overloaded-function context? A strict reading of this section suggests "no", because "p->f" is not the name of an overloaded function (it's an expression). I'm happy with that, but the core group should decide and should add an example to document the decision, whichever way it goes.
Rationale (10/99): The "strict reading" correctly reflects
the intent of the Committee, for the reason given, and no clarification
is required.
I understand that the lvalue-to-rvalue conversion was removed in London. I generally agree with this, but it means that ?: needs to be fixed:
Given:
bool test; Integer a, b; test ? a : b;What builtin do we use? The candidates are
operator ?:(bool, const Integer &, const Integer &) operator ?:(bool, Integer, Integer)which are both perfect matches.
(Not a problem in FDIS, but misleading.)
Rationale:
The description of the conditional operator in
5.16 expr.cond
handles the lvalue case before the prototype is considered.
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.4 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.
According to 14.5.2 temp.mem paragraph 4,
A specialization of a member function template does not override a virtual function from a base class.Bill Gibbons: I think that's sufficiently surprising behavior that it should be ill-formed instead.
As I recall, the main reason why a member function template cannot be virtual is that you can't easily construct reasonable vtables for an infinite set of functions. That doesn't apply to overrides.
Another problem is that you don't know that a specialization overrides until the specialization exists:
struct A { virtual void f(int); }; struct B : A { template<class T> void f(T); // does this override? };But this could be handled by saying:
template<int I> struct X; struct A { virtual void f(A<5>); }; struct B : A { template<int I, int J> void f(A<I+J>); // does not overrride }; void g(B *b) { X<t> x; b->f<3,2>(x); // specialization B::f(A<5>) makes program ill-formed }So I think there are reasonable semantics. But is it useful?
If not, I think the creation of a specialization that would have been an override had it been declared in the class should be an error.
Daveed Vandevoorde: There is real code out there that is written with this rule in mind. Changing the standard on them would not be good form, IMO.
Mike Ball: Also, if you allow template functions to be specialized outside of the class you introduce yet another non-obvious ordering constraint.
Please don't make such a change after the fact.
John Spicer: This is the result of an explicit committee decision. The reason for this rule is that it is too easy to unwittingly override a function from a base class, which was probably not what was intended when the template was written. Overriding should be a conscious decision by the class writer, not something done accidentally by a template.
Rationale (10/99): The Standard correctly reflects the
intent of the Committee.
Issue 1
Paragraph 1 says that a friend of a class template can be a template. Paragraph 2 says: A friend template may be declared within a non-template class. A friend function template may be defined within a non-template class.
I'm not sure what this wording implies about friend template definitions within template classes. The rules for class templates and normal classes should be the same: a function template can be declared or defined, but a class template can only be declared in a friend declaration.
Issue 2
Paragraph 4 says: When a function is defined in a friend function declaration in a class template, the function is defined when the class template is first instantiated. I take it that this was intended to mean that a function that is defined in a class template is not defined until the first instantiation. I think this should say that a function that is defined in a class template is defined each time the class is instantiated. This means that a function that is defined in a class template must depend on all of the template parameters of the class template, otherwise multiple definition errors could occur during instantiations. If we don't have a rule like this, compilers would have to compare the definitions of functions to see whether they are the same or not. For example:
template <class T> struct A { friend int f() { return sizeof(T); } }; A<int> ai; A<long> ac;I hope we would all agree that this program is ill-formed, even if long and int have the same size.
From Bill Gibbons:
[1] That sounds right.
[2] Whenever possible, I try to treat instantiated class templates as if they were ordinary classes with funny names. If you write:
struct A_int { friend int f() { return sizeof(int); } }; struct A_long { friend int f() { return sizeof(long); } };it is a redefinition (which is not allowed) and an ODR violation. And if you write:
template <class T, class U> struct A { friend int f() { return sizeof(U); } }; A<int,float> ai; A<long,float> ac;the corresponding non-template code would be:
struct A_int_float { friend int f() { return sizeof(float); } }; struct A_long_float { friend int f() { return sizeof(float); } };then the two definitions of "f" are identical so there is no ODR violation, but it is still a redefinition. I think this is just an editorial clarification.
Rationale (04/99): The first sub-issue reflects wording that was
changed to address the concern before the IS was issued. A close and
careful reading of the Standard already leads to the conclusion that the
example in the second sub-issue is ill-formed, so no change is needed.
Does Koenig lookup create a point of instantiation for class types? I.e., if I say:
TT<int> *p; f(p);The namespaces and classes associated with p are those associated with the type pointed to, i.e., TT<int>. However, to determine those I need to know TT<int> bases and its friends, which requires instantiation.
Or should this be special cased for templates?
Rationale:
The standard already specifies that this creates a point of instantiation.
Is the second explicit instantiation below well-formed?
template <class T> struct A { template <class T2> void f(T2){} }; template void A<int>::f(char); // okay template template void A<int>::f(float); // ?Since multiple "template<>" clauses are permitted in an explicit specialization, it might follow that multiple "template" keywords should also be permitted in an explicit instantiation. Are multiple "template" keywords not allowed in an explicit instantiation? The grammar permits it, but the grammar permits lots of stuff far weirder than that. My opinion is that, in the absence of explicit wording permitting that kind of usage (as is present for explicit specializations) that such usage is not permitted for explicit instantiations.
Rationale (04/99): The Standard does not describe the meaining
of multiple template keywords in this context, so the example
should be considered as resulting in undefined behavior according to
1.3.12 defns.undefined
.
[N1065 issue 1.19] An explicit specialization declaration may not be visible during instantiation under the template compilation model rules, even though its existence must be known to perform the instantiation correctly. For example:
translation unit #1
template<class T> struct A { }; export template<class T> void f(T) { A<T> a; }translation unit #2
template<class T> struct A { }; template<> struct A<int> { }; // not visible during instantiation template<class T> void f(T); void g() { f(1); }Rationale: This issue was addressed in the FDIS and should have been closed.
Is this valid C++? The question is whether a member constant can be specialized. My inclination is to say no.
template <class T> struct A { static const T i = 0; }; template<> const int A<int>::i = 42; int main () { return A<int>::i; }John Spicer: This is ill-formed because 9.4.2 class.static.data paragraph 4 prohibits an initializer on a definition of a static data member for which an initializer was provided in the class.
The program would be valid if the initializer were removed from the specialization.
Daveed Vandevoorde: Or at least, the specialized member should not be allowed in constant-expressions.
Bill Gibbons: Alternatively, the use of a member constant within the definition could be treated the same as the use of "sizeof(member class)". For example:
template <class T> struct A { static const T i = 1; struct B { char b[100]; }; char x[sizeof(B)]; // specialization can affect array size char y[i]; // specialization can affect array size }; template<> const int A<int>::i = 42; template<> struct A<int>::B { char z[200] }; int main () { A<int> a; return sizeof(a.x) // 200 (unspecialized value is 100) + sizeof(a.y); // 42 (unspecialized value is 1) }For the member template case, the array size "sizeof(B)" cannot be evaluated until the template is instantiated because B might be specialized. Similarly, the array size "i" cannot be evaluated until the template is instantiated.
Rationale (10/99): The Standard is already sufficiently clear
on this question.
Consider:
template <class T> void f(T&); template <class T> void f(const T&); void m() { const int p = 0; f(p); }Some compilers treat this as ambiguous; others prefer f(const T&). The question turns out to revolve around whether 14.8.2.1 temp.deduct.call paragraph 2 says what it ought to regarding the removal of cv-qualifiers and reference modifiers from template function parameters in doing type deduction.
John Spicer: The partial ordering rules as originally proposed specified that, for purposes of comparing parameter types, you remove a top level reference, and after having done that you remove top level qualifiers. This is not what is actually in the IS however. The IS says that you remove top level qualifiers and then top level references.
The original rules were intended to prefer f(A<T>) over f(const T&).
Rationale (10/99): The Standard correctly reflects the intent
of the Committee.
Questions regarding when a throw-expression temporary object is destroyed.
Section 15.1 except.throw paragraph 4 describes when the temporary is destroyed when a handler is found. But what if no handler is found:
struct A { A() { printf ("A() \n"); } A(const A&) { printf ("A(const A&)\n"); } ~A() { printf ("~A() \n"); } }; void t() { exit(0); } int main() { std::set_terminate(t); throw A(); }Does A::~A() ever execute here? (Or, in case two constructions are done, are there two destructions done?) Is it implementation-defined, analogously to whether the stack is unwound before terminate() is called (15.3 except.handle paragraph 9)?
Or what if an exception specification is violated? There are several different scenarios here:
int glob = 0; // or 1 or 2 or 3 struct A { A() { printf ("A() \n"); } A(const A&) { printf ("A(const A&)\n"); } ~A() { printf ("~A() \n"); } }; void u() { switch (glob) { case 0: exit(0); case 1: throw "ok"; case 2: throw 17; default: throw; } } void foo() throw(const char*, std::bad_exception) { throw A(); } int main() { std::set_unexpected(u); try { foo(); } catch (const char*) { printf("in handler 1\n"); } catch (std::bad_exception) { printf("in handler 2\n"); } }The case where u() exits is presumably similar to the terminate() case. But in the cases where the program goes on, A::~A() should be called for the thrown object at some point. But where does this happen? The standard doesn't really say. Since an exception is defined to be "finished" when the unexpected() function exits, it seems to me that is where A::~A() should be called -- in this case, as the throws of new (or what will become new) exceptions are made out of u(). Does this make sense?
Rationale (10/99): Neither std::exit(int) nor
std::abort() destroys temporary objects, so the exception
temporary is not destroyed when no handler is found. The original
exception object is destroyed when it is replaced by an
unexpected() handler. The Standard is sufficiently clear
on these points.
The term "throw exception" seems to sometimes refer to an expression of the form "throw expr" and sometimes just to the "expr" portion thereof.
As a result it is not quite clear to me whether when "uncaught_exception()" becomes true: before or after the temporary copy of the value of "expr".
Is there a definite consensus about that?
Rationale:
The standard is sufficiently clear; the phrase "to be thrown" indicates
that the throw itself (which includes the copy to the temporary object)
has not yet begun. The footnote in
15.5.1 except.terminate
paragraph 1 reinforces
this ordering.
Annex C lists C compatibility issues. One item not in the annex came up in a discussion in comp.std.c++.
Consider this C and C++ code:
const int j = 0; char* p = (char*)j;
Rationale (10/99): Because j is not a constant
expression in C, this code fragment has implementation-defined behavior
in C. There is no incompatibility with C resulting from the fact that
C++ defines this behavior.
Issue 1:
The working paper is not clear about how the typename/template keywords interact with using-declarations:
templateWhen the rules for typename and the similar use of template were decided, we chose to require that they be used at every reference. The way to avoid typename at every use is to declare a typedef; then the typedef name itself is known to be a type. For using-declarations, we decided that they do not introduce new declarations but rather are aliases for existing declarations, like symbolic links. This makes it unclear whether the declaration "X c;" above should be well-formed, because there is no new name declared so there is no declaration with a "this is a type" attribute. (The same problem would occur with the template keyword when a member template of a dependent class is used). I think these are the main options:struct A { typedef int X; }; template void f() { typename A ::X a; // OK using typename A ::X; // OK typename X b; // ill-formed; X must be qualified X c; // is this OK? }
The core WG already resolved this issue according to (1), but the wording does not seem to have been added to the standard. New wording needs to be drafted.
Issue 2:
Either way, one more point needs clarification. If the first option is adopted:
template<class T> struct A { struct X { }; }; template<class T> void g() { using typename A<T>::X; X c; // if this is OK, then X by itself is a type int X; // is this OK? }When "g" is instantiated, the two declarations of X are compatible (7.3.3 namespace.udecl paragraph 10). But there is no way to know this when the definition of "g" is compiled. I think this case should be ill-formed under the first option. (It cannot happen under the second option.) If the second option is adopted:
template<class T> struct A { struct X { }; }; template<class T> void g() { using A<T>::X; int X; // is this OK? }Again, the instantiation would work but there is no way to know that in the template definition. I think this case should be ill-formed under the second option. (It would already be ill-formed under the first option.)
From John Spicer:
The "not a new declaration" decision is more of a guiding principle than a hard and fast rule. For example, a name introduced in a using-declaration can have different access than the original declaration.Tentative Resolution:Like symbolic links, a using-declaration can be viewed as a declaration that declares an alias to another name, much like a typedef.
In my opinion, "X c;" is already well-formed. Why would we permit typename to be used in a using-declaration if not to permit this precise usage?
In my opinion, all that needs to be done is to clarify that the "typeness" or "templateness" attribute of the name referenced in the using-declaration is attached to the alias created by the using-declaration. This is solution #1.
The rules for multiple declarations with the same name in the same scope should treat a using-declaration which names a type as a typedef, just as a typedef of a class name is treated as a class declaration. This needs drafting work. Also see Core issue 36 .
Rationale (04/99): Any semantics associated with the
typename keyword in using-declarations should be considered an
extension.
Daveed Vandevoorde : While reading Core issue 11 I thought it implied the following possibility:
template<typename T> struct B { template<int> void f(int); }; template<typename T> struct D: B<T> { using B<T>::template f; void g() { this->f<1>(0); } // OK, f is a template };
However, the grammar for a using-declaration reads:
and nested-name-specifier never ends in "template".
Is that intentional?
Bill Gibbons :
It certainly appears to be, since we have:
Rationale (04/99): Any semantics associated with the
template keyword in using-declarations should be considered an
extension.
How can we write a function template, or member function of a class template that takes a C linkage function as a parameter when the function type depends on one of the template parameter types?
extern "C" void f(int); void g(char); template <class T> struct A { A(void (*fp)(T)); }; A<char> a1(g); // okay A<int> a2(f); // errorAnother variant of the same problem is:
extern "C" void f(int); void g(char); template <class T> void h( void (*fp)(T) ); int main() { h(g); // okay h(f); // error }
Somehow permit a language linkage to be specified as part of a function parameter declaration. i.e.
template <class T> struct A { A( extern "C" void (*fp)(T) ); }; template <class T> void h( extern "C" void (*fp)(T) );Suggested resolution: (Bill Gibbons)
The whole area of linkage needs revisiting. Declaring calling convention as a storage class was incorrect to begin with; it should be a function qualifier, as in:
void f( void (*pf)(int) c_linkage );instead of the suggested:
void f( extern "C" void (*pf)(int) );I would like to keep calling convention on the "next round" issues list, including the alternative of using function qualifiers.
And to that end, I suggest that the use of linkage specifiers to specify
calling convention be deprecated - which would make any use of linkage
specifiers in a parameter declaration deprecated.
8.3.6 dcl.fct.default paragraph 4 says:
For non-template functions, default arguments can be added in later declarations of a functions in the same scope.Why say for non-template functions? Why couldn't the following allowed?
template <class T> struct B { template <class U> inline void f(U); }; template <class T> template <class U> inline void B<T>::f(U = int) {} // adds default arguments // is this well-formed? void g() { B<int> b; b.f(); }If this is ill-formed, chapter 14 should mention this.
Rationale:
This is sufficiently clear in the standard. Allowing additional
default arguments would be an extension.
(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.
Footnotes 26 and 29 both use the phrase "following the function declarator" incorrectly: the function declarator includes the parameter list, but the footnotes make clear that they intend what's said to apply to names inside the parameter list. Presumably the phrase should be "following the function declarator-id."
Proposed Resolution (04/99): Change the text in 3.4.1 basic.lookup.unqual paragraph 6 from:
A name used in the definition of a function [footnote: This refers to unqualified names following the function declarator; such a name may be used as a type or as a default argument name in the parameter-declaration-clause, or may be used in the function body. end footnote] that is ...to:
A name used in the definition of a function following the function's declarator-id [footnote: This refers to unqualified names that occur, for instance, in a type or default argument expression in the parameter-declaration-clause or used in the function body. end footnote] that is ...Change the text in 3.4.1 basic.lookup.unqual ] paragraph 8 from:
A name used in the definition of a function that is a member function (9.3 class.mfct ) [footnote: That is, an unqualified name following the function declarator; such a name may be used as a type or as a default argument name in the parameter-declaration-clause, or may be used in the function body, or, if the function is a constructor, may be used in the expression of a mem-initializer. end footnote] of class X shall be ...to:
A name used in the definition of a member function (9.3 class.mfct ) of class X following the function's declarator-id [footnote: That is, an unqualified name that occurs, for instance, in a type or default argument expression in the parameter-declaration-clause, in the function body, or in an expression of a mem-initializer in a constructor definition. end footnote] shall be ...
If an argument used for lookup is the address of a group of overloaded functions, are there any associated namespaces or classes? What if it's the address of a function template?
My inclination is to say no to both.
From Mike Miller:
We discussed this on the reflector a few weeks ago. I'll leave the template case for the Core III experts, but I'd find it surprising if the overload case weren't handled as the obvious generalization of the single-function case. For a single function, the associated namespaces are those of the types used in the parameters and return type; I would expect that using an overloaded function name would simply be the union of the namespaces from the members of the overload set. That would be the simplest and most intuitive, IMHO -- is there an argument for doing it differently?
Proposed Resolution (04/99): In 3.4.2 basic.lookup.koenig paragraph 2, add following the last bullet in the list of associated classes and namespaces for various argument types (not a bullet itself because overload sets and templates do not have a type):
In addition, if the argument is the name or address of a set of overloaded functions and/or function templates, its associated classes and namespaces are the union of those associated with each of the members of the set: the namespace in which the function or function templates is defined and the classes and namespaces associated with its (non-dependent) parameter types and return type.
Section 3.4.2 basic.lookup.koenig includes the following:
struct A { union U {}; friend void f(U); }; struct B { struct S {}; friend void f(S); }; int main() { A::U u; f(u); // okay: A is an associated class B::S s; f(s); // error: no matching f(), B is not an associated class }Certainly the enclosing class should also be an associated class for nested class types, shouldn't it?
Proposed Resolution (10/99): Change the two referenced bullets to read:
The text of 3.8 basic.life paragraph 2 currently reads,
The phrase "an object of type" is obviously incorrect. I believe it should read "an object of POD type." Does anyone disgree?
Proposed Resolution (10/99):
As suggested.
Can you use memcpy on non-member POD subobjects of non-POD objects?
In 3.9 basic.types paragraphs 2 and 3 we have:
For any complete POD object 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*. 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. [Example elided]Paragraph 3 doesn't repeat the restriction of paragraph 2. Should it be assumed? Otherwise only complete POD types are copyable to an array of char and back, but scribbling over subobjects is OK. (Or perhaps a "distinct T object" is a complete object...)*[Footnote: By using, for example, the library functions (17.4.1.2 lib.headers ) memcpy or memmove. end footnote]For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2, if the value of obj1 is copied into obj2, using the memcpy library function, obj2 shall subsequently hold the same value as obj1.
Proposed Resolution (04/99): Change the text in 3.9 basic.types paragraph 2 from:
For any complete POD object type T, ...to:
For any object (other than a base class subobject) of POD type T, ...Change the text in 3.9 basic.types paragraph 3 from:
For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2,to:
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, ...
The cross-reference is incorrect in the first sentence after the grammar in 5.1 expr.prim paragraph 7:
A nested-name-specifier that names a class, optionally followed by the keyword template (14.8.1 temp.arg.explicit ), ...The use of the template keyword in this context is discussed in 14.2 temp.names , not 14.8.1 temp.arg.explicit .
5.3.4 expr.new paragraph 6 says:
The expression in a direct-new-declarator shall have integral type (3.9.1 basic.fundamental ) with a non-negative value.I assume the intent was to also allow enumeral types, as we do in 5.2.1 expr.sub ?
Proposed Resolution (10/99):
Replace "integral type" by "integral or enumeration type" in
5.3.4 expr.new
paragraph 6.
Can a typedef redeclaration be done within a class?
class X { typedef int I; typedef int I; };See also 9.2 class.mem , Core issue 36 , and Core issue 85 .
Proposed Resolution (10/99):
Change 7.1.3 dcl.typedef
paragraph 2
from "In a given scope" to "In a given non-class scope."
The following code does not compile with the EDG compiler:
volatile const int a = 5; int b[a];The standard, 7.1.5.1 dcl.type.cv , says:
A variable of const-qualified integral or enumeration type initialized by an integral constant expression can be used in integral constant expressions.This doesn't say it can't be const volatile-qualified, although I think that was what was intended.
Proposed Resolution (10/99): Change 7.1.5.1 dcl.type.cv to read:
Consider the following:
extern "C" void f(); namespace N { extern "C" void f(); } using N::f;According to 7.3.3 namespace.udecl paragraph 11, the using-declaration is an error:
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, the program is ill-formed.Based on the context (7.3.3 namespace.udecl paragraph 10 simply reiterates the requirements of 3.3 basic.scope ), one might wonder if the failure to exempt extern "C" functions was intentional or an oversight. After all, there is only one function f() involved, because it's extern "C", so ambiguity is not a reason to prohibit the using-declaration.
This also breaks the relatively strong parallel between extern "C" functions and typedefs established in our discussion of Core issue 14 in Santa Cruz. There the question was for using-directives:
typedef unsigned int size_t; extern "C" int f(); namespace N { typedef unsigned int size_t; extern "C" int f(); } using namespace N; int i = f(); // ambiguous "f"? size_t x; // ambiguous "size_t"?We decided for both that there was no ambiguity because each pair of declarations declares the same entity. (According to 3 basic paragraph 3, a typedef name is not an entity, but a type is; thus the declarations of size_t declare the same entity "unsigned int".)
In the context of using-declarations, there is no explicit extension of the restrictions in 3.3 basic.scope paragraph 4 except as noted above for function declarations; thus the parallel scenario for a typedef is not ill-formed:
typedef unsigned int size_t; namespace N { typedef unsigned int size_t; }; using N::size_t; // okay, both declarations // refer to the same entityI think 7.3.3 namespace.udecl paragraph 11 ought to be rewritten as:
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.
Proposed Resolution (10/99):
As suggested.
Section 7.3.4 namespace.udir paragraph 3 uses the term extended-namespace-definition three times:
If a namespace is extended by an extended-namespace-definition after a using-directive for that namespace is given, the additional members of the extended namespace and the members of namespaces nominated by using-directives in the extended-namespace-definition can be used after the extended-namespace-definition.I think the intent is clear, but unfortunately I cannot find any other mention (or definition) of this term.
Mike Miller: True enough; in Section 7.3.1 namespace.def [the grammar] it's called an extension-namespace-definition.
Proposed Resolution (10/99):
Systematically replace "extended-namespace-definition" by "extension-namespace-definition".
(From J16/99-0005 = WG21 N1182, "Proposed Resolutions for Core Language Issues 6, 14, 20, 40, and 89")
There are two sub-issues. The first concerns the statement in 8.3 dcl.meaning paragraph 1,
The id-expression of 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 (14.7 temp.spec ).The second sub-issue is regarding another statement in the same paragraph:
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 ) or nested class (9.7 class.nest ) outside of its class, the definition or explicit instantiation of a function, variable or class member of a namespace outside of its namespace, or...Analysis
The problem in the first sub-issue is that the wrong syntactic non-terminal is mentioned. The relevant portions of the grammar are:
If an unqualified-id is used as the id-expression of a declarator-id, it shall be a simple identifier except...However, it does not appear that this restriction has any meaning; all of the possible cases of unqualified-ids are represented in the list of exceptions! Rather than recasting the sentence into a correct but useless form, it would be better to remove it altogether.
The second sub-issue deals with the conditions under which a qualified-id can be used in a declarator, including "the definition of a...nested class" and "the definition or explicit instantiation of a...class member of a namespace." However, the name in a class definition is not part of a declarator; these constructs do not belong in a list of declarator contexts.
Proposed Resolution for sub-issue 1 (04/99):
The suggested resolution for the first sub-issue overlooked the fact that the existing wording has the additional effect of prohibiting the use of the non-identifier syntax for declaring other than the listed entities. Thus the proposed wording for the first sub-issue is:
Change 8.3 dcl.meaning paragraph 1 from:
The id-expression of a declarator-id shall be a simple identifier except...to:
An unqualified-id occurring in a declarator-id shall be a simple identifier except...
Proposed Resolution for sub-issue 2 (10/99):
Change 8.3 dcl.meaning paragraph 1 from:
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 ) or nested class (9.7 class.nest ) outside of its class, the definition or explicit instantiation of a function, variable or class member of a namespace outside of its namespace, or...to
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...
Proposed Resolution (04/99): Change the text in the example of section 8.3.6 dcl.fct.default paragraph 5 from:
... g will be called with the value f(1).to:
... g will be called with the value f(2).
Given:
struct S1 { int x; }; struct S2 { int x; double y; }; struct S3 { int x; double y; string s; };Once upon a time, we went through a fairly protracted discussion to ensure that S1().x would be guaranteed to be 0. Note that if we declare
void f() { S1 s1; // ... }there is no guarantee of the value of s1.x, and that is intentional. But S1().x is different, because S1() is an rvalue, and unless all of its members are defined, the effect of copying it is undefined.
Similarly, S2().x and S2().y are also defined to be equal to zero, and here it really matters for many implementations, because if S2().y is just a bunch of random bits, it is entirely possible that trying to copy S2().y will yield a floating-point trap.
However, rather to my surprise, the standard does not define the value of S3().x or S3().y, because S3 is not a POD. It does define S3().s (by running the string constructor), but once a structure is no longer a POD, the values of uninitialized members are no longer guaranteed in expressions of the form T().
In my opinion, this definition is a mistake, and the committee's intention was to zero-initialize all members that do not have an explicitly defined constructor, whether or not the class is a POD. See also paper J16/99-0014 = WG21 N1191.
Proposed Resolution (04/99): Add the following text to the end of section 8.5 dcl.init paragraph 5:
To value-initialize an object of type T means:
- if T is a class type (9 class ) with a user-declared constructor (12.1 class.ctor ), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
- if T is a non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized;
- if T is an array type, then each element is value-initialized;
- otherwise, the storage for the object is zero-initialized.
Change "default-initialization" to "value-initialization" in
5.2.3 expr.type.conv
paragraph
2 and in 8.5.1 dcl.init.aggr
paragraph 7.
8.5.1 dcl.init.aggr paragraph 2 says,
When an aggregate is initialized the initializer can be an initializer-clause consisting of a brace-enclosed, comma-separated list of initializers for the members of the aggregate.Neither of these uses of the syntactic nonterminal initializer corresponds to the grammar:
Proposed resolution (10/99): replace the quoted words with:
When an aggregate is initialized the initializer can contain an initializer-clause consisting of a brace-enclosed, comma-separated list of initializer-clauses for the members of the aggregate.
The standard says, in 9.2 class.mem paragraph 4:
A member-declarator can contain a constant-initializer only if it declares a static member (9.4 class.static ) of integral or enumeration type, see 9.4.2 class.static.data .But later, in the section on static class data member initialization, 9.4.2 class.static.data paragraph 4, it says:
If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19 expr.const ). In that case, the member can appear in integral constant expressions within its scope.The first paragraph should be modified to make it clear that it is not possible to initialize a static data member in-line with a constant-initializer if that data member is of integral (or enumeration) type, and yet not const.
Proposed Resolution (10/99): Change the sentence in 9.2 class.mem paragraph 4 to read:
A member-declarator can contain a constant-initializer only if it declares a static member (9.4 class.static ) of const integral or const enumeration type, see 9.4.2 class.static.data .
Paragraph 2 says that "the object-expression is always evaluated" when the class member syntax is used to refer to a static member. This presumably should say that the object expression is evaluated if the member access is performed, i.e., not if the overall expression is the operand of sizeof or the unevaluated branch of ?:, ||, or &&.
Proposed Resolution (10/99):
Replace "is always evaluated" by "is evaluated" in
9.4 class.static
paragraph 2.
Also see section: 3.2 basic.def.odr .
Originally, all static data members still had to be defined outside the class whether they were used or not.
But that restriction was supposed to be lifted so that static data members need not be defined outside the class unless they are used in a manner which requires their definition, in the same manner as namespace-scope variables. In particular, if an integral/enum const static data member is initialized within the class, and its address is never taken, we agreed that no namespace-scope definition was required.
For example:
struct A { static const int size = 10; int array[size]; }; int main() { A a; return 0; }However, 9.4.2 class.static.data paragraph 4 says:
The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.A narrow interpreration of "used" in this rule would make the example ill-formed because there is no namespace-scope definition of "size". A better wording for this rule would be:
The member shall still be defined in a namespace scope if it is used in the program in the manner described in 3.2 basic.def.odr . The namespace scope definition shall not contain an initializer.Also, the wording in 3.2 basic.def.odr paragraph 2:
An expression is potentially evaluated unless either it is the operand of the sizeof operator (5.3.3 expr.sizeof ), or it is the operand of the typeid operator and does not designate an lvalue of polymorphic class type (5.2.8 expr.typeid ).is incomplete because it does not mention the use of a compile-time constant as an array bound or template argument. It should say something like:
An expression is potentially evaluated unless it is the operand of the sizeof operator (5.3.3 expr.sizeof ), the operand of the typeid operator, an integral constant-expression used as an array bound or an integral constant-expression used as a template-argument for a non-reference template-parameter; and the expression does not designate an lvalue of polymorphic class type (5.2.8 expr.typeid ).
Proposed Resolution (04/99): Change the first sentence of 3.2 basic.def.odr paragraph 2 from:
An expression is potentially evaluated unless either it is the operand of the sizeof operator (5.3.3 expr.sizeof ), or it is the operand of the typeid operator and does not designate an lvalue of polymorphic class type (5.2.8 expr.typeid ).to:
An expression is potentially evaluated unless it appears where an integral constant expression is required (see 5.19 expr.const ), is the operand of the sizeof operator (5.3.3 expr.sizeof ), or is the operand of the typeid operator and the expression does not designate an lvalue of polymorphic class type (5.2.8 expr.typeid ).
11.5 class.protected paragraph 1 begins:
When a friend or a member function of a derived class references a protected nonstatic member of a base class, an access check applies in addition to those described earlier in clause 11 class.access .
This was intended to refer to nonstatic member functions and nonstatic data members. However, a protected nested type declared in a base class is, by some definition of the word, a "nonstatic" member, and therefore subject to this additional access check.
Proposed resolution (10/99):
change "protected nonstatic member" in the above to
"protected nonstatic member function or protected nonstatic data member"
to make the intent clear.
Issue 1
12.8 class.copy (From J16/99-0005 = WG21 N1182, "Proposed Resolutions for Core Language Issues 6, 14, 20, 40, and 89")
There are three related sub-issues in this issue, all dealing with the elision of copy constructors as described in 12.8 class.copy paragraph 15:
After discussion in Santa Cruz, the core group decided that sub-issue #1 required no change; the necessity of an accessible and unambiguous copy constructor is made clear in 12.2 class.temporary paragraph 1 and need not be repeated in this text. The remaining two sub-issues appear to be valid criticisms and should be addressed.
Proposed Resolution (10/99): The paragraph in question should be rewritten as follows. In addition, references to this section should be added to the index under "temporary, elimination of," "elimination of temporary," and "copy, constructor elision."
class Thing { public: Thing(); ~Thing(); Thing(const Thing&); }; Thing f() { Thing t; return t; } Thing t2 = f();Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing: the copying of the local automatic object t into the temporary object for the return value of function f() and the copying of that temporary object into object t2. Effectively, the construction of the local object t can be viewed as directly initializing the global object t2, and that object's destruction will occur at program exit. —end example]
Sections 13.3.1.4 over.match.copy and 13.3.1.5 over.match.conv should be clarified regarding the treatment of conversion functions which return reference types.
Proposed resolution (10/99):
In 13.3.1.4 over.match.copy paragraph 1, change
Conversion functions that return "reference to T" return lvalues of type T and are therefore considered to yield T for this process of selecting candidate functions.to
Conversion functions that return "reference to X" return lvalues of type X and are therefore considered to yield X for this process of selecting candidate functions.In 13.3.1.5 over.match.conv paragraph 1, change
Conversion functions that return "reference to T" return lvalues of type T and are therefore considered to yield T for this process of selecting candidate functions.to
Conversion functions that return "reference to cv2 X" return lvalues of type "cv2 X" and are therefore considered to yield X for this process of selecting candidate functions.
In 13.3.3 over.match.best paragraph 1, bullet 4 of the second set of bullets, there is a cross-reference to 8.5 dcl.init and 13.3.1.5 over.match.conv . I believe it should also reference 13.3.1.6 over.match.ref . I think the phrase "initialization by user-defined conversion" was intended to refer to all initializations using user-defined conversions, and not just the case in 13.3.1.5 over.match.conv . Referring to only 13.3.1.5 over.match.conv suggests a narrower meaning of the phrase.
13.3.1.4 over.match.copy
,
although it does deal with initialization by user-defined
conversion, does not need to be referenced because it deals with class
--> class cases, and therefore there are no standard conversions involved
that could be compared.
Section 14 temp paragraph 8 says:
A non-exported template that is neither explicitly specialized nor explicitly instantiated must be defined in every translation unit in which it is implicitly instantiated (14.7.1 temp.inst ) or explicitly instantiated (14.7.2 temp.explicit ); no diagnostic is required.Shouldn't the first underlined phrase be omitted to avoid conflict with the second underlined phrase?
From John Spicer:
The first "explicitly instantiated" is intended to mean "explicitly instantiated in some other translation unit".
Proposed Resolution (04/99): Change the text in 14 temp paragraph 8 from:
A non-exported template that is neither explicitly specialized nor explicitly instantiated must be defined in every translation unit in which it is implicitly instantiated (14.7.1 temp.inst ) or explicitly instantiated (14.7.2 temp.explicit ); no diagnostic is required.to:
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. [Note: See also 14.7.2 temp.explicit ]
The example in 14.1 temp.param paragraph 8 is:
template<int* a> struct R { /*...*/ }; int* p; R<p> w;There was a French comment was that this is an error, and there was general agreement with that.
I've been looking for the verbiage that specifies that this is an error and haven't found it. In particular, nothing in 14.1 temp.param ("Template parameters") nor 14.3.2 temp.arg.nontype ("Template non-type arguments") appears to rule out this case. (14.3.2 temp.arg.nontype paragraph 1 allows an argument to be "the name of an object or function with external linkage," with no limitation on the kinds of parameters such a name can match; "p" is, in fact, such a name.)
Should the resolution of the French comment include beefing up one or both of these sections to cover the applicable rules explicitly?
Proposed Resolution (04/99): Change the example in 14.1 temp.param paragraph 8 from:
template<int *a> struct R { /* ... */ }; template<int b[5]> struct S { /* ... */ }; int *p; R<p> w; // OK S<p> x; // OK due to parameter adjustment int v[5]; R<v> y; // OK due to implicit argument conversion S<v> z; // OK due to both adjustment and conversionto:
template<int *a> struct R { /* ... */ }; template<int b[5]> struct S { /* ... */ }; int p; R<&p> w; // OK S<&p> x; // OK due to parameter adjustment int v[5]; R<v> y; // OK due to implicit argument conversion S<v> z; // OK due to both adjustment and conversionFurthermore, in 14.3.2 temp.arg.nontype paragraph 1:
I have a request for clarification regarding a issue similar to John Wiegley's, but wrt. the ::template syntax. More precisely, where is
X::template Yallowed? (It is required for dependent X where Y is a template-id, I believe, but it doesn't seem to be disallowed elsewhere.)
The question also holds for '.template' and '->template'.
Proposed Resolution (04/99): Append to 14.2 temp.names paragraph 5:
Furthermore, names of member templates shall not be prefixed by the keyword template if the postfix-expression or qualified-id does not appear in the scope of a template. [Note: just as is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the expression on the left of the -> or ., or the nested-name-specifier is not dependent on a template-parameter. ]
The explanation in 14.3.2 temp.arg.nontype paragraph 2 of why a string literal cannot be used as a template argument leaves something to be desired:
...because a string literal is an object with internal linkage.I can't find anything that says that a string literal has internal linkage. In fact, I'd be pretty surprised if I did, since linkage is defined (in 3.5 basic.link ) strictly in terms of names, and a string literal doesn't have a name. Actually, I think that it's the namelessness of a string literal that prevents it from being a template argument; only the third and fourth bullets of 14.3.2 temp.arg.nontype paragraph 1 could conceivably apply, and both of those require that the entity have a name (i.e., that they be given as an id-expression).
Proposed Resolution (10/99): In 14.3.2 temp.arg.nontype paragraph 2, change
[Note: a string literal (2.13.4 lex.string ) is not an acceptable template-argument because a string literal is an object with internal linkage.to
[Note: a string literal (2.13.4 lex.string ) does not satisfy the requirements of any of these categories and thus is not an acceptable template-argument.
14.5.5.1 temp.over.link , paragraphs 5 and 6, describes equivalence and functional equivalence for expressions involving template parameters. As a note in paragraph 5 points out, such expressions may involve type parameters as well as non-type parameters.
Paragraph 7, however, describes the equivalence of function templates only with respect to non-type template parameters. It appears to be unspecified how to determine the equivalence of template functions whose types involve expressions that use template type parameters.
template <int I> struct S { }; // The following two declarations are equivalent: template <int I> void f(S<I>); template <int J> void f(S<J>); // The IS doesn't say whether these are equivalent: template <class T> void f(S<sizeof(T)>); template <class T> void f(S<sizeof(T)>);
Proposed resolution (10/99): Remove the three uses of the words "non-type" in 14.5.5.1 temp.over.link paragraph 7.
In 14.6 temp.res , references to the nonexistent syntactic non-terminal qualified-name occur twice in paragraph 3, twice in paragraph 4, and once in paragraph 5. There is also a reference in 14.1 temp.param paragraph 2.
Proposed resolution (10/99): Change
the reference in all these cases to qualified-id.
14.1 temp.param paragraph 13 says:
The scope of a template-parameter extends from its point of declaration until the end of its template. In particular, a template-parameter can be used in the declaration of subsequent template-parameters and their default arguments.Is the following well-formed?
template<class U = U> class X { ... };
Proposed Resolution (04/99): Change 14.1 temp.param paragraph 14 from:
A template-parameter cannot be used in preceding template-parameters or their default arguments.to:
A template-parameter cannot be used in preceding template-parameters, in their default arguments, or in its own default argument.
Problem Description: At least four of the examples in 14.7.3 temp.expl.spec have errors.
Proposed Resolution (10/99):
1. Change the example in paragraph 8 from:
[Example:to:// file #1 #include <vector> // Primary class template vector export template<class T> void f(t) { vector<T> vec; // should match the specialization /* ... */ } // file #2 #include <vector> class B { }; // Explicit specialization of vector for vector<B> template<class T> class vector<B> { /* ... */ } template<class T> void f(T); void g(B b) { f(b); // ill formed: // f<B> should refer to vector<B>, but the // specialization was not declared with the // definition of f in file #1 }—end example]
[Example:// file #1 #include <vector> // Primary class template vector export template<class T> void f(T) { std::vector<T> vec; // should match the specialization /* ... */ }; // file #2 #include <vector> class B { }; // Explicit specialization of vector for vector<B> namespace std { template<> class vector<B> { /* ... */ }; } template<class T> void f(T); void g(B b) { f(b); // ill formed: // f<B> should refer to vector<B>, but the // specialization was not declared with the // definition of f in file #1 }—end example]
2. The example in paragraph 16 as it appears in the IS:
[Example:The word 'partial' in the third comment in the example should be removed because this example does not illustrate partial specialization. Also, the two specializations of template<> template<> void A<int>::g(int, char); violate 14.7 temp.spec , paragraph 5, which reads:template<class T> struct A { void f(T); template<class X> void g(T, X); void h(T) { } }; // specialization template<> void A<int>::f(int); // out of class member template definition template<class T> template<class X> void A<T>::g(T,X) { } // member template partial specialization template<> template<class X> void A<int>::g(int, X); // member template specialization template<> template<> void A<int>::g(int, char); // X deduced as char template<> template<> void A<int>::g<char>(int, char); // X specified as char // member specialization even if defined in class definition template<> void A<int>::h(int) { }—end example]
No program shall explicitly instantiate any template more than once, both explicitly instantiate and explicitly specialize a template, or specialize a template more than once for a given set of template-arguments. An implementation is not required to diagnose a violation of this rule.Proposed resolution (10/99):
[Example:template<class T> struct A { void f(T); template<class X1> void g1(T, X1); template<class X2> void g2(T, X2); void h(T) { } }; // specialization template<> void A<int>::f(int); // out of class member template definition template<class T> template<class X1> void A<T>::g1(T,X1) { } // member template specialization template<> template<class X1> void A<int>::g1(int, X1); // member template specialization template<> template<> void A<int>::g1(int, char); // X1 deduced as char template<> template<> void A<int>::g2<char>(int, char); // X2 specified as char // member specialization even if defined in class definition template<> void A<int>::h(int) { }—end example]
3. Remove the spurious semicolon (or the curly brackets) from the end of the last line in the example in paragraph 17. This is the example as it appears in the IS:
[Example:Proposed resolution (10/99):template<class T1> class A { template<class T2> class B { void mf(); }; }; template<> template<> A<int>::B<double> { }; template<> template<> void A<char>::B<char>::mf() {};—end example]
[Example:template<class T1> class A { template<class T2> class B { void mf(); }; }; template<> template<> A<int>::B<double>; template<> template<> void A<char>::B<char>::mf();—end example]
4. Remove spurious semicolons (or curly brackets) from the specializations of mf1 and mf2 in the example in paragraph 18. This is the text of the example as it appears in the IS:
[Example:Proposed resolution (10/99):template<class T1> class A { template<class T2> class B { template<class T3> void mf1(T3); void mf2(); }; }; template<> template<class X> class A<int>::B { }; template<> template<> template<class T> void A<int>::B<double>::mf1(T t) { }; template<class Y> template<> void A<Y>::B<double>::mf2() { }; // ill-formed; B<double> is specialized but // its enclosing class template A is not—end example]
[Example:template<class T1> class A { template<class T2> class B { template<class T3> void mf1(T3); void mf2(); }; }; template<> template<class X> class A<int>::B { }; template<> template<> template<class T> void A<int>::B<double>::mf1(T t) { } template<class Y> template<> void A<Y>::B<double>::mf2() { } // ill-formed; B<double> is specialized but // its enclosing class template A is not—end example]
15.4 except.spec paragraph 3 should say what happens when two pointers to members with different exception specifications are assigned to each other, initialized with one another, etc.
Proposed Resolution (04/99): Change the text in 15.4 except.spec paragraph 3 from:
Similarly, any function or pointer to function assigned to, or initializing, a pointer to function shall only allow exceptions that are allowed by the pointer or function being assigned to or initialized.to:
A similar restriction applies to assignment to and initialization of pointers to functions, pointers to member functions, and references to functions: the target entity shall allow at least the exceptions allowed by the source value in the assignment or initialization.
The Lao character 0e0d should be 0e8d. 0e0d is both out of order and already used in the Thai characters.
Proposed resolution (10/99): As suggested.
Section | Issue # | Status | Title |
1.9 | 129 | open | Stability of uninitialized auto variables |
2.2 | 173 | review | Constraints on execution character set |
2.12 | 189 | open | Definition of operator and punctuator |
3.2 | 50 | NAD | Converting pointer to incomplete type to same type |
3.2 | 82 | dup | Definition of "using" a constant expression |
3.3.6 | 42 | NAD | Redefining names from base classes |
3.4.1 | 41 | DR | Clarification of lookup of names after declarator-id |
3.4.1 | 139 | open | Error in friend lookup example |
3.4.1 | 191 | open | Name lookup does not handle complex nesting |
3.4.1 | 192 | open | Name lookup in parameters |
3.4.2 | 12 | dup | Default arguments on different declarations for the same function and the Koenig lookup |
3.4.2 | 33 | DR | Argument dependent lookup and overloaded functions |
3.4.2 | 90 | DR | Should the enclosing class be an "associated class" too? |
3.4.2 | 91 | NAD | A union's associated types should include the union itself |
3.4.2 | 143 | drafting | Friends and Koenig lookup |
3.4.2 | 164 | review | Overlap between Koenig and normal lookup |
3.4.4 | 85 | drafting | Redeclaration of member class |
3.4.5 | 141 | open | Non-member function templates in member access expressions |
3.4.5 | 156 | open | Name lookup for conversion functions |
3.5 | 132 | drafting | Local types and linkage |
3.6.3 | 28 | drafting | 'exit', 'signal' and static object destruction |
3.8 | 89 | drafting | Object lifetime does not account for reference rebinding |
3.8 | 93 | DR | Missing word in 3.8 basic.life paragraph 2 |
3.8 | 119 | open | Object lifetime and aggregate initialization |
3.9 | 43 | DR | Copying base classes (PODs) using memcpy |
3.9.1 | 146 | open | Floating-point zero |
3.10 | 158 | open | Aliasing and qualification conversions |
4.10 | 149 | review | Accessibility and ambiguity |
4.11 | 170 | open | Pointer-to-member conversions |
5 | 71 | NAD | Incorrect cross reference |
5.1 | 122 | open | template-ids as unqualified-ids |
5.1 | 123 | DR | Bad cross-reference |
5.1 | 125 | drafting | Ambiguity in friend declaration syntax |
5.1 | 147 | review | Naming the constructor |
5.2.2 | 113 | drafting | Visibility of called function |
5.2.2 | 118 | drafting | Calls via pointers to virtual member functions |
5.2.5 | 52 | drafting | Non-static members, member selection and access checking |
5.2.9 | 53 | drafting | Lvalue-to-rvalue conversion before certain static_casts |
5.2.9 | 54 | NAD | Static_cast from private base to derived class |
5.2.9 | 128 | review | Casting between enum types |
5.2.9 | 137 | review | static_cast of cv void* |
5.2.10 | 195 | Steve Clamage | Converting between function and object pointers |
5.3.1 | 203 | open | Type of address-of-member expression |
5.3.4 | 31 | NAD | Looking up new/delete |
5.3.4 | 74 | DR | Enumeration value in direct-new-declarator |
5.3.4 | 127 | drafting | Ambiguity in description of matching deallocation function |
5.3.4 | 130 | NAD | Sequence points and new-expressions |
5.3.5 | 196 | open | Arguments to deallocation functions |
5.7 | 55 | NAD | Adding/subtracting pointer and enumeration value |
5.7 | 179 | open | Function pointers and subtraction |
5.10 | 73 | drafting | Pointer equality |
5.18 | 188 | open | Comma operator and rvalue conversion |
5.19 | 94 | drafting | Inconsistencies in the descriptions of constant expressions |
5.19 | 97 | NAD | Use of bool constants in integral constant expressions |
7 | 157 | open | Omitted typedef declarator |
7.1.1 | 69 | drafting | Storage class specifiers on template declarations |
7.1.1 | 154 | NAD | Anonymous unions in unnamed namespaces |
7.1.3 | 56 | DR | Redeclaring typedefs within classes |
7.1.5.1 | 76 | DR | Are const volatile variables considered "constant expressions"? |
7.1.5.3 | 68 | drafting | Grammar does not allow "friend class A<int>;" |
7.1.5.3 | 144 | open | Position of friend specifier |
7.2 | 172 | open | Unsigned int as underlying type of enum |
7.3 | 171 | review | Global namespace scope |
7.3.1.2 | 95 | NAD | Elaborated type specifiers referencing names declared in friend decls |
7.3.1.2 | 138 | open | Friend declaration name lookup |
7.3.1.2 | 165 | NAD | Definitions of friends and block-scope externs |
7.3.1.2 | 166 | review | Friend declarations of template-ids |
7.3.3 | 11 | extension | How do the keywords typename/template interact with using-declarations? |
7.3.3 | 36 | review | Using-declarations in multiple-declaration contexts |
7.3.3 | 101 | DR | Redeclaration of extern "C" names via using-declarations |
7.3.3 | 109 | extension | Allowing ::template in using-declarations |
7.3.3 | 169 | NAD | template-ids in using-declarations |
7.3.4 | 103 | DR | Is it extended-namespace-definition or extension-namespace-definition ? |
7.5 | 4 | drafting | Does extern "C" affect the linkage of function names with internal linkage? |
7.5 | 13 | extension | extern "C" for Parameters of Function Templates |
7.5 | 14 | NAD | extern "C" functions and declarations in different namespaces |
7.5 | 29 | drafting | Linkage of locally declared functions |
7.5 | 107 | open | Linkage of operator functions |
7.5 | 168 | NAD | C linkage for static member functions |
8.2 | 160 | open | Missing std:: qualification |
8.3 | 40 | DR | Syntax of declarator-id |
8.3 | 159 | drafting | Namespace qualification in declarators |
8.3.4 | 112 | open | Array types and cv-qualifiers |
8.3.5 | 18 | NAD | f(TYPE) where TYPE is void should be allowed |
8.3.5 | 135 | review | Class type in in-class member function definitions |
8.3.5 | 140 | drafting | Agreement of parameter declarations |
8.3.6 | 1 | drafting | What if two using-declarations refer to the same function but the declarations introduce different default-arguments? |
8.3.6 | 15 | extension | Default arguments for parameters of function templates |
8.3.6 | 65 | DR | Typo in default argument example |
8.3.6 | 66 | NAD | Visibility of default args vs overloads added after using-declaration |
8.3.6 | 136 | open | Default arguments and friend declarations |
8.5 | 5 | drafting | CV-qualifiers and type conversions |
8.5 | 35 | DR | Definition of default-initialization |
8.5 | 78 | review | Section 8.5 paragraph 9 should state it only applies to non-static objects |
8.5 | 151 | review | Terminology of zero-initialization |
8.5 | 155 | open | Brace initializer for scalar |
8.5 | 177 | open | Lvalues vs rvalues in copy-initialization |
8.5 | 178 | drafting | More on value-initialization |
8.5.1 | 163 | DR | Description of subaggregate initializer |
9 | 148 | open | POD classes and pointers to members |
9 | 175 | open | Class name injection and base name access |
9 | 176 | open | Name injection and templates |
9.2 | 75 | DR | In-class initialized members must be const |
9.2 | 80 | review | Class members with same name as class |
9.2 | 190 | open | Layout-compatible POD-struct types |
9.4 | 67 | DR | Evaluation of left side of object-expression |
9.4.2 | 48 | DR | Definitions of unused static members |
9.5 | 57 | open | Empty unions |
9.6 | 58 | open | Signedness of bit fields of enum type |
9.8 | 198 | open | Definition of "use" in local and nested classes |
10.2 | 39 | drafting | Conflicting ambiguity rules |
11 | 8 | open | Access to template arguments used in a function return type and in the nested name specifier |
11.2 | 7 | NAD | Can a class with a private virtual base class be derived from? |
11.2 | 9 | review | Clarification of access to base class members |
11.2 | 16 | review | Access to members of indirect private base classes |
11.2 | 17 | NAD | Footnote 99 should discuss the naming class when describing members that can be accessed from friends |
11.2 | 142 | drafting | Injection-related errors in access example |
11.4 | 77 | drafting | The definition of friend does not allow nested classes to be friends |
11.5 | 19 | NAD | Clarify protected member access |
11.5 | 161 | DR | Access to protected nested type |
11.8 | 10 | open | Can a nested class access its own class name as a qualified name if it is a private member of the enclosing class? |
11.8 | 45 | open | Access to nested classes |
12.1 | 194 | open | Identifying constructors |
12.2 | 86 | open | Lifetime of temporaries in query expressions |
12.2 | 117 | drafting | Timing of destruction of temporaries |
12.2 | 124 | drafting | Lifetime of temporaries in default initialization of class arrays |
12.2 | 199 | open | Order of destruction of temporaries |
12.2 | 201 | open | Order of destruction of temporaries in initializers |
12.3.1 | 152 | review | explicit copy constructors |
12.4 | 193 | open | Order of destruction of local automatics of destructor |
12.8 | 6 | extension | Should the optimization that allows a class object to alias another object also allow the case of a parameter in an inline function to alias its argument? |
12.8 | 20 | DR | Some clarifications needed for 12.8 para 15 |
12.8 | 26 | NAD | Copy constructors and default arguments |
12.8 | 111 | open | Copy constructors and cv-qualifiers |
12.8 | 185 | open | "Named" temporaries and copy elision |
13.3.1.1 | 162 | open | (&C::f)() with nonstatic members |
13.3.1.2 | 102 | NAD | Operator lookup rules do not work well with parts of the library |
13.3.1.4 | 59 | DR | Clarification of overloading and UDC to reference type |
13.3.3 | 51 | DR | Overloading and user-defined conversions |
13.3.3.1 | 84 | open | Overloading and conversion loophole used by auto_ptr |
13.3.3.1.4 | 60 | open | Reference binding and valid conversion sequences |
13.3.3.2 | 83 | review | Overloading and deprecated conversion of string literal |
13.3.3.2 | 153 | review | Misleading wording (rank of conversion) |
13.4 | 61 | NAD | Address of static member function "&p->f" |
13.4 | 115 | open | Address of template-id |
13.4 | 202 | open | Use of overloaded function name |
13.6 | 27 | NAD | Overload ambiguities for builtin ?: prototypes |
14 | 32 | DR | Clarification of explicit instantiation of non-exported templates |
14 | 72 | dup | Linkage and storage class specifiers for templates |
14 | 105 | drafting | Meaning of "template function" |
14 | 110 | open | Can template functions and classes be declared in the same scope? |
14 | 134 | drafting | Template classes and declarator-ids |
14 | 204 | open | Exported class templates |
14 | 205 | open | Templates and static data members |
14.1 | 21 | review | Can a default argument for a template parameter appear in a friend declaration? |
14.1 | 49 | DR | Restriction on non-type, non-value template arguments |
14.1 | 184 | open | Default arguments in template template-parameters |
14.1 | 187 | open | Scope of template parameter names |
14.2 | 30 | DR | Valid uses of '::template' |
14.2 | 38 | drafting | Explicit template arguments and operator functions |
14.2 | 96 | open | Syntactic disambiguation using the template keyword |
14.3.1 | 62 | drafting | Unnamed members of classes used as type parameters |
14.3.2 | 100 | DR | Clarify why string literals are not allowed as template arguments |
14.3.3 | 150 | NAD | Template template parameters and default arguments |
14.5.2 | 114 | NAD | Virtual overriding by template member function specializations |
14.5.3 | 47 | NAD | Template friend issues |
14.5.5.1 | 116 | open | Equivalent and functionally-equivalent function templates |
14.5.5.2 | 23 | open | Some questions regarding partial ordering of function templates |
14.5.5.2 | 200 | open | Partial ordering and explicit arguments |
14.6 | 120 | DR | Nonexistent non-terminal qualified-name |
14.6 | 121 | drafting | Dependent type names with non-dependent nested-name-specifiers |
14.6 | 180 | open | typename and elaborated types |
14.6 | 183 | open | typename in explicit specializations |
14.6.1 | 186 | open | Name hiding and template template-parameters |
14.6.2.1 | 108 | drafting | Are classes nested in templates dependent? |
14.6.3 | 206 | open | Semantic constraints on non-dependent names |
14.6.4 | 2 | drafting | How can dependent names be used in member declarations that appear outside of the class template definition? |
14.6.4 | 22 | DR | Template parameter with a default argument that refers to itself |
14.6.4.2 | 197 | open | Issues with two-stage lookup of dependent names |
14.7.1 | 34 | NAD | Argument dependent lookup and points of instantiation |
14.7.1 | 63 | review | Class instantiation from pointer conversion to void*, null and self |
14.7.2 | 46 | NAD | Explicit instantiation of member templates |
14.7.3 | 3 | NAD | The template compilation model rules render some explicit specialization declarations not visible during instantiation |
14.7.3 | 24 | DR | Errors in examples in 14.7.3 |
14.7.3 | 44 | review | Member specializations |
14.7.3 | 64 | review | Partial ordering to disambiguate explicit specialization |
14.7.3 | 88 | NAD | Specialization of member constant templates |
14.7.3 | 182 | open | Access checking on explicit specializations |
14.8.2.1 | 99 | NAD | Partial ordering, references and cv-qualifiers |
14.8.2.4 | 70 | review | Is an array bound a nondeduced context? |
14.8.2.4 | 181 | open | Errors in template template-parameter example |
15 | 98 | drafting | Branching into try block |
15.1 | 104 | NAD | Destroying the exception temp when no handler is found |
15.4 | 25 | DR | Exception specifications and pointers to members |
15.4 | 87 | drafting | Exception specifications on function parameters |
15.4 | 92 | open | Should exception specifications be part of the type system? |
15.4 | 126 | drafting | Exception specifications and const |
15.4 | 133 | DUP | Exception specifications and checking |
15.5.3 | 37 | NAD | When is uncaught_exception() true? |
18.4.1.3 | 79 | DUP | Alignment and placement new |
C | 81 | NAD | Null pointers and C compatability |
D.1 | 145 | drafting | Deprecation of prefix ++ |
D.2 | 167 | review | Deprecating static functions |
D.2 | 174 | open | Undeprecating global static |
E | 131 | DR | Typo in Lao characters |
unknown | 106 | drafting | Creating references to references during template deduction/instantiation |
Issue # | Section | Status | Title |
1 | 8.3.6 | drafting | What if two using-declarations refer to the same function but the declarations introduce different default-arguments? |
2 | 14.6.4 | drafting | How can dependent names be used in member declarations that appear outside of the class template definition? |
3 | 14.7.3 | NAD | The template compilation model rules render some explicit specialization declarations not visible during instantiation |
4 | 7.5 | drafting | Does extern "C" affect the linkage of function names with internal linkage? |
5 | 8.5 | drafting | CV-qualifiers and type conversions |
6 | 12.8 | extension | Should the optimization that allows a class object to alias another object also allow the case of a parameter in an inline function to alias its argument? |
7 | 11.2 | NAD | Can a class with a private virtual base class be derived from? |
8 | 11 | open | Access to template arguments used in a function return type and in the nested name specifier |
9 | 11.2 | review | Clarification of access to base class members |
10 | 11.8 | open | Can a nested class access its own class name as a qualified name if it is a private member of the enclosing class? |
11 | 7.3.3 | extension | How do the keywords typename/template interact with using-declarations? |
12 | 3.4.2 | dup | Default arguments on different declarations for the same function and the Koenig lookup |
13 | 7.5 | extension | extern "C" for Parameters of Function Templates |
14 | 7.5 | NAD | extern "C" functions and declarations in different namespaces |
15 | 8.3.6 | extension | Default arguments for parameters of function templates |
16 | 11.2 | review | Access to members of indirect private base classes |
17 | 11.2 | NAD | Footnote 99 should discuss the naming class when describing members that can be accessed from friends |
18 | 8.3.5 | NAD | f(TYPE) where TYPE is void should be allowed |
19 | 11.5 | NAD | Clarify protected member access |
20 | 12.8 | DR | Some clarifications needed for 12.8 para 15 |
21 | 14.1 | review | Can a default argument for a template parameter appear in a friend declaration? |
22 | 14.6.4 | DR | Template parameter with a default argument that refers to itself |
23 | 14.5.5.2 | open | Some questions regarding partial ordering of function templates |
24 | 14.7.3 | DR | Errors in examples in 14.7.3 |
25 | 15.4 | DR | Exception specifications and pointers to members |
26 | 12.8 | NAD | Copy constructors and default arguments |
27 | 13.6 | NAD | Overload ambiguities for builtin ?: prototypes |
28 | 3.6.3 | drafting | 'exit', 'signal' and static object destruction |
29 | 7.5 | drafting | Linkage of locally declared functions |
30 | 14.2 | DR | Valid uses of '::template' |
31 | 5.3.4 | NAD | Looking up new/delete |
32 | 14 | DR | Clarification of explicit instantiation of non-exported templates |
33 | 3.4.2 | DR | Argument dependent lookup and overloaded functions |
34 | 14.7.1 | NAD | Argument dependent lookup and points of instantiation |
35 | 8.5 | DR | Definition of default-initialization |
36 | 7.3.3 | review | Using-declarations in multiple-declaration contexts |
37 | 15.5.3 | NAD | When is uncaught_exception() true? |
38 | 14.2 | drafting | Explicit template arguments and operator functions |
39 | 10.2 | drafting | Conflicting ambiguity rules |
40 | 8.3 | DR | Syntax of declarator-id |
41 | 3.4.1 | DR | Clarification of lookup of names after declarator-id |
42 | 3.3.6 | NAD | Redefining names from base classes |
43 | 3.9 | DR | Copying base classes (PODs) using memcpy |
44 | 14.7.3 | review | Member specializations |
45 | 11.8 | open | Access to nested classes |
46 | 14.7.2 | NAD | Explicit instantiation of member templates |
47 | 14.5.3 | NAD | Template friend issues |
48 | 9.4.2 | DR | Definitions of unused static members |
49 | 14.1 | DR | Restriction on non-type, non-value template arguments |
50 | 3.2 | NAD | Converting pointer to incomplete type to same type |
51 | 13.3.3 | DR | Overloading and user-defined conversions |
52 | 5.2.5 | drafting | Non-static members, member selection and access checking |
53 | 5.2.9 | drafting | Lvalue-to-rvalue conversion before certain static_casts |
54 | 5.2.9 | NAD | Static_cast from private base to derived class |
55 | 5.7 | NAD | Adding/subtracting pointer and enumeration value |
56 | 7.1.3 | DR | Redeclaring typedefs within classes |
57 | 9.5 | open | Empty unions |
58 | 9.6 | open | Signedness of bit fields of enum type |
59 | 13.3.1.4 | DR | Clarification of overloading and UDC to reference type |
60 | 13.3.3.1.4 | open | Reference binding and valid conversion sequences |
61 | 13.4 | NAD | Address of static member function "&p->f" |
62 | 14.3.1 | drafting | Unnamed members of classes used as type parameters |
63 | 14.7.1 | review | Class instantiation from pointer conversion to void*, null and self |
64 | 14.7.3 | review | Partial ordering to disambiguate explicit specialization |
65 | 8.3.6 | DR | Typo in default argument example |
66 | 8.3.6 | NAD | Visibility of default args vs overloads added after using-declaration |
67 | 9.4 | DR | Evaluation of left side of object-expression |
68 | 7.1.5.3 | drafting | Grammar does not allow "friend class A<int>;" |
69 | 7.1.1 | drafting | Storage class specifiers on template declarations |
70 | 14.8.2.4 | review | Is an array bound a nondeduced context? |
71 | 5 | NAD | Incorrect cross reference |
72 | 14 | dup | Linkage and storage class specifiers for templates |
73 | 5.10 | drafting | Pointer equality |
74 | 5.3.4 | DR | Enumeration value in direct-new-declarator |
75 | 9.2 | DR | In-class initialized members must be const |
76 | 7.1.5.1 | DR | Are const volatile variables considered "constant expressions"? |
77 | 11.4 | drafting | The definition of friend does not allow nested classes to be friends |
78 | 8.5 | review | Section 8.5 paragraph 9 should state it only applies to non-static objects |
79 | 18.4.1.3 | DUP | Alignment and placement new |
80 | 9.2 | review | Class members with same name as class |
81 | C | NAD | Null pointers and C compatability |
82 | 3.2 | dup | Definition of "using" a constant expression |
83 | 13.3.3.2 | review | Overloading and deprecated conversion of string literal |
84 | 13.3.3.1 | open | Overloading and conversion loophole used by auto_ptr |
85 | 3.4.4 | drafting | Redeclaration of member class |
86 | 12.2 | open | Lifetime of temporaries in query expressions |
87 | 15.4 | drafting | Exception specifications on function parameters |
88 | 14.7.3 | NAD | Specialization of member constant templates |
89 | 3.8 | drafting | Object lifetime does not account for reference rebinding |
90 | 3.4.2 | DR | Should the enclosing class be an "associated class" too? |
91 | 3.4.2 | NAD | A union's associated types should include the union itself |
92 | 15.4 | open | Should exception specifications be part of the type system? |
93 | 3.8 | DR | Missing word in 3.8 basic.life paragraph 2 |
94 | 5.19 | drafting | Inconsistencies in the descriptions of constant expressions |
95 | 7.3.1.2 | NAD | Elaborated type specifiers referencing names declared in friend decls |
96 | 14.2 | open | Syntactic disambiguation using the template keyword |
97 | 5.19 | NAD | Use of bool constants in integral constant expressions |
98 | 15 | drafting | Branching into try block |
99 | 14.8.2.1 | NAD | Partial ordering, references and cv-qualifiers |
100 | 14.3.2 | DR | Clarify why string literals are not allowed as template arguments |
101 | 7.3.3 | DR | Redeclaration of extern "C" names via using-declarations |
102 | 13.3.1.2 | NAD | Operator lookup rules do not work well with parts of the library |
103 | 7.3.4 | DR | Is it extended-namespace-definition or extension-namespace-definition ? |
104 | 15.1 | NAD | Destroying the exception temp when no handler is found |
105 | 14 | drafting | Meaning of "template function" |
106 | unknown | drafting | Creating references to references during template deduction/instantiation |
107 | 7.5 | open | Linkage of operator functions |
108 | 14.6.2.1 | drafting | Are classes nested in templates dependent? |
109 | 7.3.3 | extension | Allowing ::template in using-declarations |
110 | 14 | open | Can template functions and classes be declared in the same scope? |
111 | 12.8 | open | Copy constructors and cv-qualifiers |
112 | 8.3.4 | open | Array types and cv-qualifiers |
113 | 5.2.2 | drafting | Visibility of called function |
114 | 14.5.2 | NAD | Virtual overriding by template member function specializations |
115 | 13.4 | open | Address of template-id |
116 | 14.5.5.1 | open | Equivalent and functionally-equivalent function templates |
117 | 12.2 | drafting | Timing of destruction of temporaries |
118 | 5.2.2 | drafting | Calls via pointers to virtual member functions |
119 | 3.8 | open | Object lifetime and aggregate initialization |
120 | 14.6 | DR | Nonexistent non-terminal qualified-name |
121 | 14.6 | drafting | Dependent type names with non-dependent nested-name-specifiers |
122 | 5.1 | open | template-ids as unqualified-ids |
123 | 5.1 | DR | Bad cross-reference |
124 | 12.2 | drafting | Lifetime of temporaries in default initialization of class arrays |
125 | 5.1 | drafting | Ambiguity in friend declaration syntax |
126 | 15.4 | drafting | Exception specifications and const |
127 | 5.3.4 | drafting | Ambiguity in description of matching deallocation function |
128 | 5.2.9 | review | Casting between enum types |
129 | 1.9 | open | Stability of uninitialized auto variables |
130 | 5.3.4 | NAD | Sequence points and new-expressions |
131 | E | DR | Typo in Lao characters |
132 | 3.5 | drafting | Local types and linkage |
133 | 15.4 | DUP | Exception specifications and checking |
134 | 14 | drafting | Template classes and declarator-ids |
135 | 8.3.5 | review | Class type in in-class member function definitions |
136 | 8.3.6 | open | Default arguments and friend declarations |
137 | 5.2.9 | review | static_cast of cv void* |
138 | 7.3.1.2 | open | Friend declaration name lookup |
139 | 3.4.1 | open | Error in friend lookup example |
140 | 8.3.5 | drafting | Agreement of parameter declarations |
141 | 3.4.5 | open | Non-member function templates in member access expressions |
142 | 11.2 | drafting | Injection-related errors in access example |
143 | 3.4.2 | drafting | Friends and Koenig lookup |
144 | 7.1.5.3 | open | Position of friend specifier |
145 | D.1 | drafting | Deprecation of prefix ++ |
146 | 3.9.1 | open | Floating-point zero |
147 | 5.1 | review | Naming the constructor |
148 | 9 | open | POD classes and pointers to members |
149 | 4.10 | review | Accessibility and ambiguity |
150 | 14.3.3 | NAD | Template template parameters and default arguments |
151 | 8.5 | review | Terminology of zero-initialization |
152 | 12.3.1 | review | explicit copy constructors |
153 | 13.3.3.2 | review | Misleading wording (rank of conversion) |
154 | 7.1.1 | NAD | Anonymous unions in unnamed namespaces |
155 | 8.5 | open | Brace initializer for scalar |
156 | 3.4.5 | open | Name lookup for conversion functions |
157 | 7 | open | Omitted typedef declarator |
158 | 3.10 | open | Aliasing and qualification conversions |
159 | 8.3 | drafting | Namespace qualification in declarators |
160 | 8.2 | open | Missing std:: qualification |
161 | 11.5 | DR | Access to protected nested type |
162 | 13.3.1.1 | open | (&C::f)() with nonstatic members |
163 | 8.5.1 | DR | Description of subaggregate initializer |
164 | 3.4.2 | review | Overlap between Koenig and normal lookup |
165 | 7.3.1.2 | NAD | Definitions of friends and block-scope externs |
166 | 7.3.1.2 | review | Friend declarations of template-ids |
167 | D.2 | review | Deprecating static functions |
168 | 7.5 | NAD | C linkage for static member functions |
169 | 7.3.3 | NAD | template-ids in using-declarations |
170 | 4.11 | open | Pointer-to-member conversions |
171 | 7.3 | review | Global namespace scope |
172 | 7.2 | open | Unsigned int as underlying type of enum |
173 | 2.2 | review | Constraints on execution character set |
174 | D.2 | open | Undeprecating global static |
175 | 9 | open | Class name injection and base name access |
176 | 9 | open | Name injection and templates |
177 | 8.5 | open | Lvalues vs rvalues in copy-initialization |
178 | 8.5 | drafting | More on value-initialization |
179 | 5.7 | open | Function pointers and subtraction |
180 | 14.6 | open | typename and elaborated types |
181 | 14.8.2.4 | open | Errors in template template-parameter example |
182 | 14.7.3 | open | Access checking on explicit specializations |
183 | 14.6 | open | typename in explicit specializations |
184 | 14.1 | open | Default arguments in template template-parameters |
185 | 12.8 | open | "Named" temporaries and copy elision |
186 | 14.6.1 | open | Name hiding and template template-parameters |
187 | 14.1 | open | Scope of template parameter names |
188 | 5.18 | open | Comma operator and rvalue conversion |
189 | 2.12 | open | Definition of operator and punctuator |
190 | 9.2 | open | Layout-compatible POD-struct types |
191 | 3.4.1 | open | Name lookup does not handle complex nesting |
192 | 3.4.1 | open | Name lookup in parameters |
193 | 12.4 | open | Order of destruction of local automatics of destructor |
194 | 12.1 | open | Identifying constructors |
195 | 5.2.10 | Steve Clamage | Converting between function and object pointers |
196 | 5.3.5 | open | Arguments to deallocation functions |
197 | 14.6.4.2 | open | Issues with two-stage lookup of dependent names |
198 | 9.8 | open | Definition of "use" in local and nested classes |
199 | 12.2 | open | Order of destruction of temporaries |
200 | 14.5.5.2 | open | Partial ordering and explicit arguments |
201 | 12.2 | open | Order of destruction of temporaries in initializers |
202 | 13.4 | open | Use of overloaded function name |
203 | 5.3.1 | open | Type of address-of-member expression |
204 | 14 | open | Exported class templates |
205 | 14 | open | Templates and static data members |
206 | 14.6.3 | open | Semantic constraints on non-dependent names |