Document number:  J16/08-0118 = WG21 N2608
Date:  2008-05-18
Project:  Programming Language C++
Reference:  ISO/IEC IS 14882:2003
Reply to:  William M. Miller
 Edison Design Group, Inc.
 wmm@edg.com


C++ Standard Core Language Active Issues, Revision 55


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

This document is part of a group of related documents that together describe the issues that have been raised regarding the C++ Standard. The other documents in the group are:

Section references in this document reflect the section numbering of document J16/08-0981 = WG21 N2588.

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

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

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

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


Revision History

Issue status

Issues progress through various statuses as the Core Language Working Group and, ultimately, the full 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.


Issues with "Ready" Status


666. Dependent qualified-ids without the typename keyword

Section: 14.6  [temp.res]     Status: ready     Submitter: Daveed Vandevoorde     Date: 6 December 2007

14.6 [temp.res] paragraphs 2 and 4 read,

A name used in a template declaration or definition and that is dependent on a template-parameter is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.

If a specialization of a template is instantiated for a set of template-arguments such that the qualified-id prefixed by typename does not denote a type, the specialization is ill-formed.

It is not clear whether this is intended to, or is sufficient to, render a specialization ill-formed if a dependent qualified-id that is not prefixed by typename actually does denote a type. For example,

    int i;

    template <class T> void f() {
        T::x * i; // declaration or multiplication!?
    }

    struct Foo {
        typedef int x;
    };

    struct Bar {
        static int const x = 5;
    };

    int main() {
        f<Bar>(); // multiplication
        f<Foo>(); // declaration!
    }

I think that the specialization for Foo should be ill-formed.

Proposed resolution (February, 2008):

Add the following after 14.6 [temp.res] paragraph 5:

If, for a given set of template arguments, a specialization of a template is instantiated that refers to a qualified-id that denotes a type, and the nested-name-specifier of the qualified-id depends on a template parameter, the qualified-id shall either be prefixed by typename or shall be used in a context in which it implicitly names a type as described above. [Example:

    template <class T> void f(int i) {
      T::x * i;     // T::x must not be a type
    }

    struct Foo {
      typedef int x;
    };

    struct Bar {
      static int const x = 5;
    };

    int main() {
      f<Bar>(1);     // OK
      f<Foo>(1);     // error: Foo::x is a type
    }

end example]




485. What is a “name”?

Section: 3  [basic]     Status: ready     Submitter: Gabriel Dos Reis     Date: 9 Nov 2004

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

The last two bullets contradict the definition of name in paragraph 4 because they are not identifiers.

This definition affects other parts of the Standard, as well. For example, in 3.4.2 [basic.lookup.argdep] paragraph 1,

When an unqualified name is used as the postfix-expression in a function call (5.2.2 [expr.call]), other namespaces not considered during the usual unqualified lookup (3.4.1 [basic.lookup.unqual]) may be searched, and in those namespaces, namespace-scope friend function declarations (11.4 [class.friend]) not otherwise visible may be found.

With the current definition of name, argument-dependent lookup apparently does not apply to function-notation calls to overloaded operators.

Another related question is whether a template-id is a name or not and thus would trigger an argument-dependent lookup. Personally, I have always viewed a template-id as a name, just like operator+.

