| Document number: | J16/07-0048 = WG21 N2188 |
| Date: | 2007-03-11 |
| Project: | Programming Language C++ |
| Reference: | ISO/IEC IS 14882:2003 |
| Reply to: | William M. Miller |
| Edison Design Group, Inc. | |
| wmm@edg.com |
This document contains the C++ core language issues on which the Committee (J16 + WG21) has not yet acted, that is, issues with status "Ready," "Review," "Drafting," and "Open."
This document is part of a group of related documents that together describe the issues that have been raised regarding the C++ Standard. The other documents in the group are:
The purpose of these documents 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:2003 document and corrected defects in the earlier ISO/IEC 14882:1998 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.open-std.org/jtc1/sc22/wg21. Requests for further information about these documents should include the document number, reference ISO/IEC 14882:2003, and be submitted to the InterNational Committee for Information Technology Standards (INCITS), 1250 Eye Street NW, Suite 200, Washington, DC 20005, USA.
Information regarding how to obtain a copy of the C++ Standard, join the Standard Committee, or submit an issue can be found in the C++ FAQ at http://www.jamesd.demon.co.uk/csc/faq.html. Public discussion of the C++ Standard and related issues occurs on newsgroup comp.std.c++.
Issues progress through various statuses as the Core Language Working Group and, ultimately, the full J16 and WG21 committees deliberate and act. For ease of reference, issues are grouped in these documents by their status. Issues have one of the following statuses:
Open: The issue is new or the working group has not yet formed an opinion on the issue. If a Suggested Resolution is given, it reflects the opinion of the issue's submitter, not necessarily that of the working group or the Committee as a whole.
Drafting: Informal consensus has been reached in the working group and is described in rough terms in a Tentative Resolution, although precise wording for the change is not yet available.
Review: Exact wording of a Proposed Resolution is now available for an issue on which the working group previously reached informal consensus.
Ready: The working group has reached consensus that the issue is a defect in the Standard, the Proposed Resolution is correct, and the issue is ready to forward to the full Committee for ratification as a proposed defect report.
DR: The full Committee has approved the item as a proposed defect report. The Proposed Resolution in an issue with this status reflects the best judgment of the Committee at this time regarding the action that will be taken to remedy the defect; however, the current wording of the Standard remains in effect until such time as a Technical Corrigendum or a revision of the Standard is issued by ISO.
TC1: A DR issue included in Technical Corrigendum 1. TC1 is a revision of the Standard issued in 2003.
WP: A DR issue whose resolution is reflected in the current Working Paper. The Working Paper is a draft for a future version of the Standard.
Dup: The issue is identical to or a subset of another issue, identified in a Rationale statement.
NAD: The working group has reached consensus that the issue is not a defect in the Standard. A Rationale statement describes the working group's reasoning.
Extension: The working group has reached consensus that the issue is not a defect in the Standard but is a request for an extension to the language. The working group expresses no opinion on the merits of an issue with this status; however, the issue will be maintained on the list for possible future consideration as an extension proposal.
Section 1.3.10 defns.signature, definition of "signature" omits the function name as part of the signature. Since the name participates in overload resolution, shouldn't it be included in the definition? I didn't find a definition of signature in the ARM, but I might have missed it.
Fergus Henderson: I think so. In particular, 17.4.3.1.2 lib.global.names reserves certain "function signatures" for use by the implementation, which would be wrong unless the signature includes the name.
-2- Each global function signature declared with external linkage in a header is reserved to the implementation to designate that function signature with external linkage.
-5- Each function signature from the Standard C library declared with external linkage is reserved to the implementation for use as a function signature with both extern "C" and extern "C++" linkage, or as a name of namespace scope in the global namespace.
Other uses of the term "function signature" in the description of the standard library also seem to assume that it includes the name.
James Widman:
Names don't participate in overload resolution; name lookup is separate from overload resolution. However, the word “signature” is not used in clause 13 over. It is used in linkage and declaration matching (e.g., 14.5.5.1 temp.over.link). This suggests that the name and scope of the function should be part of its signature.
Proposed resolution (October, 2006):
Replace 1.3.10 defns.signature with the following:
the name and the parameter-type-list (8.3.5 dcl.fct) of a function, as well as the class or namespace of which it is a member. If a function or function template is a class member its signature additionally includes the cv-qualifiers (if any) on the function or function template itself. The signature of a function template additionally includes its return type and its template parameter list. The signature of a function template specialization includes the signature of the template of which it is a specialization and its template arguments (whether explicitly specified or deduced). [Note: Signatures are used as a basis for name-mangling and linking. —end note]
Delete paragraph 3 and replace the first sentence of 14.5.5.1 temp.over.link as follows:
The signature of a function template specialization consists of the signature of the function template and of the actual template arguments (whether explicitly specified or deduced).The signature of a function template
consists of its function signature, its return type and its template parameter listis defined in 1.3.10 defns.signature. The names of the template parameters are significant...
(See also issue 537.)
The standard defines “signature” in two places: 1.3.10 defns.signature and 14.5.5.1 temp.over.link paragraphs 3-4. The former seems to be meant as a formal definition (I think it's the only place covering the nontemplate case), yet it lacks some bits mentioned in the latter (specifically, the notion of a “signature of a function template,” which is part of every signature of the associated function template specializations).
Also, I think the 1.3.10 defns.signature words “the information about a function that participates in overload resolution” isn't quite right either. Perhaps, “the information about a function that distinguishes it in a set of overloaded functions?”
Eric Gufford:
In 1.3.10 defns.signature the definition states that “Function signatures do not include return type, because that does not participate in overload resolution,” while 14.5.5.1 temp.over.link paragraph 4 states “The signature of a function template consists of its function signature, its return type and its template parameter list.” This seems inconsistent and potentially confusing. It also seems to imply that two identical function templates with different return types are distinct signatures, which is in direct violation of 13.3 over.match. 14.5.5.1 temp.over.link paragraph 4 should be amended to include verbiage relating to overload resolution.
Either return types are included in function signatures, or they're not, across the board. IMHO, they should be included as they are an integral part of the function declaration/definition irrespective of overloads. Then verbiage should be added about overload resolution to distinguish between signatures and overload rules. This would help clarify things, as it is commonly understood that overload resolution is based on function signature.
In short, the term “function signature” should be made consistent, and removed from its (implicit, explicit or otherwise) linkage to overload resolution as it is commonly understood.
James Widman:
The problem is that (a) if you say the return type is part of the signature of a non-template function, then you have overloading but not overload resolution on return types (i.e., what we have now with function templates). I don't think anyone wants to make the language uglier in that way. And (b) if you say that the return type is not part of the signature of a function template, you will break code. Given those alternatives, it's probably best to maintain the status quo (which the implementors appear to have rendered faithfully).
Proposed resolution (September, 2006):
This issue is resolved by the resolution of issue 357.
The original intent of the Committee when Koenig lookup was added to the language was apparently something like the following:
This approach is not reflected in the current wording of the Standard. Instead, the following appears to be the status quo:
John Spicer: Argument-dependent lookup was created to solve the problem of looking up function names within templates where you don't know which namespace to use because it may depend on the template argument types (and was then expanded to permit use in nontemplates). The original intent only concerned functions. The safest and simplest change is to simply clarify the existing wording to that effect.
Bill Gibbons: I see no reason why non-function declarations should not be found. It would take a special rule to exclude "function objects", as well as pointers to functions, from consideration. There is no such rule in the standard and I see no need for one.
There is also a problem with the wording in 3.4.2 basic.lookup.argdep paragraph 2:
If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated namespaces and classes are not considered.
This implies that if the ordinary lookup of the name finds the declaration of a data member which is a pointer to function or function object, argument-dependent lookup is still done.
My guess is that this is a mistake based on the incorrect assumption that finding any member other than a member function would be an error. I would just change "class member function" to "class member" in the quoted sentence.
Mike Miller: In light of the issue of "short-circuiting" Koenig lookup when normal lookup finds a non-function, perhaps it should be written as "...finds the declaration of a class member, an object, or a reference, the associated namespaces..."?
Andy Koenig: I think I have to weigh in on the side of extending argument-dependent lookup to include function objects and pointers to functions. I am particularly concerned about [function objects], because I think that programmers should be able to replace functions by function objects without changing the behavior of their programs in fundamental ways.
Bjarne Stroustrup: I don't think we could seriously argue from first principles that [argument-dependent lookup should find only function declarations]. In general, C++ name lookup is designed to be independent of type: First we find the name(s), then, we consider its(their) meaning. 3.4 basic.lookup states "The name lookup rules apply uniformly to all names ..." That is an important principle.
Thus, I consider text that speaks of "function call" instead of plain "call" or "application of ()" in the context of koenig lookup an accident of history. I find it hard to understand how 5.2.2 expr.call doesn't either disallow all occurrences of x(y) where x is a class object (that's clearly not intended) or requires koenig lookup for x independently of its type (by reference from 3.4 basic.lookup). I suspect that a clarification of 5.2.2 expr.call to mention function objects is in order. If the left-hand operand of () is a name, it should be looked up using koenig lookup.
John Spicer: This approach causes otherwise well-formed programs to be ill-formed, and it does so by making names visible that might be completely unknown to the author of the program. Using-directives already do this, but argument-dependent lookup is different. You only get names from using-directives if you actually use using-directives. You get names from argument-dependent lookup whether you want them or not.
This basically breaks an important reason for having namespaces. You are not supposed to need any knowledge of the names used by a namespace.
But this example breaks if argument-dependent lookup finds non-functions and if the translation unit includes the <list> header somewhere.
namespace my_ns {
struct A {};
void list(std::ostream&, A&);
void f() {
my_ns::A a;
list(cout, a);
}
}
This really makes namespaces of questionable value if you still need to avoid using the same name as an entity in another namespace to avoid problems like this.
Erwin Unruh: Before we really decide on this topic, we should have more analysis on the impact on programs. I would also like to see a paper on the possibility to overload functions with function surrogates (no, I won't write one). Since such an extension is bound to wait until the next official update, we should not preclude any outcome of the discussion.
I would like to have a change right now, which leaves open several outcomes later. I would like to say that:
Koenig lookup will find non-functions as well. If it finds a variable, the program is ill-formed. If the primary lookup finds a variable, Koenig lookup is done. If the result contains both functions and variables, the program is ill-formed. [Note: A future standard will assign semantics to such a program.]
I myself are not comfortable with this as a long-time result, but it prepares the ground for any of the following long term solutions:
The note is there to prevent compiler vendors to put their own extensions in here.
(See also issues 113 and 143.)
Notes from 04/00 meeting:
Although many agreed that there were valid concerns motivating a desire for Koenig lookup to find non-function declarations, there was also concern that supporting this capability would be more dangerous than helpful in the absence of overload resolution for mixed function and non-function declarations.
A straw poll of the group revealed 8 in favor of Koenig lookup finding functions and function templates only, while 3 supported the broader result.
Notes from the 10/01 meeting:
There was unanimous agreement on one less controversial point: if the normal lookup of the identifier finds a non-function, argument-dependent lookup should not be done.
On the larger issue, the primary point of consensus is that making this change is an extension, and therefore it should wait until the point at which we are considering extensions (which could be very soon). There was also consensus on the fact that the standard as it stands is not clear: some introductory text suggests that argument-dependent lookup finds only functions, but the more detailed text that describes the lookup does not have any such restriction.
It was also noted that some existing implementations (e.g., g++) do find some non-functions in some cases.
The issue at this point is whether we should (1) make a small change to make the standard clear (presumably in the direction of not finding the non-functions in the lookup), and revisit the issue later as an extension, or (2) leave the standard alone for now and make any changes only as part of considering the extension. A straw vote favored option (1) by a strong majority.
Additional Notes (September, 2006):
Recent discussion of this issue has emphasized the following points:
The concept of finding function pointers and function objects as part of argument-dependent lookup is not currently under active discussion in the Evolution Working Group.
The major area of concern with argument-dependent lookup is finding functions in unintended namespaces. There are current proposals to deal with this concern either by changing the definition of “associated namespace” so that fewer namespaces are considered or to provide a mechanism for enabling or disabling ADL altogether. Although this concern is conceptually distinct from the question of whether ADL finds function pointers and function objects, it is related in the sense that the current rules are perceived as finding too many functions (because of searching too many namespaces), and allowing function pointers and function objects would also increase the number of entities found by ADL.
Any expansion of ADL to include function pointers and function objects must necessarily update the overloading rules to specify how they interact with functions and function templates in the overload set. Current implementation experience (g++) is not helpful in making this decision because, although it performs a uniform lookup and finds non-function entities, it diagnoses an error in overload resolution if non-function entities are in the overload set.
There is a possible problem if types are found by ADL: it is not clear that overloading between callable entities (functions, function templates, function pointers, and function objects) and types (where the postfix syntax means a cast or construction of a temporary) is reasonable or useful.
James Widman:
There is a larger debate here about whether ADL should find object names; the proposed wording below is only intended to answer the request for wording to clarify the status quo (option 1 above) and not to suggest the outcome of the larger debate.
Proposed Resolution (October, 2006):
Replace the normative text in 3.4.2 basic.lookup.argdep paragraph 3 with the following (leaving the text of the note and example unchanged):
Let X be the lookup set produced by unqualified lookup (3.4.1 basic.lookup.unqual) and let Y be the lookup set produced by argument dependent lookup (defined as follows). If X contains
- a declaration of a class member, or
- a block-scope function declaration that is not a using-declaration, or
- a declaration that is neither a function nor a function template
then Y is empty. Otherwise Y is the set of declarations found in the namespaces associated with the argument types as described below. The set of declarations found by the lookup of the name is the union of X and Y.
Change 3.4.1 basic.lookup.unqual paragraph 4 as indicated:
When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier (3.4.3.2 namespace.qual) except that:
- Any using-directives in the associated namespace are ignored.
- Any namespace-scope friend functions or friend function templates declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup (11.4 class.friend).
- All names except those of (possibly overloaded) functions and function templates are ignored.
5.4 expr.cast paragraph 6 says,
The operand of a cast using the cast notation can be an rvalue of type “pointer to incomplete class type”. The destination type of a cast using the cast notation can be “pointer to incomplete class type”. In such cases, even if there is a inheritance relationship between the source and destination classes, whether the static_cast or reinterpret_cast interpretation is used is unspecified.
The wording seems to allow the following:
casting from void pointer to incomplete type
struct A;
struct B;
void *v;
A *a = (A*)v; // allowed to choose reinterpret_cast
variant application of static or reinterpret casting
B *b = (B*)a; // compiler can choose static_cast here
A *aa = (A*)b; // compiler can choose reinterpret_cast here
assert(aa == a); // might not hold
ability to somehow choose static_cast
It's not entirely clear how a compiler can
choose static_cast as 5.4
expr.cast paragraph 6
seems to allow. I believe the intent of 5.4
expr.cast
paragraph 6 is to force the use of reinterpret_cast when
either are incomplete class types and static_cast iff the
compiler knows both types and there is a non-ambiguous
hierarchy-traversal between that cast (or maybe not, core issue 242 talks about this). I cannot see any
other interpretation because it isn't intuitive, every compiler I've
tried agrees with me, and neither standard pointer conversions
(4.10
conv.ptr paragraph 3) nor static_cast
(5.2.9
expr.static.cast paragraph 5) talk about incomplete
class types. If the committee agrees with me, I would like to see
Proposed resolution (April, 2006):
Change 5.4 expr.cast paragraph 6 as indicated:
The operand of a cast using the cast notation can be an rvalue of type “pointer to incomplete class type.” The destination type of a cast using the cast notation can be “pointer to incomplete class type.”In such cases, even if there is a inheritance relationship between the source and destination classes, whether the static_cast or reinterpret_cast interpretation is used is unspecified.If both the operand and destination types are class types and one or both are incomplete, it is unspecified whether the static_cast or the reinterpret_cast interpretation is used, even if there is an inheritance relationship between the two classes. [Note: For example, if the classes were defined later in the translation unit, a multi-pass compiler would be permitted to interpret a cast between pointers to the classes as if the class types were complete at that point. —end note]
Are string literals from default arguments used in extern inlines supposed to have the same addresses across all translation units?
void f(const char* = "s")
inline g() {
f();
}
Must the "s" strings be the same in all copies of the inline function?
Steve Adamczyk: The totality of the standard's wisdom on this topic is (7.1.2 dcl.fct.spec paragraph 4):
A string literal in an extern inline function is the same object in different translation units.
I'd hazard a guess that a literal in a default argument expression is not "in" the extern inline function (it doesn't appear in the tokens of the function), and therefore it need not be the same in different translation units.
I don't know that users would expect such strings to have the same address, and an equally valid (and incompatible) expectation would be that the same string literal would be used for every expansion of a given default argument in a single translation unit.
Notes from April 2003 meeting:
The core working group feels that the address of a string literal should be guaranteed to be the same only if it actually appears textually within the body of the inline function. So a string in a default argument expression in a block extern declaration inside the body of a function would be the same in all instances of the function. On the other hand, a string in a default argument expression in the header of the function (i.e., outside of the body) would not be the same.
Proposed resolution (April 2003):
Change the last sentence and add the note to the end of 7.1.2 dcl.fct.spec paragraph 4:
A string literal in the body of an extern inline function is the same object in different translation units. [Note: A string literal that is encountered only in the context of a function call (in the default argument expression of the called function), is not “in” the extern inline function.]
Notes from October 2003 meeting:
We discussed ctor-initializer lists and decided that they are also part of the body. We've asked Clark Nelson to work on syntax changes to give us a syntax term for the body of a function so we can refer to it here. See also issue 452, which could use this term.
(October, 2005: moved to “review” status in concert with issue 452. With that resolution, the wording above needs no further changes.)
Proposed resolution (April, 2006):
Change the last sentence and add the note to the end of 7.1.2 dcl.fct.spec paragraph 4:
A string literal in the body of an extern inline function is the same object in different translation units. [Note: A string literal appearing in a default argument expression is not considered to be “in the body” of an inline function merely by virtue of the expression’s use in a function call from that inline function. —end note]
The current wording of 8.5.1 dcl.init.aggr paragraph 8 requires that
An initializer for an aggregate member that is an empty class shall have the form of an empty initializer-list {}.
This is overly constraining. There is no reason that the following should be ill-formed:
struct S { };
S s;
S arr[1] = { s };
Mike Miller: The wording of 8.5.1 dcl.init.aggr paragraph 8 is unclear. “An aggregate member” would most naturally mean “a member of an aggregate.” In context, however, I think it must mean “a member [of an aggregate] that is an aggregate”, that is, a subaggregate. Members of aggregates need not themselves be aggregates (cf paragraph 13 and 12.6.1 class.expl.init); it cannot be the case that an object of an empty class with a user-declared constructor must be initialized with {} when it is a member of an aggregate. This wording should be clarified, regardless of the decision on Nathan's point.
Proposed resolution (October, 2005):
This issue is resolved by the resolution of issue 413.
In 9 class paragraph 4, the first sentence says "A structure is a class definition defined with the class-key struct". As far as I know, there is no such thing as a structure in C++; it certainly isn't listed as one of the possible compound types in 3.9.2 basic.compound. And defining structures opens the question of whether a forward declaration is a structure or not. The parallel here with union (which follows immediately) suggests that structures and classes are really different things, since the same wording is used, and classes and unions do have some real differences, which manifest themselves outside of the definition. It also suggests that since one can't forward declare union with class and vice versa, the same should hold for struct and class -- I believe that the intent was that one could use struct and class interchangeably in forward declaration.
Suggested resolution:
I suggest something like the following:
If a class is defined with the class-key class, its members and base classes are private by default. If a class is defined with the class-key struct, its members and base classes are public by default. If a class is defined with the class-key union, its members are public by default, and it holds only one data member at a time. Such classes are called unions, and obey a number of additional restrictions, see 9.5 class.union.
Proposed resolution (April, 2006):
This issue is resolved by the resolution of issue 538.
The proposal says that value is true if "T is an empty class (10)". Clause 10 doesn't define an empty class, although it has a note that says a base class may "be of zero size (clause 9)" 9/3 says "Complete objects and member subobjects of class type shall have nonzero size." This has a footnote, which says "Base class subobjects are not so constrained."
The standard uses the term "empty class" in two places (8.5.1 dcl.init.aggr), but neither of those places defines it. It's also listed in the index, which refers to the page that opens clause 9, i.e. the nonzero size stuff cited above.
So, what's the definition of "empty class" that determines whether the predicate is_empty is true?
The one place where it's used is 8.5.1 dcl.init.aggr paragraph 8, which says (roughly paraphrased) that an aggregate initializer for an empty class must be "{}", and when such an initializer is used for an aggregate that is not an empty class the members are default-initialized. In this context it's pretty clear what's meant. In the type traits proposal it's not as clear, and it was probably intended to have a different meaning. The boost implementation, after it eliminates non-class types, determines whether the trait is true by comparing the size of a class derived from T to the size of an otherwise-identical class that is not derived from T.
Howard Hinnant: is_empty was created to find out whether a type could be derived from and have the empty base class optimization successfully applied. It was created in part to support compressed_pair which attempts to optimize away the space for one of its members in an attempt to reduce spatial overhead. An example use is:
template <class T, class Compare = std::less<T> >
class SortedVec
{
public:
...
private:
T* data_;
compressed_pair<Compare, size_type> comp_;
Compare& comp() {return comp_.first();}
const Compare& comp() const {return comp_.first();}
size_type& sz() {return comp_.second();}
size_type sz() const {return comp_.second();}
};
Here the compare function is optimized away via the empty base optimization if Compare turns out to be an "empty" class. If Compare turns out to be a non-empty class, or a function pointer, the space is not optimized away. is_empty is key to making this work.
This work built on Nathan's article: http://www.cantrip.org/emptyopt.html.
Clark Nelson: I've been looking at issue 413, and I've reached the conclusion that there are two different kinds of empty class. A class containing only one or more anonymous bit-field members is empty for purposes of aggregate initialization, but not (necessarily) empty for purposes of empty base-class optimization.
Of course we need to add a definition of emptiness for purposes of aggregate initialization. Beyond that, there are a couple of questions:
Notes from the October, 2005 meeting:
There are only two places in the Standard where the phrase “empty class” appears, both in 8.5.1 dcl.init.aggr paragraph 8. Because it is not clear whether the definition of “empty for initialization purposes” is suitable for use in defining the is_empty predicate, it would be better just to avoid using the phrase in the language clauses. The requirements of 8.5.1 dcl.init.aggr paragraph 8 appear to be redundant; paragraph 6 says that an initializer-list must have no more initializers than the number of elements to initialize, so an empty class already requires an empty initializer-list, and using an empty initializer-list with a non-empty class is covered adequately by paragraph 7's description of the handling of an initializer-list with fewer initializers than the number of members to initialize.
Proposed resolution (October, 2005):
Change
Static data members and anonymous bit fields are not considered members of the class for purposes of aggregate initialization. [Example:
struct A { int i; static int s; int j; int :17; int k; } a = { 1 , 2 , 3 };Here, the second initializer 2 initializes a.j and not the static data member A::s, and the third initializer 3 initializes a.k and not the padding before it. —end example]
Delete 8.5.1 dcl.init.aggr paragraph 8:
An initializer for an aggregate member that is an empty class shall have the form of an empty initializer-list {}. [Example:
struct S { }; struct A { S s; int i; } a = { { } , 3 };—end example] An empty initializer-list can be used to initialize any aggregate. If the aggregate is not an empty class, then each member of the aggregate shall be initialized with a value of the form T() (5.2.3 expr.type.conv), where T represents the type of the uninitialized member.
This resolution also resolves issue 491.
Additional note (October, 2005):
Deleting 8.5.1 dcl.init.aggr paragraph 8 altogether may not be a good idea. It would appear that, in its absence, the initializer elision rules of paragraph 11 would allow the initializer for a in the preceding example to be written { 3 } (because the empty-class member s would consume no initializers from the list).
Proposed resolution (October, 2006):
(Drafting note: this resolution also cleans up incorrect references to syntactic non-terminals in the nearby paragraphs.)
Change 8.5.1 dcl.init.aggr paragraph 4 as indicated:
An array of unknown size initialized with a brace-enclosed initializer-list containing ninitializersinitializer-clauses, where n shall be greater than zero... An empty initializer list {} shall not be used as theinitializerinitializer-clause for an array of unknown bound.
Change
Static data members and anonymous bit fields are not considered members of the class for purposes of aggregate initialization. [Example:
struct A { int i; static int s; int j; int :17; int k; } a = { 1 , 2 , 3 };Here, the second initializer 2 initializes a.j and not the static data member A::s, and the third initializer 3 initializes a.k and not the anonymous bit field before it. —end example]
Change 8.5.1 dcl.init.aggr paragraph 6 as indicated:
An initializer-list is ill-formed if the number ofinitializersinitializer-clauses exceeds the number of members...
Change 8.5.1 dcl.init.aggr paragraph 7 as indicated:
If there are fewerinitializersinitializer-clauses in the list than there are members...
Replace 8.5.1 dcl.init.aggr paragraph 8:
An initializer for an aggregate member that is an empty class shall have the form of an empty initializer-list {}. [Example:
struct S { }; struct A { S s; int i; } a = { { } , 3 };—end example] An empty initializer-list can be used to initialize any aggregate. If the aggregate is not an empty class, then each member of the aggregate shall be initialized with a value of the form T() (5.2.3 expr.type.conv), where T represents the type of the uninitialized member.
with:
If an aggregate class C contains a subaggregate member m that has no members for purposes of aggregate initialization, the initializer-clause for m shall not be omitted from an initializer-list for an object of type C unless the initializer-clauses for all members of C following m are also omitted. [Example:
struct S { } s; struct A { S s1; int i1; S s2; int i2; S s3; int i3; } a = { { }, // Required initialization 0, s, // Required initialization 0 }; // Initialization not required for A::s3 because A::i3 is also not initialized—end example]
Change 8.5.1 dcl.init.aggr paragraph 10 as indicated:
When initializing a multi-dimensional array, theinitializersinitializer-clauses initialize the elements...
Change 8.5.1 dcl.init.aggr paragraph 11 as indicated:
Braces can be elided in an initializer-list as follows. If the initializer-list begins with a left brace, then the succeeding comma-separated list ofinitializersinitializer-clauses initializes the members of a subaggregate; it is erroneous for there to be moreinitializersinitializer-clauses than members. If, however, the initializer-list for a subaggregate does not begin with a left brace, then only enoughinitializersinitializer-clauses from the list are taken to initialize the members of the subaggregate; any remaininginitializersinitializer-clauses are left to initialize the next member of the aggregate of which the current subaggregate is a member. [Example:...
Change 8.5.1 dcl.init.aggr paragraph 12 as indicated:
All implicit type conversions (clause 4 conv) are considered when initializing the aggregate member with aninitializer from an initializer-listassignment-expression. If theinitializerassignment-expression can initialize a member, the member is initialized. Otherwise, if the member is itself anon-emptysubaggregate, brace elision is assumed and theinitializerassignment-expression is considered for the initialization of the first member of the subaggregate. [Note: As specified above, brace elision cannot apply to subaggregates with no members for purposes of aggregate initialization; an initializer-clause for the entire subobject is required. —end note] [Example:... Braces are elided around theinitializerinitializer-clause for b.a1.i...
Change 8.5.1 dcl.init.aggr paragraph 15 as indicated:
When a union is initialized with a brace-enclosed initializer, the braces shall only contain aninitializerinitializer-clause for the first member of the union...
Change 8.5.1 dcl.init.aggr paragraph 16 as indicated:
[Note:asAs described above, the braces around theinitializerinitializer-clause for a union member can be omitted if the union is a member of another aggregate. —end note]
(Note: this resolution also resolves issue 491.)
There are several problems with the terms defined in 9 class paragraph 4:
A structure is a class defined with the class-key struct; its members and base classes (clause 10 class.derived) are public by default (clause 11 class.access). A union is a class defined with the class-key union; its members are public by default and it holds only one data member at a time (9.5 class.union). [Note: aggregates of class type are described in 8.5.1 dcl.init.aggr. —end note] A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-declared copy assignment operator and no user-declared destructor. Similarly, a POD-union is an aggregate union that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-declared copy assignment operator and no user-declared destructor. A POD class is a class that is either a POD-struct or a POD-union.
Although the term structure is defined here, it is used only infrequently throughout the Standard, often apparently inadvertently and consequently incorrectly:
5.2.5 expr.ref paragraph 4: the use is in a note and is arguably correct and helpful.
9.2 class.mem paragraph 11: the term is used (three times) in an example. There appears to be no reason to use it instead of “class,” but its use is not problematic.
17.1.8 defns.iostream.templates: the traits argument to the iostream class templates is (presumably unintentionally) constrained to be a structure, i.e., to use the struct keyword and not the class keyword in its definition.
B limits paragraph 2: the minimum number of declarator operators is given for structures and unions but not for classes defined using the class keyword.
B limits paragraph 2: class, structure, and union are used as disjoint terms in describing nesting levels. (The nonexistent nonterminal struct-declaration-list is used, as well.)
There does not appear to be a reason for defining the term structure. The one reference where it is arguably useful, in the note in 5.2.5 expr.ref, could be rewritten as something like, “'class objects' may be defined using the class, struct, or union class-keys; see clause 9 class.”
Based on its usage later in the paragraph and elsewhere, “POD-struct” appears to be intended to exclude unions. However, the definition of “aggregate class” in 8.5.1 dcl.init.aggr paragraph 1 includes unions. Furthermore, the name itself is confusing, leading to the question of whether it was intended that only classes defined using struct could be POD-structs or if class-classes are included. The definition should probably be rewritten as, “A POD-struct is an aggregate class defined with the class-key struct or the class-key class that has no...
In most references outside clause 9 class, POD-struct and POD-union are mentioned together and treated identically. These references should be changed to refer to the unified term, “POD class.”
Noted in passing: 18.1 lib.support.types paragraph 4 refers to the undefined terms “POD structure” and (unhyphenated) “POD union;” the pair should be replaced by a single reference to “POD class.”
Proposed resolution (April, 2006):
Change 9 class paragraph 4 as indicated:
A structure is a class defined with the class-key struct; its members and base classes (clause 10 class.derived) are public by default (clause 11 class.access).A union is a class defined with the class-key union;its members are public by default andit holds only one data member at a time (9.5 class.union). [Note: aggregates of class type are described in 8.5.1 dcl.init.aggr. —end note]A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-declared copy assignment operator and no user-declared destructor. Similarly, a POD-union is an aggregate union that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-declared copy assignment operator and no user-declared destructor. A POD class is a class that is either a POD-struct or a POD-union.A POD class is an aggregate class that has no non-static data members of non-POD type (or array of such a type) or reference, and has no user-declared copy assignment operator and no user-declared destructor. A POD-struct is a POD class defined with the class-key struct or the class-key class. A POD-union is a POD class defined with the class-key union.
Change 11.2 class.access.base paragraph 2 as indicated:
In the absence of an access-specifier for a base class, public is assumed when the derived class isdeclareddefined with the class-key struct and private is assumed when the class isdeclareddefined with the class-key class. [Example:...
Delete the note in 5.2.5 expr.ref paragraph 4:
[Note: “class objects” can be structures (9.2 class.mem) and unions (9.5 class.union). Classes are discussed in clause 9 class. —end note]
Change the commentary in the example in 9.2 class.mem paragraph 11 as indicated:
...an integer, and two pointers to
similar structuresobjects of the same type. Once this definition......the count member of the
structureobject to which sp points; s.left refers to the left subtree pointer of thestructureobject s; and...
Change 17.1.8 defns.iostream.templates as indicated:
...the argument traits is astructureclass which defines additional characteristics...
Change 18.4 lib.support.dynamic paragraph 4 as indicated:
If type is not aPOD structure or a POD unionPOD class (clause 9), the results are undefined.
Change the third bullet of B limits paragraph 2 as indicated:
Pointer, array, and function declarators (in any combination)
modifying an a class, arithmetic, structure,
union, or incomplete type in a declaration [256].
Change the nineteenth bullet of B limits paragraph 2 as indicated:
Data members in a single class, structure, or union [16 384].
Change the twenty-first bullet of B limits paragraph 2 as indicated:
Levels of nested class, structure, or union definitions in a
single struct-declaration-list
member-specification [256].
Change C.2 diff.library paragraph 6 as indicated:
The C++ Standard library provides 2 standardstructuresstructs from the C library, as shown in Table 126.
Change the last sentence of 3.9 basic.types paragraph 10 as indicated:
Scalar types,POD-struct types, POD-union typesPOD classes (clause 9 class), arrays of such types and cv-qualified versions of these types (3.9.3 basic.type.qualifier) are collectively called POD types.
Drafting note: Do not change 3.9 basic.types paragraph 11, because it's a note and the definition of “layout-compatible” is separate for POD-struct and POD-union in 9.2 class.mem.
(This resolution also resolves issue 327.)
According to 14.1 temp.param paragraph 3, the following fragment is ill-formed:
template <class T>
class X{
friend void T::foo();
};
In the friend declaration, the T:: part is a nested-name-specifier (8 dcl.decl paragraph 4), and T must be a class-name or a namespace-name (5.1 expr.prim paragraph 7). However, according to 14.1 temp.param paragraph 3, it is only a type-name. The fragment should be well-formed, and instantiations of the template allowed as long as the actual template argument is a class which provides a function member foo. As a result of this defect, any usage of template parameters in nested names is ill-formed, e.g., in the example of 14.6 temp.res paragraph 2.
Notes from 04/00 meeting:
The discussion at the meeting revealed a self-contradiction in the current IS in the description of nested-name-specifiers. According to the grammar in 5.1 expr.prim paragraph 7, the components of a nested-name-specifier must be either class-names or namespace-names, i.e., the constraint is syntactic rather than semantic. On the other hand, 3.4.3 basic.lookup.qual paragraph 1 describes a semantic constraint: only object, function, and enumerator names are ignored in the lookup for the component, and the program is ill-formed if the lookup finds anything other than a class-name or namespace-name. It was generally agreed that the syntactic constraint should be eliminated, i.e., that the grammar ought to be changed not to use class-or-namespace-name.
A related point is the explicit prohibition of use of template parameters in elaborated-type-specifiers in 7.1.5.3 dcl.type.elab paragraph 2. This rule was the result of an explicit Committee decision and should not be unintentionally voided by the resolution of this issue.
Proposed resolution (04/01):
Change 5.1 expr.prim paragraph 7 and A.4 gram.expr from
to
This resolution depends on the resolutions for issues 245 (to change the name lookup rules in elaborated-type-specifiers to include all type-names) and 283 (to categorize template type-parameters as type-names).
Notes from 10/01 meeting:
There was some sentiment for going with simply identifier in front of the "::", and stronger sentiment for going with something with a more descriptive name if possible. See also issue 180.
Notes from April 2003 meeting:
This was partly resolved by the changes for issue 125. However, we also need to add a semantic check in 3.4.3 basic.lookup.qual to allow T::foo and we need to reword the first sentence of 3.4.3 basic.lookup.qual.
Proposed resolution (October, 2004):
Change 3.4.3 basic.lookup.qual paragraph 1 as follows:
The name of a class or namespace member can be referred to after the :: scope resolution operator (5.1 expr.prim) applied to a nested-name-specifier that nominates its class or namespace. During the lookup for a name preceding the :: scope resolution operator, object, function, and enumerator names are ignored. If the name foundis not a class-name (clause 9 class) or namespace-name (7.3.1 namespace.def)does not designate a class or namespace, the program is ill-formed. [...]
Notes from the April, 2005 meeting:
The 10/2004 resolution does not take into account the fact that template type parameters do not designate class types in the context of the template definition. Further drafting is required.
Proposed resolution (April, 2006):
Change 3.4.3 basic.lookup.qual paragraph 1 as follows:
The name of a class or namespace member can be referred to after the :: scope resolution operator (5.1 expr.prim) applied to a nested-name-specifier that nominates its class or namespace. During the lookup for a name preceding the :: scope resolution operator, object, function, and enumerator names are ignored. If the name foundis not a class-name (clause 9 class) or namespace-name (7.3.1 namespace.def)does not designate a namespace or a class or dependent type, the program is ill-formed. [...]
The wholesale replacement of the phrase “template function” by the resolution of issue 105 seems to have overlooked the similar phrase “template conversion function.” This phrase appears a number of times in 13.3.3.1.2 over.ics.user paragraph 3, 14.5.2 temp.mem paragraphs 5-8, and 14.8.2 temp.deduct paragraph 1. It should be systematically replaced in similar fashion to the resolution of issue 105.
Proposed resolution (October, 2006):
Change 13.3.3.1.2 over.ics.user paragraph 3 as follows:
If the user-defined conversion is specified by atemplate conversion functionspecialization of a conversion function template, the second standard conversion sequence must have exact match rank.
Change 14.5.2 temp.mem paragraph 5 as follows:
A specialization of atemplate conversion functionconversion function template is referenced in the same way as a non-template conversion function that converts to the same type.
Change 14.5.2 temp.mem paragraph 6 as follows:
A specialization of atemplate conversion functionconversion function template is not found by name lookup. Instead, anytemplate conversion functionsconversion function templates visible in the context of the use are considered.
Change 14.5.2 temp.mem paragraph 7 as follows:
Ausing-declarationusing-declaration in a derived class cannot refer to a specialization of atemplate conversion functionconversion function template in a base class.
Change 14.5.2 temp.mem paragraph 8 as follows:
Overload resolution (13.3.3.2 over.ics.rank) and partial ordering (14.5.5.2 temp.func.order) are used to select the best conversion function among multipletemplate conversion functionsspecializations of conversion function templates and/or non-template conversion functions.
Change 14.8.2.3 temp.deduct.conv paragraph 1 as follows:
Template argument deduction is done by comparing the return type of thetemplate conversion functionconversion function template (call it P) with the type that is required as the result of the conversion (call it A) as described in 14.8.2.5 temp.deduct.type.
It is not clear how to handle the following example:
struct S {
template <typename T> S(const T&);
};
void f(const S&);
void f(int);
void g() {
enum E { e };
f(e); // ill-formed?
}
Three possibilities suggest themselves:
Fail during overload resolution. In order to perform overload resolution for the call to f, the declaration of the required specialization of the S constructor must be instantiated. This instantiation uses a local type and is thus ill-formed (14.3.1 temp.arg.type paragraph 2), rendering the example as a whole ill-formed, as well.
Treat this as a type-deduction failure. Although it is not listed currently among the causes of type-deduction failure in 14.8.2 temp.deduct paragraph 2, it could plausibly be argued that instantiating a function declaration with a local type as a template type-parameter falls under the rubric of “If a substitution in a template parameter or in the function type of the function template results in an invalid type” and thus should be a type-deduction failure. The result would be that the example is well-formed because f(const S&) would be removed from the list of viable functions.
Fail only if the function selected by overload resolution requires instantiation with a local type. This approach would require that the diagnostic resulting from the instantiation of the function type during overload resolution be suppressed and either regenerated or regurgitated once overload resolution is complete. (The example would be well-formed under this approach because f(int) would be selected as the best match.)
(See also issue 489.)
Notes from the April, 2005 meeting:
The question in the original example was whether there should be an error, even though the uninstantiable template was not needed for calling the best-matching function. The broader issue is whether a user would prefer to get an error or to call a “worse” non-template function in such cases. For example:
template<typename T> void f(T);
void f(int);
void g() {
enum E { e };
f(e); // call f(int) or get an error?
}
It was observed that the type deduction rules are intended to model, albeit selectively, the other rules of the language. This would argue in favor of the second approach, a type-deduction failure, and the consensus of the group was that the incremental benefit of other approaches was not enough to outweigh the additional complexity of specification and implementation.
Proposed resolution (October, 2005):
Add a new sub-bullet following bullet 3, sub-bullet 7 ("Attempting to give an invalid type to a non-type template parameter") of 14.8.2 temp.deduct paragraph 2:
Attempting to use a local or unnamed type as the value of a template type parameter.
Additional note (December, 2005):
The Evolution Working Group is currently considering an extension that would effectively give linkage to some (but perhaps not all) types that currently have no linkage. If the proposed resolution above is adopted and then later a change along the lines that the EWG is considering were also adopted, the result would be a silent change in the result of overload resolution, because the newly-acceptable specializations would become part of the overload set. It is not clear whether that possibility is sufficient reason to delay adoption of this resolution or not.
4 conv paragraph 1 says,
Standard conversions are implicit conversions defined for built-in types.
However, enumeration types (which take part in the integral promotions) and class types (which take part in the lvalue-to-rvalue conversion) are not “built-in” types, so the definition of “standard conversions” is wrong.
Proposed resolution (October, 2006):
Change 4 conv paragraph 1 as follows:
Standard conversions are implicit conversionsdefined for built-in typeswith built-in meaning...
Split off from issue 315.
Incidentally, another thing that ought to be cleaned up is the inconsistent use of "indirection" and "dereference". We should pick one.
Proposed resolution (October, 2006):
Change 5.3.1 expr.unary.op paragraph 1 as follows:
The unary * operator performsindirectiondereferencing: the expression to which it is applied shall be a pointer...
Change 8.3.4 dcl.array paragraph 8 as follows:
The results are added andindirectiondereferencing applied to yield an array (of five integers), which in turn is converted to a pointer to the first of the integers.
Change 8.3.5 dcl.fct paragraph 9 as follows:
The binding of *fpi(int) is *(fpi(int)), so the declaration suggests, and the same construction in an expression requires, the calling of a function fpi, and then usingindirectiondereferencing through the (pointer) result to yield an integer. In the declarator (*pif)(const char*, const char*), the extra parentheses are necessary to indicate thatindirectiondereferencing through a pointer to a function yields a function, which is then called.
Change Table 45 in 20.4.7.4 [lib.meta.trans.ptr] as follows:
The member typedef type shall be the same as T, except any top levelindirectiondereferencing has been removed.
Change the index for * and “dereferencing” no longer to refer to “indirection.”
[Drafting note: 26.5.9.2 [lib.indirect.array.assign] requires no change. Many more places in the current wording use “dereferencing” than “indirection.”]
For delete expressions, 5.3.5 expr.delete paragraph 1 says
The operand shall have a pointer type, or a class type having a single conversion function to a pointer type.
However, paragraph 3 of that same section says:
if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand's dynamic type and the static type shall have a virtual destructor or the behavior is undefined.
Since the operand must be of pointer type, its static type is necessarily the same as its dynamic type. That clause is clearly referring to the object being pointed at, and not to the pointer operand itself.
Correcting the wording gets a little complicated, because dynamic and static types are attributes of expressions, not objects, and there's no sub-expression of a delete-expression which has the relevant types.
Suggested resolution:
then there is a static type and a dynamic type that the hypothetical expression (* const-expression) would have. If that static type is different from that dynamic type, then that static type shall be a base class of that dynamic type, and that static type shall have a virtual destructor, or the behavior is undefined.
There's precedent for such use of hypothetical constructs: see 5.10 expr.eq paragraph 2, and 8.1 dcl.name paragraph 1.
10.3 class.virtual paragraph 3 has a similar problem. It refers to
the type of the pointer or reference denoting the object (the static type).
The type of the pointer is different from the type of the reference, both of which are different from the static type of '*pointer', which is what I think was actually intended. Paragraph 6 contains the exact same wording, in need of the same correction. In this case, perhaps replacing "pointer or reference" with "expression" would be the best fix. In order for this fix to be sufficient, pointer->member must be considered equivalent to (*pointer).member, in which case the "expression" referred to would be (*pointer).
12.5 class.free paragraph 4 says thatif a delete-expression is used to deallocate a class object whose static type has...
This should be changed to
if a delete-expression is used to deallocate a class object through a pointer expression whose dereferenced static type would have...
The same problem occurs later, when it says that the
static and dynamic types of the object shall be identical
In this case you could replace "object" with "dereferenced pointer expression".
Footnote 104 says that
5.3.5 expr.delete requires that ... the static type of the delete-expression's operand be the same as its dynamic type.
This would need to be changed to
the delete-expression's dereferenced operand
Proposed resolution (October, 2006):
Change 5.3.5 expr.delete paragraph 3 as follows:
In the first alternative (delete object), if the static type of the dereferenced (5.3.1 expr.unary.op) operand is different from its dynamic type, the static type shall be a base class of the dereferenced operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.
Change the footnote in 12.5 class.free paragraph 4 as follows:
A similar provision is not needed for the array version of operator delete because 5.3.5 expr.delete requires that in this situation, the static type of thedelete-expression’s operandobject to be deleted be the same as its dynamic type.
Change the footnote in 12.5 class.free paragraph 5 as follows:
If the static typein the delete-expressionof the object to be deleted is different from the dynamic type and the destructor is not virtual the size might be incorrect, but that case is already undefined; see 5.3.5 expr.delete.
[Drafting notes: No change is required for 10.3 class.virtual paragraph 7 because “the type of the pointer” includes the pointed-to type. No change is required for 12.5 class.free paragraph 4 because “...used to deallocate a class object whose static type...” already refers to the object (and not the operand expression).]
The grammar in 7 dcl.dcl paragraph 1 says that a declaration-seq is either declaration or declaration-seq declaration. Some declarations end with semicolons and others (e.g. function definitions and namespace declarations) don't. This means that users who put a semicolon after every declaration are technically writing ill-formed code. The trouble is that in this respect the standard is out of sync with reality. It's convenient to allow semicolons after every declaration, and there's no implementation difficulty in doing so. All existing compilers accept this, except in extra-pedantic mode. When all implementations disagree with the standard, it's time for the standard to change.
Suggested resolution:
In the grammar in 7 dcl.dcl paragraph 11, change the second line in the definition of declaration-seq to
Proposed resolution (October, 2006):
Add the indicated lines to the grammar definitions in 7 dcl.dcl paragraph 1:
declaration:
...
namespace-definition
empty-declaration
...
static_assert-declaration:
static_assert ( constant-expression , string-literal ) ;
empty-declaration:
;
Add the following as a new paragraph after 7 dcl.dcl paragraph 4:
An empty-declaration has no effect.
According to 8.3 dcl.meaning paragraph 1,
A declarator-id shall not be qualified except for the definition of a member function (9.3 class.mfct) or static data member (9.4 class.static) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or the definition of a previously declared explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (11.4 class.friend). When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers...
This restriction prohibits examples like the following:
void f();
void ::f(); // error: qualified declarator
namespace N {
void f();
void N::f() { } // error: qualified declarator
}
There doesn't seem to be any good reason for disallowing such declarations, and a number of implementations accept them in spite of the Standard's prohibition. Should the Standard be changed to allow them?
Notes from the April, 2006 meeting:
In discussing issue 548, the CWG agreed that the prohibition of qualified declarators inside their namespace should be removed.
Proposed resolution (October, 2006):
Remove the indicated words from 8.3 dcl.meaning paragraph 1:
...An unqualified-id occurring in a declarator-id shall be a simple identifier except for the declaration of some special functions (12.3 class.conv, 12.4 class.dtor, 13.5 over.oper) and for the declaration of template specializations or partial specializations ().A declarator-id shall not be qualified except for the definition of a member function (9.3 class.mfct) or static data member (9.4 class.static) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or the definition of a previously declared explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (11.4 class.friend).When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers, and the member shall not have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id...
[Drafting note: The omission of “outside of its class” here does not give permission for redeclaration of class members; that is still prohibited by 9.2 class.mem paragraph 1. The removal of the enumeration of the kinds of declarations in which a qualified-id can appear does allow a typedef declaration to use a qualified-id, which was not permitted before; if that is undesirable, the prohibition can be reinstated here.]
9.3.2 class.this paragraph 1, which specifies the meaning of the keyword 'this', seems to limit its usage to the *body* of non-static member functions. However 'this' is also usable in ctor-initializers which, according to the grammar in 8.4 dcl.fct.def par. 1, are not part of the body.
Proposed resolution: Changing the first part of 9.3.2 class.this par. 1 to:
In the body of a nonstatic (9.3) member function or in a ctor-initializer (12.6.2), the keyword this is a non-lvalue expression whose value is the address of the object for which the function is called.
NOTE: I'm talking of constructors as functions that are "called"; there have been discussions on c.l.c++.m as to whether constructors are "functions" and to whether this terminology is correct or not; I think it is both intuitive and in agreement with the standard wording.
Steve Adamczyk: See also issue 397, which is defining a new syntax term for the body of a function including the ctor-initializers.
Notes from the March 2004 meeting:
This will be resolved when issue 397 is resolved.
Proposed resolution (October, 2005):
Change 8.4 dcl.fct.def paragraph 1 as indicated:
Function definitions have the form
function-definition:
decl-specifier-seqopt declarator
ctor-initializeroptfunction-bodyfunction-body:
decl-specifier-seqopt declarator function-try-blockctor-initializeropt compound-statement
function-try-block
An informal reference to the body of a function should be interpreted as a reference to the nonterminal function-body.
Change the definition of function-try-block in 15 except paragraph 1:
function-try-block:
try ctor-initializeropt
function-bodycompound-statement handler-seq
Change 3.3.6 basic.scope.class paragraph 1, point 1, as indicated:
The potential scope of a name declared in a class consists not only of the declarative region following the name's point of declaration, but also of all functionbodies,bodies and default arguments, and constructor ctor-initializersin that class (including such things in nested classes).
Change 3.3.6 basic.scope.class paragraph 1, point 5, as indicated:
The potential scope of a declaration that extends to or past the end of a class definition also extends to the regions defined by its member definitions, even if the members are defined lexically outside the class (this includes static data member definitions, nested class definitions, member function definitions (including the member function bodyand, for constructor functions (12.1 class.ctor), the ctor-initializer () and any portion of the declarator part of such definitions which follows the identifier, including a parameter-declaration-clause and any default arguments (8.3.6 dcl.fct.default). [Example:...12.6.2 class.base.init )
Change footnote 32 in 3.4.1 basic.lookup.unqual paragraph 8 as indicated:
That is, an unqualified name that occurs, for instance, in a type or default argument expression in theparameter-declaration-clause,parameter-declaration-clause or in the function body, or in an expression of a mem-initializer in a constructor definition.
Change 5.1 expr.prim paragraph 3 as indicated:
...The keyword this shall be used only inside a non-static class member function body (9.3 class.mfct)or in a constructor mem-initializer (12.6.2 class.base.init)...
Change 9.2 class.mem paragraph 2 as indicated:
...Within the class member-specification, the class is regarded as complete within function bodies, default arguments, and exception-specifications, and constructor ctor-initializers(including such things in nested classes)...
Change 9.2 class.mem paragraph 9 as indicated:
Each occurrence in an expression of the name of a non-static data member or non-static member function of a class shall be expressed as a class member access (5.2.5 expr.ref), except when it appears in the formation of a pointer to member (5.3.1 expr.unary.op), oror when it appears in the body of a non-static member function of its class or of a class derived from its class (9.3.1 class.mfct.nonstatic), or when it appears in a mem-initializer for a constructor for its class or for a class derived from its class (12.6.2 class.base.init).
Change the note in 9.3 class.mfct paragraph 5 as indicated:
[Note: a name used in a member function definition (that is, in the parameter-declaration-clause including the default arguments (8.3.6 dcl.fct.default), oror in the member function body, or, for a constructor function (12.1 class.ctor), in a mem-initializer expression (12.6.2 class.base.init)) is looked up as described in 3.4 basic.lookup. —end note]
Change 9.3.1 class.mfct.nonstatic paragraph 1 as indicated:
...A non-static member function may also be called directly using the function call syntax (5.2.2 expr.call, 13.3.1.1 over.match.call) from within the body of a member function of its class or of a class derived from its class.
- from within the body of a member function of its class or of a class derived from its class, or
- from a mem-initializer (12.6.2 class.base.init) for a constructor for its class or for a class derived from its class.
Change 9.3.1 class.mfct.nonstatic paragraph 3 as indicated:
When an id-expression (5.1 expr.prim) that is not part of a class member access syntax (5.2.5 expr.ref) and not used to form a pointer to member (5.3.1 expr.unary.op) is used in the body of a non-static member function of class Xor used in the mem-initializer for a constructor of class X, if name lookup (3.4.1 basic.lookup.unqual) resolves the name in the id-expression to a non-static non-type member of class X or of a base class of X, the id-expression is transformed into a class member access expression (5.2.5 expr.ref) using (*this) (9.3.2 class.this) as the postfix-expression to the left of the . operator...
Change 12.1 class.ctor paragraph 7 as indicated:
...The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class withan empty mem-initializer-listno ctor-initializer (12.6.2 class.base.init) and an emptyfunction bodycompound-statement...
Change 12.6.2 class.base.init paragraph 4 as indicated:
...After the call to a constructor for class X has completed, if a member of X is neither specified in the constructor’s mem-initializers, nor default-initialized, nor value-initialized, nor given a value during execution of the compound-statement of the body of the constructor, the member has indeterminate value.
Change the last bullet of 12.6.2 class.base.init paragraph 5 as indicated:
Finally, the body compound-statement of the
constructor body is executed.
Change 15 except paragraph 4 as indicated:
A function-try-block associates a handler-seq with the ctor-initializer, if present, and the
function-bodycompound-statement. An exception thrown during the execution of the initializer expressions in the ctor-initializer or during the execution of thefunction-bodycompound-statement transfers control to a handler in a function-try-block in the same way as an exception thrown during the execution of a try-block transfers control to other handlers. [Example:int f(int); class C { int i; double d; public: C(int, double); }; C::C(int ii, double id) try : i(f(ii)), d(id) { // constructorfunction bodystatements } catch (...) { // handles exceptions thrown from the ctor-initializer // and from the constructorfunction bodystatements }—end example]
Change 15.2 except.ctor paragraph 2 as indicated:
When an exception is thrown, control is transferred to the nearest handler with a matching type (15.3 except.handle); “nearest” means the handler for which thecompound-statement,compound-statement or ctor-initializer, or function-bodyfollowing the try keyword was most recently entered by the thread of control and not yet exited.
12.6 class.init paragraph 2 says,
When an array of class objects is initialized (either explicitly or implicitly), the constructor shall be called for each element of the array, following the subscript order;
That implies that, given
struct POD {
int x;
};
POD data[10] = {};
this should call the implicitly declared default ctor 10 times, leaving 10 uninitialized ints, rather than value initialize each member of data, resulting in 10 initialized ints (which is required by 8.5.1 dcl.init.aggr paragraph 7).
I suggest rephrasing along the lines:
When an array is initialized (either explicitly or implicitly), each element of the array shall be initialized in turn, following the subscript order;
This would allow for PODs and other classes with a dual nature under value/default initialization, and cover copy initialization for arrays too.
Proposed resolution (October, 2006):
Change 12.6 class.init paragraph 3 as follows:
When an array of class objects is initialized (either explicitly or implicitly) and the elements are initialized by constructor, the constructor shall be called for each element of the array, following the subscript order; see 8.3.4 dcl.array.
The overload resolution rules for ranking a template against a non-template function differ for conversion functions in a surprising way. 13.3.3 over.match.best lists four checks, the last three concern this report. For the non-conversion operator case, checks 2 and 3 are applicable, whereas for the conversion operator case checks 3 and 4 are applicable. Checks 2 and 4 concern the ranking of argument and return value conversion sequences respectively. Check 3 concerns only the templatedness of the functions being ranked, and will prefer a non-template to a template. Notice that this check happens after argument conversion sequence ranking, but before return value conversion sequence ranking. This has the effect of always selecting a non-template conversion operator, as the following example shows:
struct C
{
inline operator int () { return 1; }
template <class T> inline operator T () { return 0; }
};
inline long f (long x) { return x; }
int
main (int argc, char *argv[])
{
return f (C ());
}
The non-templated C::operator int function will be selected, rather than the apparently better C::operator long<long> instantiation. This is a surprise, and resulted in a bug report where the user expected the template to be selected. In addition some C++ compilers have implemented the overload ranking as if checks 3 and 4 were transposed.
Is this ordering accidental, or is there a rationale?
Notes from the April, 2005 meeting:
The CWG agreed that the template/non-template distinction should be the final tie-breaker.
Proposed resolution (March, 2007):
In the second bulleted list of 13.3.3 over.match.best paragraph 1, move the second and third bullets to the end of the list, to read as follows:
for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,
the context is an initialization by user-defined conversion (see 8.5 dcl.init, 13.3.1.5 over.match.conv, and 13.3.1.6 over.match.ref) and the standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type of F2 to the destination type, [Example: ... —end example] or, if not that,
- F1 is a non-template function and F2 is a function template specialization, or, if not that,
F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.5.2 temp.func.order.
14.6.2 temp.dep paragraph 3 reads,
In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.
This wording applies only to definitions of class templates and members of class templates. That would make the following program ill-formed (but it probably should be well-formed):
struct B{ void f(int); };
template<class T> struct D: B { };
template<class T> void g() {
struct B{ void f(); };
struct A: D<T> {
B m;
};
A a;
a.m.f(); // Presumably, we want ::g()::B::f(), not ::B::f(int)
}
int main () {
g<int>();
return 0;
}
I suspect the wording should be something like
In the definition of a class template or a class defined (directly or indirectly) within the scope of a class template or function template, if a base class...
That should also include deeply nested classes in templates, local classes of non-template member functions of member classes of class templates, etc.
Proposed resolution (October, 2006):
Change 14.6.2 temp.dep paragraph 3 as follows:
In the definition of a class or class templateor a member of a class template, if a base classof the class templatedepends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.
The last two sentences of 14.8.2 temp.deduct paragraph 5 read:
When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in non-deduced contexts are replaced with the corresponding deduced or default argument values. If the substitution results in an invalid type, as described above, type deduction fails.
Shouldn't the substitution occur for all uses of the parameters, so that any of them could result in deduction failure?
Proposed resolution (October, 2006):
Change 14.8.2 temp.deduct paragraph 5 as follows:
...When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters innon-deduced contextsthe function type are replaced with the corresponding deduced or default argument values. If the substitution results in an invalid type, as described above, type deduction fails.
According to 15.1 except.throw paragraph 3,
The type of the throw-expression shall not be an incomplete type, or a pointer to an incomplete type other than (possibly cv-qualified) void.
This disallows cases like the following, because str has an incomplete type (an array of unknown size):
extern const char str[];
void f() {
throw str;
}
The array-to-pointer conversion is applied to the operand of throw, so there's no problem creating the exception object, which is the reason for the restriction on incomplete types. I believe this case should be permitted.
Notes from the April, 2005 meeting:
The CWG agreed that the example should be permitted. Note that the reference to throw-expression in the cited text is incorrect; a throw-expression includes the throw keyword and is always of type void. This wording problem is addressed in the proposed resolution for issue 475.
Proposed resolution (October, 2006)
Change 15.1 except.throw paragraph 3 as indicated:
...The type of the throw-expression shall notIf the type of the exception object would be an incomplete type, or a pointer to an incomplete type other than (possibly cv-qualified) void the program is ill-formed...
According to 15.2 except.ctor paragraph 2,
An object that is partially constructed or partially destroyed will have destructors executed for all of its fully constructed subobjects, that is, for subobjects for which the principal constructor (12.6.2 class.base.init) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object's destructor will be invoked. Should a constructor for an element of an automatic array throw an exception, only the constructed elements of that array will be destroyed.
The requirement for destruction of array elements explicitly applies only to automatic arrays, and one might conclude from the context that only automatic class objects are in view as well, although that is not explicitly stated. What about local static arrays and class objects? Are they intended also to be subject to the requirement that fully-constructed subobjects are to be destroyed?
Proposed resolution (October, 2006):
Change 15.2 except.ctor paragraph 2 as follows:
An object that is partially constructed or partially destroyed will have destructors executed for all of its fully constructed subobjects, that is, for subobjects for which the principal constructor (12.6.2 class.base.init) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object’s destructor will be invoked.Should a constructor for an element of an automatic array throw an exception, only the constructed elements of that array will be destroyed.If the object or array was allocated in a new-expression, the matching deallocation function (3.7.3.2 basic.stc.dynamic.deallocation, 5.3.4 expr.new, 12.5 class.free), if any, is called to free the storage occupied by the object.
In language imported directly from the C Standard, 16.2 cpp.include paragraph 5 says,
The implementation provides unique mappings for sequences consisting of one or more nondigits (2.10 lex.name) followed by a period (.) and a single nondigit.
This is clearly intended to support C header names like stdio.h. However, C++ has header names like cstdio that do not conform to this pattern but still presumably require “unique mappings.”
Proposed resolution (April, 2006):
Change 16.2 cpp.include paragraph 5 as indicated:
The implementation provides unique mappings between the delimited sequence and the external source file name for sequences consisting of one or more nondigits or digits (2.10 lex.name), optionally followed by a period (.) and a single nondigit...
(Clark Nelson will discuss this revision with WG14.)
Additional notes (October, 2006):
WG14 takes no position on this proposed change.
C99 and C++ differ in their approach to universal character names (UCNs).
Issue 248 already covers the differences in UCNs allowed for identifiers, but a more fundamental issue is that of UCNs that correspond to codes reserved by ISO 10676 for surrogate pair forms.
Specifically, C99 does not allow UCNs whose short names are in the range 0xD800 to 0xDFFF. I think C++ should have the same constraint. If someone really wants to place such a code in a character or string literal, they should use a hexadecimal escape sequence instead, for example:
wchar_t w1 = L'\xD900'; // Okay.
wchar_t w2 = L'\uD900'; // Error, not a valid character.
(Compare 6.4.3 paragraph 2 in ISO/IEC 9899/1999 with 2.2 lex.charset paragraph 2 in the C++ standard.)
2.4 lex.pptoken paragraph 2 specifies that there are 5 categories of tokens in phases 3 to 6. With 2.12 lex.operators paragraph 1, it is unclear whether new is an identifier or a preprocessing-op-or-punc; likewise for delete. This is relevant to answer the question whether
#define delete foo
is a well-formed control-line, since that requires an identifier after the define token.
(See also issue 189.)
The nonterminals operator and punctuator in 2.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:
Additional note (April, 2005):
The resolution for this problem should also address the fact that sizeof and typeid (and potentially others like decltype that may be added in the future) are described in some places as “operators” but are not listed in 13.5 over.oper paragraph 3 among the operators that cannot be overloaded.
(See also issue 369.)
3.2 basic.def.odr paragraph 1 says,
No translation unit shall contain more than one definition of any variable, function, class type, enumeration type or template.
This says nothing about references. Is it permitted to define a reference more than once in a single translation unit? (The list in paragraph 5 of things that can have definitions in multiple translation units does not include references.)
The Standard does not completely specify how to look up the type-name(s) in a pseudo-destructor-name (5.2 expr.post paragraph 1, 5.2.4 expr.pseudo), and what information it does have is incorrect and/or in the wrong place. Consider, for instance, 3.4.5 basic.lookup.classref paragraphs 2-3:
If the id-expression in a class member access (5.2.5 expr.ref) is an unqualified-id, and the type of the object expression is of a class type C (or of pointer to a class type C), the unqualified-id is looked up in the scope of class C. If the type of the object expression is of pointer to scalar type, the unqualified-id is looked up in the context of the complete postfix-expression.
If the unqualified-id is ~type-name, and the type of the object expression is of a class type C (or of pointer to a class type C), the type-name is looked up in the context of the entire postfix-expression and in the scope of class C. The type-name shall refer to a class-name. If type-name is found in both contexts, the name shall refer to the same class type. If the type of the object expression is of scalar type, the type-name is looked up in the scope of the complete postfix-expression.
There are at least three things wrong with this passage with respect to pseudo-destructors:
A pseudo-destructor call (5.2.4 expr.pseudo) is not a “class member access”, so the statements about scalar types in the object expressions are vacuous: the object expression in a class member access is required to be a class type or pointer to class type (5.2.5 expr.ref paragraph 2).
On a related note, the lookup for the type-name(s) in a pseudo-destructor name should not be described in a section entitled “Class member access.”
Although the class member access object expressions are carefully allowed to be either a class type or a pointer to a class type, paragraph 2 mentions only a “pointer to scalar type” (disallowing references) and paragraph 3 deals only with a “scalar type,” presumably disallowing pointers (although it could possibly be a very subtle way of referring to both non-class pointers and references to scalar types at once).
The other point at which lookup of pseudo-destructors is mentioned is 3.4.3 basic.lookup.qual paragraph 5:
If a pseudo-destructor-name (5.2.4 expr.pseudo) contains a nested-name-specifier, the type-names are looked up as types in the scope designated by the nested-name-specifier.
Again, this specification is in the wrong location (a pseudo-destructor-name is not a qualified-id and thus should not be treated in the “Qualified name lookup” section).
Finally, there is no place in the Standard that describes the lookup for pseudo-destructor calls of the form p->T::~T() and r.T::~T(), where p and r are a pointer and reference to scalar, respectively. To the extent that it gives any guidance at all, 3.4.5 basic.lookup.classref deals only with the case where the ~ immediately follows the . or ->, and 3.4.3 basic.lookup.qual deals only with the case where the pseudo-destructor-name contains a nested-name-specifier that designates a scope in which names can be looked up.
See document J16/06-0008 = WG21 N1938 for further discussion of this and related issues, including 244, 305, 399, and 414.
When 3.4.1 basic.lookup.unqual paragraph 10 says,
In a friend declaration naming a member function, a name used in the function declarator and not part of a template-argument in a template-id is first looked up in the scope of the member function's class. If it is not found, or if the name is part of a template-argument in a template-id, the look up is as described for unqualified names in the definition of the class granting friendship.
what does “in the scope of the member function's class” mean? Does it mean that only members of the class and its base classes are considered? Or does it mean that the same lookup is to be performed as if the name appeared in the member function's class? Implementations vary in this regard. For example:
struct s1;
namespace ns {
struct s1;
}
struct s2 {
void f(s1 &);
};
namespace ns {
struct s3 {
friend void s2::f(s1 &);
};
}
Microsoft Visual C++ and Comeau C++ resolve s1 in the friend declaration to ns::s1 and issue an error, while g++ resolves it to ::s1 and accepts the code.
Notes from the April, 2005 meeting:
The phrase “looked up in the scope of [a] class” occurs frequently throughout the Standard and always refers to the member name lookup described in 10.2 class.member.lookup. This is the first interpretation mentioned above (“only members of the class and its base classes”), resolving s1 to ns::s1. A cross-reference to 10.2 class.member.lookup will be added to 3.4.1 basic.lookup.unqual paragraph 10 to make this clearer.
In discussing this question, the CWG noticed another problem: the text quoted above applies to all template-arguments appearing in the function declarator. The intention of this rule, however, is that only template-arguments in the declarator-id should ignore the member function's class scope; template-arguments used elsewhere in the function declarator should be treated like other names. For example:
template<typename T> struct S;
struct A {
typedef int T;
void foo(S<T>);
};
template <typename T> struct B {
friend void A::foo(S<T>); // i.e., S<A::T>
};
In discussing issue 197, the question arose as to whether the handling of fundamental types in argument-dependent lookup is actually what is desired. This question needs further discussion.
Paragraph 7 of 3.4.5 basic.lookup.classref says,
If the id-expression is a conversion-function-id, its conversion-type-id shall denote the same type in both the context in which the entire postfix-expression occurs and in the context of the class of the object expression (or the class pointed to by the pointer expression).Does this mean that the following example is ill-formed?
struct A { operator int(); } a;
void foo() {
typedef int T;
a.operator T(); // 1) error T is not found in the context
// of the class of the object expression?
}
The second bullet in paragraph 1 of
3.4.3.1
class.qual
says,
a conversion-type-id of an operator-function-id is looked up both in the scope of the class and in the context in which the entire postfix-expression occurs and shall refer to the same type in both contextsHow about:
struct A { typedef int T; operator T(); };
struct B : A { operator T(); } b;
void foo() {
b.A::operator T(); // 2) error T is not found in the context
// of the postfix-expression?
}
Is this interpretation correct? Or was the intent for
this to be an error only if
T was found in both scopes and referred to different entities?
If the intent was for these to be errors, how do these rules apply to template arguments?
template <class T1> struct A { operator T1(); }
template <class T2> struct B : A<T2> {
operator T2();
void foo() {
T2 a = A<T2>::operator T2(); // 3) error? when instantiated T2 is not
// found in the scope of the class
T2 b = ((A<T2>*)this)->operator T2(); // 4) error when instantiated?
}
}
(Note bullets 2 and 3 in paragraph 1 of 3.4.3.1 class.qual refer to postfix-expression. It would be better to use qualified-id in both cases.)
Erwin Unruh: The intent was that you look in both contexts. If you find it only once, that's the symbol. If you find it in both, both symbols must be "the same" in some respect. (If you don't find it, its an error).
Mike Miller: What's not clear to me in these examples is whether what is being looked up is T or int. Clearly the T has to be looked up somehow, but the "name" of a conversion function clearly involves the base (non-typedefed) type, not typedefs that might be used in a definition or reference (cf 3 basic paragraph 7 and 12.3 class.conv paragraph 5). (This is true even for types that must be written using typedefs because of the limited syntax in conversion-type-ids — e.g., the "name" of the conversion function in the following example
typedef void (*pf)();
struct S {
operator pf();
};
is S::operator void(*)(), even though you can't write its name
directly.)
My guess is that this means that in each scope you look up the type named in the reference and form the canonical operator name; if the name used in the reference isn't found in one or the other scope, the canonical name constructed from the other scope is used. These names must be identical, and the conversion-type-id in the canonical operator name must not denote different types in the two scopes (i.e., the type might not be found in one or the other scope, but if it's found in both, they must be the same type).
I think this is all very vague in the current wording.
An example in 3.5 basic.link paragraph 6 creates two file-scope variables with the same name, one with internal linkage and one with external.
static void f();
static int i = 0; //1
void g() {
extern void f(); // internal linkage
int i; //2: i has no linkage
{
extern void f(); // internal linkage
extern int i; //3: external linkage
}
}
Is this really what we want? C99 has 6.2.2.7/7, which gives undefined behavior for having an identifier appear with internal and external linkage in the same translation unit. C++ doesn't seem to have an equivalent.
Notes from October 2003 meeting:
We agree that this is an error. We propose to leave the example but change the comment to indicate that line //3 has undefined behavior, and elsewhere add a normative rule giving such a case undefined behavior.
Proposed resolution (October, 2005):
Change 3.5 basic.link paragraph 6 as indicated:
...Otherwise, if no matching entity is found, the block scope entity receives external linkage. If, within a translation unit, the same entity is declared with both internal and external linkage, the behavior is undefined.
[Example:
static void f(); static int i = 0; // 1 void g () { extern void f (); // internal linkage int i; // 2: i has no linkage { extern void f (); // internal linkage extern int i; // 3: external linkage } }
There are three objects named i in this program. The object with internal linkage introduced by the declaration in global scope (line //1 ), the object with automatic storage duration and no linkage introduced by the declaration on line //2, and the object with static storage duration and external linkage introduced by the declaration on line //3.Without the declaration at line //2, the declaration at line //3 would link with the declaration at line //1. But because the declaration with internal linkage is hidden, //3 is given external linkage, resulting in a linkage conflict. —end example]
Notes frum the April 2006 meeting:
According to 3.5 basic.link paragraph 9, the two variables with linkage in the proposed example are not “the same entity” because they do not have the same linkage. Some other formulation will be needed to describe the relationship between those two variables.
Notes from the October 2006 meeting:
The CWG decided that it would be better to make a program with this kind of linkage mismatch ill-formed instead of having undefined behavior.
The resolution of issue 389 makes code like
static struct {
int i;
int j;
} X;
ill-formed. This breaks a lot of code for no apparent reason, since the name X is not known outside the translation unit in which it appears; there is therefore no danger of collision and no need to mangle its name.
There has also been recent discussion on the email reflectors as to whether the restrictions preventing use of types without linkage as template arguments is needed or not, with the suggestion that a mechanism like that used to give members of the unnamed namespace unique names could be used for unnamed and local types. See also issue 488, which would become moot if types without linkage could be used as template parameters.
Notes from the October, 2005 meeting:
The Evolution Working Group is discussing changes that would address this issue. CWG will defer consideration until the outcome of the EWG discussions is clear.
Notes from the April, 2006 meeting:
The CWG agreed that the restriction in 3.5 basic.link paragraph 8 on use of a type without linkage should apply only to variables and functions with external linkage, not to variables and functions with internal linkage (i.e., the example should be accepted). This is a separate issue from the question before the EWG and should be resolved independently.
Additional note (April, 2006):
Even the restriction of the rule to functions and objects with external linkage may not be exactly what we want. Consider an example like:
namespace {
struct { int i; } s;
}
The variable s has external linkage but can't be named outside its translation unit, so there's again no reason to prohibit use of a type without linkage in its declaration.
According to 3.5 basic.link paragraph 3,
A name having namespace scope (3.3.5 basic.scope.namespace) has internal linkage if it is the name of
an object, reference, function or function template that is explicitly declared static or,
an object or reference that is explicitly declared const and neither explicitly declared extern nor previously declared to have external linkage;
It is not possible to declare a reference to be const.
In ISO/IEC 14882:2003, the second bullet of 3.8 basic.life paragraph 1 reads,
if T is a class type with a non-trivial constructor (12.1 class.ctor), the constructor call has completed.
Issue 119 pointed out that aggregate initialization can be used with some classes with a non-trivial implicitly-declared default constructor, and that in such cases there is no call to the object's constructor. The resolution for that issue was to change the previously-cited wording to read,
If T is a class type with a non-trivial constructor (12.1 class.ctor, the initialization is complete.
Later (but before the WP was revised with the wording from the resolution of issue 119), issue 404 changed the 2003 wording to read,
If T is a class type and the constructor invoked to create the object is non-trivial (12.1 class.ctor), the constructor call has completed.
thus reversing the effect of issue 119, whose whole purpose was to cover objects with non-trivial constructors that are not invoked.
Through an editorial error, the post-Redmond draft (N1905) still contained the original 2003 wording that should have been replaced by the resolution of issue 119, in addition to the new wording from the resolution:
if T is a class type and the constructor invoked to create the object is non-trivial (12.1 class.ctor), the constructor call has completed. the initialization is complete.
Finally, during the application of the edits for delegating constructors (N1986), this editing error was “fixed” by retaining the original 2003 wording (which was needed for the application of the change specified in N1986), so that the current draft (N2009) reads,
if T is a class type and the constructor invoked to create the object is non-trivial (12.1 class.ctor), the principal constructor call 12.6.2 class.base.init) has completed.
Because the completion of the call to the principal constructor corresponds to the point at which the object is “fully constructed” (15.2 except.ctor paragraph 2), i.e., its initialization is complete, I believe that the exact wording of the issue 119 resolution would be correct and should be restored verbatim.
Sent in by David Abrahams:
Yes, and to add to this tangent, 3.9.1 basic.fundamental paragraph 1 states "Plain char, signed char, and unsigned char are three distinct types." Strangely, 3.9 basic.types paragraph 2 talks about how "... the underlying bytes making up the object can be copied into an array of char or unsigned char. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value." I guess there's no requirement that this copying work properly with signed chars!
Notes from October 2002 meeting:
We should do whatever C99 does. 6.5p6 of the C99 standard says "array of character type", and "character type" includes signed char (6.2.5p15), and 6.5p7 says "character type". But see also 6.2.6.1p4, which mentions (only) an array of unsigned char.
Proposed resolution (April 2003):
Change 3.8 basic.life paragraph 5 bullet 3 from
to
Change 3.8 basic.life paragraph 6 bullet 3 from
to
Change the beginning of 3.9 basic.types paragraph 2 from
For any object (other than a base-class subobject) of POD type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7 intro.memory) making up the object can be copied into an array of char or unsigned char.
to
For any object (other than a base-class subobject) of POD type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7 intro.memory) making up the object can be copied into an array of byte-character type.
Add the indicated text to 3.9.1 basic.fundamental paragraph 1:
Objects declared as characters (char) shall be large enough to store any member of the implementation's basic character set. If a character from this set is stored in a character object, the integral value of that character object is equal to the value of the single character literal form of that character. It is implementation-defined whether a char object can hold negative values. Characters can be explicitly declared unsigned or signed. Plain char, signed char, and unsigned char are three distinct types, called the byte-character types. A char, a signed char, and an unsigned char occupy the same amount of storage and have the same alignment requirements (3.9 basic.types); that is, they have the same object representation. For byte-character types, all bits of the object representation participate in the value representation. For unsigned byte-character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types. In any particular implementation, a plain char object can take on either the same values as a signed char or an unsigned char; which one is implementation-defined.
Change 3.10 basic.lval paragraph 15 last bullet from
to
Notes from October 2003 meeting:
It appears that in C99 signed char may have padding bits but no trap representation, whereas in C++ signed char has no padding bits but may have -0. A memcpy in C++ would have to copy the array preserving the actual representation and not just the value.
March 2004: The liaisons to the C committee have been asked to tell us whether this change would introduce any unnecessary incompatibilities with C.
Notes from October 2004 meeting:
The C99 Standard appears to be inconsistent in its requirements. For example, 6.2.6.1 paragraph 4 says:
The value may be copied into an object of type unsigned char [n] (e.g., by memcpy); the resulting set of bytes is called the object representation of the value.
On the other hand, 6.2 paragraph 6 says,
If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one.
Mike Miller will investigate further.
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.)
I believe that the committee has neglected to take into account one of the differences between C and C++ when defining sequence points. As an example, consider
(a += b) += c;
where a, b, and c all have type int. I believe that this expression has undefined behavior, even though it is well-formed. It is not well-formed in C, because += returns an rvalue there. The reason for the undefined behavior is that it modifies the value of `a' twice between sequence points.
Expressions such as this one are sometimes genuinely useful. Of course, we could write this particular example as
a += b; a += c;
but what about
void scale(double* p, int n, double x, double y) {
for (int i = 0; i < n; ++i) {
(p[i] *= x) += y;
}
}
All of the potential rewrites involve multiply-evaluating p[i] or unobvious circumlocations like creating references to the array element.
One way to deal with this issue would be to include built-in operators in the rule that puts a sequence point between evaluating a function's arguments and evaluating the function itself. However, that might be overkill: I see no reason to require that in
x[i++] = y;
the contents of `i' must be incremented before the assignment.
A less stringent alternative might be to say that when a built-in operator yields an lvalue, the implementation shall not subsequently change the value of that object as a consequence of that operator.
I find it hard to imagine an implementation that does not do this already. Am I wrong? Is there any implementation out there that does not `do the right thing' already for (a += b) += c?
5.17 expr.ass paragraph 1 says,
The result of the assignment operation is the value stored in the left operand after the assignment has taken place; the result is an lvalue.
What is the normative effect of the words "after the assignment has taken place"? I think that phrase ought to mean that in addition to whatever constraints the rules about sequence points might impose on the implementation, assignment operators on built-in types have the additional constraint that they must store the left-hand side's new value before returning a reference to that object as their result.
One could argue that as the C++ standard currently stands, the effect of x = y = 0; is undefined. The reason is that it both fetches and stores the value of y, and does not fetch the value of y in order to compute its new value.
I'm suggesting that the phrase "after the assignment has taken place" should be read as constraining the implementation to set y to 0 before yielding the value of y as the result of the subexpression y = 0.
Francis Glassborow:
My understanding is that for a single variable:
It is the 3) that is often ignored because in practice the compiler hardly ever codes for the read because it already has that value but in complicated evaluations with a shortage of registers, that is not always the case. Without getting too close to the hardware, I think we both know that a read too close to a write can be problematical on some hardware.
So, in x = y = 0;, the implementation must NOT fetch a value from y, instead it has to "know" what that value will be (easy because it has just computed that in order to know what it must, at some time, store in y). From this I deduce that computing the lvalue (to know where to store) and the rvalue to know what is stored are two entirely independent actions that can occur in any order commensurate with the overall requirements that both operands for an operator be evaluated before the operator is.
Erwin Unruh:
C distinguishes between the resulting value of an assignment and putting the value in store. So in C a compiler might implement the statement x=y=0; either as x=0;y=0; or as y=0;x=0; In C the statement (x += 5) += 7; is not allowed because the first += yields an rvalue which is not allowed as left operand to +=. So in C an assignment is not a sequence of write/read because the result is not really "read".
In C++ we decided to make the result of assignment an lvalue. In this case we do not have the option to specify the "value" of the result. That is just the variable itself (or its address in a different view). So in C++, strictly speaking, the statement x=y=0; must be implemented as y=0;x=y; which makes a big difference if y is declared volatile.
Furthermore, I think undefined behaviour should not be the result of a single mentioning of a variable within an expression. So the statement (x +=5) += 7; should NOT have undefined behaviour.
In my view the semantics could be:
Jerry Schwarz:
My recollection is different from Erwin's. I am confident that the intention when we decided to make assignments lvalues was not to change the semantics of evaluation of assignments. The semantics was supposed to remain the same as C's.
Ervin seems to assume that because assignments are lvalues, an assignment's value must be determined by a read of the location. But that was definitely not our intention. As he notes this has a significant impact on the semantics of assignment to a volatile variable. If Erwin's interpretation were correct we would have no way to write a volatile variable without also reading it.
Lawrence Crowl:
For x=y=0, lvalue semantics implies an lvalue to rvalue conversion on the result of y=0, which in turn implies a read. If y is volatile, lvalue semantics implies both a read and a write on y.
The standard apparently doesn't state whether there is a value dependence of the lvalue result on the completion of the assignment. Such a statement in the standard would solve the non-volatile C compatibility issue, and would be consistent with a user-implemented operator=.
Another possible approach is to state that primitive assignment operators have two results, an lvalue and a corresponding "after-store" rvalue. The rvalue result would be used when an rvalue is required, while the lvalue result would be used when an lvalue is required. However, this semantics is unsupportable for user-defined assignment operators, or at least inconsistent with all implementations that I know of. I would not enjoy trying to write such two-faced semantics.
Erwin Unruh:
The intent was for assignments to behave the same as in C. Unfortunately the change of the result to lvalue did not keep that. An "lvalue of type int" has no "int" value! So there is a difference between intent and the standard's wording.
So we have one of several choices:
I think the last one has the least impact on existing programs, but it is an ugly solution.
Andrew Koenig:
Whatever we may have intended, I do not think that there is any clean way of making
volatile int v;
int i;
i = v = 42;
have the same semantics in C++ as it does in C. Like it or not, the
subexpression v = 42 has the type ``reference to volatile int,''
so if this statement has any meaning at all, the meaning must be to store 42
in v and then fetch the value of v to assign it to i.
Indeed, if v is volatile, I cannot imagine a conscientious programmer writing a statement such as this one. Instead, I would expect to see
v = 42;
i = v;
if the intent is to store 42 in v and then fetch the (possibly
changed) value of v, or
v = 42;
i = 42;
if the intent is to store 42 in both v and i.
What I do want is to ensure that expressions such as ``i = v = 42'' have well-defined semantics, as well as expressions such as (i = v) = 42 or, more realistically, (i += v) += 42 .
I wonder if the following resolution is sufficient:
Append to 5.17 expr.ass paragraph 1:
There is a sequence point between assigning the new value to the left operand and yielding the result of the assignment expression.
I believe that this proposal achieves my desired effect of not constraining when j is incremented in x[j++] = y, because I don't think there is a constraint on the relative order of incrementing j and executing the assignment. However, I do think it allows expressions such as (i += v) += 42, although with different semantics from C if v is volatile.
Notes on 10/01 meeting:
There was agreement that adding a sequence point is probably the right solution.
Notes from the 4/02 meeting:
The working group reaffirmed the sequence-point solution, but we will look for any counter-examples where efficiency would be harmed.
For drafting, we note that ++x is defined in 5.3.2 expr.pre.incr as equivalent to x+=1 and is therefore affected by this change. x++ is not affected. Also, we should update any list of all sequence points.
Notes from October 2004 meeting:
Discussion centered around whether a sequence point “between assigning the new value to the left operand and yielding the result of the expression” would require completion of all side effects of the operand expressions before the value of the assignment expression was used in another expression. The consensus opinion was that it would, that this is the definition of a sequence point. Jason Merrill pointed out that adding a sequence point after the assignment is essentially the same as rewriting
b += a
as
b += a, b
Clark Nelson expressed a desire for something like a “weak” sequence point that would force the assignment to occur but that would leave the side effects of the operands unconstrained. In support of this position, he cited the following expression:
j = (i = j++)
With the proposed addition of a full sequence point after the assignment to i, the net effect is no change to j. However, both g++ and MSVC++ behave differently: if the previous value of j is 5, the value of the expression is 5 but j gets the value 6.
Clark Nelson will investigate alternative approaches and report back to the working group.
There are at least a couple of problems in the description of the various id-expressions in 5.1 expr.prim:
Paragraph 4 embodies an incorrect assumption about the syntax of qualified-ids:
The operator :: followed by an identifier, a qualified-id, or an operator-function-id is a primary-expression.
The problem here is that the :: is actually part of the syntax of qualified-id; consequently, “:: followed by... a qualified-id” could be something like “:: ::i,” which is ill-formed. Presumably this should say something like, “A qualified-id with no nested-name-specifier is a primary-expression.”
More importantly, some kinds of id-expressions are not described by 5.1 expr.prim. The structure of this section is that the result, type, and lvalue-ness are specified for each of the cases it covers:
paragraph 4 deals with qualified-ids that have no nested-name-specifier
paragraph 7 deals with bare identifiers and with qualified-ids containing a nested-name-specifier that names a class
paragraph 8 deals with qualified-ids containing a nested-name-specifier that names a namespace
This treatment leaves unspecified all the non-identifier unqualified-ids (operator-function-id, conversion-function-id, and template-id), as well as (perhaps) “:: template-id” (it's not clear whether the “:: followed by a qualified-id” case is supposed to apply to template-ids or not). Note also that the proposed resolution of issue 301 slightly exacerbates this problem by removing the form of operator-function-id that contains a tmeplate-argument-list; as a result, references like “::operator+<X>” are no longer covered in 5.1 expr.prim.
The resolution to issue 195 makes “converting a pointer to a function into a pointer to an object type or vice versa” conditionally-supported behavior. In doing so, however, it overlooked the fact that void is not an “object type” (3.9 basic.types paragraph 9). The wording should be amended to allow conversion to and from void* types.
At least a couple of places in the IS state that indirection through a null pointer produces undefined behavior: 1.9 intro.execution paragraph 4 gives "dereferencing the null pointer" as an example of undefined behavior, and 8.3.2 dcl.ref paragraph 4 (in a note) uses this supposedly undefined behavior as justification for the nonexistence of "null references."
However, 5.3.1 expr.unary.op paragraph 1, which describes the unary "*" operator, does not say that the behavior is undefined if the operand is a null pointer, as one might expect. Furthermore, at least one passage gives dereferencing a null pointer well-defined behavior: 5.2.8 expr.typeid paragraph 2 says
If the lvalue expression is obtained by applying the unary * operator to a pointer and the pointer is a null pointer value (4.10 conv.ptr), the typeid expression throws the bad_typeid exception (18.5.3 lib.bad.typeid).
This is inconsistent and should be cleaned up.
Bill Gibbons:
At one point we agreed that dereferencing a null pointer was not undefined; only using the resulting value had undefined behavior.
For example:
char *p = 0;
char *q = &*p;
Similarly, dereferencing a pointer to the end of an array should be allowed as long as the value is not used:
char a[10];
char *b = &a[10]; // equivalent to "char *b = &*(a+10);"
Both cases come up often enough in real code that they should be allowed.
Mike Miller:
I can see the value in this, but it doesn't seem to be well reflected in the wording of the Standard. For instance, presumably *p above would have to be an lvalue in order to be the operand of "&", but the definition of "lvalue" in 3.10 basic.lval paragraph 2 says that "an lvalue refers to an object." What's the object in *p? If we were to allow this, we would need to augment the definition to include the result of dereferencing null and one-past-the-end-of-array.
Tom Plum:
Just to add one more recollection of the intent: I was very happy when (I thought) we decided that it was only the attempt to actually fetch a value that creates undefined behavior. The words which (I thought) were intended to clarify that are the first three sentences of the lvalue-to-rvalue conversion, 4.1 conv.lval:
An lvalue (3.10 basic.lval) of a non-function, non-array type T can be converted to an rvalue. If T is an incomplete type, a program that necessitates this conversion is ill-formed. If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.
In other words, it is only the act of "fetching", of lvalue-to-rvalue conversion, that triggers the ill-formed or undefined behavior. Simply forming the lvalue expression, and then for example taking its address, does not trigger either of those errors. I described this approach to WG14 and it may have been incorporated into C 1999.
Mike Miller:
If we admit the possibility of null lvalues, as Tom is suggesting here, that significantly undercuts the rationale for prohibiting "null references" -- what is a reference, after all, but a named lvalue? If it's okay to create a null lvalue, as long as I don't invoke the lvalue-to-rvalue conversion on it, why shouldn't I be able to capture that null lvalue as a reference, with the same restrictions on its use?
I am not arguing in favor of null references. I don't want them in the language. What I am saying is that we need to think carefully about adopting the permissive approach of saying that it's all right to create null lvalues, as long as you don't use them in certain ways. If we do that, it will be very natural for people to question why they can't pass such an lvalue to a function, as long as the function doesn't do anything that is not permitted on a null lvalue.
If we want to allow &*(p=0), maybe we should change the definition of "&" to handle dereferenced null specially, just as typeid has special handling, rather than changing the definition of lvalue to include dereferenced nulls, and similarly for the array_end+1 case. It's not as general, but I think it might cause us fewer problems in the long run.
Notes from the October 2003 meeting:
See also issue 315, which deals with the call of a static member function through a null pointer.
We agreed that the approach in the standard seems okay: p = 0; *p; is not inherently an error. An lvalue-to-rvalue conversion would give it undefined behavior.
Proposed resolution (October, 2004):
(Note: the resolution of issue 453 also resolves part of this issue.)
Add the indicated words to 3.10 basic.lval paragraph 2:
An lvalue refers to an object or function or is an empty lvalue (5.3.1 expr.unary.op).
Add the indicated words to 5.3.1 expr.unary.op paragraph 1:
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points, if any. If the pointer is a null pointer value (4.10 conv.ptr) or points one past the last element of an array object (5.7 expr.add), the result is an empty lvalue and does not refer to any object or function. An empty lvalue is not modifiable. If the type of the expression is “pointer to T,” the type of the result is “T.” [Note: a pointer to an incomplete type (other than cv void) can be dereferenced. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to an rvalue, see 4.1 conv.lval.—end note]
Add the indicated words to 4.1 conv.lval paragraph 1:
If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, or if the lvalue is an empty lvalue (5.3.1 expr.unary.op), a program that necessitates this conversion has undefined behavior.
Change 1.9 intro.execution as indicated:
Certain other operations are described in this International Standard as undefined (for example, the effect ofdereferencing the null pointerdivision by zero).
Note (March, 2005):
The 10/2004 resolution interacts with the resolution of issue 73. We added wording to 3.9.2 basic.compound paragraph 3 to the effect that a pointer containing the address one past the end of an array is considered to “point to” another object of the same type that might be located there. The 10/2004 resolution now says that it would be undefined behavior to use such a pointer to fetch the value of that object. There is at least the appearance of conflict here; it may be all right, but it at needs to be discussed further.
Notes from the April, 2005 meeting:
The CWG agreed that there is no contradiction between this direction and the resolution of issue 73. However, “not modifiable” is a compile-time concept, while in fact this deals with runtime values and thus should produce undefined behavior instead. Also, there are other contexts in which lvalues can occur, such as the left operand of . or .*, which should also be restricted. Additional drafting is required.
Consider the following example:
template <typename T>
const T* f(bool b) {
static T t1 = T();
static const T t2 = T();
return &(b ? t1 : t2); // error?
}
According to 5.16 expr.cond, this function is well-formed if T is a class type and ill-formed otherwise. If the second and third operands of a conditional expression are lvalues of the same class type except for cv-qualification, the result of the conditional expression is an lvalue; if they are lvalues of the same non-class type except for cv-qualification, the result is an rvalue.
This difference seems gratuitous and should be removed.
There appear to be two different specifications for when aliasing is permitted. One is in 3.10 basic.lval paragraph 15:
If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined
the dynamic type of the object,
a cv-qualified version of the dynamic type of the object,
a type similar (as defined in 4.4 conv.qual) to the dynamic type of the object,
a type that is the signed or unsigned type corresponding to the dynamic type of the object,
a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union),
a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
a char or unsigned char type.
There is also a much more restrictive specification in 5.17 expr.ass paragraph 8:
If the value being stored in an object is accessed from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is undefined.
This affects, for example, the definedness of operations on union members: when may a value be stored into one union member and accessed via another.
It should be noted that this conflict existed in C90 and is unchanged in C99 (see, for example, section 6.5 paragraph 7 and section 6.5.16.1 paragraph 3 of ISO/IEC 9899:1999, which directly parallel the sections cited above).
Notes from the October, 2006 meeting:
This issue is based on a misunderstanding of the intent of the wording in 5.17 expr.ass paragraph 8. Instead of being a general statement about aliasing, it's describing the situation in which the source of the value being assigned is storage that overlaps the storage of the target object. The proposed resolution should make that clearer rather than changing the specification.
Consider:
template <int* p> struct S {
static const int I = 3;
};
int i;
int a[S<&i>::I];
Clearly this should be valid, but a pedantic reading of 5.19 expr.const would suggest that this is invalid because “&i” is not permitted in integral constant expressions.
Proposed resolution (October, 2005):
Change the last sentence of 5.19 expr.const paragraph 1 as indicated:
In particular, except in non-type template-arguments or sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call, or comma operators shall not be used.
(Note: the same text is changed by the resolution of issue 367.)
Notes from April, 2006 meeting:
The proposed resolution could potentially be read as saying that the prohibited operations and operators would be permitted in integral constant expressions that are non-type template-arguments. John Spicer is investigating an alternate approach, to say that expressions in non-type template arguments are not part of the expression in which the template-id appears (in contrast to the operand of sizeof, which is part of the containing expression).
The Standard does not fully describe the syntax to be used when a member of an explicitly-specialized member class or member class template is defined in namespace scope. 14.7.3 temp.expl.spec paragraph 4 says that the “explicit specialization syntax” (presumably referring to “template<>”) is not used in defining a member of an explicit specialization when a class template is explicitly specialized as a class. However, nothing is said anywhere about how to define a member of a specialization when:
the entity being specialized is a class (member of a template class) rather than a class template.
the result of the specialization is a class template rather than a class (cf 14.7.3 temp.expl.spec paragraph 18, which describes this case as a “member template that... remain[s] unspecialized”).
(See paper J16/05-0148 = WG21 N1888 for further details, including a survey of existing implementation practice.)
Notes from the October, 2005 meeting:
The CWG felt that the best approach, balancing consistency with implementation issues and existing practice, would be to require that template<> be used when defining members of all explicit specializations, including those currently covered by 14.7.3 temp.expl.spec paragraph 4.
According to 6.6 stmt.jump paragraph 2,
On exit from a scope (however accomplished), destructors (12.4 class.dtor) are called for all constructed objects with automatic storage duration (3.7.2 basic.stc.auto) (named objects or temporaries) that are declared in that scope, in the reverse order of their declaration.
This wording is problematic for temporaries and for parameters. First, temporaries are not "declared," so this requirement does not apply to them, in spite of the assertion in the quoted text that it does.
Second, although the parameters of a function are declared in the called function, they are constructed and destroyed in the calling context, and the order of evaluation of the arguments is unspecified (cf 5.2.2 expr.call paragraphs 4 and 8). The order of destruction of the parameters might, therefore, be different from the reverse order of their declaration.
Notes from 04/01 meeting:
Any resolution of this issue should be careful not to introduce requirements that are redundant or in conflict with those of other parts of the IS. This is especially true in light of the pending issues with respect to the destruction of temporaries (see issues 86, 124, 199, and 201). If possible, the wording of a resolution should simply reference the relevant sections.
It was also noted that the temporary for a return value is also destroyed "out of order."
Note that issue 378 picks a nit with the wording of this same paragraph.
Here's an example:
typedef struct S { ... } S;
void fs(S *x) { ... }
The big question is, to what declaration does the reference to identifier S actually refer? Is it the S that's declared as a typedef name, or the S that's declared as a class name (or in C terms, as a struct tag)? (In either case, there's clearly only one type to which it could refer, since a typedef declaration does not introduce a new type. But the debugger apparently cares about more than just the identity of the type.)
Here's a classical, closely related example:
struct stat { ... };
int stat();
... stat( ... ) ...
Does the identifier stat refer to the class or the function? Obviously, in C, you can't refer to the struct tag without using the struct keyword, because it is in a different name space, so the reference must be to the function. In C++, the reference is also to the function, but for a completely different reason.
Now in C, typedef names and function names are in the same name space, so the natural extrapolation would be that, in the first example, S refers to the typedef declaration, as it would in C. But C++ is not C. For the purposes of this discussion, there are two important differences between C and C++
The first difference is that, in C++, typedef names and class names are not in separate name spaces. On the other hand, according to section 3.3.7 basic.scope.hiding (Name hiding), paragraph 2:
A class name (9.1) or enumeration name (7.2) can be hidden by the name of an object, function, or enumerator declared in the same scope. If a class or enumeration name and an object, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the object, function, or enumerator name is visible.
Please consider carefully the phrase I have highlighted, and the fact that a typedef name is not the name of an object, function or enumerator. As a result, this example:
struct stat { ... };
typedef int stat;
Which would be perfectly legal in C, is disallowed in C++, both implicitly (see the above quote) and explicitly (see section 7.1.3 dcl.typedef (The typedef specifier), paragraph 3):
In a given scope, a typedef specifier shall not be used to redefine the name of any type declared in that scope to refer to a different type. Similarly, in a given scope, a class or enumeration shall not be declared with the same name as a typedef-name that is declared in that scope and refers to a type other than the class or enumeration itself.
From which we can conclude that in C++ typedef names do not hide class names declared in the same scope. If they did, the above example would be legal.
The second difference is that, in C++, a typedef name that refers to a class is a class-name; see 7.1.3 dcl.typedef paragraph 4:
A typedef-name that names a class is a class-name(9.1). If a typedef-name is used following the class-key in an elaborated-type-specifier (7.1.5.3) or in the class-head of a class declaration (9), or is used as the identifier in the declarator for a constructor or destructor declaration (12.1, 12.4), the program is ill-formed.
This implies, for instance, that a typedef-name referring to a class can be used in a nested-name-specifier (i.e. before :: in a qualified name) or following ~ to refer to a destructor. Note that using a typedef-name as a class-name in an elaborated-type-specifier is not allowed. For example:
struct X { };
typedef struct X X2;
X x; // legal
X2 x2; // legal
struct X sx; // legal
struct X2 sx2; // illegal
The final relevant piece of the standard is 7.1.3 dcl.typedef paragraph 2:
In a given scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers.
This of course is what allows the original example, to which let us now return:
typedef struct S { ... } S;
void fs(S *x) { ... }
The question, again is, to which declaration of S does the reference actually refer? In C, it would clearly be to the second, since the first would be accessible only by using the struct keyword. In C++, if typedef names hid class names declared in the same scope, the answer would be the same. But we've already seen that typedef names do not hide class names declared in the same scope.
So to which declaration does the reference to S refer? The answer is that it doesn't matter. The second declaration of S, which appears to be a declaration of a typedef name, is actually a declaration of a class name (7.1.3 dcl.typedef paragraph 4), and as such is simply a redeclaration. Consider the following example:
typedef int I, I; extern int x, x; void f(), f();
To which declaration would a reference to I, x or f refer? It doesn't matter, because the second declaration of each is really just a redeclaration of the thing declared in the first declaration. So to save time, effort and complexity, the second declaration of each doesn't add any entry to the compiler's symbol table.
Note (March, 2005):
Matt Austern: Is this legal?
struct A { };
typedef struct A A;
struct A* p;
Am I right in reading the standard [to say that this is ill-formed]? On the one hand it's a nice uniform rule. On the other hand, it seems likely to confuse users. Most people are probably used to thinking that 'typedef struct A A' is a null operation, and, if this code really is illegal, it would seem to be a gratuitous C/C++ incompatibility.
Mike Miller: I think you're right. 7.1.3 dcl.typedef paragraph 1:
A name declared with the typedef specifier becomes a typedef-name.
7.1.3 dcl.typedef paragraph 2:
In a given non-class scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers.
After the typedef declaration in the example, the name X has been “redefined” — it is no longer just a class-name, it has been “redefined” to be a typedef-name (that, by virtue of the fact that it refers to a class type, is also a class-name).
John Spicer: In C, and originally in C++, an elaborated-type-specifier did not consider typedef names, so “struct X* x” would find the class and not the typedef.
When C++ was changed to make typedefs visible to elaborated-type-specifier lookups, I believe this issue was overlooked and inadvertantly made ill-formed.
I suspect we need add text saying that if a given scope contains both a class/enum and a typedef, that an elaborated type specifier lookup finds the class/enum.
Mike Miller: I'm a little uncomfortable with this approach. The model we have for declaring a typedef in the same scope as a class/enum is redefinition, not hiding (like the “struct stat” hack). This approach seems to assume that the typedef hides the class/enum, which can then be found by an elaborated-type-specifier, just as if it were hidden by a variable, function, or enumerator.
Also, this approach reduces but doesn't eliminate the incompatibility with C. For example:
struct S { };
{
typedef struct S S;
struct S* p; // still ill-formed
}
My preference would be for something following the basic principle that declaring a typedef-name T in a scope where T already names the type designated by the typedef should have no effect on whether an elaborated-type-specifier in that or a nested scope is well-formed or not. Another way of saying that is that a typedef-name that designates a same-named class or enumeration in the same or a containing scope is transparent with respect to elaborated-type-specifiers.
John Spicer: This strikes me as being a rather complicated solution. When we made the change to make typedefs visible to elaborated-type-specifiers we did so knowing it would make some C cases ill-formed, so this does not bother me. We've lived with the C incompatibility for many years now, so I don't personally feel a need to undo it. I also don't like the fact that you have to essentially do the old-style elaborated-type-specifier lookup to check the result of the lookup that found the typedef.
I continue to prefer the direction I described earlier where if a given scope contains both a class/enum and a typedef, that an elaborated-type-specifier lookup finds the class/enum.
Notes from the April, 2005 meeting:
The CWG agreed with John Spicer's approach, i.e., permitting a typedef-name to be used in an elaborated-type-specifier only if it is declared in the same scope as the class or enumeration it names.
7.1.3 dcl.typedef paragraph 1 says,
The typedef specifier shall not be used in a function-definition (8.4 dcl.fct.def)...
Does this mean that the following is ill-formed?
void f() {
typedef int INT;
}
7.3.1.2 namespace.memdef paragraph 3 says,
If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace... When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered.It is not clear from this passage how to determine whether an entity is "first declared" in a friend declaration. One question is whether a using-declaration influences this determination. For instance:
void foo();
namespace A{
using ::foo;
class X{
friend void foo();
};
}
Is the friend declaration a reference to ::foo or
a different foo?
Part of the question involves determining the meaning of the word "synonym" in 7.3.3 namespace.udecl paragraph 1:
A using-declaration introduces a name into the declarative region in which the using-declaration appears. That name is a synonym for the name of some entity declared elsewhere.Is "using ::foo;" the declaration of a function or not?
More generally, the question is how to describe the lookup of the name in a friend declaration.
John Spicer: When a declaration specifies an unqualified name, that name is declared, not looked up. There is a mechanism in which that declaration is linked to a prior declaration, but that mechanism is not, in my opinion, via normal name lookup. So, the friend always declares a member of the nearest namespace scope regardless of how that name may or may not already be declared there.
Mike Miller: 3.4.1 basic.lookup.unqual paragraph 7 says:
A name used in the definition of a class X outside of a member function body or nested class definition shall be declared in one of the following ways:... [Note: when looking for a prior declaration of a class or function introduced by a friend declaration, scopes outside of the innermost enclosing namespace scope are not considered.]The presence of this note certainly implies that this paragraph describes the lookup of names in friend declarations.
John Spicer: It most certainly does not. If that section described the friend lookup it would yield the incorrect results for the friend declarations of f and g below. I don't know why that note is there, but it can't be taken to mean that that is how the friend lookup is done.
void f(){}
void g(){}
class B {
void g();
};
class A : public B {
void f();
friend void f(); // ::f not A::f
friend void g(); // ::g not B::g
};
Mike Miller: If so, the lookups for friend functions and classes behave differently. Consider the example in 3.4.4 basic.lookup.elab paragraph 3:
struct Base {
struct Data; // OK: declares nested Data
friend class Data; // OK: nested Data is a friend
};
If the friend declaration is not a reference to ::foo, there is a related but separate question: does the friend declaration introduce a conflicting (albeit "invisible") declaration into namespace A, or is it simply a reference to an as-yet undeclared (and, in this instance, undeclarable) A::foo? Another part of the example in 3.4.4 basic.lookup.elab paragraph 3 is related:
struct Data {
friend struct Glob; // OK: Refers to (as yet) undeclared Glob
// at global scope.
};
John Spicer: You can't refer to something that has not yet been declared. The friend is a declaration of Glob, it just happens to declare it in a such a way that its name cannot be used until it is redeclared.
(A somewhat similar question has been raised in connection with issue 36. Consider:
namespace N {
struct S { };
}
using N::S;
struct S; // legal?
According to 9.1 class.name paragraph 2,
A declaration consisting solely of class-key identifier ; is either a redeclaration of the name in the current scope or a forward declaration of the identifier as a class name.
Should the elaborated type declaration in this example be considered a redeclaration of N::S or an invalid forward declaration of a different class?)
(See also issues 95, 136, 139, 143, 165, and 166, as well as paper J16/00-0006 = WG21 N1229.)
Here's an interesting case:
int f;
namespace N {
extern "C" void f () {}
}
As far as I can tell, this is not precluded by the ODR section
(3.2
basic.def.odr)
or the extern "C" section (7.5
dcl.link).
However, I believe many compilers
do not do name mangling on variables and (more-or-less by definition)
on extern "C" functions. That means the variable and the function
in the above end up having the same name at link time. EDG's front
end, g++, and the Sun compiler all get essentially the same error,
which is a compile-time assembler-level error because of the
duplicate symbols (in other words, they fail to check for this, and the
assembler complains). MSVC++ 7 links the program without error,
though I'm not sure how it is interpreted.
Do we intend for this case to be valid? If not, is it a compile time error (required), or some sort of ODR violation (no diagnostic required)? If we do intend for it to be valid, are we forcing many implementations to break binary compatibility by requiring them to mangle variable names?
Personally, I favor a compile-time error, and an ODR prohibition on such things in separate translation units.
Notes from the 4/02 meeting:
The working group agreed with the proposal. We feel a diagnostic should be required for declarations within one translation unit. We also noted that if the variable in global scope in the above example were declared static we would still expect an error.
Relevant sections in the standard are 7.5 dcl.link paragraph 6 and 3.5 basic.link paragraph 9. We feel that the definition should be written such that the entities in conflict are not "the same entity" but merely not allowed together.
Additional note (September, 2004)
This problem need not involve a conflict between a function and a variable; it can also arise with two variable declarations:
int x;
namespace N {
extern "C" int x;
}
The wording of 7.5 dcl.link paragraph 5 is suspect:
If two declarations of the same function or object specify different linkage-specifications (that is, the linkage-specifications of these declarations specify different string-literals), the program is ill-formed if the declarations appear in the same translation unit, and the one definition rule (3.2) applies if the declarations appear in different translation units.
But what if only one of the declarations has a linkage-specification, while the other is left with the default C++ linkage? Shouldn't this restriction be phrased in terms of the functions’ or objects’ language linkage rather than linkage-specifications?
(Additional note [wmm]: Is the ODR the proper vehicle for enforcing this requirement? This is dealing with declarations, not necessarily definitions. Shouldn't this say “ill-formed, no diagnostic required” instead of some vague reference to the ODR?)
This case is nonstandard by 8.3 dcl.meaning paragraph 1 (there is a requirement that the specialization first be declared within the namespace before being defined outside of the namespace), but probably should be allowed:
namespace NS1 {
template<class T>
class CDoor {
public:
int mtd() { return 1; }
};
}
template<> int NS1::CDoor<char>::mtd()
{
return 0;
}
Notes from October 2002 meeting:
There was agreement that we wanted to allow this.
8.3.2 dcl.ref paragraph 4 says:
A reference shall be initialized to refer to a valid object or function. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the "object" obtained by dereferencing a null pointer, which causes undefined behavior ...]
What is a "valid" object? In particular the expression "valid object" seems to exclude uninitialized objects, but the response to Core Issue 363 clearly says that's not the intent. This is an example (overloading construction on constness of *this) by John Potter, which I think is supposed to be legal C++ though it binds references to objects that are not initialized yet:
struct Fun {
int x, y;
Fun (int x, Fun const&) : x(x), y(42) { }
Fun (int x, Fun&) : x(x), y(0) { }
};
int main () {
const Fun f1 (13, f1);
Fun f2 (13, f2);
cout << f1.y << " " << f2.y << "\n";
}
Suggested resolution: Changing the final part of 8.3.2 dcl.ref paragraph 4 to:
A reference shall be initialized to refer to an object or function.
From its point of declaration on (see 3.3.1
basic.scope.pdecl)
its name is an lvalue
which refers to that object or function. The reference may be
initialized to refer to an uninitialized object but, in that case,
it is usable in limited ways (3.8
basic.life, paragraph 6)
[Note: On the other hand, a declaration like this:
int & ref = *(int*)0;
is ill-formed because ref will not refer to any object or function
]
I also think a "No diagnostic is required." would better be added (what about something like int& r = r; ?)
Proposed Resolution (October, 2004):
(Note: the following wording depends on the proposed resolution for issue 232.)
Change 8.3.2 dcl.ref paragraph 4 as follows:
A reference shall be initialized to refer to a valid object or function.If an lvalue to which a reference is directly bound designates neither an existing object or function of an appropriate type (8.5.3 dcl.init.ref), nor a region of memory of suitable size and alignment to contain an object of the reference's type (1.8 intro.object, 3.8 basic.life, 3.9 basic.types), the behavior is undefined. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the“object”empty lvalue obtained by dereferencing a null pointer, whichcauses undefined behavior. Asdoes not designate an object or function. Also, as described in 9.6 class.bit, a reference cannot be bound directly to a bit-field. ]The name of a reference shall not be used in its own initializer. Any other use of a reference before it is initialized results in undefined behavior. [Example:
int& f(int&); int& g(); extern int& ir3; int* ip = 0; int& ir1 = *ip; // undefined behavior: null pointer int& ir2 = f(ir3); // undefined behavior: ir3 not yet initialized int& ir3 = g(); int& ir4 = f(ir4); // ill-formed: ir4 used in its own initializer—end example]
Rationale: The proposed wording goes beyond the specific concerns of the issue, primarily in response to messages 10498-10506 on the core reflector. It was noted that, while the current wording makes cases like int& r = r; ill-formed (because r in the initializer does not "refer to a valid object"), an inappropriate initialization can only be detected, if at all, at runtime and thus "undefined behavior" is a more appropriate treatment. Nevertheless, it was deemed desirable to continue to require a diagnostic for obvious compile-time cases.
It was also noted that the current Standard does not say anything about using a reference before it is initialized. It seemed reasonable to address both of these concerns in the same wording proposed to resolve this issue.
Notes from the April, 2005 meeting:
The CWG decided that whether to require an implementation to diagnose initialization of a reference to itself should be handled as a separate issue (504) and also suggested referring to “storage” instead of “memory” (because 1.8 intro.object defines an object as a “region of storage”).
Proposed Resolution (April, 2005):
(Note: the following wording depends on the proposed resolution for issue 232.)
Change 8.3.2 dcl.ref paragraph 4 as follows:
A reference shall be initialized to refer to a valid object or function.If an lvalue to which a reference is directly bound designates neither an existing object or function of an appropriate type (8.5.3 dcl.init.ref), nor a region of storage of suitable size and alignment to contain an object of the reference's type (1.8 intro.object, 3.8 basic.life, 3.9 basic.types), the behavior is undefined. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the“object”empty lvalue obtained by dereferencing a null pointer, whichcauses undefined behavior. Asdoes not designate an object or function. Also, as described in 9.6 class.bit, a reference cannot be bound directly to a bit-field. ]Any use of a reference before it is initialized results in undefined behavior. [Example:
int& f(int&); int& g(); extern int& ir3; int* ip = 0; int& ir1 = *ip; // undefined behavior: null pointer int& ir2 = f(ir3); // undefined behavior: ir3 not yet initialized int& ir3 = g(); int& ir4 = f(ir4); // undefined behavior: ir4 used in its own initializer—end example]
Note (February, 2006):
The word “use” in the last paragraph of the proposed resolution was intended to refer to the description in 3.2 basic.def.odr paragraph 2. However, that section does not define what it means for a reference to be “used,” dealing only with objects and functions. Additional drafting is required to extend 3.2 basic.def.odr paragraph 2 to apply to references.
EDG rejects this code:
template <typename T>
struct S {};
void f (S<int (*)[]>);
G++ accepts it.
This is another case where the standard isn't very clear:
The language from 8.3.5 dcl.fct is:
If the type of a parameter includes a type of the form "pointer to array of unknown bound of T" or "reference to array of unknown bound of T," the program is ill-formed.Since "includes a type" is not a term defined in the standard, we're left to guess what this means. (It would be better if this were a recursive definition, the way a type theoretician would do it:
Notes from April 2003 meeting:
We agreed that the example should be allowed.
The following example appears to be well-formed, with the partial specialization matching the type of Y::f(), even though it is rejected by many compilers:
template<class T> struct X;
template<class R> struct X< R() > {
};
template<class F, class T> void test(F T::* pmf) {
X<F> x;
}
struct Y {
void f() {
}
};
int main() {
test( &Y::f );
}
However, 8.3.5 dcl.fct paragraph 4 says,
A cv-qualifier-seq shall only be part of the function type for a non-static member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration. The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. In the latter case, the cv-qualifiers are ignored.
This specification makes it impossible to write a partial specialization for a const member function:
template<class R> struct X<R() const> {
};
A template argument is not one of the permitted contexts for cv-qualification of a function type. This restriction should be removed.
Notes from the April, 2006 meeting:
During the meeting the CWG was of the opinion that the “R() const” specialization would not match the const member function even if it were allowed and so classified the issue as NAD. Questions have been raised since the meeting, however, suggesting that the template argument in the partial specialization would, in fact, match the type of a const member function (see, for example, the very similar usage via typedefs in 9.3 class.mfct paragraph 9). The issue is thus being left open for renewed discussion at the next meeting.
According to the definition of value initialization (8.5 dcl.init paragraph 5), non-union class types without user-declared constructors are value-initialized by value-initializing each of their members rather than by executing the (generated) default constructor. However, a number of other items in the Standard are described in relationship to the execution of the constructor:
12.4 class.dtor paragraph 6: “Bases and members are destroyed in the reverse order of the completion of their constructor.” If a given base or member is value-initialized without running its constructor, is it destroyed? (For that matter, paragraph 10 refers to “constructed” objects; is an object that is value-initialized without invoking a constructor “constructed?”)
15.2 except.ctor paragraph 2: “An object that is partially constructed or partially destroyed will have destructors executed for all of its fully constructed subobjects, that is, for subobjects for which the constructor has completed execution...”
3.8 basic.life paragraph 1: The lifetime of an object begins when “the constructor call has completed.” (In the TC1 wording — “if T is a class type with a non-trivial constructor (12.1 class.ctor), the constructor call has completed” — the lifetime of some value-initialized objects never began; in the current wording — “the constructor invoked to create the object is non-trivial” — the lifetime begins before any of the members are initialized.)
Proposed resolution (October, 2005):
Add the indicated words to 8.5 dcl.init paragraph 6:
A program that calls for default-initialization or value-initialization of an entity of reference type is ill-formed. If T is a cv-qualified type, the cv-unqualified version of T is used for these definitions of zero-initialization, default-initialization, and value-initialization. Even when value-initialization of an object does not call that object's constructor, the object is deemed to have been fully constructed once its initialization is complete and thus subject to provisions of this International Standard applying to “constructed” objects, objects “for which the constructor has completed execution,” etc.
Notes from April, 2006 meeting:
There was some concern about whether this wording covered (or needed to cover) cases where an object is “partially constructed.” Another approach might be simply to define value initialization to be “construction.” Returned to “drafting” status for further investigation.
The wording resulting from the resolution of issue 302 does not quite implement the intent of the issue. The revised wording of 3.2 basic.def.odr paragraph 2 is:
A default constructor for a class is used by default initialization or value initialization as specified in 8.5 dcl.init.
This sounds as if 8.5 dcl.init specifies how and under what circumstances value initialization uses a default constructor (which was, in fact, the case for default initialization in the original wording). However, the normative text there makes it plain that value initialization does not call the default constructor (the permission granted to implementations to call the default constructor for value initialization is in a non-normative footnote).
The example that occasioned this observation raises an additional question. Consider:
struct POD {
const int x;
};
POD data = POD();
According to the (revised) resolution of issue 302, this code is ill-formed because the implicitly-declared default constructor will be implicitly defined as a result of being used by value initialization (12.1 class.ctor paragraph 7), and the implicitly-defined constructor fails to initialize a const-qualified member (12.6.2 class.base.init paragraph 4). This seems unfortunate, because the (trivial) default constructor of a POD class is otherwise not used — default initialization applies only to non-PODs — and it is not actually needed in value initialization. Perhaps value initialization should be defined to “use” the default constructor only for non-POD classes? If so, both of these problems would be resolved by rewording the above-referenced sentence of 3.2 basic.def.odr paragraph 2 as:
A default constructor for a non-POD class is used by default initialization or value initializationas specified in(8.5 dcl.init).
Notes from the April, 2006 meeting:
The approach favored by the CWG was to leave 3.2 basic.def.odr unchanged and to add normative wording to 8.5 dcl.init indicating that it is unspecified whether the default constructor is called.
Notes from the October, 2006 meeting:
The CWG now prefers that it should not be left unspecified whether programs of this sort are well- or ill-formed; instead, the Standard should require that the default constructor be defined in such cases. Three possibilities of implementing this decision were discussed:
Change 3.2 basic.def.odr to state flatly that the default constructor is used by value initialization (removing the implication that 8.5 dcl.init determines the conditions under which it is used).
Change 8.5 dcl.init to specify that non-union class objects with no user-declared constructor are value-initialized by first zero-initializing the object and then calling the (implicitly-defined) default constructor, replacing the current specification of value-initializing each of its sub-objects.
Add a normative statement to 8.5 dcl.init that value-initialization causes the implicitly-declared default constructor to be implicitly defined, even if it is not called.
There is an inconsistency in the handling of references vs pointers in user defined conversions and overloading. The reason for that is that the combination of 8.5.3 dcl.init.ref and 4.4 conv.qual circumvents the standard way of ranking conversion functions, which was probably not the intention of the designers of the standard.
Let's start with some examples, to show what it is about:
struct Z { Z(){} };
struct A {
Z x;
operator Z *() { return &x; }
operator const Z *() { return &x; }
};
struct B {
Z x;
operator Z &() { return x; }
operator const Z &() { return x; }
};
int main()
{
A a;
Z *a1=a;
const Z *a2=a; // not ambiguous
B b;
Z &b1=b;
const Z &b2=b; // ambiguous
}
So while both classes A and B are structurally equivalent, there is a difference in operator overloading. I want to start with the discussion of the pointer case (const Z *a2=a;): 13.3.3 over.match.best is used to select the best viable function. Rule 4 selects A::operator const Z*() as best viable function using 13.3.3.2 over.ics.rank since the implicit conversion sequence const Z* -> const Z* is a better conversion sequence than Z* -> const Z*.
So what is the difference to the reference case? Cv-qualification conversion is only applicable for pointers according to 4.4 conv.qual. According to 8.5.3 dcl.init.ref paragraphs 4-7 references are initialized by binding using the concept of reference-compatibility. The problem with this is, that in this context of binding, there is no conversion, and therefore there is also no comparing of conversion sequences. More exactly all conversions can be considered identity conversions according to 13.3.3.1.4 over.ics.ref paragraph 1, which compare equal and which has the same effect. So binding const Z* to const Z* is as good as binding const Z* to Z* in terms of overloading. Therefore const Z &b2=b; is ambiguous. [13.3.3.1.4 over.ics.ref paragraph 5 and 13.3.3.2 over.ics.rank paragraph 3 rule 3 (S1 and S2 are reference bindings ...) do not seem to apply to this case]
There are other ambiguities, that result in the special treatment of references: Example:
struct A {int a;};
struct B: public A { B() {}; int b;};
struct X {
B x;
operator A &() { return x; }
operator B &() { return x; }
};
main()
{
X x;
A &g=x; // ambiguous
}
Since both references of class A and B are reference compatible with references of class A and since from the point of ranking of implicit conversion sequences they are both identity conversions, the initialization is ambiguous.
So why should this be a defect?
So overall I think this was not the intention of the authors of the standard.
So how could this be fixed? For comparing conversion sequences (and only for comparing) reference binding should be treated as if it was a normal assignment/initialization and cv-qualification would have to be defined for references. This would affect 8.5.3 dcl.init.ref paragraph 6, 4.4 conv.qual and probably 13.3.3.2 over.ics.rank paragraph 3.
Another fix could be to add a special case in 13.3.3 over.match.best paragraph 1.
The resolution of issue 372 leaves unclear whether the following are well-formed or not:
class C {
typedef int I; // private
template <int> struct X;
template <int> friend struct Y;
}
template <C::I> struct C::X { }; // C::I accessible to member?
template <C::I> struct Y { }; // C::I accessible to friend?
Presumably the answer to both questions is “yes,”, but the new wording does not address template-parameters.
After the adoption of the wording for extended friend declarations, we now have this new paragraph in 11.4 class.friend:
A friend declaration that does not declare a function shall have one of the following forms:
friend elaborated-type-specifier ;
friend simple-type-specifier ;
friend typename-specifier ;
But what about friend class templates? Should the following examples compile in C++0x?
template< template <class> class T >
struct A{ friend T; };
template< class > struct C;
struct B{ friend C; };
Does the restriction in 11.5 class.protected apply to upcasts across protected inheritance, too? For instance,
struct B {
int i;
};
struct I: protected B { };
struct D: I {
void f(I* ip) {
B* bp = ip; // well-formed?
bp->i = 5; // aka "ip->i = 5;"
}
};
I think the rationale for the 11.5 class.protected restriction applies equally well here — you don't know whether ip points to a D object or not, so D::f can't be trusted to treat the protected B subobject consistently with the policies of its actual complete object type.
The current treatment of “accessible base class” in 11.2 class.access.base paragraph 4 clearly makes the conversion from I* to B* well-formed. I think that's wrong and needs to be fixed. The rationale for the accessibility of a base class is whether “an invented public member” of the base would be accessible at the point of reference, although we obscured that a bit in the reformulation; it seems to me that the invented member ought to be considered a non-static member for this purpose and thus subject to 11.5 class.protected.
(See also issues 385 and 471.).Notes from October 2004 meeting:
The CWG tentatively agreed that casting across protective inheritance should be subject to the additional restriction in 11.5 class.protected.
Split off from issue 86.
Should binding a reference to the result of a "," operation whose second operand is a temporary extend the lifetime of the temporary?
const SFileName &C = ( f(), SFileName("abc") );
Notes from the March 2004 meeting:
We think the temporary should be extended.
Proposed resolution (October, 2004):
Change 12.2 class.temporary paragraph 2 as indicated:
... In all these cases, the temporaries created during the evaluation of the expression initializing the reference, except the temporary that is the overall result of the expression [Footnote: For example, if the expression is a comma expression (5.18 expr.comma) and the value of its second operand is a temporary, the reference is bound to that temporary.] and to which the reference is bound, are destroyed at the end of the full-expression in which they are created and in the reverse order of the completion of their construction...
[Note: this wording partially resolves issue 86. See also issue 446.]
Notes from the April, 2005 meeting:
The CWG suggested a different approach from the 10/2004 resolution, leaving 12.2 class.temporary unchanged and adding normative wording to 5.18 expr.comma specifying that, if the result of the second operand is a temporary, that temporary is the result of the comma expression as well.
Mark Mitchell raised a number of issues related to the resolution of issue 244 and of destructor lookup in general.
Issue 244 says:
... in a qualified-id of the form:::opt nested-name-specifieropt class-name :: ~ class-name
the second class-name is looked up in the same scope as the first.
But if the reference is "p->X::~X()", the first class-name is looked up in two places (normal lookup and a lookup in the class of p). Does the new wording mean:
This is a test case that illustrates the issue:
struct A {
typedef A C;
};
typedef A B;
void f(B* bp) {
bp->B::~B(); // okay B found by normal lookup
bp->C::~C(); // okay C found by class lookup
bp->B::~C(); // B found by normal lookup C by class -- okay?
bp->C::~B(); // C found by class lookup B by normal -- okay?
}
A second issue concerns destructor references when the class involved is a template class.
namespace N {
template <typename T> struct S {
~S();
};
}
void f(N::S<int>* s) {
s->N::S<int>::~S();
}
The issue here is that the grammar uses "~class-name" for destructor names, but in this case S is a template name when looked up in N.
Finally, what about cases like:
template <typename T> void f () {
typename T::B x;
x.template A<T>::template B<T>::~B();
}
When parsing the template definition, what checks can be done on "~B"?
Sandor Mathe adds :
The standard correction for issue 244 (now in DR status) is still incomplete.
Paragraph 5 of 3.4.3 basic.lookup.qual is not applicable for p->T::~T since there is no nested-name-specifier. Section 3.4.5 basic.lookup.classref describes the lookup of p->~T but p->T::~T is still not described. There are examples (which are non-normative) that illustrate this sort of lookup but they still leave questions unanswered. The examples imply that the name after ~ should be looked up in the same scope as the name before the :: but it is not stated. The problem is that the name to the left of the :: can be found in two different scopes. Consider the following:
struct S {
struct C { ~C() { } };
};
typedef S::C D;
int main() {
D* p;
p->C::~D(); // valid?
}
Should the destructor call be valid? If there were a nested name specifier, then D should be looked for in the same scope as C. But here, C is looked for in 2 different ways. First, it is searched for in the type of the left hand side of -> and it is also looked for in the lexical context. It is found in one or if both, they must match. So, C is found in the scope of what p points at. Do you only look for D there? If so, this is invalid. If not, you would then look for D in the context of the expression and find it. They refer to the same underlying destructor so this is valid. The intended resolution of the original defect report of the standard was that the name before the :: did not imply a scope and you did not look for D inside of C. However, it was not made clear whether this was to be resolved by using the same lookup mechanism or by introducing a new form of lookup which is to look in the left hand side if that is where C was found, or in the context of the expression if that is where C was found. Of course, this begs the question of what should happen when it is found in both? Consider the modification to the above case when C is also found in the context of the expression. If you only look where you found C, is this now valid because it is in 1 of the two scopes or is it invalid because C was in both and D is only in 1?
struct S {
struct C { ~C() { } };
};
typedef S::C D;
typedef S::C C;
int main() {
D* p;
p->C::~D(); // valid?
}
I agree that the intention of the committee is that the original test case in this defect is broken. The standard committee clearly thinks that the last name before the last :: does not induce a new scope which is our current interpretation. However, how this is supposed to work is not defined. This needs clarification of the standard.
Martin Sebor adds this example (September 2003), along with errors produced by the EDG front end:
namespace N {
struct A { typedef A NA; };
template <class T> struct B { typedef B NB; typedef T BT; };
template <template <class> class T> struct C { typedef C NC; typedef T<A> CA; };
}
void foo (N::A *p)
{
p->~NA ();
p->NA::~NA ();
}
template <class T>
void foo (N::B<T> *p)
{
p->~NB ();
p->NB::~NB ();
}
template <class T>
void foo (typename N::B<T>::BT *p)
{
p->~BT ();
p->BT::~BT ();
}
template <template <class> class T>
void foo (N::C<T> *p)
{
p->~NC ();
p->NC::~NC ();
}
template <template <class> class T>
void foo (typename N::C<T>::CA *p)
{
p->~CA ();
p->CA::~CA ();
}
Edison Design Group C/C++ Front End, version 3.3 (Sep 3 2003 11:54:55)
Copyright 1988-2003 Edison Design Group, Inc.
"t.cpp", line 16: error: invalid destructor name for type "N::B<T>"
p->~NB ();
^
"t.cpp", line 17: error: qualifier of destructor name "N::B<T>::NB" does not
match type "N::B<T>"
p->NB::~NB ();
^
"t.cpp", line 30: error: invalid destructor name for type "N::C<T>"
p->~NC ();
^
"t.cpp", line 31: error: qualifier of destructor name "N::C<T>::NC" does not
match type "N::C<T>"
p->NC::~NC ();
^
4 errors detected in the compilation of "t.cpp".
John Spicer: The issue here is that we're unhappy with the destructor names when doing semantic analysis of the template definitions (not during an instantiation).
My personal feeling is that this is reasonable. After all, why would you call p->~NB for a class that you just named as N::B<T> and you could just say p->~B?
Additional note (September, 2004)
The resolution for issue 244 removed the discussion of p->N::~S, where N is a namespace-name. However, the resolution did not make this construct ill-formed; it simply left the semantics undefined. The meaning should either be defined or the construct made ill-formed.
According to the Standard (although not implemented this way in most implementations), the following code exhibits non-intuitive behavior:
struct T {
operator short() const;
operator int() const;
};
short s;
void f(const T& t) {
s = t; // surprisingly calls T::operator int() const
}
The reason for this choice is 13.6 over.built paragraph 18:
For every triple (L, VQ, R), where L is an arithmetic type, VQ is either volatile or empty, and R is a promoted arithmetic type, there exist candidate operator functions of the form
VQ L& operator=(VQ L&, R);
Because R is a "promoted arithmetic type," the second argument to the built-in assignment operator is int, causing the unexpected choice of conversion function.
Suggested resolution: Provide built-in assignment operators for the unpromoted arithmetic types.
Related to the preceding, but not resolved by the suggested resolution, is the following problem. Given:
struct T {
operator int() const;
operator double() const;
};
I believe the standard requires the following assignment to be ambiguous (even though I expect that would surprise the user):
double x;
void f(const T& t) { x = t; }
The problem is that both of these built-in operator=()s exist (13.6 over.built paragraph 18):
double& operator=(double&, int);
double& operator=(double&, double);
Both are an exact match on the first argument and a user conversion on the second. There is no rule that says one is a better match than the other.
The compilers that I have tried (even in their strictest setting) do not give a peep. I think they are not following the standard. They pick double& operator=(double&, double) and use T::operator double() const.
I hesitate to suggest changes to overload resolution, but a possible resolution might be to introduce a rule that, for built-in operator= only, also considers the conversion sequence from the second to the first type. This would also resolve the earlier question.
It would still leave x += t etc. ambiguous -- which might be the desired behavior and is the current behavior of some compilers.
Notes from the 04/01 meeting:
The difference between initialization and assignment is disturbing. On the other hand, promotion is ubiquitous in the language, and this is the beginning of a very slippery slope (as the second report above demonstrates).
Static data members of template classes and of nested classes of template classes are not themselves templates but receive much the same treatment as template. For instance, 14 temp paragraph 1 says that templates are only "classes or functions" but implies that "a static data member of a class template or of a class nested within a class template" is defined using the template-declaration syntax.
There are many places in the clause, however, where static data members of one sort or another are overlooked. For instance, 14 temp paragraph 6 allows static data members of class templates to be declared with the export keyword. I would expect that static data members of (non-template) classes nested within class templates could also be exported, but they are not mentioned here.
Paragraph 8, however, overlooks static data members altogether and deals only with "templates" in defining the effect of the export keyword; there is no description of the semantics of defining a static data member of a template to be exported.
These are just two instances of a systematic problem. The entire clause needs to be examined to determine which statements about "templates" apply to static data members, and which statements about "static data members of class templates" also apply to static data members of non-template classes nested within class templates.
(The question also applies to member functions of template classes; see issue 217, where the phrase "non-template function" in 8.3.6 dcl.fct.default paragraph 4 is apparently intended not to include non-template member functions of template classes. See also issue 108, which would benefit from understanding nested classes of class templates as templates. Also, see issue 249, in which the usage of the phrase "member function template" is questioned.)
Notes from the 4/02 meeting:
Daveed Vandevoorde will propose appropriate terminology.
The following is the wording from 14.2 temp.names paragraphs 4 and 5 that discusses the use of the "template" keyword following . or -> and in qualified names.
class X {
public:
template<std::size_t> X* alloc();
template<std::size_t> static X* adjust();
};
template<class T> void f(T* p) {
T* p1 = p->alloc<200>();
// ill-formed: < means less than
T* p2 = p->template alloc<200>();
// OK: < starts template argument list
T::adjust<100>();
// ill-formed: < means less than
T::template adjust<100>();
// OK: < starts explicit qualification
}
—end example]
If a name prefixed by the keyword template is not the name of a member template, the program is ill-formed. [Note: the keyword template may not be applied to non-template members of class templates. ]
The whole point of this feature is to say that the "template" keyword is needed to indicate that a "<" begins a template parameter list in certain contexts. The constraints in paragraph 5 leave open to debate certain cases.First, I think it should be made more clear that the template name must be followed by a template argument list when the "template" keyword is used in these contexts. If we don't make this clear, we would have to add several semantic clarifications instead. For example, if you say "p->template f()", and "f" is an overload set containing both templates and nontemplates: a) is this valid? b) are the nontemplates in the overload set ignored? If the user is forced to write "p->template f<>()" it is clear that this is valid, and it is equally clear that nontemplates in the overload set are ignored. As this feature was added purely to provide syntactic guidance, I think it is important that it otherwise have no semantic implications.
I propose that paragraph 5 be modified to:
(See also issue 30 and document J16/00-0008 = WG21 N1231.)
Notes from 04/00 meeting:
The discussion of this issue revived interest in issues 11 and 109.
Notes from the October 2003 meeting:
We reviewed John Spicer's paper N1528 and agreed with his recommendations therein.
The EDG front-end accepts:
template <typename T>
struct A {
template <typename U>
struct B {};
};
template <typename T>
struct C : public A<T>::template B<T> {
};
It rejects this code if the base-specifier is spelled A<T>::B<T>.
However, the grammar for a base-specifier does not allow the template keyword.
Suggested resolution:
It seems to me that a consistent approach to the solution that looks like it will be adopted for issue 180 (which deals with the typename keyword in similar contexts) would be to assume that B is a template if it is followed by a "<". After all, an expression cannot appear in this context.Notes from the 4/02 meeting:
We agreed that template must be allowed in this context. The syntax needs to be changed. We also opened the related issue 343.
Consider this example:
class Foo {
public:
template< typename T > T *get();
};
template< typename U >
U *testFoo( Foo &foo ) {
return foo.get< U >(); //#1
}
I am under the impression that this should compile without requiring the insertion of the template keyword before get in the expression at //#1. This notion is supported by this note excerpted from 14.2 temp.names/5:
[Note: just as is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the expression on the left of the -> or ., or the nested-name-specifier is not dependent on a template parameter.]
But 14.2 temp.names/4 contains this text:
When the name of a member template specialization appears after . or -> in a postfix-expression, or after nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.
The only way that I can read this to support my assumption above is if I assume that the phrase postfix-expression is used twice above with different meaning. That is I read the first use as referring to the full expression while the second use refers to the subexpression preceding the operator. Is this the correct determination of intent? I find this text confusing. Would it be an improvement if the second occurrence of "postfix-expression" should be replaced by "the subexpression preceding the operator". Of course that begs the question "where is subexpression actually defined in the standard?"
John Spicer: I agree that the code should work, and that we should tweak the wording.
Is this allowed?
template<typename T>
struct X
{
static int s[];
int c;
};
template<typename T>
int X<T>::s[sizeof(X<T>)];
int* p = X<char>::s;
I have a compiler claiming that, for the purpose of sizeof(), X<T> is an incomplete type, when it tries to instantiate X<T>::s. It seems to me that X<char> should be considered complete enough for sizeof even though the size of s isn't known yet.
John Spicer: This is a problematic construct that is currently allowed but which I think should be disallowed.
I tried this with a number of compilers. None of which did the right thing. The EDG front end accepts it, but gives X<...>::s the wrong size.
It appears that most compilers evaluate the "declaration" part of the static data member definition only once when the definition is processed. The initializer (if any) is evaluated for each instantiation.
This problem is solvable, and if it were the only issue with incomplete arrays as template static data members, then it would make sense to solve it, but there are other problems.
The first problem is that the size of the static data member is only known if a template definition of the static data member is present. This is weird to start with, but it also means that sizes would not be available in general for exported templates.
The second problem concerns the rules for specialization. An explicit specialization for a template instance can be provided up until the point that a use is made that would cause an implicit instantiation. A reference like "sizeof(X<char>::s)" is not currently a reference that would cause an implicit instantiation of X<char>::s. This means you could use such a sizeof and later specialize the static data member with a different size, meaning the earlier sizeof gave the wrong result. We could, of course, change the "use" rules, but I'd rather see us require that static data members that are arrays have a size specified in the class or have a size based on their initializer.
Notes from the October 2003 meeting:
The example provided is valid according to the current standard. A static data member must be instantiated (including the processing of its initializer, if any) if there is any reference to it. The compiler need not, however, put out a definition in that translation unit. The standard doesn't really have a concept of a "partial instantiation" for a static data member, and although we considered adding that, we decided that to get all the size information that seems to be available one needs a full instantiation in any case, so there's no need for the concept of a partial instantiation.
Note (June, 2006):
Mark Mitchell suggested the following example:
template <int> void g();
template <typename T>
struct S {
static int i[];
void f();
};
template <typename T>
int S<T>::i[] = { 1 };
template <typename T>
void S<T>::f() {
g<sizeof (i) / sizeof (int)>();
}
template <typename T>
int S<int>::i[] = { 1, 2 };
Which g is called from S<int>::f()?
If the program is valid, then surely one would expect g<2> to be called.
If the program is valid, does S<T>::i have a non-dependent type in S<T>::f? If so, is it incomplete, or is it int[1]? (Here, int[1] would be surprising, since S<int>::i actually has type int[2].)
If the program is invalid, why?
For a simpler example, consider:
template <typename T>
struct S {
static int i[];
const int N = sizeof (i);
};
This is only valid if the type of i is dependent, meaning that the sizeof expression isn't evaluated until the class is instantiated.
In the following example, the template parameter in the partial specialization is non-deducible:
template <class T> struct A { typedef T U; };
template <class T> struct C { };
template <class T> struct C<typename A<T>::U> { };
Several compilers issue errors for this case, but there appears to be nothing in the Standard that would make this ill-formed; it simply seems that the partial specialization will never be matched, so the primary template will be used for all specializations. Should it be ill-formed?
Notes from the April, 2006 meeting:
It was noted that there are similar issues for constructors and conversion operators with non-deducible parameters, and that they should probably be dealt with similarly.
The Standard does not specify how member and nonmember function templates are to be ordered. This question arises with an example like the following:
struct A {
template<class T> void operator<<(T&);
};
template<class T> struct B { };
template<class T> void operator<<(A&, B<T>&);
int main() {
A a;
B<A> b;
a << b;
}
The two candidates for “a << b” are:
How should we treat the implicit this parameter of #1 and the explicit first parameter of #2?
Option 0: Make them unordered.
Option 1: If either function is a non-static member function, ignore any this parameter and ignore the first parameter of any non-member function. This option will select #2, as “B<T>&” is more specialized than “T&”.
Option 2: Treat the this parameter as if it were of reference to object type, and then perform comparison to the first parameter of the other function. The other function's first parameter will either be another this parameter, or it will be a by-value or by-reference object parameter. In the example above, this option will also select #2.
The difference between option 1 and option 2 can be seen in the following example:
struct A { };
template<class T> struct B {
template<typename R> int operator*(R&); // #1
};
template <typename T> int operator*(T&, A&); // #2
int main() {
A a;
B<A> b;
b * a;
}
Should this select #1, select #2, or be ambiguous? Option 1 will select #2, because “A&” is more specialized than “T&”. Option 2 will make this example ambiguous, because “B<A>&” is more specialized than “T&”.
If one were considering two non-member templates,
template <typename T> int operator*(T&, A&); // #2
template <typename T, typename R> int operator*(B<A>&, R&); // #3
the current rules would make these unordered. Option 2 thus seems more consistent with this existing behavior.
Notes from the April, 2006 meeting:
The group favored option 2.
Consider the following example:
template <class T> struct Outer {
struct Inner {
Inner* self();
};
};
template <class T> Outer<T>::Inner*
Outer<T>::Inner::self() { return this; }
According to 14.6 temp.res paragraph 3 (before the salient wording was inadvertently removed, see issue 559),
A qualified-id that refers to a type and in which the nested-name-specifier depends on a template-parameter (14.6.2 temp.dep) but does not refer to a member of the current instantiation (14.6.2.1 temp.dep.type) shall be prefixed by the keyword typename to indicate that the qualified-id denotes a type, forming a typename-specifier.
Because Outer<T>::Inner is a member of the current instantiation, the Standard does not currently require that it be prefixed with typename when it is used in the return type of the definition of the self() member function. However, it is difficult to parse this definition correctly without knowing that the return type is, in fact, a type, which is what the typename keyword is for. Should the Standard be changed to require typename in such contexts?
Is this program valid?
template <typename T> int g(int);
class h{};
template <typename T> int l(){h j; return g<T>(j);}
template <typename T> int g(const h&);
class j{};
int jj(){return l<j>();}
The key issue is when "g" is looked up, i.e., whether both overloaded template "g" functions are available at the call site or only the first. Clearly, the entire postfix-expression "g<T>(j)" is dependent, but when is the set of available template functions determined?
For consistency with the rules about when the set of available overloads is determined when calling a function given by an unqualified-id, I would think that we should postpone determining the set of template functions if (and only if) any of the explicit template arguments are dependent.
John Spicer: I agree that there should be a core issue for this. The definition of "dependent name" (14.6.2 temp.dep paragraph 1) should probably be modified to cover this case. It currently only handles cases where the function name is a simple identifier.
Notes from the March 2004 meeting:
A related issue is a call with a qualified name and dependent arguments, e.g., x::y(depa, depb).
The list of cases in 14.6.1 temp.local about when a template parameter is hidden seems to be incomplete.
Consider
// example-1
struct S {
int C;
template<class> void f();
};
template<class C>
void S::f()
{
C c; // #1
}
Someone asked whether line #1 is well-formed and I responded "no" based on my understanding of the rules in 14.6.1. After a second looking, I've realized that the above case is currently missing from the list.
The list in 14.6.1 covers cases like
// example-2
template<class T>
struct S {
int C;
void f();
};
template<class C>
void S<C>::f()
{
C c; // ERROR: 'C' is 'S::C' not the template parameter
}
or
// example-3
struct A { int C; }
template<class C>
struct S : A {
C c; // ERROR: 'C' is 'A::C', not the template parameter
};
But the case of a 'member template' is missing. I believe it should
follow the same rule as above. The reason is this.
In the case listed in 14.6.1 (having to do with members of classes), the "algorithm" seems to be this:
I believe that any rule, coherent with 14.6.1/5 and 14.6.1/7, for covering the cases of member templates (example-1) will be described by the above "algorithm".
Am I missing something?
[1] of course, the standard text does not formally speak of "template parameter scope", but we all know that the template parameters "live" somewhere. I'm using that terminology to designate the declarative region of the template parameters.
Mike Miller: I have a somewhat different perspective on this question. I think your example-1 is fundamentally different from your example-2 and example-3. Looking, for instance, at your example-2, I see four nested scopes:
namespace scope
template scope (where the parameter is)
class S scope
S::f() block scope
Naturally, S::C hides the template parameter C. The same is true of your example-3, with three scopes:
namespace scope
template scope
class S scope (includes 10.2 base class lookup)
Again, it's clear that the C inherited from A hides the template parameter in the containing scope.
The scopes I see in your example-1, however, are different:
namespace scope
struct S scope
template scope (where the parameter is)
S::f() block scope
Here it seems clear to me that the template parameter hides the class member.
It might help to look at the case where the function template is defined inline in the class:
struct S {
int C;
template<class C> int f() {
C c; // #1
}
};
It would be pretty strange, I think, if the #1 C were the member and not the template parameter. It would also be odd if the name lookup were different between an inline definition and an out-of-line definition.
See also issue 459.
Notes from the March 2004 meeting:
Basically, the standard is okay. We think Gaby's desired cases like #1 should be ill-formed.
There is a wording problem in 14.6.1 temp.local paragraph 7. It says:
In the definition of a member of a class template that appears outside of the class template definition, the name of a member of this template hides the name of a template-parameter.
It should say "hides the name of a template-parameter of the class template (but not a template-parameter of the member, if the member is itself a template)" or words to that effect.
14.6.2.2 temp.dep.expr paragraph 3 says,
An id-expression is type-dependent if it contains:
- an identifier that was declared with a dependent type...
This treatment seems inadequate with regard to id-expressions in function calls:
According to 14.6.2.1 temp.dep.type paragraph 6,
A type is dependent if it is
- ...
- a compound type constructed from any dependent type...
This would apply to the type of a member function of a class template if any of its parameters are dependent, even if the return type is not dependent. However, there is no need for a call to such a function to be a type-dependent expression because the type of the expression is known at definition time.
This wording does not handle the case of overloaded functions, some of which might have dependent types (however defined) and others not.
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.
Implementations differ in their treatment of the following code:
template <class T>
struct A {
typename T::X x;
};
template <class T>
struct B {
typedef T* X;
A<B> a;
};
int main ()
{
B<int> b;
}
Some implementations accept it. At least one rejects it because the instantiation of A<B<int> > requires that B<int> be complete, and it is not at the point at which A<B<int> > is being instantiated.
Erwin Unruh:
In my view the programm is ill-formed. My reasoning:
So each class needs the other to be complete.
The problem can be seen much easier if you replace the typedef with
typedef T (*X) [sizeof(B::a)];
Now you have a true recursion. The compiler cannot easily distinguish between a true recursion and a potential recursion.
John Spicer:
Using a class to form a qualified name does not require the class to be complete, it only requires that the named member already have been declared. In other words, this kind of usage is permitted:
class A {
typedef int B;
A::B ab;
};
In the same way, once B has been declared in A, it is also visible to any template that uses A through a template parameter.
The standard could be more clear in this regard, but there are two notes that make this point. Both 3.4.3.1 class.qual and 5.1 expr.prim paragraph 7 contain a note that says "a class member can be referred to using a qualified-id at any point in its potential scope (3.3.6 basic.scope.class)." A member's potential scope begins at its point of declaration.
In other words, a class has three states: incomplete, being completed, and complete. The standard permits a qualified name to be used once a name has been declared. The quotation of the notes about the potential scope was intended to support that.
So, in the original example, class A does not require the type of T to be complete, only that it have already declared a member X.
Bill Gibbons:
The template and non-template cases are different. In the non-template case the order in which the members become declared is clear. In the template case the members of the instantiation are conceptually all created at the same time. The standard does not say anything about trying to mimic the non-template case during the instantiation of a class template.
Mike Miller:
I think the relevant specification is 14.6.4.1 temp.point paragraph 3, dealing with the point of instantiation:
For a class template specialization... if the specialization is implicitly instantiated because it is referenced from within another template specialization, if the context from which the specialization is referenced depends on a template parameter, and if the specialization is not instantiated previous to the instantiation of the enclosing template, the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.
That means that the point of instantiation of A<B<int> > is before that of B<int>, not in the middle of B<int> after the declaration of B::X, and consequently a reference to B<int>::X from A<B<int> > is ill-formed.
To put it another way, I believe John's approach requires that there be an instantiation stack, with the results of partially-instantiated templates on the stack being available to instantiations above them. I don't think the Standard mandates that approach; as far as I can see, simply determining the implicit instantiations that need to be done, rewriting the definitions at their respective points of instantiation with parameters substituted (with appropriate "forward declarations" to allow for non-instantiating references), and compiling the result normally should be an acceptable implementation technique as well. That is, the implicit instantiation of the example (using, e.g., B_int to represent the generated name of the B<int> specialization) could be something like
struct B_int;
struct A_B_int {
B_int::X x; // error, incomplete type
};
struct B_int {
typedef int* X;
A_B_int a;
};
Notes from 10/01 meeting:
This was discussed at length. The consensus was that the template case should be treated the same as the non-template class case it terms of the order in which members get declared/defined and classes get completed.
Proposed resolution:
In 14.6.4.1 temp.point paragraph 3 change:
the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.
To:
the point of instantiation is the same as the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the nearest enclosing declaration. [Note: The point of instantiation is still at namespace scope but any declarations preceding the point of instantiation, even if not at namespace scope, are considered to have been seen.]
Add following paragraph 3:
If an implicitly instantiated class template specialization, class member specialization, or specialization of a class template references a class, class template specialization, class member specialization, or specialization of a class template containing a specialization reference that directly or indirectly caused the instantiation, the requirements of completeness and ordering of the class reference are applied in the context of the specialization reference.
and the following example
template <class T> struct A {
typename T::X x;
};
struct B {
typedef int X;
A<B> a;
};
template <class T> struct C {
typedef T* X;
A<C> a;
};
int main ()
{
C<int> c;
}
Notes from the October 2002 meeting:
This needs work. Moved back to drafting status.
According to 14.6.4.2 temp.dep.candidate,
For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found using the usual lookup rules (3.4.1 basic.lookup.unqual, 3.4.2 basic.lookup.argdep) except that:
For the part of the lookup using unqualified name lookup (3.4.1 basic.lookup.unqual), only function declarations with external linkage from the template definition context are found.
For the part of the lookup using associated namespaces (3.4.2 basic.lookup.argdep), only function declarations with external linkage found in either the template definition context or the template instantiation context are found.
It is not at all clear why a call using a template-id would be treated differently from one not using a template-id. Furthermore, is it really necessary to exclude internal linkage functions from the lookup? Doesn't the ODR give implementations sufficient latitude to handle this case without another wrinkle on name lookup?
(See also issue 524.)
Notes from the April, 2006 meeting:
The consensus of the group was that template-ids should not be treated differently from unqualified-ids (although it's not clear how argument-dependent lookup works for template-ids), and that internal-linkage functions should be found by the lookup (although they may result in errors if selected by overload resolution).
Note (June, 2006):
Although the notes from the Berlin meeting indicate that argument-dependent lookup for template-ids is under-specified in the Standard, further examination indicates that that is not the case: the note in 14.8.1 temp.arg.explicit paragraph 8 clearly indicates that argument-dependent lookup is to be performed for template-ids, and 3.4.2 basic.lookup.argdep paragraph 4 describes the lookup performed:
When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier (3.4.3.2 namespace.qual) except that:
Any using-directives in the associated namespace are ignored.
Any namespace-scope friend functions declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup (11.4 class.friend).
Three points have been raised where the wording in 14.7.1 temp.inst may not be sufficiently clear.
A class template specialization is implicitly instantiated... if the completeness of the class type affects the semantics of the program...
It is not clear what it means for the "completeness... [to affect] the semantics." Consider the following example:
template<class T> struct A;
extern A<int> a;
void *foo() { return &a; }
template<class T> struct A
{
#ifdef OPTION
void *operator &() { return 0; }
#endif
};
The question here is whether it is necessary for template class A to declare an operator & for the semantics of the program to be affected. If it does not do so, the meaning of &a will be the same whether the class is complete or not and thus arguably the semantics of the program are not affected.
Presumably what was intended is whether the presence or absence of certain member declarations in the template class might be relevant in determining the meaning of the program. A clearer statement may be desirable.
If the overload resolution process can determine the correct function to call without instantiating a class template definition, it is unspecified whether that instantiation actually takes place.
The intent of this wording, as illustrated in the example in that paragraph, is to allow a "smart" implementation not to instantiate class templates if it can determine that such an instantiation will not affect the result of overload resolution, even though the algorithm described in clause 13 over requires that all the viable functions be enumerated, including functions that might be found as members of specializations.
Unfortunately, the looseness of the wording allowing this latitude for implementations makes it unclear what "the overload resolution process" is — is it the algorithm in 13 over or something else? — and what "the correct function" is.
If an implicit instantiation of a class template specialization is required and the template is declared but not defined, the program is ill-formed.
Here, it is not clear what conditions "require" an implicit instantiation. From the context, it would appear that the intent is to refer to the conditions in paragraph 4 that cause a specialization to be instantiated.
This interpretation, however, leads to different treatment of template and non-template incomplete classes. For example, by this interpretation,
class A;
template <class T> struct TA;
extern A a;
extern TA<int> ta;
void f(A*);
void f(TA<int>*);
int main()
{
f(&a); // well-formed; undefined if A
// has operator &() member
f(&ta); // ill-formed: cannot instantiate
}
A different approach would be to understand "required" in paragraph 6 to mean that a complete type is required in the expression. In this interpretation, if an incomplete type is acceptable in the context and the class template definition is not visible, the instantiation is not attempted and the program is well-formed.
The meaning of "required" in paragraph 6 must be clarified.
Notes on 10/01 meeting:
It was felt that item 1 is solved by addition of the word "might" in the resolution for issue 63; item 2 is not much of a problem; and item 3 could be solved by changing "required" to "required to be complete".
Issue 470 specified the explicit instantiation of members of explicitly-instantiated class templates. In restricting the affected members to those “whose definition is visible at the point of instantiation,” however, this resolution introduced an incompatibility between explicitly instantiating a member function or static data member and explicitly instantiating the class template of which it is a member (14.7.2 temp.explicit paragraph 3 requires only that the class template definition, not that of the member function or static data member, be visible at the point of the explicit instantiation). It would be better to treat the member instantiations the same, regardless of whether they are directly or indirectly explicitly instantiated.
Notes from the April, 2006 meeting:
In forwarding document J16/06-0057 = WG21 N1987 to be approved by the full Committee, the CWG reaffirmed its position that explicitly instantiating a class template only explicitly instantiates those of its members that have been defined before the point of the explicit instantiation. The effect of the position advocated above would be to require all non-exported member functions to be defined in the translation unit in which the class template is explicitly instantiated (cf paragraph 4), and we did not want to require that. We did agree that the “visible” terminology should be replaced by wording along the lines of “has been defined.”
The Standard does not definitively say when the inline specifier may be used in an explicit instantiation. For example, the following would seem to be innocuous, as the function being instantiated is already inline:
template <typename T> struct S {
void f() { }
};
template inline void S<int>::f();
However, presumably one would want to prohibit something like:
template <typename T> void f(T) { }
template inline void f(int);
7.1.2 dcl.fct.spec paragraph 4 (after application of the resolution of issue 317) comes close to covering the obvious problematic cases:
If the definition of a function appears in a translation unit before its first declaration as inline, the program is ill-formed. If a function with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required.
This would seem to prohibit the latter case, but apparently would not handle an exported template that was instantiated as inline (because the definition might not appear in the same translation unit as the inline instantiation). It would be better to make a clear statement regarding the use of inline in explicit instantiations.
Notes from the April, 2006 meeting:
The CWG favored completely disallowing the inline keyword in explicit instantiation directives.
Paragraph 17 of 14.7.3 temp.expl.spec says,
A member or a member template may be nested within many enclosing class templates. In an explicit specialization for such a member, the member declaration shall be preceded by a template<> for each enclosing class template that is explicitly specialized.
This is curious, because paragraph 3 only allows explicit specialization of members of implicitly-instantiated class specializations, not explicit specializations. Furthermore, paragraph 4 says,
Definitions of members of an explicitly specialized class are defined in the same manner as members of normal classes, and not using the explicit specialization syntax.
Paragraph 18 provides a clue for resolving the apparent contradiction:
In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well. In such explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding the explicit specialization declaration of the member.
It appears from this and the following example that the phrase “explicitly specialized” in paragraphs 17 and 18, when referring to enclosing class templates, does not mean that explicit specializations have been declared for them but that their names in the qualified-id are followed by template argument lists. This terminology is confusing and should be changed.
Proposed resolution (October, 2005):
Change 14.7.3 temp.expl.spec paragraph 17 as indicated:
A member or a member template may be nested within many enclosing class templates. In an explicit specialization for such a member, the member declaration shall be preceded by a template<> for each enclosing class templatethat is explicitly specializedspecialization. [Example:...
Change 14.7.3 temp.expl.spec paragraph 18 as indicated:
In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized,except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as wellthat is, the template-id naming the template may be composed of template parameter names rather than template-arguments.InFor each unspecialized template in such an explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding theexplicit specializationdeclaration of the member. The types of the template-parameters in the template-parameter-list shall be the same as those specified in the primary template definition. In such declarations, an unspecialized template-id shall not precede the name of a template specialization in the qualified-id naming the member. [Example:...
Notes from the April, 2006 meeting:
The revised wording describing “unspecialized” templates needs more work to ensure that the parameter names in the template-id are in the correct order; the distinction between template argyments and parameters is also probably not clear enough. It might be better to replace this paragraph completely and avoid the “unspecialized” wording altogether.
An expression used in an if statement is implicitly converted to type bool (6.4 stmt.select). According to the rules of template argument deduction for conversion functions given in 14.8.2.3 temp.deduct.conv, the following example is ill-formed:
struct X {
template<class T> operator const T&() const;
};
int main()
{
if( X() ) {}
}
Following the logic in 14.8.2.3 temp.deduct.conv, A is bool and P is const T (because cv-qualification is dropped from P before the reference is removed), and deduction fails.
It's not clear whether this is the intended outcome or not.
Notes from the April, 2005 meeting:
The CWG observed that there is nothing special about either bool or the context in the example above; instead, it will be a problem wherever a copy occurs, because cv-qualification is always dropped in a copy operation. This appears to be a case where the conversion deduction rules are not properly symmetrical with the rules for arguments. The example should be accepted.
Issue 226 removed the original prohibition on default template-arguments for function templates. However, the note in 14.8.2.5 temp.deduct.type paragraph 19 still reflects that prohibition. It should be revised or removed.
See also issue 37.
Given this piece of code and S having a user-defined ctor, at precisely which point must std::uncaught_exception() return true and where false?
try { S s0; throw s0; } catch (S s2) { }
My understanding of the semantics of the code is as follows:
Is my understanding correct?
15.1 except.throw paragraph 3 talks about “the exception object” when describing the semantics of the throw-expression:
a throw-expression initializes a temporary object, called the exception object...
However, 15.5.1 except.terminate paragraph 1 talks about “the expression to be thrown” when enumerating the conditions under which terminate() is called:
when the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught (15.1 except.throw), calls a user function that exits via an uncaught exception...
And, 15.5.3 except.uncaught paragraph 1 refers to “the object to be thrown” in the description of uncaught_exception():
The function std::uncaught_exception() returns true after completing evaluation of the object to be thrown...
Are all these objects one and the same? I believe the answer is important in case the construction of the temporary exception object throws another exception.
Suppose they are the same. Then uncaught_exception() invoked from the copy ctor for s1 (from the example [above]) must return false and a new exception (e.g., bad_alloc) may be thrown and caught by a matching handler (i.e., without calling terminate()).
But if they are not the same, then uncaught_exception() invoked from the copy ctor for s1 must return true and throwing another exception would end up calling terminate(). This would, IMO, have pretty severe consequences on writing exception safe exception classes.
As in the first case, different compilers behave differently, with most compilers not calling terminate() when the ctor for the temporary exception object throws. Unfortunately, the two compilers that I trust the most do call terminate().
FWIW, my feeling is that it should be possible for the copy ctor invoked to initialize the temporary exception object to safely exit by throwing another exception, and that the new exception should be allowed to be caught without calling terminate.
Mike Miller: The way I see this, a throw-expression has an assignment-expression as an operand. This expression is “the expression to be thrown.” Evaluation of this expression yields an object; this object is “the object to be thrown.” This object is then copied to the exception object.
Martin Sebor: Here's a survey of the return value from uncaught_exception() in the various stages of exception handling, implemented by current compilers:
| expr | temp | unwind | handlr | 2nd ex | |
|---|---|---|---|---|---|
| HP aCC 6 | 0 | 0 | 1 | 0 | OK |
| Compaq C++ 6.5 | 0 | 0 | 1 | 1 | ABRT |
| EDG eccp 3.4 | 0 | 1 | 1 | 1 | ABRT |
| g++ 3.4.2 | 0 | 0 | 1 | 0 | OK |
| Intel C++ 7.0 | 0 | 0 | 1 | 0 | OK |
| MIPSpro 7.4.1 | 0 | 0 | 1 | 1 | ABRT |
| MSVC 7.0 | 0 | 0 | 1 | 0 | OK |
| SunPro 5.5 | 1 | 1 | 1 | 0 | OK |
| VisualAge 6.0 | 0 | 1 | 1 | 1 | OK |
In the table above:
| expr | is the evaluation of the assignment-expression in the throw-expression |
| temp | is the invocation of the copy ctor for the unnamed temporary exception object created by the runtime. |
| unwind | is stack unwinding. |
| handlr | is the invocation of the copy ctor in the exception-declaration in the catch handler. |
| 2nd ex | describes the behavior of the implementation when the invocation of the copy ctor for the unnamed temporary exception object [temp] throws another exception. |
Proposed resolution (October, 2004):
Change 15.1 except.throw paragraph 3 as follows:
A throw-expression initializes a temporary object, called the exception object,theby copying the thrown object (i.e., the result of evaluating its assignment-expression operand) to it. The type ofwhichthe exception object is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T,” respectively. [Note: the temporary object createdforby a throw-expressionthatwhose operand is a string literal is never of type char* or wchar_t*; that is, the special conversions for string literals from the types “array of const char” and “array of const wchar_t” to the types “pointer to char” and “pointer to wchar_t,” respectively (4.2 conv.array), are never applied to the operand of a throw-expression. —end note] The temporary is an lvalue and is used to initialize the variable named in the matching handler (15.3 except.handle). The type of the operand of a throw-expression shall not be an incomplete type, or a pointer to an incomplete type other than (possibly cv-qualified) void. [...]
Change the note in 15.3 except.handle paragraph 3 as follows:
[Note: a throw-expression operand thatwhichis an integral constant expression of integer type that evaluates to zero does not match a handler of pointer type; that is, the null pointer constant conversions (4.10 conv.ptr, 4.11 conv.mem) do not apply. —end note]
Change 15.5.1 except.terminate paragraph 1 bullet 1 as follows:
when the exception handling mechanism, after completing evaluation of theexpression to be thrownoperand of throw but before the exception is caught (15.1 except.throw), calls a user function that exits via an uncaught exception,
Change 15.5.3 except.uncaught paragraph 1 as follows:
The function std::uncaught_exception() returns true after completing evaluation of theobject to be thrownoperand of throw until completing the initialization of the exception-declaration in the matching handler (18.6.4 lib.uncaught).
Change 18.6.4 lib.uncaught paragraph 1 by adding the indicated words:
Returns: true after completing evaluation of the operand of a throw-expression until either completing initialization of the exception-declaration in the matching handler or entering unexpected() due to the throw; or after entering terminate() for any reason other than an explicit call to terminate(). [Note: This includes stack unwinding (15.2 except.ctor). —end note]
Notes from the April, 2005 meeting:
The CWG discussed this resolution both within the group and with other interested parties. Among the points that were made:
Martin Sebor pointed to a posting in which he argues that writing copy constructors is more difficult if an exception during the copy to the exception object will result in a call to std::terminate().
In response to a question about why the copy to the exception object is different from the copy from the exception object to the object in the exception-declaration, it was observed that the writer of the handler can avoid the second copy (by using a reference declaration), but the first copy is unavoidable.
John Spicer observed that not exiting via exception should be a design constraint for copy constructors in exception objects, regardless of whether std::terminate() is called or not.
Adopting the position that uncaught_exception() returns false during the copy to the exception object would reduce the differences between the case where that copy is elided and the case where it is performed.
Jason Merrill observed that making uncaught_exception() return false during the copy to the exception object would simplify the code generated by g++; as it currently stands, the compiler must generate code to catch exceptions during that copy so std::terminate() can be called.
Bjarne Stroustrup worried that allowing the copy constructor to throw an exception during the copy to the exception object could result in a serious and specific exception being silently transformed into a more trivial and generic one (although the CWG later noted that this risk already exists if something in the expression being thrown throws an exception before the expression completes).
The CWG felt that more input from a wider audience was necessary before a decision could be made on the appropriate resolution.
Notes from the April, 2006 meeting:
The CWG agreed with the position that std::uncaught_exception() should return false during the copy to the exception object and that std::terminate() should not be called if that constructor exits with an exception. The issue was returned to “drafting” status for rewording to reflect this position.
The C++ Standard uses the phrase “indeterminate value” without defining it. C99 defines it as “either an unspecified value or a trap representation.” Should C++ follow suit?
In addition, 4.1 conv.lval paragraph 1 says that applying the lvalue-to-rvalue conversion to an “object [that] is uninitialized” results in undefined behavior; this should be rephrased in terms of an object with an indeterminate value.
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 execution requirements on a conforming implementation are described twice in the Standard, once in 1.9 intro.execution paragraphs 5-6 and again in paragraph 11. These descriptions differ in at least a couple of important ways:
The most significant discrepancy has to do with the way output is described. In paragraph 11, the least requirements are described in terms of data written at program termination, clearly allowing arbitrary buffering, whereas in paragraph 6, the observable behavior is described in terms of calls to I/O functions. For example, there are compilers which transform a call to printf with a single argument into a call to fputs. That's valid under paragraph 11, but not under paragraph 6.
Also, in paragraph 6, volatile accesses and I/O operations are included in a single sequence, suggesting that they are equally constrained by sequencing requirements, whereas in paragraph 11, they are clearly not.
There are also editorial discrepancies that should be cleaned up.
According to 2.1 lex.phases paragraph 1, in translation phase 1,
Any source file character not in the basic source character set (2.2 lex.charset) is replaced by the universal-character-name that designates that character.
If a character that is not in the basic character set is preceded by a backslash character, for example
"\á"
the result is equivalent to
"\\u00e1"
that is, a backslash character followed by the spelling of the universal-character-name. This is different from the result in C99, which accepts characters from the extended source character set without replacing them with universal-character-names.
2.13.4 lex.string paragraph 5 reads
Escape sequences and universal-character-names in string literals have the same meaning as in character literals, except that the single quote ' is representable either by itself or by the escape sequence \', and the double quote " shall be preceded by a \. In a narrow string literal, a universal-character-name may map to more than one char element due to multibyte encoding.
The first sentence refers us to 2.13.2 lex.ccon, where we read in the first paragraph that "An ordinary character literal that contains a single c-char has type char [...]." Since the grammar shows that a universal-character-name is a c-char, something like '\u1234' must have type char (and thus be a single char element); in paragraph 5, we read that "A universal-character-name is translated to the encoding, in the execution character set, of the character named. If there is no such encoding, the universal-character-name is translated to an implemenation-defined encoding."
This is in obvious contradiction with the second sentence. In addition, I'm not really clear what is supposed to happen in the case where the execution (narrow-)character set is UTF-8. Consider the character \u0153 (the oe in the French word oeuvre). Should '\u0153' be a char, with an "error" value, say '?' (in conformance with the requirement that it be a single char), or an int, with the two char values 0xC5, 0x93, in an implementation defined order (in conformance with the requirement that a character representable in the execution character set be represented). Supposing the former, should "\u0153" be the equivalent of "?" (in conformance with the first sentence), or "\xC5\x93" (in conformance with the second).
Notes from October 2003 meeting:
We decided we should forward this to the C committee and let them resolve it. Sent via e-mail to John Benito on November 14, 2003.
Reply from John Benito:
I talked this over with the C project editor, we believe this was handled by the C committee before publication of the current standard.
WG14 decided there needed to be a more restrictive rule for one-to-one mappings: rather than saying "a single c-char" as C++ does, the C standard says "a single character that maps to a single-byte execution character"; WG14 fully expect some (if not many or even most) UCNs to map to multiple characters.
Because of the fundamental differences between C and C++ character types, I am not sure the C committee is qualified to answer this satisfactorily for WG21. WG14 is willing to review any decision reached for compatibility.
I hope this helps.
3 basic paragraph 8, while not incorrect, does not allow for linkage of operators and conversion functions. It says:
An identifier used in more than one translation unit can potentially refer to the same entity in these translation units depending on the linkage (3.5 basic.link) of the identifier specified in each translation unit.
Clause 3 basic paragraph 4 says:
A name is a use of an identifier (2.10 lex.name) that denotes an entity or label (6.6.4 stmt.goto, 6.1 stmt.label).
Just three paragraphs later, it says
Two names are the same if
- they are identifiers composed of the same character sequence; or
- they are the names of overloaded operator functions formed with the same operator; or
- they are the names of user-defined conversion functions formed with the same type.
The last two bullets contradict the definition of name in paragraph 4 because they are not identifiers.
This definition affects other parts of the Standard, as well. For example, in 3.4.2 basic.lookup.argdep paragraph 1,
When an unqualified name is used as the postfix-expression in a function call (5.2.2 expr.call), other namespaces not considered during the usual unqualified lookup (3.4.1 basic.lookup.unqual) may be searched, and in those namespaces, namespace-scope friend function declarations (11.4 class.friend) not otherwise visible may be found.
With the current definition of name, argument-dependent lookup apparently does not apply to function-notation calls to overloaded operators.
Another related question is whether a template-id is a name or not and thus would trigger an argument-dependent lookup. Personally, I have always viewed a template-id as a name, just like operator+.
Sections 3.3.2 basic.scope.local to 3.3.6 basic.scope.class define and summarize different kinds of scopes in a C++ program. However it is missing a description for the scope of template parameters. I believe a section is needed there — even though some information may be found in clause 14.
The various uses of the term “declarative region” throughout the Standard indicate that the term is intended to refer to the entire block, class, or namespace that contains a given declaration. For example, 3.3 basic.scope paragraph 2 says, in part:
[Example: in
int j = 24; int main() { int i = j, j; j = 42; }The declarative region of the first j includes the entire example... The declarative region of the second declaration of j (the j immediately before the semicolon) includes all the text between { and }...
However, the actual definition given for “declarative region” in 3.3 basic.scope paragraph 1 does not match this usage:
Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity.
Because (except in class scope) a name cannot be used before it is declared, this definition contradicts the statement in the example and many other uses of the term throughout the Standard. As it stands, this definition is identical to that of the scope of a name.
The term “scope” is also misused. The scope of a declaration is defined in 3.3 basic.scope paragraph 1 as the region in which the name being declared is valid. However, there is frequent use of the phrase “the scope of a class,” not referring to the region in which the class's name is valid but to the declarative region of the class body, and similarly for namespaces, functions, exception handlers, etc. There is even a mention of looking up a name “in the scope of the complete postfix-expression” (3.4.5 basic.lookup.classref paragraph 3), which is the exact inverse of the scope of a declaration.
This terminology needs a thorough review to make it logically consistent. (Perhaps a discussion of the scope of template parameters could also be added to section 3.3 basic.scope at the same time, as all other kinds of scopes are described there.)
The current description of unqualified name lookup in 3.4.1 basic.lookup.unqual paragraph 8 does not correctly handle complex cases of nesting. The Standard currently reads,
A name used in the definition of a function that is a member function (9.3) of a class X shall be declared in one of the following ways:In particular, this formulation does not handle the following example:
- before its use in the block in which it is used or in an enclosing block (6.3), or
- shall be a member of class X or be a member of a base class of X (10.2), or
- if X is a nested class of class Y (9.7), shall be a member of Y, or shall be a member of a base class of Y (this lookup applies in turn to Y's enclosing classes, starting with the innermost enclosing class), or
- if X is a local class (9.8) or is a nested class of a local class, before the definition of class X in a block enclosing the definition of class X, or
- if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or nested class within a local class of a function that is a member of N, before the member function definition, in namespace N or in one of N's enclosing namespaces.
struct outer {
static int i;
struct inner {
void f() {
struct local {
void g() {
i = 5;
}
};
}
};
};
Here the reference to i is from a member function of a local
class of a member function of a nested class. Nothing in the rules
allows outer::i to be found, although intuitively it should
be found.
A more comprehensive formulation is needed that allows traversal of any combination of blocks, local classes, and nested classes. Similarly, the final bullet needs to be augmented so that a function need not be a (direct) member of a namespace to allow searching that namespace when the reference is from a member function of a class local to that function. That is, the current rules do not allow the following example:
int j; // global namespace
struct S {
void f() {
struct local2 {
void g() {
j = 5;
}
};
}
};
The description of name lookup in the parameter-declaration-clause of member functions in 3.4.1 basic.lookup.unqual paragraphs 7-8 is flawed in at least two regards.
First, both paragraphs 7 and 8 apply to the parameter-declaration-clause of a member function definition and give different rules for the lookup. Paragraph 7 applies to names "used in the definition of a class X outside of a member function body...," which includes the parameter-declaration-clause of a member function definition, while paragraph 8 applies to names following the function's declarator-id (see the proposed resolution of issue 41), including the parameter-declaration-clause.
Second, paragraph 8 appears to apply to the type names used in the parameter-declaration-clause of a member function defined inside the class definition. That is, it appears to allow the following code, which was not the intent of the Committee:
struct S {
void f(I i) { }
typedef int I;
};
There seems to be some confusion in the Standard regarding the relationship between 3.4.1 basic.lookup.unqual (Unqualified name lookup) and 3.4.2 basic.lookup.argdep (Argument-dependent lookup). For example, 3.4.1 basic.lookup.unqual paragraph 3 says,
The lookup for an unqualified name used as the postfix-expression of a function call is described in 3.4.2 basic.lookup.argdep.
In other words, nothing in 3.4.1 basic.lookup.unqual applies to function names; the entire lookup is described in 3.4.2 basic.lookup.argdep.
3.4.2 basic.lookup.argdep does not appear to share this view of its responsibility. The closest it comes is in 3.4.2 basic.lookup.argdep paragraph 2a:
...the set of declarations found by the lookup of the function name is the union of the set of declarations found using ordinary unqualified lookup and the set of declarations found in the namespaces and classes associated with the argument types.
Presumably, "ordinary unqualified lookup" is a reference to the processing described in 3.4.1 basic.lookup.unqual, but, as noted above, 3.4.1 basic.lookup.unqual explicitly precludes applying that processing to function names. The details of "ordinary unqualified lookup" of function names are not described anywhere.
The other clauses that reference 3.4.2 basic.lookup.argdep, clauses 13 over and 14 temp, are split over the question of the relationship between 3.4.1 basic.lookup.unqual and 3.4.2 basic.lookup.argdep. 13.3.1.1.1 over.call.func paragraph 3, for instance, says
The name is looked up in the context of the function call following the normal rules for name lookup in function calls (3.4.2 basic.lookup.argdep).
I.e., this reference assumes that 3.4.2 basic.lookup.argdep is self-contained. The same is true of 13.3.1.2 over.match.oper paragraph 3, second bullet:
The set of non-member candidates is the result of the unqualified lookup of operator@ in the context of the expression according to the usual rules for name lookup in unqualified function calls (3.4.2 basic.lookup.argdep), except that all member functions are ignored.
On the other hand, however, 14.6.4.2 temp.dep.candidate paragraph 1 explicitly assumes that 3.4.1 basic.lookup.unqual and 3.4.2 basic.lookup.argdep are both involved in function name lookup and do different things:
For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found using the usual lookup rules (3.4.1 basic.lookup.unqual, 3.4.2 basic.lookup.argdep) except that:
- For the part of the lookup using unqualified name lookup (3.4.1 basic.lookup.unqual), only function declarations with external linkage from the template definition context are found.
- For the part of the lookup using associated namespaces (3.4.2 basic.lookup.argdep), only function declarations with external linkage found in either the template definition context or the template instantiation context are found.
Suggested resolution:
Change 3.4.1 basic.lookup.unqual paragraph 1 from
...name lookup ends as soon as a declaration is found for the name.
to
...name lookup ends with the first scope containing one or more declarations of the name.
Change the first sentence of 3.4.1 basic.lookup.unqual paragraph 3 from
The lookup for an unqualified name used as the postfix-expression of a function call is described in 3.4.2 basic.lookup.argdep.
to
An unqualified name used as the postfix-expression of a function call is looked up as described below. In addition, argument-dependent lookup (3.4.2 basic.lookup.argdep) is performed on this name to complete the resulting set of declarations.
The last bullet of the second paragraph of section 3.4.2 basic.lookup.argdep says that:
If T is a template-id, its associated namespaces and classes are the namespace in which the template is defined; for member templates, the member template's class; the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces in which any template template arguments are defined; and the classes in which any member templates used as template template arguments are defined.
The first problem with this wording is that it is misleading, since one cannot get such a function argument whose type would be a template-id. The bullet should be speaking about template specializations instead.
The second problem is owing to the use of the word "defined" in the phrases "are the namespace in which the template is defined", "in which any template template arguments are defined", and "as template template arguments are defined". The bullet should use the word "declared" instead, since scenarios like the one below are possible:
namespace A {
template<class T>
struct test {
template<class U>
struct mem_templ { };
};
// declaration in namespace 'A'
template<> template<>
struct test<int>::mem_templ<int>;
void foo(test<int>::mem_templ<int>&)
{ }
}
// definition in the global namespace
template<> template<>
struct A::test<int>::mem_templ<int> {
};
int main()
{
A::test<int>::mem_templ<int> inst;
// According to the current definition of 3.4.2
// foo is not found.
foo(inst);
}
In addition, the bullet doesn't make it clear whether a T which is a class template specialization must also be treated as a class type, i.e. if the contents of the second bullet of the second paragraph of section 3.4.2 basic.lookup.argdep.
must apply to it or not. The same stands for a T which is a function template specialization. This detail can make a difference in an example such as the one below:
- If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces in which its associated classes are defined. [This wording is as updated by core issue 90.]
template<class T>
struct slist_iterator {
friend bool operator==(const slist_iterator& x, const slist_iterator& y)
{ return true; }
};
template<class T>
struct slist {
typedef slist_iterator<T> iterator;
iterator begin()
{ return iterator(); }
iterator end()
{ return iterator(); }
};
int main()
{
slist<int> my_list;
slist<int>::iterator mi1 = my_list.begin(), mi2 = my_list.end();
// Must the the friend function declaration
// bool operator==(const slist_iterator<int>&, const slist_iterator<int>&);
// be found through argument dependent lookup? I.e. is the specialization
// 'slist<int>' the associated class of the arguments 'mi1' and 'mi2'. If we
// apply only the contents of the last bullet of 3.4.2/2, then the type
// 'slist_iterator<int>' has no associated classes and the friend declaration
// is not found.
mi1 == mi2;
}
Suggested resolution:
Replace the last bullet of the second paragraph of section 3.4.2 basic.lookup.argdep
with
- If T is a template-id, its associated namespaces and classes are the namespace in which the template is defined; for member templates, the member template's class; the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces in which any template template arguments are defined; and the classes in which any member templates used as template template arguments are defined.
- If T is a class template specialization, its associated namespaces and classes are those associated with T when T is regarded as a class type; the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces in which the primary templates making template template arguments are declared; and the classes in which any primary member templates used as template template arguments are declared.
- If T is a function template specialization, its associated namespaces and classes are those associated with T when T is regarded as a function type; the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces in which the primary templates making template template arguments are declared; and the classes in which any primary member templates used as template template arguments are declared.
Replace the second bullet of the second paragraph of section 3.4.2 basic.lookup.argdep
with
- If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces in which its associated classes are defined.
- If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces in which its associated classes are declared [Note: in case of any of the associated classes being a class template specialization, its associated namespace is acually the namespace containing the declaration of the primary class template of the class template specialization].
The resolution of issue 33 added the following wording in 3.4.2 basic.lookup.argdep:
In addition, if the argument is the name or address of a set of overloaded functions and/or function templates, its associated classes and namespaces are the union of those associated with each of the members of the set: the namespace in which the function or function template is defined and the classes and namespaces associated with its (non-dependent) parameter types and return type.
This wording is self-contradictory: although it claims that the treatment of overload sets is intended to be “the union of those associated with each of the members of the set,” it says that the namespace of which each function or function template is a member is to be considered an associated namespace. That is different from the case of a non-overloaded function argument; in that case, because only the type of the argument is considered, the namespace of which the function is a member is not an associated namespace. This should be rectified so that overloaded and unoverloaded functions really are treated the same.
Both 3.4.3.1 class.qual and 3.4.3.2 namespace.qual specify that some lookups are to be performed “in the context of the entire postfix-expression,” ignoring the fact that qualified-ids can appear outside of expressions.
It was suggested in document J16/05-0156 = WG21 N1896 that these uses be changed to “the context in which the qualified-id occurs,” but it isn't clear that this formulation adequately covers all the places a qualified-id can occur.
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.
Is this case valid? G++ compiles it.
namespace X {
namespace Y {
struct X {
void f()
{
using X::Y;
namespace Z = X::Y;
}
};
}
}
The relevant citation from the standard is 3.4.6 basic.lookup.udir: "When looking up a namespace-name in a using-directive or namespace-alias-definition, only namespace names are considered." This statement could reasonably be interpreted to apply only to the last element of a qualified name, and that's the way EDG and Microsoft seem to interpret it.
However, since a class can't contain a namespace, it seems to me that this interpretation is, shall we say, sub optimal. If the X qualifiers in the above example are interpreted as referring to the struct X, an error of some sort is inevitable, since there can be no namespace for the qualified name to refer to. G++ apparently interprets 3.4.6 basic.lookup.udir as applying to nested-name-specifiers in those contexts as well, which makes a valid interpretation of the test possible.
I'm thinking it might be worth tweaking the words in 3.4.6 basic.lookup.udir to basically mandate the more useful interpretation. Of course a person could argue that the difference would matter only to a perverse program. On the other hand, namespaces were invented specifically to enable the building of programs that would otherwise be considered perverse. Where name clashes are concerned, one man's perverse is another man's real world.
It is unclear to what extent entities without names match across translation units. For example,
struct S {
int :2;
enum { a, b, c } x;
static class {} *p;
};
If this declaration appears in multiple translation units, are all these members "the same" in each declaration?
A similar question can be asked about non-member declarations:
// Translation unit 1:
extern enum { d, e, f } y;
// Translation unit 2:
extern enum { d, e, f } y;
// Translation unit 3:
enum { d, e, f } y;
Is this valid C++? Is it valid C?
James Kanze: S::p cannot be defined, because to do so requires a type specifier and the type cannot be named. ::y is valid C because C only requires compatible, not identical, types. In C++, it appears that there is a new type in each declaration, so it would not be valid. This differs from S::x because the unnamed type is part of a named type — but I don't know where or if the Standard says that.
John Max Skaller: It's not valid C++, because the type is a synthesised, unique name for the enumeration type which differs across translation units, as if:
extern enum _synth1 { d,e,f} y;
..
extern enum _synth2 { d,e,f} y;
had been written.
However, within a class, the ODR implies the types are the same:
class X { enum { d } y; };
in two translation units ensures that the type of member y is the same: the two X's obey the ODR and so denote the same class, and it follows that there's only one member y and one type that it has.
(See also issues 132 and 216.)
The standard says that an unnamed class or enum definition can be given a "name for linkage purposes" through a typedef. E.g.,
typedef enum {} E;
extern E *p;
can appear in multiple translation units.
How about the following combination?
// Translation unit 1:
struct S;
extern S *q;
// Translation unit 2:
typedef struct {} S;
extern S *q;
Is this valid C++?
Also, if the answer is "yes", consider the following slight variant:
// Translation unit 1:
struct S {}; // <<-- class has definition
extern S *q;
// Translation unit 2:
typedef struct {} S;
extern S *q;
Is this a violation of the ODR because two definitions of type S consist of differing token sequences?
The following declarations are allowed within a translation unit:
struct S;
enum { S };
However, 3.5 basic.link paragraph 9 seems to say these two declarations cannot appear in two different translation units. That also would mean that the inclusion of a header containing the above in two different translation units is not valid C++.
I suspect this is an oversight and that users should be allowed to have the declarations above appear in different translation units. (It is a fairly common thing to do, I think.)
Mike Miller: I think you meant "enum E { S };" -- enumerators only have external linkage if the enumeration does (3.5 basic.link paragraph 4), and 3.5 basic.link paragraph 9 only applies to entities with external linkage.
I don't remember why enumerators were given linkage; I don't think it's necessary for mangling non-type template arguments. In any event, I can't think why cross-TU name collisions between enumerators and other entities would cause a problem, so I guess a change here would be okay. I can think of three changes that would have that effect:
Daveed Vandevoorde: I don't think any of these are sufficient in the sense that the problem isn't limited to enumerators. E.g.:
struct X; extern void X();shouldn't create cross-TU collisions either.
Mike Miller: So you're saying that cross-TU collisions should only be prohibited if both names denote entities of the same kind (both functions, both objects, both types, etc.), or if they are both references (regardless of what they refer to, presumably)?
Daveed Vandevoorde: Not exactly. Instead, I'm saying that if two entities (with external linkage) can coexist when they're both declared in the same translation unit (TU), then they should also be allowed to coexist when they're declared in two different translation units.
For example:
int i; void i(); // ErrorThis is an error within a TU, so I don't see a reason to make it valid across TUs.
However, "tag names" (class/struct/union/enum) can sometimes coexist with identically named entities (variables, functions & enumerators, but not namespaces, templates or type names).
Is a compiler allowed to interleave constructor calls when performing dynamic initialization of nonlocal objects? What I mean by interleaving is: beginning to execute a particular constructor, then going off and doing something else, then going back to the original constructor. I can't find anything explicit about this in clause 3.6.2 basic.start.init.
I'll present a few different examples, some of which get a bit wild. But a lot of what this comes down to is exactly what the standard means when it talks about the order of initialization. If it says that some object x must be initialized before a particular event takes place, does that mean that x's constructor must be entered before that event, or does it mean that it must be exited before that event? If object x must be initialized before object y, does that mean that x's constructor must exit before y's constructor is entered?
(The answer to that question might just be common sense, but I couldn't find an answer in clause 3.6.2 basic.start.init. Actually, when I read 3.6.2 basic.start.init carefully, I find there are a lot of things I took for granted that aren't there.)
OK, so a few specific scenerios.
<runtime gunk>
<Enter A's constructor>
<Enter f>
<runtime gunk>
<Enter B's constructor>
<Enter f>
<Leave f>
<Leave B's constructor>
<Leave f>
<Leave A's constructor>
The implication of a 'yes' answer for users is that any function
called by a constructor, directly or indirectly, must be reentrant.At this point, you might be thinking we could avoid all of this nonsense by removing compilers' freedom to defer initialization until after the beginning of main(). I'd resist that, for two reasons. First, it would be a huge change to make after the standard has been out. Second, that freedom is necessary if we want to have support for dynamic libraries. I realize we don't yet say anything about dynamic libraries, but I'd hate to make decisions that would make such support even harder.
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'.]
There are several problems with 3.7 basic.stc:
3.7 basic.stc paragraph 2 says that "Static and automatic storage durations are associated with objects introduced by declarations (3.1 basic.def) and implicitly created by the implementation (12.2 class.temporary)."
In fact, objects "implicitly created by the implementation" are the temporaries described in (12.2 class.temporary), and have neither static nor automatic storage duration, but a totally different duration, described in 12.2 class.temporary.
3.7 basic.stc uses the expression "local object" in several places, without ever defining it. Presumably, what is meant is "an object declared at block scope", but this should be said explicitly.
In a recent discussion in comp.lang.c++.moderated, on poster interpreted "local objects" as including temporaries. This would require them to live until the end of the block in which they are created, which contradicts 12.2 class.temporary. If temporaries are covered by this section, and the statement in 3.7 basic.stc seems to suggest, and they aren't local objects, then they must have static storage duration, which isn't right either.
I propose adding a fourth storage duration to the list after 3.7 basic.stc paragraph 1:
And rewriting the second paragraph of this section as follows:
Temporary storage duration is associated with objects implicitly created by the implementation, and is described in 12.2 class.temporary. Static and automatic storage durations are associated with objects defined by declarations; in the following, an object defined by a declaration with block scope is a local object. The dynamic storage duration is associated with objects created by the operator new.
Steve Adamczyk: There may well be an issue here, but one should bear in mind the difference between storage duration and object lifetime. As far as I can see, there is no particular problem with temporaries having automatic or static storage duration, as appropriate. The point of 12.2 class.temporary is that they have an unusual object lifetime.
Notes from Ocrober 2002 meeting:
It might be desirable to shorten the storage duration of temporaries to allow reuse of them. The as-if rule allows some reuse, but such reuse requires analysis, including noting whether the addresses of such temporaries have been taken.
3.7.3.2 basic.stc.dynamic.deallocation paragraph 4 mentions that the effect of using an invalid pointer value is undefined. However, the standard never says what it means to 'use' a value.
There are a number of possible interpretations, but it appears that each of them leads to undesired conclusions:
int *x = new int(0); delete x; x = 0;into undefined behaviour. As this is a common idiom, this is clearly undesirable.
int *x = new int(0); delete x; x->~int();into undefined behaviour; according to 5.2.4 expr.pseudo, the variable x is 'evaluated' as part of evaluating the pseudo destructor call. This, in turn, would mean that all containers (23 lib.containers) of pointers show undefined behaviour, e.g. 23.2.2.3 lib.list.modifiers requires to invoke the destructor as part of the clear() method of the container.
If any other meaning was intended for 'using an expression', that meaning should be stated explicitly.
(See also issue 623.)
When an object is deleted, 3.7.3.2 basic.stc.dynamic.deallocation says that the deallocation “[renders] invalid all pointers referring to any part of the deallocated storage.” According to 3.9.2 basic.compound paragraph 3, a pointer whose address is one past the end of an array is considered to point to an unrelated object that happens to reside at that address. Does this need to be clarified to specify that the one-past-the-end pointer of an array is not invalidated by deleting the following object? (See also 5.3.5 expr.delete paragraph 4, which also mentions that the system deallocation function renders a pointer invalid.)
Any use of a pointer to deleted storage, even if the pointer is not dereferenced, produces undefined behavior (3.7.3.2 basic.stc.dynamic.deallocation paragraph 4). The reason for this restriction is that, on some historical architectures, deallocating an object might free a memory segment, resulting in a hardware exception if a pointer referring to that segment were loaded into a pointer register, and on those architectures use of a pointer register for moving and comparing pointers was the most efficient mechanism for these operations.
It is not clear whether current or foreseeable architectures still require such a draconian restriction or whether it is feasible to relax it only to forbid a smaller range of operations. Of particular concern is the use of atomic pointers, which might be used in race conditions involving deallocation, where the loser of the race might encounter this undefined behavior.
(See also issue 312.)
Consider
extern "C" int printf (const char *,...);
struct Base { Base();};
struct Derived: virtual public Base {
Derived() {;}
};
Derived d;
extern Derived& obj = d;
int i;
Base::Base() {
if ((Base *) &obj) i = 4;
printf ("i=%d\n", i);
}
int main() { return 0; }
12.7 class.cdtor paragraph 2 makes this valid, but 3.8 basic.life paragraph 5 implies that it isn't valid.
Steve Adamczyk: A second issue:
extern "C" int printf(const char *,...);
struct A { virtual ~A(); int x; };
struct B : public virtual A { };
struct C : public B { C(int); };
struct D : public C { D(); };
int main() { D t; printf("passed\n");return 0; }
A::~A() {}
C::C(int) {}
D::D() : C(this->x) {}
Core issue 52 almost, but not quite, says that in evaluating "this->x" you do a cast to the virtual base class A, which would be an error according to 12.7 class.cdtor paragraph 2 because the base class B constructor hasn't started yet. 5.2.5 expr.ref should be clarified to say that the cast does need to get done.
James Kanze submitted the same issue via comp.std.c++ on 11 July 2003:
Richard Smith: Nonsense. You can use "this" perfectly happily in a constructor, just be careful that (a) you're not using any members that are not fully initialised, and (b) if you're calling virtual functions you know exactly what you're doing.
In practice, and I think in intent, you are right. However, the standard makes some pretty stringent restrictions in 3.8 basic.life. To start with, it says (in paragraph 1):
The lifetime of an object is a runtime property of the object. The lifetime of an object of type T begins when:(Emphasis added.) Then when we get down to paragraph 5, it says:The lifetime of an object of type T ends when:
- storage with the proper alignment and size for type T is obtained, and
- if T is a class type with a non-trivial constructor, the constructor calls has COMPLETED.
- if T is a class type with a non-trivial destructor, the destructor call STARTS, or
- the storage which the object occupies is reused or released.
Before the lifetime of an object has started but after the storage which the object will occupy has been allocated [which sounds to me like it would include in the constructor, given the text above] or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. [...] If the object will be or was of a non-POD class type, the program has undefined behavior if:
[...]
- the pointer is implicitly converted to a pointer to a base class type, or [...]
I can't find any exceptions for the this pointer.
Note that calling a non-static function in the base class, or even constructing the base class in initializer list, involves an implicit conversion of this to a pointer to the base class. Thus undefined behavior. I'm sure that this wasn't the intent, but it would seem to be what this paragraph is saying.
An lvalue referring to an out-of-lifetime non-POD class objects can be used in limited ways, subject to the restrictions in 3.8 basic.life paragraph 6:
if the original object will be or was of a non-POD class type, the program has undefined behavior if:
the lvalue is used to access a non-static data member or call a non-static member function of the object, or
the lvalue is implicitly converted (4.10 conv.ptr) to a reference to a base class type, or
the lvalue is used as the operand of a static_cast (5.2.9 expr.static.cast) except when the conversion is ultimately to cv char& or cv unsigned char& ), or
the lvalue is used as the operand of a dynamic_cast (5.2.7 expr.dynamic.cast) or as the operand of typeid.
There are at least a couple of questionable things in this list. First, there is no “implicit conversion to a reference to a base class,” as assumed by the second bullet. Presumably this is intended to say that the lvalue is bound to a reference to a base class, and the cross-reference should be to 8.5.3 dcl.init.ref, not to 4.10 conv.ptr (which deals with pointer conversions). However, even given that adjustment, it is not clear why it is forbidden to bind a reference to a non-virtual base class of an out-of-lifetime object, as that is just an address offset calculation. (Binding to a virtual base, of course, would require access to the value of the object and thus cannot be done outside the object's lifetime.)
The third bullet also appears questionable. It's not clear why static_cast is discussed at all here, as the only permissible static_cast conversions involving reference types and non-POD classes are to references to base or derived classes and to the same type, modulo cv-qualification; if implicit “conversion” to a base class reference is forbidden in the second bullet, why would an explicit conversion be permitted in the third? Was this intended to refer to reinterpret_cast? Also, is there a reason to allow char types but disallow array-of-char types (which are more likely to be useful than a single char)?
In 3.9 basic.types paragraph 10, the standard makes it quite clear that volatile qualified types are PODs:
Arithmetic types (3.9.1 basic.fundamental), enumeration types, pointer types, and pointer to member types (3.9.2 basic.compound), and cv-qualified versions of these types (3.9.3 basic.type.qualifier) are collectively called scalar types. Scalar types, POD-struct types, POD-union types (clause 9 class), arrays of such types and cv-qualified versions of these types (3.9.3 basic.type.qualifier) are collectively called POD types.
However in 3.9 basic.types paragraph 3, the standard makes it clear that PODs can be copied “as if” they were a collection of bytes by memcpy:
For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the value of obj1 is copied into obj2, using the std::memcpy library function, obj2 shall subsequently hold the same value as obj1.
The problem with this is that a volatile qualified type may need to be copied in a specific way (by copying using only atomic operations on multithreaded platforms, for example) in order to avoid the “memory tearing” that may occur with a byte-by-byte copy.
I realise that the standard says very little about volatile qualified types, and nothing at all (yet) about multithreaded platforms, but nonetheless this is a real issue, for the following reason:
The forthcoming TR1 will define a series of traits that provide information about the properties of a type, including whether a type is a POD and/or has trivial construct/copy/assign operations. Libraries can use this information to optimise their code as appropriate, for example an array of type T might be copied with a memcpy rather than an element-by-element copy if T is a POD. This was one of the main motivations behind the type traits chapter of the TR1. However it's not clear how volatile types (or POD's which have a volatile type as a member) should be handled in these cases.
Notes from the April, 2005 meeting:
It is not clear whether the volatile qualifier actually guarantees atomicity in this way. Also, the work on the memory model for multithreading being done by the Evolution Working Group seems at this point likely to specify additional semantics for volatile data, and that work would need to be considered before resolving this issue.
Is the following example well-formed?
struct S {
static char a[5];
};
char S::a[]; // Unspecified bound in definition
3.5 basic.link paragraph 10 certainly makes allowance for declarations to differ in the presence or absence of a major array bound. However, 3.1 basic.def paragraph 5 says that
A program is ill-formed if the definition of any object gives the object an incomplete type (3.9 basic.types).
3.9 basic.types paragraph 7 says,
The declared type of an array object might be an array of unknown size and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points (“array of unknown bound of T” and “array of N T”) are different types.
This wording appears to make no allowance for the C concept of “composite type;” instead, each declaration is said to have its own type. By this interpretation, the example is ill-formed, because the type declared by the definition of S::a is incomplete.
If the example is intended to be well-formed, the Standard needs explicit wording stating that an omitted array bound in a declaration is implicitly taken from that of a visible declaration of that object, if any.
3.9.1 basic.fundamental does not impose a requirement on the floating point types that there be an exact representation of the value zero. This omission is significant in 4.12 conv.bool paragraph 1, in which any non-zero value converts to the bool value true.
Suggested resolution: require that all floating point types have an exact representation of the value zero.
3.9.1 basic.fundamental paragraph 2 says that
There are four signed integer types: "signed char", "short int", "int", and "long int."
This would indicate that const int is not a signed integer type.
There is no normative requirement on the ranges of the integral types, although the footnote in 3.9.1 basic.fundamental paragraph 2 indicates the intent (for int, at least) that they match the values given in the <climits> header. Should there be an explicit requirement of some sort?
(See also paper N1693.)
4.1 conv.lval paragraph 1 says,
If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.
I think there are at least three related issues around this specification:
Presumably assigning a valid value to an uninitialized object allows it to participate in the lvalue-to-rvalue conversion without undefined behavior (otherwise the number of programs with defined behavior would be vanishingly small :-). However, the wording here just says "uninitialized" and doesn't mention assignment.
There's no exception made for unsigned char types. The wording in 3.9.1 basic.fundamental was carefully crafted to allow use of unsigned char to access uninitialized data so that memcpy and such could be written in C++ without undefined behavior, but this statement undermines that intent.
It's possible to get an uninitialized rvalue without invoking the lvalue-to-rvalue conversion. For instance:
struct A {
int i;
A() { } // no init of A::i
};
int j = A().i; // uninitialized rvalue
There doesn't appear to be anything in the current IS wording that says that this is undefined behavior. My guess is that we thought that in placing the restriction on use of uninitialized objects in the lvalue-to-rvalue conversion we were catching all possible cases, but we missed this one.
In light of the above, I think the discussion of uninitialized objects ought to be removed from 4.1 conv.lval paragraph 1. Instead, something like the following ought to be added to 3.9 basic.types paragraph 4 (which is where the concept of "value" is introduced):
Any use of an indeterminate value (5.3.4 expr.new, 8.5 dcl.init, 12.6.2 class.base.init) of any type other than char or unsigned char results in undefined behavior.
John Max Skaller:
A().i had better be an lvalue; the rules are wrong. Accessing a member of a structure requires it be converted to an lvalue, the above calculation is 'as if':
struct A {
int i;
A *get() { return this; }
};
int j = (*A().get()).i;
and you can see the bracketed expression is an lvalue.
A consequence is:
int &j= A().i; // OK, even if the temporary evaporates
j now refers to a 'destroyed' value. Any use of j is an error. But the binding at the time is valid.
According to 4.1 conv.lval paragraph 1, applying the lvalue-to-rvalue conversion to any uninitialized object results in undefined behavior. However, character types are intended to allow any data, including uninitialized objects and padding, to be copied (hence the statements in 3.9.1 basic.fundamental paragraph 1 that “For character types, all bits of the object representation participate in the value representation” and in 3.10 basic.lval paragraph 15 that char and unsigned char types can alias any object). The lvalue-to-rvalue conversion should be permitted on uninitialized objects of character type without evoking undefined behavior.
Section 4.4 conv.qual covers the case of multi-level pointers, but does not appear to cover the case of pointers to arrays of pointers. The effect is that arrays are treated differently from simple scalar values.
Consider for example the following code: (from the thread "Pointer to array conversion question" begun in comp.lang.c++.moderated)
int main()
{
double *array2D[2][3];
double * (*array2DPtr1)[3] = array2D; // Legal
double * const (*array2DPtr2)[3] = array2DPtr1; // Legal
double const * const (*array2DPtr3)[3] = array2DPtr2; // Illegal
}
and compare this code with:-
int main()
{
double *array[2];
double * *ppd1 = array; // legal
double * const *ppd2 = ppd1; // legal
double const * const *ppd3 = ppd2; // certainly legal (4.4/4)
}
The problem appears to be that the pointed to types in example 1 are unrelated since nothing in the relevant section of the standard covers it - 4.4 conv.qual does not mention conversions of the form "cv array of N pointer to T" into "cv array of N pointer to cv T"
It appears that reinterpret_cast is the only way to perform the conversion.
Suggested resolution:
Artem Livshits proposed a resolution :-
"I suppose if the definition of "similar" pointer types in 4.4 conv.qual paragraph 4 was rewritten like this:
it would address the problem.T1 is cv1,0 P0 cv1,1 P1 ... cv1,n-1 Pn-1 cv1,n T
and
T2 is cv1,0 P0 cv1,1 P1 ... cv1,n-1 Pn-1 cv1,n T
where Pi is either a "pointer to" or a "pointer to an array of Ni"; besides P0 may be also a "reference to" or a "reference to an array of N0" (in the case of P0 of T2 being a reference, P0 of T1 may be nothing).
In fact I guess Pi in this notation may be also a "pointer to member", so 4.4 conv.qual/{4,5,6,7} would be nicely wrapped in one paragraph."
It is not clear what constraints are placed on a floating point implementation by the wording of the Standard. For instance, is an implementation permitted to generate a "fused multiply-add" instruction if the result would be different from what would be obtained by performing the operations separately? To what extent does the "as-if" rule allow the kinds of optimizations (e.g., loop unrolling) performed by FORTRAN compilers?
Lisa Lippincott mentioned this case to me:
A[0] = 0; A[A[0]] = 1;
This seems to use the old value of A[0] other than to calculate the new value, which is said to be undefined, but it also seems reasonable, since the old value is used in order to select the object to modify, so there's no ordering ambiguity.
Steve Adamczyk: the ordering rule referred to is in 5 expr paragraph 4.
Notes from the March 2004 meeting:
Clark Nelson mentions that the C committee may have done something on this.
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:
(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. ]
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.
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
}
Notes from October 2002 meeting:
This was moved back to open for lack of a champion. Martin O'Riordan is not expected to be attending meetings.
The original proposed resolution for issue 160 included changing extended_type_info (5.2.8 expr.typeid paragraph 1, footnote 61) to std::extended_type_info. There was no consensus on whether this name ought to be part of namespace std or in a vendor-specific namespace, so the question was moved into a separate issue.
5.2.8 expr.typeid paragraph 4 says,
When typeid is applied to a type-id, the result refers to a std::type_info object representing the type of the type-id. If the type of the type-id is a reference type, the result of the typeid expression refers to a std::type_info object representing the referenced type. If the type of the type-id is a class type or a reference to a class type, the class shall be completely-defined.
I'm wondering whether this is not overly restrictive. I can't think of a reason to require that T be completely-defined in typeid(T) when T is a class type. In fact, several popular compilers enforce that restriction for typeid(T), but not for typeid(T&). Can anyone explain this?
Nathan Sidwell: I think this restriction is so that whenever the compiler has to emit a typeid object of a class type, it knows what the base classes are, and can therefore emit an array of pointers-to-base-class typeids. Such a tree is necessary to implement dynamic_cast and exception catching (in a commonly implemented and obvious manner). If the class could be incomplete, the compiler might have to emit a typeid for incomplete Foo in one object file and a typeid for complete Foo in another object file. The compilation system will then have to make sure that (a) those compare equal and (b) the complete Foo gets priority, if that is applicable.
Unfortunately, there is a problem with exceptions that means there still can be a need to emit typeids for incomplete class. Namely one can throw a pointer-to-pointer-to-incomplete. To implement the matching of pointer-to-derived being caught by pointer-to-base, it is necessary for the typeid of a pointer type to contain a pointer to the typeid of the pointed-to type. In order to do the qualification matching on a multi-level pointer type, one has a chain of pointer typeids that can terminate in the typeid of an incomplete type. You cannot simply NULL-terminate the chain, because one must distinguish between different incomplete types.
Dave Abrahams: So if implementations are still required to be able to do it, for all practical purposes, why aren't we letting the user have the benefits?
Notes from the April, 2006 meeting:
There was some concern expressed that this might be difficult under the IA64 ABI. It was also observed that while it is necessary to handle exceptions involving incomplete types, there is no requirement that the RTTI data structures be used for exception handling.
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 error
More seriously, template argument deduction can give surprising
results:
struct A {
int i;
virtual void f() = 0;
};
struct B : A {
int j;
B() : j(5) {}
virtual void f();
};
struct C : B {
C() { j = 10; }
};
template <class T>
int DefaultValue( int (T::*m) ) {
return T().*m;
}
... DefaultValue( &B::i ) // Error: A is abstract
... DefaultValue( &C::j ) // returns 5, not 10.
Suggested resolution: 5.3.1 expr.unary.op should be changed to read,
If the member is a nonstatic member (perhaps by inheritance) of the class nominated by the nested-name-specifier of the qualified-id having type T, the type of the result is "pointer to member of class nested-name-specifier of type T."and the comment in the example should be changed to read,
// has type int B::*
Notes from 04/00 meeting:
The rationale for the current treatment is to permit the widest possible use to be made of a given address-of-member expression. Since a pointer-to-base-member can be implicitly converted to a pointer-to-derived-member, making the type of the expression a pointer-to-base-member allows the result to initialize or be assigned to either a pointer-to-base-member or a pointer-to-derived-member. Accepting this proposal would allow only the latter use.
Additional notes:
Another problematic example has been mentioned:
class Base {
public:
int func() const;
};
class Derived : public Base {
};
template<class T>
class Templ {
public:
template<class S>
Templ(S (T::*ptmf)() const);
};
void foo()
{
Templ<Derived> x(&Derived::func); // ill-formed
}
In this example, even though the conversion of &Derived::func to int (Derived::*)() const is permitted, the initialization of x cannot be done because template argument deduction for the constructor fails.
If the suggested resolution were adopted, the amount of code broken by the change might be reduced by adding an implicit conversion from pointer-to-derived-member to pointer-to-base-member for appropriate address-of-member expressions (not for arbitrary pointers to members, of course).
(See also issue 247.)
In 5.3.1 expr.unary.op, part of paragraph 7 describes how to compute the negative of an unsigned quantity:
The negative of an unsigned quantity is computed by subtracting its value from 2n, where n is the number of bits in the promoted operand. The type of the result is the type of the promoted operand.
According to this method, -0U will get the value 2n - 0 = 2n, where n is the number of bits in an unsigned int. However, 2n is obviously out of the range of values representable by an unsigned int and thus not the actual value of -0U. The get the result, a truncating conversion must be applied.
Requirements for the alignment of pointers returned by new-expressions are given in 5.3.4 expr.new paragraph 10:
For arrays of char and unsigned char, the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the most stringent alignment requirement (3.9 basic.types) of any object type whose size is no greater than the size of the array being created.
The intent of this wording is that the pointer returned by the new-expression will be suitably aligned for any data type that might be placed into the allocated storage (since the allocation function is constrained to return a pointer to maximally-aligned storage). However, there is an implicit assumption that each alignment requirement is an integral multiple of all smaller alignment requirements. While this is probably a valid assumption for all real architectures, there's no reason that the Standard should require it.
For example, assume that int has an alignment requirement of 3 bytes and double has an alignment requirement of 4 bytes. The current wording only requires that a buffer that is big enough for an int or a double be aligned on a 4-byte boundary (the more stringent requirement), but that would allow the buffer to be allocated on an 8-byte boundary — which might not be an acceptable location for an int.
Suggested resolution: Change "of any object type" to "of every object type."
A similar assumption can be found in 5.2.10 expr.reinterpret.cast paragraph 7:
...converting an rvalue of type "pointer to T1" to the type "pointer to T2" (where ... the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value...
Suggested resolution: Change the wording to
...converting an rvalue of type "pointer to T1" to the type "pointer to T2" (where ... the alignment requirements of T1 are an integer multiple of those of T2) and back to its original type yields the original pointer value...
The same change would also be needed in paragraph 9.
According to the C++ Standard section 5.3.4 expr.new paragraph 21 it is unspecified whether the allocation function is called before evaluating the constructor arguments or after evaluating the constructor arguments but before entering the constructor.
On top of that paragraph 17 of the same section insists that
If any part of the object initialization described above [Footnote: This may include evaluating a new-initializer and/or calling a constructor.] terminates by throwing an exception and a suitable deallocation function is found, the deallocation function is called to free the memory in which the object was being constructed... If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object's memory to be freed...
Now suppose we have:
struct copy_throw {
copy_throw(const copy_throw&)
{ throw std::logic_error("Cannot copy!"); }
copy_throw(long, copy_throw)
{ }
copy_throw()
{ }
};
int main()
try {
copy_throw an_object, /* undefined behaviour */
* a_pointer = ::new copy_throw(0, an_object);
return 0;
}
catch(const std::logic_error&)
{ }
Here the new-expression '::new copy_throw(0, an_object)' throws an exception when evaluating the constructor's arguments and before the allocation function is called. However, 5.3.4 expr.new paragraph 17 prescribes that in such a case the implementation shall call the deallocation function to free the memory in which the object was being constructed, given that a matching deallocation function can be found.
So a call to the Standard library deallocation function '::operator delete(void*)' shall be issued, but what argument is an implementation supposed to supply to the deallocation function? As per 5.3.4 expr.new paragraph 17 - the argument is the address of the memory in which the object was being constructed. Given that no memory has yet been allocated for the object, this will qualify as using an invalid pointer value, which is undefined behaviour by virtue of 3.7.3.2 basic.stc.dynamic.deallocation paragraph 4.
Suggested resolution:
Change the first sentence of 5.3.4 expr.new paragraph 17 to read:
If the memory for the object being created has already been successfully allocated and any part of the object initialization described above...
Looking up operator new in a new-expression uses a different mechanism from ordinary lookup. According to 5.3.4 expr.new paragraph 9,
If the new-expression begins with a unary :: operator, the allocation function's name is looked up in the global scope. Otherwise, if the allocated type is a class type T or array thereof, the allocation function's name is looked up in the scope of T. If this lookup fails to find the name, or if the allocated type is not a class type, the allocation function's name is looked up in the global scope.
Note in particular that the scope in which the new-expression occurs is not considered. For example,
void f() {
void* operator new(std::size_t, void*);
int* i = new int; // okay?
}
In this example, the implicit reference to operator new(std::size_t) finds the global declaration, even though the block-scope declaration of operator new with a different signature would hide it from an ordinary reference.
This seems strange; either the block-scope declaration should be ill-formed or it should be found by the lookup.
Notes from October 2004 meeting:
The CWG agreed that the block-scope declaration should not be found by the lookup in a new-expression. It would, however, be found by ordinary lookup if the allocation function were invoked explicitly.
(See also issue 256.)
An implementation may have an unspecified amount of array allocation overhead (5.3.4 expr.new paragraph 10), so that evaluation of a new-expression in which the new-type-id is T[n] involves a request for more than n * sizeof(T) bytes of storage through the relevant operator new[] function.
The placement operator new[] function does not and cannot check whether the requested size is less than or equal to the size of the provided region of memory (18.4.1.3 lib.new.delete.placement paragraphs 5-6). A program using placement array new must calculate what the requested size will be in advance.
Therefore any program using placement array new must take into account the implementation's array allocation overhead, which cannot be obtained or calculated by any portable means.
Notes from the April, 2005 meeting:
While the CWG agreed that there is no portable means to accomplish this task in the current language, they felt that a paper is needed to analyze the numerous mechanisms that might address the problem and advance a specific proposal. There is no volunteer to write such a paper at this time.
Issue 256 was closed without action, principally on the the grounds that an implementation could provide a means (command-line option, #pragma, etc.) for requesting that the allocation size be checked for validity, but that “it would not be appropriate to require this overhead for every array allocation in every program.”
This rationale may be giving too much weight to the overhead such a check would add, especially when compared to the likely cost of actually doing the storage allocation. In particular, the test essentially amounts to something like
if (max_allocation_size / sizeof(T) < num_elements)
throw std::bad_alloc();
(noting that max_allocation_size/sizeof(T) is a compile-time constant). It might make more sense to turn the rationale around and require the check, assuming that implementations could provide a mechanism for suppressing it if needed.
Suggested resolution:
In 5.3.4 expr.new paragraph 7, add the following words before the example:
If the value of the expression is such that the size of the allocated object would exceed the implementation-defined limit, an exception of type std::bad_alloc is thrown and no storage is obtained.
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[](std::size_t) or operator new[](std::size_t, const std::nothrow_t&) in the standard library.
This statement might be read as requiring an implementation, when processing an array delete-expression and calling the deallocation function, to perform the inverse of the calculation applied to the result of the allocation function to produce the value of the new-expression. (5.3.5 expr.delete paragraph 2 requires that the operand of an array delete-expression "be the pointer value which resulted from a previous array new-expression.") However, it is not completely clear whether the "shall" expresses an implementation requirement or a program requirement (or both). Furthermore, there is no direct statement about user-defined deallocation functions.
Suggested resolution: A note should be added to 5.3.5 expr.delete to clarify that any offset added in an array new-expression must be subtracted in the array delete-expression.
Does the Standard require that the deallocation function will be called if the destructor throws an exception? For example,
struct S {
~S() { throw 0; }
};
void f() {
try {
delete new S;
}
catch(...) { }
}
The question is whether the memory for the S object will be freed or not. It doesn't appear that the Standard answers the question, although most people would probably assume that it will be freed.
Notes from 04/01 meeting:
There is a widespread feeling that it is a poor programming practice to allow destructors to terminate with an exception (see issue 219). This question is thus viewed as a tradeoff between efficiency and supporting "bad code." It was observed that there is no way in the current language to protect against a throwing destructor, since the throw might come from a virtual override.
It was suggested that the resolution to the issue might be to make it implementation-defined whether the storage is freed if the destructor throws. Others suggested that the Standard should require that the storage be freed, with the understanding that implementations might have a flag to allow optimizing away the overhead. Still others thought that both this issue and issue 219 should be resolved by forbidding a destructor to exit via an exception. No consensus was reached.
The requirements for the operand of the delete operators are given in 5.3.5 expr.delete paragraph 2:
In either alternative, the value of the operand of delete may be a null pointer value. If it is not a null pointer value, in the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a subobject (1.8 intro.object) representing a base class of such an object (clause 10 class.derived). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete shall be the pointer value which resulted from a previous array new-expression. If not, the behavior is undefined.
There are no restrictions on the type of a null pointer, only on a pointer that is not null. That seems wrong.
The meaning of an old-style cast is described in terms of const_cast, static_cast, and reinterpret_cast in 5.4 expr.cast paragraph 5. Ignoring const_cast for the moment, it basically says that if the conversion performed by a given old-style cast is one of those performed by static_cast, the conversion is interpreted as if it were a static_cast; otherwise, it's interpreted as if it were a reinterpret_cast, if possible. The following example is given in illustration:
struct A {};
struct I1 : A {};
struct I2 : A {};
struct D : I1, I2 {};
A *foo( D *p ) {
return (A*)( p ); // ill-formed static_cast interpretation
}
The obvious intent here is that a derived-to-base pointer conversion is one of the conversions that can be performed using static_cast, so (A*)(p) is equivalent to static_cast<A*>(p), which is ill-formed because of the ambiguity.
Unfortunately, the description of static_cast in 5.2.9 expr.static.cast does NOT support this interpretation. The problem is in the way 5.2.9 expr.static.cast lists the kinds of casts that can be performed using static_cast. Rather than saying something like "All standard conversions can be performed using static_cast," it says
An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration "T t(e);" is well-formed, for some invented temporary variable t.
Given the declarations above, the hypothetical declaration
A* t(p);
is NOT well-formed, because of the ambiguity. Therefore the old-style cast (A*)(p) is NOT one of the conversions that can be performed using static_cast, and (A*)(p) is equivalent to reinterpret_cast<A*>(p), which is well-formed under 5.2.10 expr.reinterpret.cast paragraph 7.
Other situations besides ambiguity which might raise similar questions include access violations, casting from virtual base to derived, and casting pointers-to-members when virtual inheritance is involved.
The current Standard leaves it implementation-defined whether integer division rounds the result toward 0 or toward negative infinity and thus whether the result of % may be negative. C99, apparently reflecting (nearly?) unanimous hardware practice, has adopted the rule that integer division rounds toward 0, thus requiring that the result of -1 % 5 be -1. Should the C++ Standard follow suit?
On a related note, does INT_MIN % -1 invoke undefined behavior? The % operator is defined in terms of the / operator, and INT_MIN / -1 overflows, which by 5 expr paragraph 5 causes undefined behavior; however, that is not the “result” of the % operation, so it's not clear. The wording of 5.6 expr.mul paragraph 4 appears to allow % to cause undefined behavior only when the second operand is 0.
In C, this is ill-formed (cf C99 6.5.8):
void f(char* s) {
if (s < 0) { }
}
...but in C++, it's not. Why? Who would ever need to write (s > 0) when they could just as well write (s != 0)?
This has been in the language since the ARM (and possibly earlier); apparently it's because the pointer conversions (4.10 conv.ptr) need to be performed on both operands whenever one of the operands is of pointer type. So it looks like the "null-ptr-to-real-pointer-type" conversion is hitching a ride with the other pointer conversions.
The relational operators have unspecified results when comparing pointers that refer to objects that are not members of the same object or elements of the same array (5.9 expr.rel paragraph 2, second bullet). This restriction (which dates from C89) stems from the desire not to penalize implementations on architectures with segmented memory by forcing them essentially to simulate a flat address space for the purpose of these comparisons. If such an implementation requires that objects and arrays to fit within a single segment, this restriction enables pointer comparison to be done simply by comparing the offset portion of the pointers, which could be much faster than comparing the full pointer values.
The problem with this restriction in C++ is that it forces users of the Standard Library containers to use less<T*> instead of the built-in < operator to provide a total ordering on pointers, a usage that is inconvenient and error-prone. Can the existing restriction be relaxed in some way to allow the built-in operator to provide a total ordering? (John Spicer pointed out that the actual comparison for a segmented architecture need only supply a total ordering of pointer values, not necessarily the complete linearization of the address space.)
Does an explicit temporary of an integral type qualify as an integral constant expression? For instance,
void* p = int(); // well-formed?
It would appear to be, since int() is an explicit type conversion according to 5.2.3 expr.type.conv (at least, it's described in a section entitled "Explicit type conversion") and type conversions to integral types are permitted in integral constant expressions (5.19 expr.const). However, this reasoning is somewhat tenuous, and some at least have argued otherwise.
I've seen some pieces of code recently that put complex expressions involving overload resolution inside sizeof operations in constant expressions in templates.
5.19 expr.const paragraph 1 implies that some kinds of nonconstant expressions are allowed inside a sizeof in a constant expression, but it's not clear that this was intended to extend all the way to things like overload resolution. Allowing such things has some hidden costs. For example, name mangling has to be able to represent all operators, including calls, and not just the operators that can appear in constant expressions.
template <int I> struct A {};
char xxx(int);
char xxx(float);
template <class T> A<sizeof(xxx((T)0))> f(T){}
int main()
{
f(1);
}
If complex expressions are indeed allowed, it should be because of an explicit committee decision rather than because of some looseness in this section of the standard.
Notes from the 4/02 meeting:
Any argument for restricting such expressions must involve a cost/benefit ratio: a restriction would be palatable only if it causes minimum hardship for users and allows a substantial reduction in implementation cost. If we propose a restriction, it must be one that library writers can live with.
Lots of these cases fail with current compilers, so there can't be a lot of existing code using them. We plan to find out what cases there are in libraries like Loki and Boost.
We noted that in many cases one can move the code into a class to get the same result. The implementation problem comes up when the expression-in-sizeof is in a template deduction context or part of a template signature. The problem cases are ones where an error causes deduction to fail, as opposed to contexts where an error causes a diagnostic. The latter contexts are easier to handle; however, there are situations where "fail deduction" may be the desired behavior.
Notes from the April 2003 meeting:
Here is a better example:
extern "C" int printf(const char *, ...);
char f(int);
int f(...);
// Approach 1 -- overload resolution in template class
// No problem
template <class T> struct conv_int {
static const bool value = (sizeof(f(T())) == 1);
};
// Approach 2 -- overload resolution in type deduction
// Difficult
template <int I> struct A {
static const int value = I;
};
template <class T> bool conv_int2(A<sizeof(f(T()))> p) {
return p.value == 1;
}
int main() {
printf("short: %d\n", conv_int<short>::value);
printf("int *: %d\n", conv_int<int *>::value);
printf("short: %d\n", conv_int2<short>());
printf("int *: %d\n", conv_int2<int *>());
}
The core working group liked the idea of a restriction that says that expressions inside sizeof in template signature contexts must be otherwise valid as nontype template argument expressions (i.e., integer operations only, limited casts). This of course is subject to whether users can live with that restriction. This topic was brought up in full committee, but there was limited feedback from other groups.
It was also noted that if typeof (whatever it is called) is added, there may be a similar issue there.
Note (March, 2005):
Dave Abrahams (quoting a Usenet posting by Vladimir Marko): The decltype and auto proposal (revision 3: N1607) presents
template <class T,class U>
decltype((*(T*)0)+(*(U*)0)) add(const T& t,const U& u);
as a valid declaration (if the proposal is accepted). If [the restrictions in the April, 2003 note] really applied to decltype, the declaration above would be invalid. AFAICT every non-trivial use of decltype in a template function declaration would be invalid. And for me this would render my favorite proposal useless.
I would propose to allow any kind of expression inside sizeof (and decltype) and explicitly add sizeof (and decltype) expressions involving template-parameters to non-deduced contexts (add a bullet to 14.8.2.4 temp.deduct.partial paragraph 4).
Jaakko Jarvi: Just reinforcing that this is important and hope for insights. The topic is discussed a bit on page 10 of the latest revision of the proposal (N1705). Here's a quote from the proposal:
However, it is crucial that no restrictions are placed on what kinds of expressions are allowed inside decltype, and therefore also inside sizeof. We suggest that issue 339 is resolved to require the compiler to fail deduction (apply the SFINAE principle), and not produce an error, for as large set of invalid expressions in operands of sizeof or decltype as is possible to comfortably implement. We wish that implementors aid in classifying the kinds of expressions that should produce errors, and the kinds that should lead to failure of deduction.
Paragraph 6.6 stmt.jump paragraph 2 of the standard says:
On exit from a scope (however accomplished), destructors (12.4 class.dtor) are called for all constructed objects with automatic storage duration (3.7.2 basic.stc.auto) (named objects or temporaries) that are declared in that scope.
It refers to objects "that are declared" but the text in parenthesis also mentions temporaries, which cannot be declared. I think that text should be removed.
This is related to issue 276.
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:
enum { }; // ill-formed
typedef class { }; // ill-formed
—end example]
In the absence of any explicit restrictions in
7.1.3
dcl.typedef
, this paragraph appears
to allow declarations like the following:
typedef struct S { }; // no declarator
typedef enum { e1 }; // no declarator
In fact, the final example in
7
dcl.dcl
paragraph 3 would seem to
indicate that this is intentional: since it is illustrating the
requirement that the decl-specifier-seq must introduce a name
in declarations in which the init-declarator-list is omitted,
presumably the addition of a class name would have made the example
well-formed.
On the other hand, there is no good reason to allow such declarations; the only reasonable scenario in which they might occur is a mistake on the programmer's part, and it would be a service to the programmer to require that such errors be diagnosed.
Suppose we've got this class definition:
struct X {
void f();
static int n;
};
I think I can deduce from the existing standard that the following member definitions are ill-formed:
static void X::f() { }
static int X::n;
To come to that conclusion, however, I have to put together several things in different parts of the standard. I would have expected to find an explicit statement of this somewhere; in particular, I would have expected to find it in 7.1.1 dcl.stc. I don't see it there, or anywhere.
Gabriel Dos Reis: Or in 3.5 basic.link which is about linkage. I would have expected that paragraph to say that that members of class types have external linkage when the enclosing class has an external linkage. Otherwise 3.5 basic.link paragraph 8:
Names not covered by these rules have no linkage.
might imply that such members do not have linkage.
Notes from the April, 2005 meeting:
The question about the linkage of class members is already covered by 3.5 basic.link paragraph 5.
The constraints on type-specifiers given in 7.1.5 dcl.type paragraphs 2 and 3 (at most one type-specifier except as specified, at least one type-specifier, no redundant cv-qualifiers) are couched in terms of decl-specifier-seqs and declarations. However, they should also apply to constructs that are not syntactically declarations and that are defined to use type-specifier-seqs, including 5.3.4 expr.new, 6.6 stmt.jump, 8.1 dcl.name, and 12.3.2 class.conv.fct.
The phrase “top-level cv-qualifier” is used numerous times in the Standard, but it is not defined. The phrase could be misunderstood to indicate that the const in something like const T& is at the “top level,” because where it appears is the highest level at which it is permitted: T& const is ill-formed.
7.1.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?
The auto specifier can be used only in certain contexts, as specified in 7.1.5.4 dcl.spec.auto paragraphs 2-3:
Otherwise (auto appearing with no type specifiers other than cv-qualifiers), the auto type-specifier signifies that the type of an object being declared shall be deduced from its initializer. The name of the object being declared shall not appear in the initializer expression.
This use of auto is allowed when declaring objects in a block (6.3 stmt.block), in namespace scope (3.3.5 basic.scope.namespace), and in a for-init-statement (6.5.3 stmt.for). The decl-specifier-seq shall be followed by one or more init-declarators, each of which shall have a non-empty initializer of either of the following forms:
= assignment-expression
( assignment-expression )
It was intended that auto could be used only at the top level of a declaration, but it is not clear whether this wording is sufficient to forbid usage like the following:
template <class T> struct A {};
template <class T> void f(A<T> x) {}
void g()
{
f(A<short>());
A<auto> x = A<short>();
}
Section 7.3.3 namespace.udecl paragraph 8 says:
A using-declaration is a declaration and can therefore be used repeatedly where (and only where) multiple declarations are allowed.It contains the following example:
namespace A {
int i;
}
namespace A1 {
using A::i;
using A::i; // OK: double declaration
}
void f()
{
using A::i;
using A::i; // error: double declaration
}
However, if "using A::i;" is really a declaration, and not a definition, it is far from clear that repeating it should be an error in either context.
Consider:
namespace A {
int i;
void g();
}
void f() {
using A::g;
using A::g;
}
Surely the definition of f should be analogous to
void f() {
void g();
void g();
}
which is well-formed because "void g();" is a declaration and
not a definition.
Indeed, if the double using-declaration for A::i is prohibited in f, why should it be allowed in namespace A1?
Proposed Resolution (04/99): Change the comment "// error: double declaration" to "// OK: double declaration". (This should be reviewed against existing practice.)
Notes from 04/00 meeting:
The core language working group was unable to come to consensus over what kind of declaration a using-declaration should emulate. In a straw poll, 7 members favored allowing using-declarations wherever a non-definition declaration could appear, while 4 preferred to allow multiple using-declarations only in namespace scope (the rationale being that the permission for multiple using-declarations is primarily to support its use in multiple header files, which are seldom included anywhere other than namespace scope). John Spicer pointed out that friend declarations can appear multiple times in class scope and asked if using-declarations would have the same property under the "like a declaration" resolution.
As a result of the lack of agreement, the issue was returned to "open" status.
See also issues 56, 85, and 138..
Additional notes (January, 2005):
Some related issues have been raised concerning the following example (modified from a C++ validation suite test):
struct A
{
int i;
static int j;
};
struct B : A { };
struct C : A { };
struct D : virtual B, virtual C
{
using B::i;
using C::i;
using B::j;
using C::j;
};
Currently, it appears that the using-declarations of i are ill-formed, on the basis of 7.3.3 namespace.udecl paragraph 10:
Since a using-declaration is a declaration, the restrictions on declarations of the same name in the same declarative region (3.3 basic.scope) also apply to using-declarations.
Because the using-declarations of i refer to different objects, declaring them in the same scope is not permitted under 3.3 basic.scope. It might, however, be preferable to treat this case as many other ambiguities are: allow the declaration but make the program ill-formed if a name reference resolves to the ambiguous declarations.
The status of the using-declarations of j, however, is less clear. They both declare the same entity and thus do not violate the rules of 3.3 basic.scope. This might (or might not) violate the restrictions of 9.2 class.mem paragraph 1:
Except when used to declare friends (11.4 class.friend) or to introduce the name of a member of a base class into a derived class (7.3.3 namespace.udecl, 11.3 class.access.dcl), member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class. A member shall not be declared twice in the member-specification, except that a nested class or member class template can be declared and then later defined.
Do the using-declarations of j repeatedly declare the same member? Or is the preceding sentence an indication that a using-declaration is not a declaration of a member?
The following came up recently on comp.lang.c++.moderated (edited for brevity):
namespace N1 {
template<typename T> void f( T* x ) {
// ... other stuff ...
delete x;
}
}
namespace N2 {
using N1::f;
template<> void f<int>( int* ); // A: ill-formed
class Test {
~Test() { }
friend void f<>( Test* x ); // B: ill-formed?
};
}
I strongly suspect, but don't have standardese to prove, that the friend declaration in line B is ill-formed. Can someone show me the text that allows or disallows line B?
Here's my reasoning: Writing "using" to pull the name into namespace N2 merely allows code in N2 to use the name in a call without qualification (per 7.3.3 namespace.udecl). But just as declaring a specialization must be done in the namespace where the template really lives (hence line A is ill-formed), I suspect that declaring a specialization as a friend must likewise be done using the original namespace name, not obliquely through a "using". I see nothing in 7.3.3 namespace.udecl that would permit this use. Is there?
Andrey Tarasevich: 14.5.3 temp.friend paragraph 2 seems to get pretty close: "A friend declaration that is not a template declaration and in which the name of the friend is an unqualified 'template-id' shall refer to a specialization of a function template declared in the nearest enclosing namespace scope".
Herb Sutter: OK, thanks. Then the question in this is the word "declared" -- in particular, we already know we cannot declare a specialization of a template in any other namespace but the original one.
John Spicer: This seems like a simple question, but it isn't.
First of all, I don't think the standard comments on this usage one way or the other.
A similar example using a namespace qualified name is ill-formed based on 8.3 dcl.meaning paragraph 1:
namespace N1 {
void f();
}
namespace N2 {
using N1::f;
class A {
friend void N2::f();
};
}
Core issue 138 deals with this example:
void foo();
namespace A{
using ::foo;
class X{
friend void foo();
};
}
The proposed resolution (not yet approved) for issue 138 is that the friend declares a new foo that conflicts with the using-declaration and results in an error.
Your example is different than this though because the presence of the explicit argument list means that this is not declaring a new f but is instead using a previously declared f.
One reservation I have about allowing the example is the desire to have consistent rules for all of the "declaration like" uses of template functions. Issue 275 (in DR status) addresses the issue of unqualified names in explicit instantiation and explicit specialization declarations. It requires that such declarations refer to templates from the namespace containing the explicit instantiation or explicit specialization. I believe this rule is necessary for those directives but is not really required for friend declarations -- but there is the consistency issue.
Notes from April 2003 meeting:
This is related to issue 138. John Spicer is supposed to update his paper on this topic. This is a new case not covered in that paper. We agreed that the B line should be allowed.
The Standard does not appear to specify what happens for code like the following:
namespace one {
template<typename T> void fun(T);
}
using one::fun;
template<typename T> void fun(T);
7.3.3 namespace.udecl paragraph 13 does not appear to apply because it deals only with functions, not function templates:
If a function declaration in namespace scope or block scope has the same name and the same parameter types as a function introduced by a using-declaration, and the declarations do not declare the same function, the program is ill-formed.
John Spicer: For function templates I believe the rule should be that if they have the same function type (parameter types and return type) and have identical template parameter lists, the program is ill-formed.
It is not clear whether some of the wording in 7.5 dcl.link that applies only to function types and names ought also to apply to object names. In particular, paragraph 3 says,
Every implementation shall provide for linkage to functions written in the C programming language, "C", and linkage to C++ functions, "C++".
Nothing is said about variable names, apparently meaning that implementations need not provide C (or even C++!) linkage for variable names. Also, paragraph 5 says,
Except for functions with C++ linkage, a function declaration without a linkage specification shall not precede the first linkage specification for that function. A