Proposed Resolution (November, 2006):

  1. Change clause 3 [basic] paragraphs 3-8 as follows:

    1. An entity is a value, object, subobject, base class subobject, array element, variable, reference, function, instance of a function, enumerator, type, class member, template, template specialization, namespace, or parameter pack.

    2. A name is a use of an identifier identifier (2.10 [lex.name]), operator-function-id (13.5 [over.oper]), conversion-function-id (12.3.2 [class.conv.fct]), or template-id (14.2 [temp.names]) that denotes an entity or label (6.6.4 [stmt.goto], 6.1 [stmt.label]). A variable is introduced by the declaration of an object. The variable’s name denotes the object.

    3. Every name that denotes an entity is introduced by a declaration. Every name that denotes a label is introduced either by a goto statement (6.6.4 [stmt.goto]) or a labeled-statement (6.1 [stmt.label]).

    4. A variable is introduced by the declaration of an object. The variable's name denotes the object.

    5. Some names denote types, classes, enumerations, or templates. In general, it is necessary to determine whether or not a name denotes one of these entities before parsing the program that contains it. The process that determines this is called name lookup (3.4 [basic.lookup]).

    6. Two names are the same if

      • they are identifiers identifiers composed of the same character sequence; or

      • they are the names of overloaded operator functions operator-function-ids formed with the same operator; or

      • they are the names of user-defined conversion functions conversion-function-ids formed with the same type., or

      • they are template-ids that refer to the same class or function (14.4 [temp.type]).

    7. An identifier A name used in more than one translation unit can potentially refer to the same entity in these translation units depending on the linkage (3.5 [basic.link]) of the identifier name specified in each translation unit.

  2. Change 3.3.6 [basic.scope.class] paragraph 1 item 5 as follows:

    The potential scope of a declaration that extends to or past the end of a class definition also extends to the regions defined by its member definitions, even if the members are defined lexically outside the class (this includes static data member definitions, nested class definitions, member function definitions (including the member function body and any portion of the declarator part of such definitions which follows the identifier declarator-id, including a parameter-declaration-clause and any default arguments (8.3.6 [dcl.fct.default]).

    [Drafting note: This last change is not really mandated by the issue, but it's another case of “identifier” confusion.]

(This proposed resolution also resolves issue 309.)




141. Non-member function templates in member access expressions

Section: 3.4.5  [basic.lookup.classref]     Status: ready     Submitter: fvali     Date: 31 July 1999

3.4.5 [basic.lookup.classref] paragraph 1 says,

In a class member access expression (5.2.5 [expr.ref] ), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2 [temp.names] ) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class or function template.

There do not seem to be any circumstances in which use of a non-member template function would be well-formed as the id-expression of a class member access expression.

Proposed Resolution (November, 2006):

Change 3.4.5 [basic.lookup.classref] paragraph 1 as follows:

In a class member access expression (5.2.5 [expr.ref]), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2 [temp.names]) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class or function template...



28. 'exit', 'signal' and static object destruction

Section: 3.6.3  [basic.start.term]     Status: ready     Submitter: Martin J. O'Riordan     Date: 19 Oct 1997

The C++ standard has inherited the definition of the 'exit' function more or less unchanged from ISO C.

However, when the 'exit' function is called, objects of static extent which have been initialised, will be destructed if their types posses a destructor.

In addition, the C++ standard has inherited the definition of the 'signal' function and its handlers from ISO C, also pretty much unchanged.

The C standard says that the only standard library functions that may be called while a signal handler is executing, are the functions 'abort', 'signal' and 'exit'.

This introduces a bit of a nasty turn, as it is not at all unusual for the destruction of static objects to have fairly complex destruction semantics, often associated with resource release. These quite commonly involve apparently simple actions such as calling 'fclose' for a FILE handle.

Having observed some very strange behaviour in a program recently which in handling a SIGTERM signal, called the 'exit' function as indicated by the C standard.

But unknown to the programmer, a library static object performed some complicated resource deallocation activities, and the program crashed.

The C++ standard says nothing about the interaction between signals, exit and static objects. My observations, was that in effect, because the destructor called a standard library function other than 'abort', 'exit' or 'signal', while transitively in the execution context of the signal handler, it was in fact non-compliant, and the behaviour was undefined anyway.

This is I believe a plausible judgement, but given the prevalence of this common programming technique, it seems to me that we need to say something a lot more positive about this interaction.

Curiously enough, the C standard fails to say anything about the analogous interaction with functions registered with 'atexit' ;-)

Proposed Resolution (10/98):

The current Committee Draft of the next version of the ISO C standard specifies that the only standard library function that may be called while a signal handler is executing is 'abort'. This would solve the above problem.

[This issue should remain open until it has been decided that the next version of the C++ standard will use the next version of the C standard as the basis for the behavior of 'signal'.]

Notes (November, 2006):

C89 is slightly contradictory here: It allows any signal handler to terminate by calling abort, exit, longjmp, but (for asynchronous signals, i.e. not those produced by abort or raise) then makes calling any library function other than signal with the current signal undefined behavior (C89 7.7.1.1). For synchronous signals, C99 forbids calls to raise, but imposes no other restrictions. For asynchronous signals, C99 allows only calls to abort, _Exit, and signal with the current signal (C99 7.14.1.1). The current C++ WP refers to “plain old functions” and “conforming C programs” (18.8 [support.runtime] paragraph 6).

Proposed Resolution (November, 2006):

Change the footnote in 18.8 [support.runtime] paragraph 6 as follows:

In particular, a signal handler using exception handling is very likely to have problems. Also, invoking std::exit may cause destruction of objects, including those of the standard library implementation, which, in general, yields undefined behavior in a signal handler (see 1.9 [intro.execution]).



644. Should a trivial class type be a literal type?

Section: 3.9  [basic.types]     Status: ready     Submitter: Alisdair Meredith     Date: 8 Aug 2007

The original proposed wording for 3.9 [basic.types] paragraph 11 required a constexpr constructor for a literal class only “if the class has at least one user-declared constructor.” This wording was dropped during the review by CWG out of a desire to ensure that literal types not have any uninitialized members. Thus, a class like

    struct pixel {
        int x, y;
    };

is not a literal type. However, if an object of that type is aggregate-initialized or value-initialized, there can be no uninitialized members; the missing wording should be restored in order to permit use of expressions like pixel().x as constant expressions.

Proposed resolution (February, 2008):

Change 3.9 [basic.types] paragraph 10 as follows:

A type is a literal type if it is:



118. Calls via pointers to virtual member functions

Section: 5.2.2  [expr.call]     Status: ready     Submitter: Martin O'Riordan     Date: 17 May 1999

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.non-static] , 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.

Proposed resolution (February, 2008):

  1. Change 5.2.2 [expr.call] paragraph 1 as follows:

    ... For a member function call, the postfix expression shall be an implicit (9.3.1 [class.mfct.non-static], 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. The first expression in the postfix expression is then called the object expression, and; the call is as a member of the object pointed to or referred to by the object expression (5.2.5 [expr.ref], 5.5 [expr.mptr.oper]). In the case of an implicit class member access, the implied object is the one pointed to by this. [Note: a member function call of the form f() is interpreted as (*this).f() (see 9.3.1 [class.mfct.non-static]). —end note] If a function or member function name is used, the name can be overloaded (clause 13 [over]), in which case the appropriate function shall be selected according to the rules in 13.3 [over.match]. 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 If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called. Otherwise, its final overrider (10.3 [class.virtual]) in the dynamic type of the object expression is called. ...
  2. Change 5.5 [expr.mptr.oper] paragraph 4 as follows:

    The first operand is called the object expression. If the dynamic type of the object expression does not contain the member to which the pointer refers, the behavior is undefined.



288. Misuse of "static type" in describing pointers

Section: 5.3.5  [expr.delete]     Status: ready     Submitter: James Kuyper     Date: 19 May 2001

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 that
if 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 (December, 2006):

  1. Change 5.3.5 [expr.delete] paragraph 3 as follows:

  2. In the first alternative (delete object), if the static type of the operand object to be deleted is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type of the object to be deleted 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.
  3. Change the footnote in 12.5 [class.free] paragraph 4 as follows:

  4. 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 the delete-expression’s operand object to be deleted be the same as its dynamic type.
  5. Change the footnote in 12.5 [class.free] paragraph 5 as follows:

  6. If the static type in the delete-expression of 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).]




661. Semantics of arithmetic comparisons

Section: 5.9  [expr.rel]     Status: ready     Submitter: Daveed Vandevoorde     Date: 27 November 2007

The actual semantics of arithmetic comparison — e.g., whether 1 < 2 yields true or false — appear not to be specified anywhere in the Standard. The C Standard has a general statement that

Each of the operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) shall yield 1 if the specified relation is true and 0 if it is false.

There is no corresponding statement in the C++ Standard.

Proposed resolution (February, 2008):

  1. Append the following paragraph to the end of 5.9 [expr.rel]:

  2. If both operands (after conversions) are of arithmetic type, each of the operators shall yield true if the specified relation is true and false if it is false.
  3. Append the following paragraph to the end of 5.10 [expr.eq]:

  4. Each of the operators shall yield true if the specified relation is true and false if it is false.



276. Order of destruction of parameters and temporaries

Section: 6.6  [stmt.jump]     Status: ready     Submitter: James Kanze     Date: 28 Mar 2001

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.

Proposed Resolution (November, 2006):

Change 6.6 [stmt.jump] paragraph 2 as follows:

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. variables with automatic storage duration (3.7.2 [basic.stc.auto]) that have been constructed in that scope are destroyed in the reverse order of their construction. [Note: For temporaries, see 12.2 [class.temporary]. —end note] Transfer out of a loop...



663. Valid Cyrillic identifier characters

Section: E  [extendid]     Status: ready     Submitter: Steve Clamage     Date: 30 November 2007

The C99 and C++ Standards disagree about the validity of two Cyrillic characters for use in identifiers. C++ (E [extendid]) says that 040d is valid in an identifier but that 040e is not; C99 (Annex D) says exactly the opposite. In fact, both characters should be accepted in identifiers; see the Unicode chart.

Proposed resolution (February, 2008):

The reference in paragraph 2 should be changed to ISO/IEC TR 10176:2003 and the table should be changed to conform to the one in that document (beginning on page 34).






Issues with "Review" Status


637. Sequencing rules and example disagree

Section: 1.9  [intro.execution]     Status: review     Submitter: Ofer Porat     Date: 2 June 2007

In 1.9 [intro.execution] paragraph 16, the following expression is still listed as an example of undefined behavior:

    i = ++i + 1;

However, it appears that the new sequencing rules make this expression well-defined:

  1. The assignment side-effect is required to be sequenced after the value computations of both its LHS and RHS (5.17 [expr.ass] paragraph 1).

  2. The LHS (i) is an lvalue, so its value computation involves computing the address of i.

  3. In order to value-compute the RHS (++i + 1), it is necessary to first value-compute the lvalue expression ++i and then do an lvalue-to-rvalue conversion on the result. This guarantees that the incrementation side-effect is sequenced before the computation of the addition operation, which in turn is sequenced before the assignment side effect. In other words, it yields a well-defined order and final value for this expression.

It should be noted that a similar expression

    i = i++ + 1;

is still not well-defined, since the incrementation side-effect remains unsequenced with respect to the assignment side-effect.

It's unclear whether making the expression in the example well-defined was intentional or just a coincidental byproduct of the new sequencing rules. In either case either the example should be fixed, or the rules should be changed.

Clark Nelson: In my opinion, the poster's argument is perfectly correct. The rules adopted reflect the CWG's desired outcome for issue 222. At the Portland meeting, I presented (and still sympathize with) Tom Plum's case that these rules go a little too far in nailing down required behavior; this is a consequence of that.

One way or another, a change needs to be made, and I think we should seriously consider weakening the resolution of issue 222 to keep this example as having undefined behavior. This could be done fairly simply by having the sequencing requirements for an assignment expression depend on whether it appears in an lvalue context.

James Widman: How's this for a possible re-wording?

In all cases, the side effect of the assignment expression is sequenced after the value computations of the right and left operands. Furthermore, if the assignment expression appears in a context where an lvalue is required, the side effect of the assignment expression is sequenced before its value computation.

Notes from the February, 2008 meeting:

There was no real support in the CWG for weakening the resolution of issue 222 and returning the example to having undefined behavior. No one knew of an implementation that doesn't already do the (newly) right thing for such an example, so there was little motivation to go out of our way to increase the domain of undefined behavior. So the proposed resolution is to change the example to one that definitely does have undependable behavior in existing practice, and undefined behavior under the new rules.

Also, the new formulation of the sequencing rules approved in Oxford contained the wording that by and large resolved issue 222, so with the resolution of this issue, we can also close issue 222.

Proposed resolution (March, 2008):

Change the example in 1.9 [intro.execution] paragraph 16 as follows:

    i = v[i++];             // the behavior is undefined
    i = 7, i++, i++;        // i becomes 9
    i = ++i i++ + 1;        // the behavior is undefined
    i = i + 1;              // the value of i is incremented



639. What makes side effects “different” from one another?

Section: 1.9  [intro.execution]     Status: review     Submitter: James Widman     Date: 26 July 2007

Is the behavior undefined in the following example?

    void f() {
         int n = 0;
         n = --n;
    }

1.9 [intro.execution] paragraph 16 says,

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

It's not clear to me whether the two side-effects in n=--n are “different.” As far as I can tell, it seems that both side-effects involve the assignment of -1 to n, which in a sense makes them non-“different.” But I don't know if that's the intent. Would it be better to say “another” instead of “a different?”

On a related note, can we include this example to illustrate?

    void f( int, int );
    void g( int a ) { f( a = -1, a = -1 ); } // Undefined?

Proposed resolution (March, 2008):

Change paragraph 16 as follows:

...If a side effect on a scalar object is unsequenced relative to either a different another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. [Example:

    void f(int, int);
    void g(int i, int* v) {
        i = v[i++];         // the behavior is undefined
        i = 7, i++, i++;    // i becomes 9

        i = ++i + 1;        // the behavior is undefined
        i = i + 1;          // the value of i is incremented

        f(i = -1, i = -1);  // the behavior is undefined
    }

end example] When calling...




608. Determining the final overrider of a virtual function

Section: 10.3  [class.virtual]     Status: review     Submitter: Mike Miller     Date: 7 December 2006

According to 10.3 [class.virtual] paragraph 2:

Then in any well-formed class, for each virtual function declared in that class or any of its direct or indirect base classes there is a unique final overrider that overrides that function and every other overrider of that function. The rules for member lookup (10.2 [class.member.lookup]) are used to determine the final overrider for a virtual function in the scope of a derived class but ignoring names introduced by using-declarations.

I think that description is wrong on at least a couple of counts. First, consider the following example:

    struct A { virtual void f(); };
    struct B: A { };
    struct C: A { void f(); };
    struct D: B, C { };

What is the “unique final overrider” of A::f() in D? According to 10.3 [class.virtual] paragraph 2, we determine that by looking up f in D using the lookup rules in 10.2 [class.member.lookup]. However, that lookup determines that f in D is ambiguous, so there is no “unique final overrider” of A::f() in D. Consequently, because “any well-formed class” must have such an overrider, D must be ill-formed.

Of course, we all know that D is not ill-formed. In fact, 10.3 [class.virtual] paragraph 10 contains an example that illustrates exactly this point:

struct A {
    virtual void f();
};
struct B1 : A {     // note non-virtual derivation
    void f();
};
struct B2 : A {
    void f();
};
struct D : B1, B2 { // D has two separate A subobjects
};

In class D above there are two occurrences of class A and hence two occurrences of the virtual member function A::f. The final overrider of B1::A::f is B1::f and the final overrider of B2::A::f is B2::f.

It appears that the requirement for a “unique final overrider” in 10.3 [class.virtual] paragraph 2 needs to say something about sub-objects. Whatever that “something” is, you can't just say “look up the name in the derived class using 10.2 [class.member.lookup].”

There's another problem with using the 10.2 [class.member.lookup] lookup to specify the final overrider: name lookup just looks up the name, while the overriding relationship is based not only on the name but on a matching parameter-type-list and cv-qualification. To illustrate this point:

    struct X {
        virtual void f();
    };
    struct Y: X {
        void f(int);
    };
    struct Z: Y { };

What is the “unique final overrider” of X::f() in A? Again, 10.3 [class.virtual] paragraph 2 says you're supposed to look up f in Z to find it; however, what you find is Y::f(int), not X::f(), and that's clearly wrong.

Proposed Resolution (December, 2006):

Change 10.3 [class.virtual] paragraph 2 as follows:

Then in any well-formed class, for each virtual function declared in that class or any of its direct or indirect base classes there is a unique final overrider that overrides that function and every other overrider of that function. The rules for member lookup (10.2 [class.member.lookup]) are used to determine the final overrider for a virtual function in the scope of a derived class but ignoring names introduced by using-declaration s. A virtual member function vf of a class C is a final overrider unless the most derived class (1.8 [intro.object]) of which C is a base class (if any) declares or inherits another member function that overrides vf. In a derived class, if a virtual member function of a base class subobject has more than one final overrider, the program is ill-formed.



462. Lifetime of temporaries bound to comma expressions

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

Split off from issue 86.

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

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

Notes from the March 2004 meeting:

We think the temporary should be extended.

Proposed resolution (October, 2004):

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

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

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

Notes from the April, 2005 meeting:

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

Proposed Resolution (November, 2006):

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

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



542. Value initialization of arrays of POD-structs

Section: 12.6  [class.init]     Status: review     Submitter: Alisdair Meredith     Date: 27 October 2005

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



257. Abstract base constructors and virtual base initialization

Section: 12.6.2  [class.base.init]     Status: review     Submitter: Mike Miller     Date: 1 Nov 2000

Must a constructor for an abstract base class provide a mem-initializer for each virtual base class from which it is directly or indirectly derived? Since the initialization of virtual base classes is performed by the most-derived class, and since an abstract base class can never be the most-derived class, there would seem to be no reason to require constructors for abstract base classes to initialize virtual base classes.

It is not clear from the Standard whether there actually is such a requirement or not. The relevant text is found in 12.6.2 [class.base.init] paragraph 6:

All sub-objects representing virtual base classes are initialized by the constructor of the most derived class (1.8 [intro.object]). If the constructor of the most derived class does not specify a mem-initializer for a virtual base class V, then V's default constructor is called to initialize the virtual base class subobject. If V does not have an accessible default constructor, the initialization is ill-formed. A mem-initializer naming a virtual base class shall be ignored during execution of the constructor of any class that is not the most derived class.

This paragraph requires only that the most-derived class's constructor have a mem-initializer for virtual base classes. Should the silence be construed as permission for constructors of classes that are not the most-derived to omit such mem-initializers?

Christopher Lester, on comp.std.c++, March 19, 2004: If any of you reading this posting happen to be members of the above working group, I would like to encourage you to review the suggestion contained therein, as it seems to me that the final tenor of the submission is both (a) correct (the silence of the standard DOES mandate the omission) and (b) describes what most users would intuitively expect and desire from the C++ language as well.

The suggestion is to make it clearer that constructors for abstract base classes should not be required to provide initialisers for any virtual base classes they contain (as only the most-derived class has the job of initialising virtual base classes, and an abstract base class cannot possibly be a most-derived class).

For example:

struct A {
  A(const int i, const int j) {};
};

struct B1 : virtual public A {
  virtual void moo()=0;
  B1() {};   // (1) Look! not "B1() : A(5,6) {};"
};

struct B2 : virtual public A {
  virtual void cow()=0;
  B2() {};   // (2) Look! not "B2() : A(7,8) {};"
};

struct C : public B1, public B2 {
  C() : A(2,3) {};
  void moo() {};
  void cow() {};
};

int main() {
  C c;
  return 0;
};

I believe that, by not expressly forbidding it, the standard does (and should!) allow the above code. However, as the standard doesn't expressly allow it either (have I missed something?) there appears to be room for misunderstanding. For example, g++ version 3.2.3 (and maybe other versions as well) rejects the above code with messages like:

	In constructor `B1::B1()':
	no matching function for call to `A::A()'
	candidates are: A::A(const A&)
         	        A::A(int, int)

Fair enough, the standard is perhaps not clear enough. But it seems to be a shame that although this issue was first raised in 2000, we are still living with it today.

Note that we can work-around, and persuade g++ to compile the above by either (a) providing a default constructor A() for A, or (b) supplying default values for i and j in A(i,j), or (c) replace the construtors B1() and B2() with the forms shown in the two comments in the above example.

All three of these workarounds may at times be appropriate, but equally there are other times when all of these workarounds are particularly bad. (a) and (b) may be very bad if you are trying to enforce string contracts among objects, while (c) is just barmy (I mean why did I have to invent random numbers like 5, 6, 7 and 8 just to get the code to compile?).

So to to round up, then, my plea to the working group is: "at the very least, please make the standard clearer on this issue, but preferrably make the decision to expressly allow code that looks something like the above"

Proposed resolution (March, 2008):

  1. Add the indicated text (moved from paragraph 6) to the end of 12.6.2 [class.base.init] paragraph 3:

  2. ...The initialization of each base and member constitutes a full-expression. Any expression in a mem-initializer is evaluated as part of the full-expression that performs the initialization. A mem-initializer where the mem-initializer-id names a virtual base class is ignored during execution of a constructor of any class that is not the most derived class.
  3. Change 12.6.2 [class.base.init] paragraph 4 as follows:

  4. If a given non-static data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class, then

    [Note: An abstract base class (10.4 [class.abstract]) is never a most derived class, thus its constructors never initialize virtual base classes, therefore the corresponding mem-initializers may be omitted. —end note] After the call to a constructor for class X has completed, 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.

  5. Change 12.6.2 [class.base.init] paragraph 5 as follows:

  6. Initialization shall proceeds in the following order:

    [Note: the declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. —end note]

    [Drafting note: The “shall” clauses above were rewritten to accord with the usual phrasing throughout the rest of the Standard.]

  7. Remove all the normative text in 12.6.2 [class.base.init] paragraph 6, keeping the example:

  8. All subobjects representing virtual base classes are initialized by the constructor of the most derived class (1.8 [intro.object]). If the constructor of the most derived class does not specify a mem-initializer for a virtual base class V, then V's default constructor is called to initialize the virtual base class subobject. If V does not have an accessible default constructor, the initialization is ill-formed. A mem-initializer naming a virtual base class shall be ignored during execution of the constructor of any class that is not the most derived class. [Example:...



111. Copy constructors and cv-qualifiers

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

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

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

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

Proposed Resolution (November, 2006):

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




535. Copy construction without a copy constructor

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

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

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

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

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

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

Proposed Resolution (February, 2008):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  12. A non-user-provided copy assignment operator is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type it is used (3.2 [basic.def.odr]). A program is ill-formed if the class for which a copy assignment operator is implicitly defined or explicitly defaulted has: a non-static data member of const or reference type.

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

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

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

    • a base class with an inaccessible copy assignment operator.

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

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

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

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

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

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

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

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

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

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

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

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

(This resolution also resolves issue 111.)

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

Notes from February, 2008 meeting:

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




574. Definition of “copy assignment operator”

Section: 12.8  [class.copy]     Status: review     Submitter: Steve Adamczyk     Date: 15 April 2006

Is the following a “copy assignment operator?”

    struct A {
        const A& operator=(const A&) volatile;
    };

12.8 [class.copy] paragraph 9 doesn't say one way or the other whether cv-qualifiers on the function are allowed. (A similar question applies to the const case, but I avoided that example because it seems so wrong one tends to jump to a conclusion before seeing what the standard says.)

Since the point of the definition of “copy assignment operator” is to control whether the compiler generates a default version if the user doesn’t, I suspect the correct answer is that neither const nor volatile cv-qualification on operator= should be allowed for a “copy assignment operator.” A user can write an operator= like that, but it doesn't affect whether the compiler generates the default one.

Proposed Resolution (November, 2006):

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

A user-declared copy assignment operator X::operator= is a non-static non-template non-volatile non-const member function of class X with exactly one parameter of type X, X&, const X&, volatile X& or const volatile X&.

[Drafting note: If a user-declared volatile operator= prevented the implicit declaration of the copy assignment operator, all assignments for objects of the given class (even to non-volatile objects) would pay the penalty for volatile write accesses in the user-declared operator=, despite not needing it.]




641. Overload resolution and conversion-to-same-type operators

Section: 13.3.2  [over.match.viable]     Status: review     Submitter: Nathan Sidwell     Date: 2 Aug 2007

12.3.2 [class.conv.fct] paragraph 1 says,

A conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to (possibly cv-qualified) void.

At what point is this enforced, and how is it enforced?

  1. Does such a user-declared conversion operator participate in overload resolution? Or is it never entered into the overload set?
  2. If it does participate in overload resolution, what happens if it is selected? Is the program ill-formed (and diagnostic required), or is it silently ignored? The above wording doesn't really make it clear.

Consider this test case:

    struct abc;

    struct xyz {
       xyz();

       xyz(xyz &);

       operator xyz& (); // #1
       operator abc& (); // #2
    };

    struct abc : xyz {};

    void foo(xyz &);

    void bar() {
             foo (xyz ());
    }

If such conversion functions are part of the overload set, #1 is a better conversion than #2 to convert the temporary xyz object to a non-const reference required for foo's operand. If such conversion functions are not part of the overload set, then #2 would be selected, and AFAICT the program would be well formed.

If the conversion functions are not part of the overload set, then it would seem one cannot take their address. For instance, adding the following line to the above test case would find no suitable function:

    xyz &(xyz::*ptr) () = &xyz::operator xyz &;

Notes from the October, 2007 meeting:

The intent of 12.3.2 [class.conv.fct] paragraph 1 is that overload resolution not be attempted at all for the listed cases; that is, if the target type is void, the object's type, or a base of the object's type, the conversion is done directly without considering any conversion functions. Consequently, the questions about whether the conversion function is part of the overload set or not are moot. The wording will be changed to make this clearer.

Proposed Resolution (October, 2007):

Change the footnote in 12.3.2 [class.conv.fct] paragraph 1 as follows:

A conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to (possibly cv-qualified) void. [Footnote: These conversions are considered as standard conversions for the purposes of overload resolution (13.3.3.1 [over.best.ics], 13.3.3.1.4 [over.ics.ref]) and therefore initialization (8.5 [dcl.init]) and explicit casts (5.2.9 [expr.static.cast]). A conversion to void does not invoke any conversion function (5.2.9 [expr.static.cast]). Even though never directly called to perform a conversion, such conversion functions can be declared and can potentially be reached through a call to a virtual conversion function in a base class —end footnote]

Additional note (March, 2008):

A slight change to the example above indicates that there is a need for a normative change as well as the clarification of the rationale in the October, 2007 proposed resolution. If the declaration of foo were changed to

    void foo(const xyz&);

with the current wording, the call foo(xyz()) would be interpreted as foo(xyz().operator abc&()) instead of binding the parameter directly to the rvalue, which is clearly wrong.

Proposed resolution (March, 2008):

  1. Change the footnote in 12.3.2 [class.conv.fct] paragraph 1 as described in the October, 2007 proposed resolution.

  2. Change 8.5.3 [dcl.init.ref] paragraph 5 as follows:

  3. A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

    [Drafting note: this resolution makes the example in the issue description ill-formed.]




495. Overload resolution with template and non-template conversion functions

Section: 13.3.3  [over.match.best]     Status: review     Submitter: Nathan Sidwell     Date: 20 Dec 2004

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:




603. Type equivalence and unsigned overflow

Section: 14.4  [temp.type]     Status: review     Submitter: James Widman     Date: 3 November 2006

One of the requirements for two template-ids to refer to the same class or function (14.4 [temp.type] paragraph 1) is that

If we have some template of the form

  template <unsigned char c> struct A;

does this imply that A<'\001'> and A<257> (for an eight-bit char) refer to different specializations?

Jens Maurer: Looks like it should say something like, “their corresponding converted non-type template arguments of integral or enumeration type have identical values.”

Proposed resolution (April, 2007):

The change to 14.4 [temp.type] paragraph 1 shown in document J16/07-0118 = WG21 N2258, in which the syntactic non-terminal template-argument is changed to the English term “template argument” is sufficient to remove the confusion about whether the value before or after conversion is used in matching template-ids.




588. Searching dependent bases of classes local to function templates

Section: 14.6.2  [temp.dep]     Status: review     Submitter: James Widman     Date: 21 June 2006

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



621. Template argument deduction from function return types

Section: 14.7.3  [temp.expl.spec]     Status: review     Submitter: Richard Corden     Date: 16 February 2007

It does not appear that the following example is well-formed, although most compilers accept it:

    template <typename T> T foo();
    template <> int foo();

The reason is that 14.7.3 [temp.expl.spec] paragraph 11 only allows trailing template-arguments to be omitted if they “can be deduced from the function argument type,” and there are no function arguments in this example.

14.7.3 [temp.expl.spec] should probably say “function type” instead of “function argument type.” Also, a subsection should probably be added to 14.8.2 [temp.deduct] to cover “Deducing template arguments from declarative contexts” or some such. It would be essentially the same as 14.8.2.2 [temp.deduct.funcaddr] except that the function type from the declaration would be used as the type of P.

Proposed resolution (March, 2008):

  1. Insert the following as a new subsection after 14.8.2.5 [temp.deduct.type]:

  2. 14.8.2.6 Deducing template arguments in a declaration that names a specialization of a function template [temp.deduct.funcdecl]

    Template arguments can be deduced from the function type specified when declaring a specialization of a function template. [Note: this can occur in the context of an explicit specialization, an explicit instantiation, or a friend declaration. —end note] The function template's function type and the declared type are used as the types of P and A, and the deduction is done as described in 14.8.2.5 [temp.deduct.type].

  3. Change 14.7.3 [temp.expl.spec] paragraph 11 as follows:

  4. A trailing template-argument can be left unspecified in the template-id naming an explicit function template specialization provided it can be deduced from the function argument type (14.8.2.6 [temp.deduct.funcdecl])...



488. Local types, overload resolution, and template argument deduction

Section: 14.8.2  [temp.deduct]     Status: review     Submitter: Mark Mitchell     Date: 24 Nov 2004

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:

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

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

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